Browse Source

🎟 构建平台业务模块

master
wangxiang 3 years ago
parent
commit
407f8ac125
  1. 4
      kicc-platform/kicc-platform-api/kicc-system-api/src/main/java/com/cloud/kicc/system/api/entity/Menu.java
  2. 1
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/controller/MenuController.java
  3. 1
      kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/impl/MenuServiceImpl.java
  4. 1
      kicc-ui/src/store/modules/user.ts
  5. 10
      kicc-ui/src/utils/dateUtil.ts
  6. 10
      kicc-ui/src/utils/helper/treeHelper.ts
  7. 85
      kicc-ui/src/views/system/menu/MenuModal.vue
  8. 103
      kicc-ui/src/views/system/menu/index.vue
  9. 203
      kicc-ui/src/views/system/menu/menu.data.ts
  10. 10
      kicc-ui/types/index.d.ts

4
kicc-platform/kicc-platform-api/kicc-system-api/src/main/java/com/cloud/kicc/system/api/entity/Menu.java

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField; @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.cloud.kicc.common.data.entity.CommonEntity;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -87,6 +88,7 @@ public class Menu extends CommonEntity { @@ -87,6 +88,7 @@ public class Menu extends CommonEntity {
* 子菜单
*/
@TableField(exist = false)
private List<Menu> children = new ArrayList<Menu>();
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<Menu> children = new ArrayList();
}

1
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/controller/MenuController.java

@ -41,6 +41,7 @@ public class MenuController { @@ -41,6 +41,7 @@ public class MenuController {
return new LambdaQueryWrapper<Menu>()
.like(StrUtil.isNotBlank(menu.getName()), Menu::getName, menu.getName())
.eq(StrUtil.isNotBlank(menu.getHideMenu()), Menu::getHideMenu, menu.getHideMenu())
.between(StrUtil.isNotBlank(menu.getBeginTime()) && StrUtil.isNotBlank(menu.getEndTime()), Menu::getCreateTime, menu.getBeginTime(), menu.getEndTime())
.orderByAsc(Menu::getSort);
}

1
kicc-platform/kicc-platform-biz/kicc-system-biz/src/main/java/com/cloud/kicc/system/service/impl/MenuServiceImpl.java

@ -46,6 +46,7 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements Me @@ -46,6 +46,7 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements Me
menuVo.setComponent(menu.getComponent());
// 检测是否是根节点,0:代表根节点
if (MenuEnum.MENU_0.getValue().equals(menu.getParentId()) && !ReUtil.isMatch(RegexConstants.MATCHER_URL, menu.getPath())) {
menuVo.setComponent("Layout");
menuVo.setPath("/" + menu.getPath());
} else {
menuVo.setPath(menu.getPath());

1
kicc-ui/src/store/modules/user.ts

@ -71,6 +71,7 @@ export const useUserStore = defineStore({ @@ -71,6 +71,7 @@ export const useUserStore = defineStore({
},
setPermissions(permissions: string[]) {
this.permissions = permissions;
setAuthCache(PERMISSIONS_KEY, permissions);
},
setAccessToken(accessToken: string) {
this.access_token = accessToken;

10
kicc-ui/src/utils/dateUtil.ts

@ -21,4 +21,14 @@ export function formatToDate(date: moment.MomentInput = undefined, format = DATE @@ -21,4 +21,14 @@ export function formatToDate(date: moment.MomentInput = undefined, format = DATE
return moment(date).format(format);
}
/** 添加日期范围 */
export function convertDateRange(params: Recordable, dateRangeField: string): Recordable {
const search = params, dateRange = search[dateRangeField];
if (null != dateRange && dateRange?.length > 0) {
search.beginTime = dateRange[0];
search.endTime = dateRange[1];
}
return search;
}
export const dateUtil = moment;

10
kicc-ui/src/utils/helper/treeHelper.ts

@ -24,20 +24,16 @@ export function listToTree<T = any>(list: any[], config: Partial<TreeHelperConfi @@ -24,20 +24,16 @@ export function listToTree<T = any>(list: any[], config: Partial<TreeHelperConfi
const nodeMap = new Map();
const result: T[] = [];
const { id, children, parentId } = conf;
for (const node of list) {
node[children] = node[children] || [];
nodeMap.set(node[id], node);
}
for (const node of list) nodeMap.set(node[id], node);
for (const node of list) {
const parent = nodeMap.get(node[parentId]);
(parent ? parent.children : result).push(node);
(parent ? parent[children] || (parent[children] = []) : result).push(node);
}
return result;
}
/** 树形转换集合 */
export function treeToList<T = any>(tree: any, config: Partial<TreeHelperConfig> = {}): T {
export function treeToList<T = any>(tree: any[], config: Partial<TreeHelperConfig> = {}): T {
config = getConfig(config);
const { children } = config;
const result: any = [...tree];

85
kicc-ui/src/views/system/menu/MenuModal.vue

@ -1,72 +1,75 @@ @@ -1,72 +1,75 @@
<template>
<BasicModal
<BasicModal width="720px"
v-bind="$attrs"
@register="registerDrawer"
showFooter
:title="getTitle"
width="50%"
@ok="handleSubmit"
@register="registerModal"
>
<BasicForm @register="registerForm" />
<BasicForm @register="registerForm"/>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
/**
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔
*/
import { ref, unref } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from './menu.data';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { listMenu, addMenu, editMenu } from '/@/api/system/menu';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { listMenu, addMenu, editMenu, getMenu } from '/@/api/system/menu';
import { listToTree } from '/@/utils/helper/treeHelper';
// emit
const emit = defineEmits(['success', 'register']);
const emits = defineEmits(['success', 'register']);
//props
const props = defineProps({
updateFlag: Boolean
});
var isUpdate = ref(props.updateFlag);//
const tag = ref('');
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
labelWidth: 100,
schemas: formSchema,
showActionButtonGroup: false,
actionColOptions: {
span: 23,
},
baseColProps: { span: 24 }
});
const [registerDrawer, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
setFieldsValue({
...data.record,
});
}
const treeData = await menuList();
treeData.unshift({ id: '0', name: '顶级菜单' });
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data: ModalInnerData = {}) => {
await resetFields();
listMenu().then(data => {
updateSchema({
field: 'parentId',
componentProps: { treeData },
componentProps: {
treeData: listToTree(data)
}
});
});
const getTitle = computed(() => (!unref(isUpdate) ? '新增菜单' : '编辑菜单'));
tag.value = data._tag;
const menuId = data.record?.id;
const props: ModalProps = { confirmLoading: false };
switch (unref(tag)) {
case 'add':
props.title = '新增菜单';
menuId && await setFieldsValue({ parentId: menuId });
break;
case 'edit':
props.title = '编辑菜单';
await setFieldsValue(await getMenu({ id: menuId }) || {});
break;
}
setModalProps(props);
});
async function handleSubmit() {
try {
const values = await validate();
const formData = await validate();
setModalProps({ confirmLoading: true });
if(unref(isUpdate)) {
await menuUpdate(values);
}else{
await menuAdd(values);
switch (unref(tag)) {
case 'add':
await addMenu(formData);
break;
case 'edit':
await editMenu(formData);
break;
}
emits('success');
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}

103
kicc-ui/src/views/system/menu/index.vue

@ -1,51 +1,71 @@ @@ -1,51 +1,71 @@
<template>
<div>
<BasicTable @register="registerTable" @fetch-success="onFetchSuccess">
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleCreate">新增菜单</a-button>
<a-button type="default" @click="expandAll">展开全部</a-button>
<a-button type="default" @click="collapseAll">折叠全部</a-button>
<a-button v-auth="['menu_add']"
type="primary"
@click="handleAdd"
>新增菜单</a-button>
<a-button type="default"
@click="expandAll"
>展开全部</a-button>
<a-button type="default"
@click="collapseAll"
>折叠全部</a-button>
</template>
<template #action="{ record }">
<TableAction
:actions="[
<TableAction :actions="[
{
icon: 'clarity:note-edit-line',
onClick: handleEdit.bind(null, record),
label: '编辑',
icon: 'fa6-regular:pen-to-square',
auth: ['menu_edit'],
onClick: handleEdit.bind(null, record)
},
{
label: '新增',
icon: 'ant-design:plus-circle-outlined',
auth: ['menu_add'],
onClick: handleAdd.bind(null, record),
},
{
label: '删除',
icon: 'ant-design:delete-outlined',
auth: ['menu_del'],
color: 'error',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
confirm: handleDel.bind(null, record),
},
]"
}]"
/>
</template>
</BasicTable>
<MenuModal @register="registerDrawer" @success="handleSuccess" />
<MenuModal @register="registerModal" @success="handleSuccess"/>
</div>
</template>
<script lang="ts" setup>
import { nextTick, ref } from 'vue';
/**
* Copyright © 2020-2022 <a href="http://www.entfrm.com/">entfrm</a> All rights reserved.
* author entfrm开发团队-王翔
*/
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { getMenu, listMenu, delMenu } from '/@/api/system/menu';
import { listMenu, delMenu } from '/@/api/system/menu';
import { useModal } from '/@/components/Modal';
import MenuModal from './MenuModal.vue';
import { listToTree } from '/@/utils/helper/treeHelper';
import { columns, searchFormSchema } from './menu.data';
import { useMessage } from '/@/hooks/web/useMessage';
import { convertDateRange } from '/@/utils/dateUtil';
import MenuModal from './MenuModal.vue';
const { createMessage } = useMessage();
const [registerDrawer, { openModal }] = useModal();
const [registerModal, { openModal }] = useModal();
const [registerTable, { reload, expandAll,collapseAll }] = useTable({
title: '菜单列表',
api: listMenu,
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
schemas: searchFormSchema
},
isTreeTable: true,
pagination: false,
@ -56,58 +76,37 @@ @@ -56,58 +76,37 @@
showIndexColumn: false,
canResize: false,
actionColumn: {
width: 180,
width: 220,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: undefined,
fixed: false
},
afterFetch: (result) => listToTree(result),
handleSearchInfoFn: (queryParams) => convertDateRange(queryParams, 'dateRange')
});
var updateFlag = ref(true);
//expose
defineExpose({
updateFlag
});
/**
* 创建菜单
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
/** 新增按钮操作,行内新增与工具栏局域新增通用 */
function handleAdd(record: Recordable) {
openModal(true,{ _tag: 'add', record });
}
/**
* 编辑菜单
*/
/** 编辑按钮操作,行内编辑 */
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
});
openModal(true, { _tag: 'edit', record });
}
/**
* 删除菜单
*/
async function handleDelete(record: Recordable) {
await menuDel({ id: record.id });
/** 删除按钮操作,行内删除 */
async function handleDel(record: Recordable) {
await delMenu({ id: record.id });
createMessage.success('删除成功!');
handleSuccess();
}
/**
* 成功后重载表格
*/
/** 处理表单提交成功 */
function handleSuccess() {
debugger
reload();
}
function onFetchSuccess() {
//
nextTick(expandAll);
}
</script>

203
kicc-ui/src/views/system/menu/menu.data.ts

@ -1,3 +1,10 @@ @@ -1,3 +1,10 @@
/**
* @program: kicc-ui
* @description:
* @author: entfrm开发团队-
* @create: 2022/4/21
*/
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { h } from 'vue';
@ -8,52 +15,58 @@ export const columns: BasicColumn[] = [ @@ -8,52 +15,58 @@ export const columns: BasicColumn[] = [
{
title: '菜单名称',
dataIndex: 'name',
width: 200,
align: 'left',
align: 'left'
},
{
title: '图标',
dataIndex: 'icon',
width: 50,
width: 80,
customRender: ({ record }) => {
return h(Icon, { icon: record.icon });
}
},
{
title: '排序',
dataIndex: 'sort',
width: 80
},
{
title: '权限标识',
dataIndex: 'permission',
width: 180,
customRender: ({record}) => {
return record.permission || h(Tag, { color: 'red' }, () => '暂无数据');
}
},
{
title: '组件',
dataIndex: 'component',
customRender: ({record}) => {
return record.component || h(Tag, { color: 'red' }, () => '暂无数据');
}
},
{
title: '排序',
dataIndex: 'sort',
width: 50,
},
{
title: '状态',
dataIndex: 'status',
title: '是否隐藏',
dataIndex: 'hideMenu',
width: 80,
customRender: ({ record }) => {
const status = record.status;
const enable = ~~status === 0;
const hideMenu = record.hideMenu;
// 采用二进制~~取反,只要为null或者0等等一些其他的空元素都会转为0
// 第一次取反会运算为-1,在后一次取反会运算为0
const enable = ~~hideMenu === 0;
const color = enable ? 'green' : 'red';
const text = enable ? '启用' : '停用';
const text = enable ? '显示' : '隐藏';
return h(Tag, { color: color }, () => text);
},
}
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
},
dataIndex: 'createTime'
}
];
const isDir = (type: string) => type === 'M';
/** 菜单类型 */
const isMenu = (type: string) => type === 'C';
/** 按钮类型 */
const isButton = (type: string) => type === 'F';
export const searchFormSchema: FormSchema[] = [
@ -62,22 +75,33 @@ export const searchFormSchema: FormSchema[] = [ @@ -62,22 +75,33 @@ export const searchFormSchema: FormSchema[] = [
label: '菜单名',
component: 'Input',
componentProps: {
placeholder: '请输入名称',
placeholder: '请输入菜单名称'
},
colProps: { span: 8 },
colProps: { span: 8 }
},
{
field: 'beginTime',
label: '起始时间',
component: 'DatePicker',
colProps: { span: 8 },
field: 'hideMenu',
label: '是否隐藏',
component: 'Select',
componentProps: {
options: [
{ label: '显示', value: '0' },
{ label: '隐藏', value: '1' }
]
},
colProps: { span: 7 }
},
{
field: 'endTime',
label: '截止时间',
component: 'DatePicker',
colProps: { span: 8 },
field: 'dateRange',
label: '创建时间',
component: 'RangePicker',
componentProps: {
style: { width:'100%' },
format: 'yyyy-MM-dd',
placeholder: ['开始日期','结束日期']
},
colProps: { span: 8 }
}
];
export const formSchema: FormSchema[] = [
@ -85,29 +109,8 @@ export const formSchema: FormSchema[] = [ @@ -85,29 +109,8 @@ export const formSchema: FormSchema[] = [
field: 'id',
label: 'ID',
component: 'Input',
show: false,
show: false
},
{
field: 'type',
label: '菜单类型',
component: 'RadioButtonGroup',
defaultValue: 'M',
componentProps: {
options: [
{ label: '目录', value: 'M' },
{ label: '菜单', value: 'C' },
{ label: '按钮', value: 'F' },
],
},
colProps: { lg: 24, md: 24 },
},
{
field: 'name',
label: '菜单名称',
component: 'Input',
required: true,
},
{
field: 'parentId',
label: '上级菜单',
@ -116,71 +119,80 @@ export const formSchema: FormSchema[] = [ @@ -116,71 +119,80 @@ export const formSchema: FormSchema[] = [
replaceFields: {
title: 'name',
key: 'id',
value: 'id',
value: 'id'
},
getPopupContainer: () => document.body,
getPopupContainer: () => document.body
}
},
},
{
field: 'sort',
label: '排序',
component: 'InputNumber',
required: true,
field: 'type',
label: '菜单类型',
component: 'RadioButtonGroup',
defaultValue: 'M',
componentProps: {
options: [
{ label: '目录', value: 'M' },
{ label: '菜单', value: 'C' },
{ label: '按钮', value: 'F' }
]
}
},
{
field: 'icon',
label: '图标',
label: '菜单图标',
component: 'IconPicker',
required: true,
ifShow: ({ values }) => !isButton(values.type),
},
{
field: 'name',
label: '菜单名称',
component: 'Input',
required: true,
colProps: {
span: 12
}
},
{
field: 'path',
label: '路由地址',
component: 'Input',
required: true,
ifShow: ({ values }) => !isButton(values.type),
colProps: {
span: 12
},
ifShow: ({ values }) => !isButton(values.type)
},
{
field: 'component',
label: '组件路径',
component: 'Input',
ifShow: ({ values }) => !isButton(values.type),
colProps: {
span: 12
},
ifShow: ({ values }) => isMenu(values.type)
},
{
field: 'permission',
label: '权限标识',
component: 'Input',
ifShow: ({ values }) => isButton(values.type),
},
{
field: 'status',
label: '状态',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '启用', value: '0' },
{ label: '禁用', value: '1' },
],
colProps: {
span: 12
},
ifShow: ({ values }) => isButton(values.type)
},
{
field: 'target',
label: '是否外链',
component: 'RadioButtonGroup',
defaultValue: '0',
field: 'sort',
label: '显示排序',
component: 'InputNumber',
componentProps: {
options: [
{ label: '否', value: '0' },
{ label: '是', value: '1' },
],
style: { width:'100%' }
},
ifShow: ({ values }) => !isButton(values.type),
required: true,
colProps: {
span: 12
}
},
{
field: 'keepalive',
label: '是否缓存',
@ -192,20 +204,25 @@ export const formSchema: FormSchema[] = [ @@ -192,20 +204,25 @@ export const formSchema: FormSchema[] = [
{ label: '是', value: '1' },
],
},
ifShow: ({ values }) => isMenu(values.type),
colProps: {
span: 12
},
ifShow: ({ values }) => isMenu(values.type)
},
{
field: 'show',
label: '是否显示',
field: 'hideMenu',
label: '是否隐藏',
component: 'RadioButtonGroup',
defaultValue: '0',
componentProps: {
options: [
{ label: '', value: '0' },
{ label: '', value: '1' },
],
{ label: '', value: '0' },
{ label: '', value: '1' },
]
},
ifShow: ({ values }) => !isButton(values.type),
colProps: {
span: 12
},
ifShow: ({ values }) => !isButton(values.type)
}
];

10
kicc-ui/types/index.d.ts vendored

@ -32,3 +32,13 @@ declare interface ComponentElRef<T extends HTMLElement = HTMLDivElement> { @@ -32,3 +32,13 @@ declare interface ComponentElRef<T extends HTMLElement = HTMLDivElement> {
declare type ComponentRef<T extends HTMLElement = HTMLDivElement> = ComponentElRef<T> | null;
declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>;
/** 模态框传递数据通用类型定义 */
declare type ModalInnerData = {
// 模态框标签类型
_tag: string;
// 表格记录行数
record: Recordable;
[key: string]: any;
};

Loading…
Cancel
Save