mirror of
https://github.com/jekip/naive-ui-admin.git
synced 2026-02-09 07:52:27 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa8b33acbe | ||
|
|
54e68db0c2 | ||
|
|
9542345b54 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,3 +1,15 @@
|
|||||||
|
# 1.4 (2021-07-21)
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- vite降至2.3.6
|
||||||
|
- 多标签页交互优化
|
||||||
|
|
||||||
|
- ### ✨ Features
|
||||||
|
- 新增 `TableAction` 组件
|
||||||
|
- 新增 `菜单权限管理` 示例
|
||||||
|
- 新增 `角色权限管理` 示例
|
||||||
|
- 持续更新更多实用组件及示例,感谢Star
|
||||||
|
|
||||||
|
|
||||||
# 1.3 (2021-07-19)
|
# 1.3 (2021-07-19)
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
- 修复多标签页左右切换按钮自适应展示
|
- 修复多标签页左右切换按钮自适应展示
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ Naive Ui Admin 是一个免费开源的中后台模版,使用了最新的`vue3
|
|||||||
- [x] 异常页面
|
- [x] 异常页面
|
||||||
- [x] 结果页面
|
- [x] 结果页面
|
||||||
- [x] 设置页面
|
- [x] 设置页面
|
||||||
|
- [x] 系统设置
|
||||||
|
- [x] 菜单权限
|
||||||
|
- [x] 角色权限
|
||||||
|
|
||||||
### 页面组件
|
### 页面组件
|
||||||
#### ProTable
|
#### ProTable
|
||||||
|
|||||||
93
mock/system/menu.ts
Normal file
93
mock/system/menu.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { resultSuccess } from '../_util'
|
||||||
|
|
||||||
|
const menuList = (() => {
|
||||||
|
const result: any[] = [
|
||||||
|
{
|
||||||
|
label: 'Dashboard',
|
||||||
|
key: 'dashboard',
|
||||||
|
type: 1,
|
||||||
|
subtitle:'dashboard',
|
||||||
|
openType:1,
|
||||||
|
auth:'dashboard',
|
||||||
|
path:'/dashboard',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '主控台',
|
||||||
|
key: 'console',
|
||||||
|
type: 1,
|
||||||
|
subtitle:'console',
|
||||||
|
openType:1,
|
||||||
|
auth:'console',
|
||||||
|
path:'/dashboard/console',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '工作台',
|
||||||
|
key: 'workplace',
|
||||||
|
type: 1,
|
||||||
|
subtitle:'workplace',
|
||||||
|
openType:1,
|
||||||
|
auth:'workplace',
|
||||||
|
path:'/dashboard/workplace',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单管理',
|
||||||
|
key: 'form',
|
||||||
|
type: 1,
|
||||||
|
subtitle:'form',
|
||||||
|
openType:1,
|
||||||
|
auth:'form',
|
||||||
|
path:'/form',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '基础表单',
|
||||||
|
key: 'basic-form',
|
||||||
|
type: 1,
|
||||||
|
subtitle:'basic-form',
|
||||||
|
openType:1,
|
||||||
|
auth:'basic-form',
|
||||||
|
path:'/form/basic-form',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '分步表单',
|
||||||
|
key: 'step-form',
|
||||||
|
type: 1,
|
||||||
|
subtitle:'step-form',
|
||||||
|
openType:1,
|
||||||
|
auth:'step-form',
|
||||||
|
path:'/form/step-form',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单详情',
|
||||||
|
key: 'detail',
|
||||||
|
type: 1,
|
||||||
|
subtitle:'detail',
|
||||||
|
openType:1,
|
||||||
|
auth:'detail',
|
||||||
|
path:'/form/detail',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return result
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
url: '/api/menu/list',
|
||||||
|
timeout: 1000,
|
||||||
|
method: 'get',
|
||||||
|
response: () => {
|
||||||
|
const list = menuList()
|
||||||
|
return resultSuccess({
|
||||||
|
list
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
50
mock/system/role.ts
Normal file
50
mock/system/role.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { resultSuccess, doCustomTimes } from '../_util'
|
||||||
|
|
||||||
|
|
||||||
|
function getMenuKeys() {
|
||||||
|
let keys = ['dashboard', 'console', 'workplace', 'basic-form', 'step-form', 'detail']
|
||||||
|
let newKeys = []
|
||||||
|
doCustomTimes(parseInt(Math.random()*6), () => {
|
||||||
|
let key = keys[Math.floor(Math.random() * keys.length)];
|
||||||
|
newKeys.push(key)
|
||||||
|
})
|
||||||
|
return Array.from(new Set(newKeys));
|
||||||
|
}
|
||||||
|
|
||||||
|
const roleList = ((pageSize) => {
|
||||||
|
const result: any[] = []
|
||||||
|
doCustomTimes(pageSize, () => {
|
||||||
|
result.push({
|
||||||
|
id: '@integer(10,100)',
|
||||||
|
name: '@cname()',
|
||||||
|
explain: '@cname()',
|
||||||
|
isDefault: '@boolean()',
|
||||||
|
menu_keys: getMenuKeys(),
|
||||||
|
create_date: `@date('yyyy-MM-dd hh:mm:ss')`,
|
||||||
|
'status|1': ['normal', 'enable', 'disable'],
|
||||||
|
});
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
url: '/api/role/list',
|
||||||
|
timeout: 1000,
|
||||||
|
method: 'get',
|
||||||
|
response: ({ query }) => {
|
||||||
|
const { page = 1, pageSize = 10 } = query;
|
||||||
|
const list = roleList(Number(pageSize))
|
||||||
|
return resultSuccess({
|
||||||
|
page: Number(page),
|
||||||
|
pageSize: Number(pageSize),
|
||||||
|
pageCount: 60,
|
||||||
|
list
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +25,14 @@ const adminInfo = {
|
|||||||
{
|
{
|
||||||
roleName: '工作台',
|
roleName: '工作台',
|
||||||
value: 'dashboard_workplace',
|
value: 'dashboard_workplace',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
roleName: '基础列表',
|
||||||
|
value: 'basic_list',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
roleName: '基础列表删除',
|
||||||
|
value: 'basic_list_delete',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "naive-ui-admin",
|
"name": "naive-ui-admin",
|
||||||
"version": "1.2",
|
"version": "1.4",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Ahjung",
|
"name": "Ahjung",
|
||||||
"email": "735878602@qq.com",
|
"email": "735878602@qq.com",
|
||||||
@@ -80,9 +80,9 @@
|
|||||||
"stylelint-scss": "^3.19.0",
|
"stylelint-scss": "^3.19.0",
|
||||||
"tailwindcss": "^2.2.4",
|
"tailwindcss": "^2.2.4",
|
||||||
"typescript": "^4.3.2",
|
"typescript": "^4.3.2",
|
||||||
"vite": "^2.4.2",
|
"vite": "2.3.6",
|
||||||
"vite-plugin-html": "^2.0.7",
|
"vite-plugin-html": "^2.0.7",
|
||||||
"vite-plugin-mock": "^2.9.1",
|
"vite-plugin-mock": "^2.9.3",
|
||||||
"vite-plugin-style-import": "^1.0.1",
|
"vite-plugin-style-import": "^1.0.1",
|
||||||
"vue-eslint-parser": "^7.8.0"
|
"vue-eslint-parser": "^7.8.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,32 +1,23 @@
|
|||||||
import http from '@/utils/http/axios'
|
import http from '@/utils/http/axios'
|
||||||
import {
|
|
||||||
GetByUserIdParams,
|
|
||||||
GetMenuListByUserIdResult,
|
|
||||||
GetAuthCodeByUserIdResult
|
|
||||||
} from './model/menuModel'
|
|
||||||
|
|
||||||
enum Api {
|
|
||||||
adminMenus = '/menus',
|
|
||||||
GetBtnCodeListByUserId = '/getBtnCodeListByUserId'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 根据用户id获取用户菜单
|
* @description: 根据用户id获取用户菜单
|
||||||
*/
|
*/
|
||||||
export function adminMenus() {
|
export function adminMenus() {
|
||||||
return http.request<GetMenuListByUserIdResult>({
|
return http.request({
|
||||||
url: Api.adminMenus,
|
url: '/menus',
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据用户Id获取权限编码
|
* 获取tree菜单列表
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export function getBtnCodeListByUserId(params: GetByUserIdParams) {
|
export function getMenuList(params) {
|
||||||
return http.request<GetAuthCodeByUserIdResult>({
|
return http.request({
|
||||||
url: Api.GetBtnCodeListByUserId,
|
url: '/menu/list',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
|||||||
11
src/api/system/role.ts
Normal file
11
src/api/system/role.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import http from '@/utils/http/axios'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 角色列表
|
||||||
|
*/
|
||||||
|
export function getRoleList() {
|
||||||
|
return http.request({
|
||||||
|
url: '/role/list',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -43,7 +43,11 @@ const columns = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '地址',
|
title: '地址',
|
||||||
key: 'address'
|
key: 'address',
|
||||||
|
auth: ['amdin'], // 同时根据权限控制是否显示
|
||||||
|
ifShow: (row) => {
|
||||||
|
return true; // 根据业务控制是否显示
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '日期',
|
title: '日期',
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
export { default as BasicTable } from './src/Table.vue';
|
export { default as BasicTable } from './src/Table.vue';
|
||||||
|
export { default as TableAction } from './src/components/TableAction.vue';
|
||||||
|
export * from './src/types/table';
|
||||||
|
export * from './src/types/tableAction';
|
||||||
|
|
||||||
|
|||||||
145
src/components/Table/src/components/TableAction.vue
Normal file
145
src/components/Table/src/components/TableAction.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tableAction">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
|
||||||
|
<n-button v-bind="action" class="mx-2">{{ action.label }}</n-button>
|
||||||
|
</template>
|
||||||
|
<n-dropdown
|
||||||
|
v-if="dropDownActions && getDropdownList.length"
|
||||||
|
trigger="hover"
|
||||||
|
:options="getDropdownList"
|
||||||
|
@select="select"
|
||||||
|
>
|
||||||
|
<slot name="more"></slot>
|
||||||
|
<n-button v-bind="getMoreProps" class="mx-2" v-if="!$slots.more" icon-placement="right">
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span>更多</span>
|
||||||
|
<n-icon size="14" class="ml-1">
|
||||||
|
<DownOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
<!-- <template #icon>-->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </template>-->
|
||||||
|
</n-button>
|
||||||
|
</n-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType, computed, toRaw } from 'vue';
|
||||||
|
import { ActionItem } from '@/components/Table';
|
||||||
|
import { usePermission } from '@/hooks/web/usePermission';
|
||||||
|
import { isString, isBoolean, isFunction } from "@/utils/is";
|
||||||
|
import { DownOutlined } from '@vicons/antd'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'TableAction',
|
||||||
|
components: { DownOutlined },
|
||||||
|
props: {
|
||||||
|
actions: {
|
||||||
|
type: Array as PropType<ActionItem[]>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
dropDownActions: {
|
||||||
|
type: Array as PropType<ActionItem[]>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
type: String as PropType<String>,
|
||||||
|
default: 'button'
|
||||||
|
},
|
||||||
|
select:{
|
||||||
|
type: Function as PropType<Function>,
|
||||||
|
default: () =>{ }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
|
||||||
|
const getTooltip = computed(() => {
|
||||||
|
return (data: string | TooltipProps): TooltipProps => {
|
||||||
|
if (isString(data)) {
|
||||||
|
return { title: data, placement: 'bottom' };
|
||||||
|
} else {
|
||||||
|
return Object.assign({ placement: 'bottom' }, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const actionType = props.style === 'button' ? 'default' : props.style === 'text' ? 'primary' : 'default'
|
||||||
|
const actionText = props.style === 'button' ? undefined : props.style === 'text' ? true : undefined
|
||||||
|
|
||||||
|
const getMoreProps = computed(() => {
|
||||||
|
return {
|
||||||
|
text: actionText,
|
||||||
|
type: actionType,
|
||||||
|
size: "small"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getDropdownList = computed(() => {
|
||||||
|
return (toRaw(props.dropDownActions) || [])
|
||||||
|
.filter((action) => {
|
||||||
|
return hasPermission(action.auth) && isIfShow(action);
|
||||||
|
})
|
||||||
|
.map((action, index) => {
|
||||||
|
const { label, popConfirm } = action;
|
||||||
|
return {
|
||||||
|
size: 'small',
|
||||||
|
text: actionText,
|
||||||
|
type: actionType,
|
||||||
|
...action,
|
||||||
|
...popConfirm,
|
||||||
|
onConfirm: popConfirm?.confirm,
|
||||||
|
onCancel: popConfirm?.cancel
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function isIfShow(action: ActionItem): boolean {
|
||||||
|
const ifShow = action.ifShow;
|
||||||
|
|
||||||
|
let isIfShow = true;
|
||||||
|
|
||||||
|
if (isBoolean(ifShow)) {
|
||||||
|
isIfShow = ifShow;
|
||||||
|
}
|
||||||
|
if (isFunction(ifShow)) {
|
||||||
|
isIfShow = ifShow(action);
|
||||||
|
}
|
||||||
|
return isIfShow;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getActions = computed(() => {
|
||||||
|
return (toRaw(props.actions) || [])
|
||||||
|
.filter((action) => {
|
||||||
|
return hasPermission(action.auth) && isIfShow(action);
|
||||||
|
})
|
||||||
|
.map((action) => {
|
||||||
|
const { popConfirm } = action;
|
||||||
|
//需要展示什么风格,自己修改一下参数
|
||||||
|
return {
|
||||||
|
size: 'small',
|
||||||
|
text: actionText,
|
||||||
|
type: actionType,
|
||||||
|
...action,
|
||||||
|
...(popConfirm || {}),
|
||||||
|
onConfirm: popConfirm?.confirm,
|
||||||
|
onCancel: popConfirm?.cancel,
|
||||||
|
enable: !!popConfirm,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
getActions,
|
||||||
|
getDropdownList,
|
||||||
|
getTooltip,
|
||||||
|
getMoreProps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -2,6 +2,9 @@ import { ref, Ref, ComputedRef, unref, computed, watch, toRaw } from 'vue';
|
|||||||
import type { BasicColumn, BasicTableProps } from '../types/table';
|
import type { BasicColumn, BasicTableProps } from '../types/table';
|
||||||
import { isEqual, cloneDeep } from 'lodash-es';
|
import { isEqual, cloneDeep } from 'lodash-es';
|
||||||
import { isArray, isString } from '@/utils/is';
|
import { isArray, isString } from '@/utils/is';
|
||||||
|
import { usePermission } from '@/hooks/web/usePermission';
|
||||||
|
import { isString, isBoolean, isFunction } from "@/utils/is";
|
||||||
|
import { ActionItem } from "@/components/Table";
|
||||||
|
|
||||||
export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||||
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
||||||
@@ -9,13 +12,34 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
|||||||
|
|
||||||
const getColumnsRef = computed(() => {
|
const getColumnsRef = computed(() => {
|
||||||
const columns = cloneDeep(unref(columnsRef));
|
const columns = cloneDeep(unref(columnsRef));
|
||||||
|
|
||||||
|
handleActionColumn(propsRef, columns);
|
||||||
|
if (!columns) return [];
|
||||||
return columns;
|
return columns;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
|
||||||
|
function isIfShow(action: ActionItem): boolean {
|
||||||
|
const ifShow = action.ifShow;
|
||||||
|
|
||||||
|
let isIfShow = true;
|
||||||
|
|
||||||
|
if (isBoolean(ifShow)) {
|
||||||
|
isIfShow = ifShow;
|
||||||
|
}
|
||||||
|
if (isFunction(ifShow)) {
|
||||||
|
isIfShow = ifShow(action);
|
||||||
|
}
|
||||||
|
return isIfShow;
|
||||||
|
}
|
||||||
|
|
||||||
const getPageColumns = computed(() => {
|
const getPageColumns = computed(() => {
|
||||||
const pageColumns = unref(getColumnsRef);
|
const pageColumns = unref(getColumnsRef);
|
||||||
const columns = cloneDeep(pageColumns);
|
const columns = cloneDeep(pageColumns);
|
||||||
return columns
|
return columns.filter((column) => {
|
||||||
|
return hasPermission(column.auth) && isIfShow(column);
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -26,6 +50,13 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
|
||||||
|
const { actionColumn } = unref(propsRef);
|
||||||
|
if (!actionColumn) return;
|
||||||
|
columns.push({
|
||||||
|
...actionColumn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//设置
|
//设置
|
||||||
function setColumns(columnList: string[]) {
|
function setColumns(columnList: string[]) {
|
||||||
|
|||||||
@@ -41,5 +41,9 @@ export const basicProps = {
|
|||||||
showPagination: {
|
showPagination: {
|
||||||
type: [String, Boolean],
|
type: [String, Boolean],
|
||||||
default: 'auto'
|
default: 'auto'
|
||||||
}
|
},
|
||||||
|
actionColumn: {
|
||||||
|
type: Object as PropType<BasicColumn>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/components/Table/src/types/tableAction.ts
Normal file
26
src/components/Table/src/types/tableAction.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { NButton, NTooltip } from 'naive-ui';
|
||||||
|
import { RoleEnum } from '/@/enums/roleEnum';
|
||||||
|
|
||||||
|
export interface ActionItem extends NButton.props {
|
||||||
|
onClick?: Fn;
|
||||||
|
label?: string;
|
||||||
|
color?: 'success' | 'error' | 'warning';
|
||||||
|
icon?: string;
|
||||||
|
popConfirm?: PopConfirm;
|
||||||
|
disabled?: boolean;
|
||||||
|
divider?: boolean;
|
||||||
|
// 权限编码控制是否显示
|
||||||
|
auth?: RoleEnum | RoleEnum[] | string | string[];
|
||||||
|
// 业务控制是否显示
|
||||||
|
ifShow?: boolean | ((action: ActionItem) => boolean);
|
||||||
|
tooltip?: string | TooltipProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PopConfirm {
|
||||||
|
title: string;
|
||||||
|
okText?: string;
|
||||||
|
cancelText?: string;
|
||||||
|
confirm: Fn;
|
||||||
|
cancel?: Fn;
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ export enum PageEnum {
|
|||||||
REDIRECT_NAME = 'Redirect',
|
REDIRECT_NAME = 'Redirect',
|
||||||
// 首页
|
// 首页
|
||||||
BASE_HOME = '/dashboard',
|
BASE_HOME = '/dashboard',
|
||||||
|
//首页跳转默认路由
|
||||||
|
BASE_HOME_REDIRECT = '/dashboard/console',
|
||||||
// 错误
|
// 错误
|
||||||
ERROR_PAGE_NAME = 'ErrorPage',
|
ERROR_PAGE_NAME = 'ErrorPage',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function usePermission() {
|
|||||||
* 可用于 v-if 显示逻辑
|
* 可用于 v-if 显示逻辑
|
||||||
* */
|
* */
|
||||||
function hasPermission(accesses: string[]): boolean {
|
function hasPermission(accesses: string[]): boolean {
|
||||||
if (!accesses.length) return true
|
if (!accesses ||!accesses.length) return true
|
||||||
return _someRoles(accesses)
|
return _someRoles(accesses)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="copyright">
|
<div class="copyright">
|
||||||
naive-ui-admin 1.3 · Made by Ah jung
|
naive-ui-admin 1.4 · Made by Ah jung
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -20,15 +20,15 @@
|
|||||||
</span>
|
</span>
|
||||||
<div ref="navScroll" class="tabs-card-scroll">
|
<div ref="navScroll" class="tabs-card-scroll">
|
||||||
<div ref="navRef" class="tabs-card-nav" :style="getNavStyle">
|
<div ref="navRef" class="tabs-card-nav" :style="getNavStyle">
|
||||||
<Draggable :list="tabsList" animation="300" item-key="fullPath">
|
<Draggable :list="tabsList" animation="300" item-key="fullPath" class="flex">
|
||||||
<template #item="{element}">
|
<template #item="{element}">
|
||||||
<div class="tabs-card-scroll-item"
|
<div class="tabs-card-scroll-item"
|
||||||
:class="{'active-item':activeKey === element.path }"
|
:class="{'active-item':activeKey === element.path }"
|
||||||
@click.stop="goPage(element)"
|
@click.stop="goPage(element)"
|
||||||
@contextmenu="handleContextMenu">
|
@contextmenu="handleContextMenu($event,element)">
|
||||||
<span>{{ element.meta.title }}</span>
|
<span>{{ element.meta.title }}</span>
|
||||||
<n-icon size="14" @click.stop="closeTabItem(element)">
|
<n-icon size="14" @click.stop="closeTabItem(element)" v-if="element.path != baseHome">
|
||||||
<CloseOutlined/>
|
<CloseOutlined />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -73,6 +73,7 @@ import { useAsyncRouteStore } from '@/store/modules/asyncRoute'
|
|||||||
import { RouteItem } from '@/store/modules/tabsView'
|
import { RouteItem } from '@/store/modules/tabsView'
|
||||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting'
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting'
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
|
// @ts-ignore
|
||||||
import Draggable from 'vuedraggable/src/vuedraggable'
|
import Draggable from 'vuedraggable/src/vuedraggable'
|
||||||
import { PageEnum } from '@/enums/pageEnum'
|
import { PageEnum } from '@/enums/pageEnum'
|
||||||
import {
|
import {
|
||||||
@@ -86,7 +87,6 @@ import {
|
|||||||
} from '@vicons/antd'
|
} from '@vicons/antd'
|
||||||
import { renderIcon } from '@/utils/index'
|
import { renderIcon } from '@/utils/index'
|
||||||
import elementResizeDetectorMaker from 'element-resize-detector'
|
import elementResizeDetectorMaker from 'element-resize-detector'
|
||||||
import { useProjectSettingStore } from "@/store/modules/projectSetting";
|
|
||||||
import { useDesignSetting } from '@/hooks/setting/useDesignSetting'
|
import { useDesignSetting } from '@/hooks/setting/useDesignSetting'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -118,6 +118,7 @@ export default defineComponent({
|
|||||||
const navRef: any = ref(null)
|
const navRef: any = ref(null)
|
||||||
const navScroll: any = ref(null)
|
const navScroll: any = ref(null)
|
||||||
const navWrap: any = ref(null)
|
const navWrap: any = ref(null)
|
||||||
|
const isCurrent = ref(false)
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
activeKey: route.fullPath,
|
activeKey: route.fullPath,
|
||||||
@@ -152,28 +153,34 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
//tags 右侧下拉菜单
|
//tags 右侧下拉菜单
|
||||||
const TabsMenuOptions = [
|
const TabsMenuOptions = computed(() => {
|
||||||
{
|
const isDisabled = unref(tabsList).length <= 1 ? true : false
|
||||||
label: '刷新当前',
|
return [
|
||||||
key: '1',
|
{
|
||||||
icon: renderIcon(ReloadOutlined)
|
label: '刷新当前',
|
||||||
},
|
key: '1',
|
||||||
{
|
icon: renderIcon(ReloadOutlined)
|
||||||
label: '关闭当前',
|
},
|
||||||
key: '2',
|
{
|
||||||
icon: renderIcon(CloseOutlined)
|
label: `关闭当前`,
|
||||||
},
|
key: '2',
|
||||||
{
|
disabled: unref(isCurrent) || isDisabled,
|
||||||
label: '关闭其他',
|
icon: renderIcon(CloseOutlined)
|
||||||
key: '3',
|
},
|
||||||
icon: renderIcon(ColumnWidthOutlined)
|
{
|
||||||
},
|
label: '关闭其他',
|
||||||
{
|
key: '3',
|
||||||
label: '关闭全部',
|
disabled:isDisabled,
|
||||||
key: '4',
|
icon: renderIcon(ColumnWidthOutlined)
|
||||||
icon: renderIcon(MinusOutlined)
|
},
|
||||||
}
|
{
|
||||||
]
|
label: '关闭全部',
|
||||||
|
key: '4',
|
||||||
|
disabled:isDisabled,
|
||||||
|
icon: renderIcon(MinusOutlined)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
let routes: RouteItem[] = []
|
let routes: RouteItem[] = []
|
||||||
|
|
||||||
@@ -288,7 +295,7 @@ export default defineComponent({
|
|||||||
const closeAll = () => {
|
const closeAll = () => {
|
||||||
localStorage.removeItem('routes')
|
localStorage.removeItem('routes')
|
||||||
tabsViewStore.closeAllTabs()
|
tabsViewStore.closeAllTabs()
|
||||||
router.replace('/')
|
router.replace(PageEnum.BASE_HOME)
|
||||||
updateNavScroll()
|
updateNavScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,8 +380,9 @@ export default defineComponent({
|
|||||||
return state.navStyle
|
return state.navStyle
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleContextMenu(e) {
|
function handleContextMenu(e,item) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
isCurrent.value = PageEnum.BASE_HOME_REDIRECT === item.path
|
||||||
state.showDropdown = false
|
state.showDropdown = false
|
||||||
nextTick().then(() => {
|
nextTick().then(() => {
|
||||||
state.showDropdown = true
|
state.showDropdown = true
|
||||||
@@ -419,6 +427,7 @@ export default defineComponent({
|
|||||||
navScroll,
|
navScroll,
|
||||||
route,
|
route,
|
||||||
tabsList,
|
tabsList,
|
||||||
|
baseHome:PageEnum.BASE_HOME_REDIRECT,
|
||||||
goPage,
|
goPage,
|
||||||
closeTabItem,
|
closeTabItem,
|
||||||
closeLeft,
|
closeLeft,
|
||||||
|
|||||||
@@ -60,7 +60,9 @@ import {
|
|||||||
NInputNumber,
|
NInputNumber,
|
||||||
NLoadingBarProvider,
|
NLoadingBarProvider,
|
||||||
NModal,
|
NModal,
|
||||||
NUpload
|
NUpload,
|
||||||
|
NTree,
|
||||||
|
NSpin
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
|
|
||||||
const naive = create({
|
const naive = create({
|
||||||
@@ -124,7 +126,9 @@ const naive = create({
|
|||||||
NInputNumber,
|
NInputNumber,
|
||||||
NLoadingBarProvider,
|
NLoadingBarProvider,
|
||||||
NModal,
|
NModal,
|
||||||
NUpload
|
NUpload,
|
||||||
|
NTree,
|
||||||
|
NSpin
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export const asyncRoutes = [ErrorPageRoute, ...routeModuleList];
|
|||||||
|
|
||||||
|
|
||||||
//普通路由 无需验证权限
|
//普通路由 无需验证权限
|
||||||
export const constantRouter = [LoginRoute, RootRoute, RedirectRoute]
|
export const constantRouter:any[] = [LoginRoute, RootRoute, RedirectRoute]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(''),
|
history: createWebHashHistory(''),
|
||||||
|
|||||||
@@ -33,6 +33,15 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
title: '基础列表',
|
title: '基础列表',
|
||||||
},
|
},
|
||||||
component: () => import('@/views/list/basicList/index.vue')
|
component: () => import('@/views/list/basicList/index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'basic-info/:id?',
|
||||||
|
name: 'basic-info',
|
||||||
|
meta: {
|
||||||
|
title: '基础详情',
|
||||||
|
hidden:true
|
||||||
|
},
|
||||||
|
component: () => import('@/views/list/basicList/info.vue')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
50
src/router/modules/system.ts
Normal file
50
src/router/modules/system.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { RouteRecordRaw } from 'vue-router'
|
||||||
|
import { Layout } from '@/router/constant';
|
||||||
|
import { ToolOutlined } from '@vicons/antd'
|
||||||
|
import { OptionsSharp } from '@vicons/ionicons5'
|
||||||
|
import { renderIcon } from '@/utils/index'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name 路由名称, 必须设置,且不能重名
|
||||||
|
* @param meta 路由元信息(路由附带扩展信息)
|
||||||
|
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||||
|
* @param meta.disabled 禁用整个菜单
|
||||||
|
* @param meta.title 菜单名称
|
||||||
|
* @param meta.icon 菜单图标
|
||||||
|
* @param meta.keepAlive 缓存该路由
|
||||||
|
* @param meta.sort 排序越小越排前
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
const routes: Array<RouteRecordRaw> = [
|
||||||
|
{
|
||||||
|
path: '/system',
|
||||||
|
name: 'System',
|
||||||
|
redirect: '/system/menu',
|
||||||
|
component: Layout,
|
||||||
|
meta: {
|
||||||
|
title: '系统设置',
|
||||||
|
icon: renderIcon(OptionsSharp),
|
||||||
|
sort: 1
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'menu',
|
||||||
|
name: 'system_menu',
|
||||||
|
meta: {
|
||||||
|
title: '菜单权限管理',
|
||||||
|
},
|
||||||
|
component: () => import('@/views/system/menu/menu.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'role',
|
||||||
|
name: 'system_role',
|
||||||
|
meta: {
|
||||||
|
title: '角色权限管理',
|
||||||
|
},
|
||||||
|
component: () => import('@/views/system/role/role.vue')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default routes
|
||||||
@@ -42,3 +42,34 @@ export const withInstall = <T>(component: T, alias?: string) => {
|
|||||||
};
|
};
|
||||||
return component as T & Plugin;
|
return component as T & Plugin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到对应的节点
|
||||||
|
* */
|
||||||
|
let result = null
|
||||||
|
export function getTreeItem(data: any[], key?: string | number) {
|
||||||
|
data.map(item => {
|
||||||
|
if (item.key === key) {
|
||||||
|
result = item;
|
||||||
|
} else {
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
getTreeItem(item.children, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到所有节点
|
||||||
|
* */
|
||||||
|
let treeAll = []
|
||||||
|
export function getTreeAll(data: any[]) {
|
||||||
|
data.map(item => {
|
||||||
|
treeAll.push(item.key)
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
getTreeAll(item.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return treeAll
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。表单域标签也可支持响应式。
|
表单页用于向用户收集或验证信息,基础表单常见于数据项较少的表单场景。表单域标签也可支持响应式。
|
||||||
</n-card>
|
</n-card>
|
||||||
</div>
|
</div>
|
||||||
<n-card :bordered="false" class="proCard mt-4">
|
<n-card :bordered="false" class="mt-4 proCard">
|
||||||
<n-grid cols="2 s:1 m:3 l:3 xl:3 2xl:3" responsive="screen">
|
<n-grid cols="2 s:1 m:3 l:3 xl:3 2xl:3" responsive="screen">
|
||||||
<n-grid-item offset="0 s:0 m:1 l:1 xl:1 2xl:1">
|
<n-grid-item offset="0 s:0 m:1 l:1 xl:1 2xl:1">
|
||||||
<n-form
|
<n-form
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
ref="formRef"
|
ref="formRef"
|
||||||
class="py-8"
|
class="py-8"
|
||||||
>
|
>
|
||||||
<n-form-item label="预约姓名" path="name">
|
<n-form-item label="预约姓名1" path="name">
|
||||||
<n-input v-model:value="formValue.name" placeholder="输入姓名"/>
|
<n-input v-model:value="formValue.name" placeholder="输入姓名"/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="预约号码" path="mobile">
|
<n-form-item label="预约号码" path="mobile">
|
||||||
@@ -155,16 +155,18 @@ export default defineComponent({
|
|||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const { uploadUrl } = globSetting
|
const { uploadUrl } = globSetting
|
||||||
|
|
||||||
|
const defaultValueRef = () => ({
|
||||||
|
name: '',
|
||||||
|
mobile: '',
|
||||||
|
remark: '',
|
||||||
|
sex: 1,
|
||||||
|
matter: null,
|
||||||
|
doctor: null,
|
||||||
|
datetime: [],
|
||||||
|
})
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
formValue: {
|
formValue: defaultValueRef(),
|
||||||
name: '',
|
|
||||||
mobile: '',
|
|
||||||
remark: '',
|
|
||||||
sex: 1,
|
|
||||||
matter: null,
|
|
||||||
doctor: null,
|
|
||||||
datetime: [],
|
|
||||||
},
|
|
||||||
//图片列表 通常查看和编辑使用 绝对路径 | 相对路径都可以
|
//图片列表 通常查看和编辑使用 绝对路径 | 相对路径都可以
|
||||||
uploadList: [
|
uploadList: [
|
||||||
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||||
@@ -188,6 +190,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
formRef.value.restoreValidation()
|
formRef.value.restoreValidation()
|
||||||
|
state.formValue = Object.assign(state.formValue, defaultValueRef())
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadChange(list: string[]) {
|
function uploadChange(list: string[]) {
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ export const columns = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '地址',
|
title: '地址',
|
||||||
key: 'address'
|
key: 'address',
|
||||||
|
auth: ['basic_list'], // 同时根据权限控制是否显示
|
||||||
|
ifShow: (_column) => {
|
||||||
|
return true; // 根据业务控制是否显示
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '开始日期',
|
title: '开始日期',
|
||||||
@@ -39,34 +43,35 @@ export const columns = [
|
|||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
key: 'date',
|
key: 'date',
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: '操作',
|
// title: '操作',
|
||||||
key: 'actions',
|
// key: 'actions',
|
||||||
width: 150,
|
// width: 150,
|
||||||
//简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
// //简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
||||||
render() {
|
// render() {
|
||||||
return [
|
// return [
|
||||||
h(
|
// h(
|
||||||
NButton,
|
// NButton,
|
||||||
{
|
// {
|
||||||
size: 'small',
|
// size: 'small',
|
||||||
type: 'error',
|
// type: 'error',
|
||||||
style: 'margin-right:10px',
|
// style: 'margin-right:10px',
|
||||||
onClick: () => {
|
// onClick: () => {
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
{ default: () => '删除' }
|
// { default: () => '删除' }
|
||||||
),
|
// ),
|
||||||
h(
|
// h(
|
||||||
NButton,
|
// NButton,
|
||||||
{
|
// {
|
||||||
size: 'small',
|
// size: 'small',
|
||||||
onClick: () => {
|
// onClick: () => {
|
||||||
}
|
//
|
||||||
},
|
// }
|
||||||
{ default: () => '编辑' }
|
// },
|
||||||
)
|
// { default: () => '编辑' }
|
||||||
]
|
// )
|
||||||
}
|
// ]
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
:request="loadDataTable"
|
:request="loadDataTable"
|
||||||
:row-key="row => row.id"
|
:row-key="row => row.id"
|
||||||
ref="actionRef"
|
ref="actionRef"
|
||||||
|
:actionColumn="actionColumn"
|
||||||
@update:checked-row-keys="onCheckedRow"
|
@update:checked-row-keys="onCheckedRow"
|
||||||
>
|
>
|
||||||
<template #tableTitle>
|
<template #tableTitle>
|
||||||
@@ -17,9 +18,14 @@
|
|||||||
新建
|
新建
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction></TableAction>
|
||||||
|
</template>
|
||||||
</BasicTable>
|
</BasicTable>
|
||||||
|
|
||||||
<n-modal v-model:show="showModal" :show-icon="false" preset="dialog" title="新建">
|
<n-modal v-model:show="showModal" :show-icon="false" preset="dialog" title="新建">
|
||||||
@@ -57,10 +63,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, toRefs, ref, h } from 'vue'
|
import { defineComponent, reactive, toRefs, ref, h } from 'vue'
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
import { BasicTable } from '@/components/Table'
|
import { BasicTable, TableAction } from '@/components/Table'
|
||||||
import { getTableList } from '@/api/table/list'
|
import { getTableList } from '@/api/table/list'
|
||||||
import { columns } from './columns'
|
import { columns } from './columns'
|
||||||
import { PlusOutlined } from '@vicons/antd'
|
import { PlusOutlined } from '@vicons/antd'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
name: {
|
name: {
|
||||||
@@ -82,8 +89,9 @@ const rules = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { BasicTable, PlusOutlined },
|
components: { BasicTable, PlusOutlined, TableAction },
|
||||||
setup() {
|
setup() {
|
||||||
|
const router = useRouter()
|
||||||
const formRef: any = ref(null)
|
const formRef: any = ref(null)
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const actionRef = ref()
|
const actionRef = ref()
|
||||||
@@ -99,6 +107,62 @@ export default defineComponent({
|
|||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
name: 'xiaoMa'
|
name: 'xiaoMa'
|
||||||
},
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 250,
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
render(record) {
|
||||||
|
return h(
|
||||||
|
TableAction,
|
||||||
|
{
|
||||||
|
style: 'button',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
icon: 'ic:outline-delete-outline',
|
||||||
|
onClick: handleDelete.bind(null, record),
|
||||||
|
// 根据业务控制是否显示 isShow 和 auth 是并且关系
|
||||||
|
ifShow: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 根据权限控制是否显示: 有权限,会显示,支持多个
|
||||||
|
auth: ['basic_list'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
ifShow: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
auth: ['basic_list'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dropDownActions: [
|
||||||
|
{
|
||||||
|
label: '启用',
|
||||||
|
key: 'enabled',
|
||||||
|
// 根据业务控制是否显示: 非enable状态的不显示启用按钮
|
||||||
|
ifShow: (record) => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '禁用',
|
||||||
|
key: 'disabled',
|
||||||
|
ifShow: () => {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
select:(key) => {
|
||||||
|
message.info(`您点击了,${key} 按钮`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
function addTable() {
|
function addTable() {
|
||||||
@@ -135,6 +199,21 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleEdit(record: Recordable) {
|
||||||
|
console.log('点击了编辑', record);
|
||||||
|
router.push({ name: 'basic-info', params: { id: record.id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(record: Recordable) {
|
||||||
|
console.log('点击了删除', record);
|
||||||
|
message.info('点击了删除')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(record: Recordable) {
|
||||||
|
console.log('点击了启用', record);
|
||||||
|
message.info('点击了删除')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
formRef,
|
formRef,
|
||||||
@@ -145,7 +224,10 @@ export default defineComponent({
|
|||||||
loadDataTable,
|
loadDataTable,
|
||||||
onCheckedRow,
|
onCheckedRow,
|
||||||
reloadTable,
|
reloadTable,
|
||||||
addTable
|
addTable,
|
||||||
|
handleEdit,
|
||||||
|
handleDelete,
|
||||||
|
handleOpen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
38
src/views/list/basicList/info.vue
Normal file
38
src/views/list/basicList/info.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="n-layout-page-header">
|
||||||
|
<n-card :bordered="false" title="基础详情">
|
||||||
|
基础详情,有时也用于显示只读信息。
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
<n-card :bordered="false" class="proCard mt-4" size="small" :segmented="{content: 'hard'}">
|
||||||
|
<n-descriptions label-placement="left" class="py-2">
|
||||||
|
<n-descriptions-item>
|
||||||
|
<template #label>收款人姓名</template>
|
||||||
|
啊俊
|
||||||
|
</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="收款账户">NaiveUiAdmin@qq.com</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="付款类型">支付宝</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="付款账户">NaiveUiAdmin@163.com</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="转账金额">¥1980.00</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="状态">
|
||||||
|
<n-tag type="success"> 已到账</n-tag>
|
||||||
|
</n-descriptions-item>
|
||||||
|
</n-descriptions>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, ref } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -29,7 +29,6 @@
|
|||||||
</n-input>
|
</n-input>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item
|
<n-form-item
|
||||||
|
|
||||||
path="isCaptcha">
|
path="isCaptcha">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<mi-captcha
|
<mi-captcha
|
||||||
@@ -110,7 +109,7 @@ export default defineComponent({
|
|||||||
formInline: {
|
formInline: {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
password: '123456',
|
password: '123456',
|
||||||
isCaptcha: false
|
isCaptcha: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const rules = {
|
const rules = {
|
||||||
|
|||||||
137
src/views/system/menu/CreateDrawer.vue
Normal file
137
src/views/system/menu/CreateDrawer.vue
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<n-drawer v-model:show="isDrawer" :width="width" :placement="placement">
|
||||||
|
<n-drawer-content :title="title" closable>
|
||||||
|
<n-form
|
||||||
|
:model="formParams"
|
||||||
|
:rules="rules"
|
||||||
|
ref="formRef"
|
||||||
|
label-placement="left"
|
||||||
|
:label-width="100"
|
||||||
|
>
|
||||||
|
<n-form-item label="类型" path="type">
|
||||||
|
<span>{{ formParams.type === 1 ? '侧边栏菜单' : '' }}</span>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="标题" path="label">
|
||||||
|
<n-input placeholder="请输入标题" v-model:value="formParams.label"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="副标题" path="subtitle">
|
||||||
|
<n-input placeholder="请输入副标题" v-model:value="formParams.subtitle"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="路径" path="path">
|
||||||
|
<n-input placeholder="请输入路径" v-model:value="formParams.path"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="打开方式" path="openType">
|
||||||
|
<n-radio-group v-model:value="formParams.openType" name="openType">
|
||||||
|
<n-space>
|
||||||
|
<n-radio :value="1">当前窗口</n-radio>
|
||||||
|
<n-radio :value="2">新窗口</n-radio>
|
||||||
|
</n-space>
|
||||||
|
</n-radio-group>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="菜单权限" path="auth">
|
||||||
|
<n-input placeholder="请输入权限,多个权限用,分割" v-model:value="formParams.auth"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="隐藏侧边栏" path="hidden">
|
||||||
|
<n-switch v-model:value="formParams.hidden"/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<n-space>
|
||||||
|
<n-button type="primary" :loading="subLoading" @click="formSubmit">提交</n-button>
|
||||||
|
<n-button @click="handleReset">重置</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</n-drawer-content>
|
||||||
|
</n-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, ref, toRefs, watch, createVNode, computed, unref } from 'vue'
|
||||||
|
import { CheckOutlined } from '@vicons/antd'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
label: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入标题',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入路径',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CreateDrawer',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '添加顶级菜单'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 450
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: { CheckOutlined },
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const message = useMessage()
|
||||||
|
const formRef: any = ref(null)
|
||||||
|
const defaultValueRef = () => ({
|
||||||
|
label: '',
|
||||||
|
type: 1,
|
||||||
|
subtitle: '',
|
||||||
|
openType: 1,
|
||||||
|
auth: '',
|
||||||
|
path: '',
|
||||||
|
hidden: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
isDrawer: false,
|
||||||
|
subLoading: false,
|
||||||
|
formParams: defaultValueRef(),
|
||||||
|
placement: "right",
|
||||||
|
alertText: '该功能主要实时预览各种布局效果,更多完整配置在 projectSetting.ts 中设置,建议在生产环境关闭该布局预览功能。',
|
||||||
|
})
|
||||||
|
|
||||||
|
function openDrawer(isDrawer) {
|
||||||
|
state.isDrawer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDrawer() {
|
||||||
|
state.isDrawer = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function formSubmit() {
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
message.success('添加成功')
|
||||||
|
handleReset()
|
||||||
|
closeDrawer()
|
||||||
|
} else {
|
||||||
|
message.error('请填写完整信息')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReset() {
|
||||||
|
formRef.value.restoreValidation()
|
||||||
|
state.formParams = Object.assign(state.formParams, defaultValueRef())
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
formRef,
|
||||||
|
rules,
|
||||||
|
formSubmit,
|
||||||
|
handleReset,
|
||||||
|
openDrawer,
|
||||||
|
closeDrawer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
268
src/views/system/menu/menu.vue
Normal file
268
src/views/system/menu/menu.vue
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="n-layout-page-header">
|
||||||
|
<n-card :bordered="false" title="菜单权限管理">
|
||||||
|
页面数据为 Mock 示例数据,非真实数据。
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<n-grid class="mt-4" cols="1 s:1 m:1 l:3 xl:3 2xl:3" responsive="screen" :x-gap="12">
|
||||||
|
<n-gi span="1">
|
||||||
|
<n-card :segmented="{ content: 'hard' }" :bordered="false" size="small">
|
||||||
|
<template #header>
|
||||||
|
<n-space>
|
||||||
|
<n-dropdown trigger="hover" @select="selectAddMenu" :options="addMenuOptions">
|
||||||
|
<n-button type="info" ghost icon-placement="right">
|
||||||
|
添加菜单
|
||||||
|
<template #icon>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<n-icon size="14">
|
||||||
|
<DownOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
</n-dropdown>
|
||||||
|
<n-button type="info" ghost icon-placement="left" @click="packHandle">
|
||||||
|
全部{{ expandedKeys.length ? '收起' : '展开' }}
|
||||||
|
<template #icon>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<n-icon size="14">
|
||||||
|
<AlignLeftOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="w-full menu">
|
||||||
|
<n-input type="input" v-model:value="pattern" placeholder="输入菜单名称搜索">
|
||||||
|
<template #suffix>
|
||||||
|
<n-icon size="18" class="cursor-pointer">
|
||||||
|
<SearchOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
</n-input>
|
||||||
|
<div class="py-3 menu-list">
|
||||||
|
<template v-if="loading">
|
||||||
|
<div class="flex items-center justify-center py-4">
|
||||||
|
<n-spin size="medium"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<n-tree
|
||||||
|
block-line
|
||||||
|
cascade
|
||||||
|
checkable
|
||||||
|
:virtual-scroll="true"
|
||||||
|
:pattern="pattern"
|
||||||
|
:data="treeData"
|
||||||
|
:expandedKeys="expandedKeys"
|
||||||
|
style="max-height: 650px;overflow: hidden"
|
||||||
|
@update:selected-keys="selectedTree"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
<n-gi span="2">
|
||||||
|
<n-card :segmented="{ content: 'hard' }" :bordered="false" size="small">
|
||||||
|
<template #header>
|
||||||
|
<n-space>
|
||||||
|
<n-icon size="18">
|
||||||
|
<FormOutlined />
|
||||||
|
</n-icon>
|
||||||
|
<span>编辑菜单{{ treeItemTitle ? `:${treeItemTitle}`:''}}</span>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
<n-alert type="info" closable>
|
||||||
|
从菜单列表选择一项后,进行编辑
|
||||||
|
</n-alert>
|
||||||
|
<n-form
|
||||||
|
:model="formParams"
|
||||||
|
:rules="rules"
|
||||||
|
ref="formRef"
|
||||||
|
label-placement="left"
|
||||||
|
:label-width="100"
|
||||||
|
v-if="isEditMenu"
|
||||||
|
class="py-4"
|
||||||
|
>
|
||||||
|
<n-form-item label="类型" path="type">
|
||||||
|
<span>{{ formParams.type === 1 ? '侧边栏菜单' : '' }}</span>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="标题" path="label">
|
||||||
|
<n-input placeholder="请输入标题" v-model:value="formParams.label"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="副标题" path="subtitle">
|
||||||
|
<n-input placeholder="请输入副标题" v-model:value="formParams.subtitle"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="路径" path="path">
|
||||||
|
<n-input placeholder="请输入路径" v-model:value="formParams.path"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="打开方式" path="openType">
|
||||||
|
<n-radio-group v-model:value="formParams.openType" name="openType">
|
||||||
|
<n-space>
|
||||||
|
<n-radio :value="1">当前窗口</n-radio>
|
||||||
|
<n-radio :value="2">新窗口</n-radio>
|
||||||
|
</n-space>
|
||||||
|
</n-radio-group>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="菜单权限" path="auth">
|
||||||
|
<n-input placeholder="请输入权限,多个权限用,分割" v-model:value="formParams.auth"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item path="auth" style="margin-left: 100px">
|
||||||
|
<n-space>
|
||||||
|
<n-button type="primary" :loading="subLoading" @click="formSubmit">保存修改</n-button>
|
||||||
|
<n-button @click="handleReset">重置</n-button>
|
||||||
|
</n-space>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</n-card>
|
||||||
|
</n-gi>
|
||||||
|
</n-grid>
|
||||||
|
|
||||||
|
<CreateDrawer ref="createDrawerRef" :title="drawerTitle"></CreateDrawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, unref, reactive, toRefs, onMounted, computed } from 'vue'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
import { DownOutlined, AlignLeftOutlined, SearchOutlined, FormOutlined } from '@vicons/antd'
|
||||||
|
import { getMenuList } from '@/api/system/menu'
|
||||||
|
import { getTreeItem } from '@/utils/index'
|
||||||
|
import CreateDrawer from './CreateDrawer.vue'
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
label: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入标题',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入路径',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { DownOutlined, AlignLeftOutlined, SearchOutlined, FormOutlined, CreateDrawer },
|
||||||
|
setup() {
|
||||||
|
const formRef: any = ref(null)
|
||||||
|
const createDrawerRef = ref()
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const isAddSon = computed(() => {
|
||||||
|
return state.treeItemKey.length ? false : true
|
||||||
|
})
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
addMenuOptions: [
|
||||||
|
{
|
||||||
|
label: '添加顶级菜单',
|
||||||
|
key: 'home',
|
||||||
|
disabled: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '添加子菜单',
|
||||||
|
key: 'son',
|
||||||
|
disabled: isAddSon
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loading: true,
|
||||||
|
subLoading: false,
|
||||||
|
isEditMenu: false,
|
||||||
|
treeData: [],
|
||||||
|
treeItemKey: [],
|
||||||
|
treeItemTitle: '',
|
||||||
|
expandedKeys: [],
|
||||||
|
formParams: {},
|
||||||
|
pattern: '',
|
||||||
|
drawerTitle: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function selectAddMenu(key) {
|
||||||
|
state.drawerTitle = key === 'home' ? '添加顶栏菜单' : `添加子菜单:${ state.treeItemTitle }`
|
||||||
|
openCreateDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreateDrawer() {
|
||||||
|
const { openDrawer } = createDrawerRef.value
|
||||||
|
openDrawer()
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedTree(keys) {
|
||||||
|
if (keys.length) {
|
||||||
|
const treeItem = getTreeItem(unref(state.treeData), keys[0])
|
||||||
|
state.treeItemKey = keys
|
||||||
|
state.treeItemTitle = treeItem.label
|
||||||
|
state.formParams = Object.assign(state.formParams, treeItem)
|
||||||
|
state.isEditMenu = true
|
||||||
|
} else {
|
||||||
|
state.isEditMenu = false
|
||||||
|
state.treeItemKey = []
|
||||||
|
state.treeItemTitle = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReset() {
|
||||||
|
const treeItem = getTreeItem(unref(state.treeData), state.treeItemKey[0])
|
||||||
|
state.formParams = Object.assign(state.formParams, treeItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formSubmit() {
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
message.error('抱歉,您没有该权限')
|
||||||
|
} else {
|
||||||
|
message.error('请填写完整信息')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function packHandle() {
|
||||||
|
if (state.expandedKeys.length) {
|
||||||
|
state.expandedKeys = []
|
||||||
|
} else {
|
||||||
|
state.expandedKeys = state.treeData.map(item => item.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onExpandedKeys(keys) {
|
||||||
|
// let key = keys[0]
|
||||||
|
// console.log(state.expandedKeys)
|
||||||
|
// if(state.expandedKeys.includes(key)){
|
||||||
|
// state.expandedKeys.splice(state.expandedKeys.findIndex(item => item === key), 1)
|
||||||
|
// console.log(state.expandedKeys)
|
||||||
|
// }else{
|
||||||
|
// state.expandedKeys.push(key)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const treeMenuList = await getMenuList()
|
||||||
|
state.expandedKeys = treeMenuList.list.map(item => item.key)
|
||||||
|
state.treeData = treeMenuList.list
|
||||||
|
state.loading = false
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
createDrawerRef,
|
||||||
|
formRef,
|
||||||
|
rules,
|
||||||
|
selectedTree,
|
||||||
|
handleReset,
|
||||||
|
formSubmit,
|
||||||
|
packHandle,
|
||||||
|
onExpandedKeys,
|
||||||
|
selectAddMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
36
src/views/system/role/columns.ts
Normal file
36
src/views/system/role/columns.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { h } from 'vue'
|
||||||
|
import { NTag, NButton } from 'naive-ui'
|
||||||
|
|
||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
title: 'id',
|
||||||
|
key: 'id'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色名称',
|
||||||
|
key: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
key: 'explain'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否默认角色',
|
||||||
|
key: 'isDefault',
|
||||||
|
render(row) {
|
||||||
|
return h(
|
||||||
|
NTag,
|
||||||
|
{
|
||||||
|
type: row.isDefault ? 'success' : 'error'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => row.isDefault ? '是' : '否'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
key: 'create_date'
|
||||||
|
}
|
||||||
|
]
|
||||||
274
src/views/system/role/role.vue
Normal file
274
src/views/system/role/role.vue
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="n-layout-page-header">
|
||||||
|
<n-card :bordered="false" title="角色权限管理">
|
||||||
|
页面数据为 Mock 示例数据,非真实数据。
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
<n-card :bordered="false" class="mt-4 proCard">
|
||||||
|
<BasicTable
|
||||||
|
:columns="columns"
|
||||||
|
:request="loadDataTable"
|
||||||
|
:row-key="row => row.id"
|
||||||
|
ref="actionRef"
|
||||||
|
:actionColumn="actionColumn"
|
||||||
|
@update:checked-row-keys="onCheckedRow"
|
||||||
|
>
|
||||||
|
<template #tableTitle>
|
||||||
|
<n-button type="primary">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon>
|
||||||
|
<PlusOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
添加角色
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #action="{ record }">
|
||||||
|
<TableAction></TableAction>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</BasicTable>
|
||||||
|
|
||||||
|
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<n-modal v-model:show="showModal" :show-icon="false" preset="dialog" :title="editRoleTitle">
|
||||||
|
<div class="py-3 menu-list">
|
||||||
|
<n-tree
|
||||||
|
block-line
|
||||||
|
cascade
|
||||||
|
checkable
|
||||||
|
:virtual-scroll="true"
|
||||||
|
:data="treeData"
|
||||||
|
:expandedKeys="expandedKeys"
|
||||||
|
:checked-keys="checkedKeys"
|
||||||
|
style="max-height: 950px;overflow: hidden"
|
||||||
|
@update:checked-keys="checkedTree"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template #action>
|
||||||
|
<n-space>
|
||||||
|
<n-button type="info" ghost icon-placement="left" @click="packHandle">
|
||||||
|
全部{{ expandedKeys.length ? '收起' : '展开' }}
|
||||||
|
</n-button>
|
||||||
|
|
||||||
|
<n-button type="info" ghost icon-placement="left" @click="checkedAllHandle">
|
||||||
|
全部{{ checkedAll ? '取消' : '选择' }}
|
||||||
|
</n-button>
|
||||||
|
<n-button type="primary" :loading="formBtnLoading" @click="confirmForm">提交</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</n-modal>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs, ref, h, onMounted } from 'vue'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
import { DownOutlined, AlignLeftOutlined, SearchOutlined, FormOutlined } from '@vicons/antd'
|
||||||
|
import { BasicTable, TableAction } from '@/components/Table'
|
||||||
|
import { getRoleList } from '@/api/system/role'
|
||||||
|
import { getMenuList } from '@/api/system/menu'
|
||||||
|
import { columns } from './columns'
|
||||||
|
import { PlusOutlined } from '@vicons/antd'
|
||||||
|
import { getTreeAll } from "@/utils";
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
name: {
|
||||||
|
required: true,
|
||||||
|
trigger: ['blur', 'input'],
|
||||||
|
message: '请输入名称'
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
required: true,
|
||||||
|
trigger: ['blur', 'input'],
|
||||||
|
message: '请输入地址'
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
trigger: ['blur', 'change'],
|
||||||
|
message: '请选择日期'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicTable, TableAction, PlusOutlined, AlignLeftOutlined },
|
||||||
|
setup() {
|
||||||
|
const formRef: any = ref(null)
|
||||||
|
const message = useMessage()
|
||||||
|
const actionRef = ref()
|
||||||
|
const state = reactive({
|
||||||
|
showModal: false,
|
||||||
|
formBtnLoading: false,
|
||||||
|
checkedAll: false,
|
||||||
|
editRoleTitle: '',
|
||||||
|
treeData: [],
|
||||||
|
expandedKeys: [],
|
||||||
|
checkedKeys: ['console', 'step-form'],
|
||||||
|
formParams: {
|
||||||
|
name: '',
|
||||||
|
address: '',
|
||||||
|
date: []
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
pageSize: 5,
|
||||||
|
name: 'xiaoMa'
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 250,
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
render(record) {
|
||||||
|
return h(
|
||||||
|
TableAction,
|
||||||
|
{
|
||||||
|
style: 'button',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: '菜单权限',
|
||||||
|
onClick: handleMenuAuth.bind(null, record),
|
||||||
|
// 根据业务控制是否显示 isShow 和 auth 是并且关系
|
||||||
|
ifShow: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 根据权限控制是否显示: 有权限,会显示,支持多个
|
||||||
|
auth: ['basic_list'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
ifShow: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
auth: ['basic_list'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
icon: 'ic:outline-delete-outline',
|
||||||
|
onClick: handleDelete.bind(null, record),
|
||||||
|
// 根据业务控制是否显示 isShow 和 auth 是并且关系
|
||||||
|
ifShow: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 根据权限控制是否显示: 有权限,会显示,支持多个
|
||||||
|
auth: ['basic_list'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadDataTable = async (params) => {
|
||||||
|
const data = await getRoleList(params);
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCheckedRow(rowKeys) {
|
||||||
|
console.log(rowKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
actionRef.value.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmForm(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
state.formBtnLoading = true
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
message.success('新建成功')
|
||||||
|
setTimeout(() => {
|
||||||
|
state.showModal = false
|
||||||
|
reloadTable()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
message.error('请填写完整信息')
|
||||||
|
}
|
||||||
|
state.formBtnLoading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(record: Recordable) {
|
||||||
|
console.log('点击了编辑', record);
|
||||||
|
router.push({ name: 'basic-info', params: { id: record.id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(record: Recordable) {
|
||||||
|
console.log('点击了删除', record);
|
||||||
|
message.info('点击了删除')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen(record: Recordable) {
|
||||||
|
console.log('点击了启用', record);
|
||||||
|
message.info('点击了删除')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMenuAuth(record: Recordable) {
|
||||||
|
state.editRoleTitle = `分配 ${ record.name } 的菜单权限`
|
||||||
|
state.checkedKeys = record.menu_keys
|
||||||
|
state.showModal = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkedTree(keys) {
|
||||||
|
state.checkedKeys = [state.checkedKeys, ...keys]
|
||||||
|
}
|
||||||
|
|
||||||
|
function packHandle() {
|
||||||
|
if (state.expandedKeys.length) {
|
||||||
|
state.expandedKeys = []
|
||||||
|
} else {
|
||||||
|
state.expandedKeys = state.treeData.map(item => item.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkedAllHandle() {
|
||||||
|
if (!state.checkedAll) {
|
||||||
|
state.checkedKeys = getTreeAll(state.treeData)
|
||||||
|
state.checkedAll = true
|
||||||
|
} else {
|
||||||
|
state.checkedKeys = []
|
||||||
|
state.checkedAll = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const treeMenuList = await getMenuList()
|
||||||
|
state.expandedKeys = treeMenuList.list.map(item => item.key)
|
||||||
|
state.treeData = treeMenuList.list
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
formRef,
|
||||||
|
columns,
|
||||||
|
rules,
|
||||||
|
actionRef,
|
||||||
|
confirmForm,
|
||||||
|
loadDataTable,
|
||||||
|
onCheckedRow,
|
||||||
|
reloadTable,
|
||||||
|
handleEdit,
|
||||||
|
handleDelete,
|
||||||
|
handleOpen,
|
||||||
|
handleMenuAuth,
|
||||||
|
checkedTree,
|
||||||
|
packHandle,
|
||||||
|
checkedAllHandle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='less' scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,7 @@ module.exports = {
|
|||||||
// darkMode: 'class',
|
// darkMode: 'class',
|
||||||
plugins: [createEnterPlugin()],
|
plugins: [createEnterPlugin()],
|
||||||
purge: {
|
purge: {
|
||||||
enabled: false,
|
enable: process.env.NODE_ENV === 'production',
|
||||||
content: ['./index.html', './src/**/*.{vue,ts,tsx}'],
|
content: ['./index.html', './src/**/*.{vue,ts,tsx}'],
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
@@ -11,19 +11,19 @@ module.exports = {
|
|||||||
zIndex: {
|
zIndex: {
|
||||||
'-1': '-1',
|
'-1': '-1',
|
||||||
},
|
},
|
||||||
},
|
colors: {
|
||||||
colors: {
|
primary: {
|
||||||
primary: {
|
DEFAULT: '#0960bd',
|
||||||
DEFAULT: '#0960bd',
|
// dark: primaryColorDark,
|
||||||
// dark: primaryColorDark,
|
},
|
||||||
|
},
|
||||||
|
screens: {
|
||||||
|
sm: '576px',
|
||||||
|
md: '768px',
|
||||||
|
lg: '992px',
|
||||||
|
xl: '1200px',
|
||||||
|
'2xl': '1600px',
|
||||||
},
|
},
|
||||||
},
|
|
||||||
screens: {
|
|
||||||
sm: '576px',
|
|
||||||
md: '768px',
|
|
||||||
lg: '992px',
|
|
||||||
xl: '1200px',
|
|
||||||
'2xl': '1600px',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
55
yarn.lock
55
yarn.lock
@@ -505,10 +505,10 @@
|
|||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
"@rollup/plugin-node-resolve@^13.0.0":
|
"@rollup/plugin-node-resolve@^13.0.2":
|
||||||
version "13.0.0"
|
version "13.0.2"
|
||||||
resolved "https://registry.nlark.com/@rollup/plugin-node-resolve/download/@rollup/plugin-node-resolve-13.0.0.tgz#352f07e430ff377809ec8ec8a6fd636547162dc4"
|
resolved "https://registry.nlark.com/@rollup/plugin-node-resolve/download/@rollup/plugin-node-resolve-13.0.2.tgz?cache=0&sync_timestamp=1626393688566&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40rollup%2Fplugin-node-resolve%2Fdownload%2F%40rollup%2Fplugin-node-resolve-13.0.2.tgz#bfe58e9bfc7284ee0fabc6da7fabcb268f04e0a4"
|
||||||
integrity sha1-NS8H5DD/N3gJ7I7Ipv1jZUcWLcQ=
|
integrity sha1-v+WOm/xyhO4Pq8baf6vLJo8E4KQ=
|
||||||
dependencies:
|
dependencies:
|
||||||
"@rollup/pluginutils" "^3.1.0"
|
"@rollup/pluginutils" "^3.1.0"
|
||||||
"@types/resolve" "1.17.1"
|
"@types/resolve" "1.17.1"
|
||||||
@@ -620,10 +620,10 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256"
|
resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256"
|
||||||
integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==
|
integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==
|
||||||
|
|
||||||
"@types/mockjs@^1.0.3":
|
"@types/mockjs@^1.0.4":
|
||||||
version "1.0.3"
|
version "1.0.4"
|
||||||
resolved "https://registry.nlark.com/@types/mockjs/download/@types/mockjs-1.0.3.tgz?cache=0&sync_timestamp=1621241889026&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fmockjs%2Fdownload%2F%40types%2Fmockjs-1.0.3.tgz#bd8ee3c7cbbd9a18788ab677b9e4f97c8d0bb0bf"
|
resolved "https://registry.nlark.com/@types/mockjs/download/@types/mockjs-1.0.4.tgz?cache=0&sync_timestamp=1625771765667&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fmockjs%2Fdownload%2F%40types%2Fmockjs-1.0.4.tgz#e706951d5e33b4f0a4bb73b1f8b124e26f081de0"
|
||||||
integrity sha1-vY7jx8u9mhh4irZ3ueT5fI0LsL8=
|
integrity sha1-5waVHV4ztPCku3Ox+LEk4m8IHeA=
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "15.12.4"
|
version "15.12.4"
|
||||||
@@ -2016,6 +2016,11 @@ esbuild@0.11.3:
|
|||||||
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.11.3.tgz#b57165b907be4ffba651f6450538ce8d8c1d5eb0"
|
resolved "https://registry.nlark.com/esbuild/download/esbuild-0.11.3.tgz#b57165b907be4ffba651f6450538ce8d8c1d5eb0"
|
||||||
integrity sha1-tXFluQe+T/umUfZFBTjOjYwdXrA=
|
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:
|
esbuild@^0.12.6, esbuild@^0.12.8:
|
||||||
version "0.12.14"
|
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"
|
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"
|
||||||
@@ -2297,7 +2302,7 @@ fast-glob@^3.1.1, fast-glob@^3.2.5:
|
|||||||
micromatch "^4.0.2"
|
micromatch "^4.0.2"
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
fast-glob@^3.2.6:
|
fast-glob@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.nlark.com/fast-glob/download/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
|
resolved "https://registry.nlark.com/fast-glob/download/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
|
||||||
integrity sha1-/Wy3otfpqnp4RhEehaGW1rL3ZqE=
|
integrity sha1-/Wy3otfpqnp4RhEehaGW1rL3ZqE=
|
||||||
@@ -4204,7 +4209,7 @@ postcss@^8.1.10:
|
|||||||
nanoid "^3.1.23"
|
nanoid "^3.1.23"
|
||||||
source-map-js "^0.6.2"
|
source-map-js "^0.6.2"
|
||||||
|
|
||||||
postcss@^8.1.6, postcss@^8.2.1, postcss@^8.3.5:
|
postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.10, postcss@^8.3.5:
|
||||||
version "8.3.5"
|
version "8.3.5"
|
||||||
resolved "https://registry.nlark.com/postcss/download/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
|
resolved "https://registry.nlark.com/postcss/download/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709"
|
||||||
integrity sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=
|
integrity sha1-mCIWsRNBK8IKhiiekeuZSVKltwk=
|
||||||
@@ -5307,19 +5312,19 @@ vite-plugin-html@^2.0.7:
|
|||||||
fs-extra "^9.1.0"
|
fs-extra "^9.1.0"
|
||||||
html-minifier-terser "^5.1.1"
|
html-minifier-terser "^5.1.1"
|
||||||
|
|
||||||
vite-plugin-mock@^2.9.1:
|
vite-plugin-mock@^2.9.3:
|
||||||
version "2.9.1"
|
version "2.9.3"
|
||||||
resolved "https://registry.nlark.com/vite-plugin-mock/download/vite-plugin-mock-2.9.1.tgz?cache=0&sync_timestamp=1625545918347&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvite-plugin-mock%2Fdownload%2Fvite-plugin-mock-2.9.1.tgz#bc67868082c0a7bfa13a3deb97f6a1bf57be12ff"
|
resolved "https://registry.nlark.com/vite-plugin-mock/download/vite-plugin-mock-2.9.3.tgz#1afedd17a5a0a64c71acb7355f86977398f46912"
|
||||||
integrity sha1-vGeGgILAp7+hOj3rl/ahv1e+Ev8=
|
integrity sha1-Gv7dF6WgpkxxrLc1X4aXc5j0aRI=
|
||||||
dependencies:
|
dependencies:
|
||||||
"@rollup/plugin-node-resolve" "^13.0.0"
|
"@rollup/plugin-node-resolve" "^13.0.2"
|
||||||
"@types/mockjs" "^1.0.3"
|
"@types/mockjs" "^1.0.4"
|
||||||
chalk "^4.1.1"
|
chalk "^4.1.1"
|
||||||
chokidar "^3.5.2"
|
chokidar "^3.5.2"
|
||||||
connect "^3.7.0"
|
connect "^3.7.0"
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
esbuild "0.11.3"
|
esbuild "0.11.3"
|
||||||
fast-glob "^3.2.6"
|
fast-glob "^3.2.7"
|
||||||
path-to-regexp "^6.2.0"
|
path-to-regexp "^6.2.0"
|
||||||
|
|
||||||
vite-plugin-style-import@^1.0.1:
|
vite-plugin-style-import@^1.0.1:
|
||||||
@@ -5333,17 +5338,17 @@ vite-plugin-style-import@^1.0.1:
|
|||||||
es-module-lexer "^0.6.0"
|
es-module-lexer "^0.6.0"
|
||||||
magic-string "^0.25.7"
|
magic-string "^0.25.7"
|
||||||
|
|
||||||
vite@^2.4.2:
|
vite@2.3.6:
|
||||||
version "2.4.2"
|
version "2.3.6"
|
||||||
resolved "https://registry.nlark.com/vite/download/vite-2.4.2.tgz#07d00615775c808530bc9f65641062b349b67929"
|
resolved "https://registry.nlark.com/vite/download/vite-2.3.6.tgz#1f7cfde88a51a802d69000c7bac85d481c2e871c"
|
||||||
integrity sha1-B9AGFXdcgIUwvJ9lZBBis0m2eSk=
|
integrity sha1-H3z96IpRqALWkADHushdSBwuhxw=
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.12.8"
|
esbuild "^0.12.5"
|
||||||
postcss "^8.3.5"
|
postcss "^8.2.10"
|
||||||
resolve "^1.20.0"
|
resolve "^1.19.0"
|
||||||
rollup "^2.38.5"
|
rollup "^2.38.5"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.1"
|
||||||
|
|
||||||
vooks@^0.2.4, vooks@^0.2.6:
|
vooks@^0.2.4, vooks@^0.2.6:
|
||||||
version "0.2.6"
|
version "0.2.6"
|
||||||
|
|||||||
Reference in New Issue
Block a user