feat: add modal component

pull/2/head
xingyu4j 2022-11-04 16:43:11 +08:00
parent 1c1cafdc7f
commit d79b23c8a2
7 changed files with 386 additions and 341 deletions

View File

@ -0,0 +1,3 @@
import XModal from './src/XModal.vue'
export { XModal }

View File

@ -0,0 +1,62 @@
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes'
import { computed, useAttrs, useSlots } from 'vue'
const slots = useSlots()
const props = defineProps({
id: propTypes.string.def('model_1'),
modelValue: propTypes.bool.def(false),
fullscreen: propTypes.bool.def(false),
loading: propTypes.bool.def(false),
title: propTypes.string.def('弹窗'),
width: propTypes.string.def('800'),
height: propTypes.string.def('480'),
minWidth: propTypes.string.def('460'),
minHeight: propTypes.string.def('320'),
showFooter: propTypes.bool.def(true)
})
const getBindValue = computed(() => {
const delArr: string[] = ['title']
const attrs = useAttrs()
const obj = { ...attrs, ...props }
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
})
</script>
<template>
<vxe-modal
v-bind="getBindValue"
:width="width"
:height="height"
:title="title"
min-width="460"
min-height="320"
:loading="loading"
:fullscreen="fullscreen"
destroy-on-close
show-zoom
resize
transfer
:show-footer="showFooter"
>
<template v-if="slots.header" #header>
<slot name="header"></slot>
</template>
<template v-if="slots.default" #default>
<slot name="default"></slot>
</template>
<template v-if="slots.corner" #corner>
<slot name="corner"></slot>
</template>
<template v-if="slots.footer" #footer>
<slot name="footer"></slot>
</template>
</vxe-modal>
</template>

View File

@ -4,6 +4,7 @@ import { Form } from '@/components/Form'
import { Table } from '@/components/Table'
import { Search } from '@/components/Search'
import { Dialog } from '@/components/Dialog'
import { XModal } from '@/components/XModal'
import { DictTag } from '@/components/DictTag'
import { ContentWrap } from '@/components/ContentWrap'
import { Descriptions } from '@/components/Descriptions'
@ -14,6 +15,7 @@ export const setupGlobCom = (app: App<Element>): void => {
app.component('Table', Table)
app.component('Search', Search)
app.component('Dialog', Dialog)
app.component('XModal', XModal)
app.component('DictTag', DictTag)
app.component('ContentWrap', ContentWrap)
app.component('Descriptions', Descriptions)

View File

@ -4,6 +4,7 @@ import { VxeGridProps } from 'vxe-table'
export const useVxeGrid = (allSchemas, getPageApi) => {
const gridOptions = reactive<VxeGridProps>({
loading: false,
height: 800,
rowConfig: {
keyField: 'id',
isHover: true

View File

@ -114,8 +114,10 @@ VXETable.setup({
titleColon: true // 是否显示标题冒号
},
modal: {
width: 600, // 窗口的宽度
height: 400, // 窗口的高度
width: 800, // 窗口的宽度
height: 600, // 窗口的高度
minWidth: 460,
minHeight: 320,
showZoom: true, // 标题是否标显示最大化与还原按钮
resize: true, // 是否允许窗口边缘拖动调整窗口大小
marginSize: 0, // 只对 resize 启用后有效,用于设置可拖动界限范围,如果为负数则允许拖动超出屏幕边界

View File

@ -1,3 +1,244 @@
<template>
<ContentWrap>
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择菜单状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
</el-form-item>
</el-form>
<vxe-toolbar>
<template #buttons>
<vxe-button status="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
<Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
</vxe-button>
<vxe-button @click="xTable?.setAllTreeExpand(true)"></vxe-button>
<vxe-button @click="xTable?.clearTreeExpand()"></vxe-button>
</template>
</vxe-toolbar>
<vxe-table
show-overflow
keep-source
ref="xTable"
:loading="tableLoading"
:row-config="{ keyField: 'id' }"
:column-config="{ resizable: true }"
:tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
:print-config="{}"
:export-config="{}"
:data="tableData"
class="xtable"
>
<vxe-column title="菜单名称" field="name" width="200" tree-node>
<template #default="{ row }">
<Icon :icon="row.icon" />
<span class="ml-3">{{ row.name }}</span>
</template>
</vxe-column>
<vxe-column title="菜单类型" field="type">
<template #default="{ row }">
<DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="row.type" />
</template>
</vxe-column>
<vxe-column title="路由地址" field="path" />
<vxe-column title="组件路径" field="component" />
<vxe-column title="权限标识" field="permission" />
<vxe-column title="排序" field="sort" />
<vxe-column title="状态" field="status">
<template #default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
</vxe-column>
<vxe-column title="创建时间" field="createTime" formatter="formatDate" />
<vxe-column title="操作" width="200">
<template #default="{ row }">
<vxe-button
type="text"
status="primary"
v-hasPermi="['system:menu:update']"
@click="handleUpdate(row)"
>
<Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
</vxe-button>
<vxe-button
type="text"
status="primary"
v-hasPermi="['system:menu:delete']"
@click="handleDelete(row)"
>
<Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
</vxe-button>
</template>
</vxe-column>
</vxe-table>
</ContentWrap>
<XModal v-model="dialogVisible" id="menuModel" :title="dialogTitle">
<template #default>
<!-- 对话框(添加 / 修改) -->
<el-form
:model="menuForm"
:rules="rules"
:inline="true"
label-width="120px"
label-position="right"
>
<el-row :gutter="24">
<el-col :span="24">
<el-form-item label="上级菜单">
<el-tree-select
node-key="id"
v-model="menuForm.parentId"
:props="menuProps"
:data="menuOptions"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="menuForm.type">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="菜单图标">
<IconSelect v-model="menuForm.icon" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="路由地址" prop="path">
<template #label>
<Tooltip
titel="路由地址"
message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
/>
</template>
<el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="路由地址" prop="component">
<el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type !== 1">
<el-col :span="12">
<el-form-item label="权限标识" prop="permission">
<template #label>
<Tooltip
titel="权限标识"
message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
/>
</template>
<el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
</el-form-item>
</el-col>
</template>
<el-col :span="12">
<el-form-item label="显示排序" prop="sort">
<el-input-number
v-model="menuForm.sort"
controls-position="right"
:min="0"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单状态" prop="status">
<el-radio-group v-model="menuForm.status">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="显示状态" prop="status">
<template #label>
<Tooltip
titel="显示状态"
message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
/>
</template>
<el-radio-group v-model="menuForm.visible">
<el-radio-button key="true" :label="true">显示</el-radio-button>
<el-radio-button key="false" :label="false">隐藏</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="缓存状态" prop="keepAlive">
<template #label>
<Tooltip
titel="缓存状态"
message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
/>
</template>
<el-radio-group v-model="menuForm.keepAlive">
<el-radio-button key="true" :label="true">缓存</el-radio-button>
<el-radio-button key="false" :label="false">不缓存</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</template>
<template #footer>
<!-- 操作按钮 -->
<el-button
v-if="['create', 'update'].includes(actionType)"
type="primary"
:loading="actionLoading"
@click="submitForm"
:content="t('action.save')"
/>
<el-button @click="dialogVisible = false" :content="t('dialog.close')" />
</template>
</XModal>
</template>
<script setup lang="ts">
import * as MenuApi from '@/api/system/menu'
import { MenuVO } from '@/api/system/menu/types'
@ -164,256 +405,3 @@ onMounted(async () => {
getTree()
})
</script>
<template>
<ContentWrap>
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择菜单状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }}
</el-button>
</el-form-item>
</el-form>
<vxe-toolbar>
<template #buttons>
<vxe-button status="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
<Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
</vxe-button>
<vxe-button @click="xTable?.setAllTreeExpand(true)"></vxe-button>
<vxe-button @click="xTable?.clearTreeExpand()"></vxe-button>
</template>
</vxe-toolbar>
<vxe-table
show-overflow
keep-source
ref="xTable"
:loading="tableLoading"
:row-config="{ keyField: 'id' }"
:column-config="{ resizable: true }"
:tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
:print-config="{}"
:export-config="{}"
:data="tableData"
>
<vxe-column title="菜单名称" field="name" width="200" tree-node>
<template #default="{ row }">
<Icon :icon="row.icon" />
<span class="ml-3">{{ row.name }}</span>
</template>
</vxe-column>
<vxe-column title="菜单类型" field="type">
<template #default="{ row }">
<DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="row.type" />
</template>
</vxe-column>
<vxe-column title="路由地址" field="path" />
<vxe-column title="组件路径" field="component" />
<vxe-column title="权限标识" field="permission" />
<vxe-column title="排序" field="sort" />
<vxe-column title="状态" field="status">
<template #default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
</vxe-column>
<vxe-column title="创建时间" field="createTime" formatter="formatDate" />
<vxe-column title="操作" width="200">
<template #default="{ row }">
<vxe-button
type="text"
status="primary"
v-hasPermi="['system:menu:update']"
@click="handleUpdate(row)"
>
<Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
</vxe-button>
<vxe-button
type="text"
status="primary"
v-hasPermi="['system:menu:delete']"
@click="handleDelete(row)"
>
<Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
</vxe-button>
</template>
</vxe-column>
</vxe-table>
</ContentWrap>
<vxe-modal
v-model="dialogVisible"
id="menuModel"
:title="dialogTitle"
width="800"
height="6f00"
min-width="460"
min-height="320"
show-zoom
resize
transfer
show-footer
>
<template #default>
<!-- 对话框(添加 / 修改) -->
<el-form
:model="menuForm"
:rules="rules"
:inline="true"
label-width="120px"
label-position="right"
>
<el-row :gutter="24">
<el-col :span="24">
<el-form-item label="上级菜单">
<el-tree-select
node-key="id"
v-model="menuForm.parentId"
:props="menuProps"
:data="menuOptions"
check-strictly
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单类型" prop="type">
<el-radio-group v-model="menuForm.type">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="菜单图标">
<IconSelect v-model="menuForm.icon" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="路由地址" prop="path">
<template #label>
<Tooltip
titel="路由地址"
message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
/>
</template>
<el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="路由地址" prop="component">
<el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type !== 1">
<el-col :span="12">
<el-form-item label="权限标识" prop="permission">
<template #label>
<Tooltip
titel="权限标识"
message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
/>
</template>
<el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
</el-form-item>
</el-col>
</template>
<el-col :span="12">
<el-form-item label="显示排序" prop="sort">
<el-input-number
v-model="menuForm.sort"
controls-position="right"
:min="0"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单状态" prop="status">
<el-radio-group v-model="menuForm.status">
<el-radio-button
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<template v-if="menuForm.type !== 3">
<el-col :span="12">
<el-form-item label="显示状态" prop="status">
<template #label>
<Tooltip
titel="显示状态"
message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
/>
</template>
<el-radio-group v-model="menuForm.visible">
<el-radio-button key="true" :label="true">显示</el-radio-button>
<el-radio-button key="false" :label="false">隐藏</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
<template v-if="menuForm.type === 2">
<el-col :span="12">
<el-form-item label="缓存状态" prop="keepAlive">
<template #label>
<Tooltip
titel="缓存状态"
message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
/>
</template>
<el-radio-group v-model="menuForm.keepAlive">
<el-radio-button key="true" :label="true">缓存</el-radio-button>
<el-radio-button key="false" :label="false">不缓存</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</template>
</el-row>
</el-form>
</template>
<template #footer>
<!-- 操作按钮 -->
<el-button
v-if="['create', 'update'].includes(actionType)"
type="primary"
:loading="actionLoading"
@click="submitForm"
>
{{ t('action.save') }}
</el-button>
<el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
</template>
</vxe-modal>
</template>

View File

@ -1,3 +1,76 @@
<template>
<ContentWrap>
<vxe-grid ref="xGrid" v-bind="gridOptions" class="xtable-scrollbar">
<template #toolbar_buttons>
<el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
<Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
</el-button>
</template>
<template #status_default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #action_default="{ row }">
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleUpdate(row.id)"
>
<Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleDetail(row)"
>
<Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:delete']"
@click="handleDelete(row.id)"
>
<Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
</el-button>
</template>
</vxe-grid>
</ContentWrap>
<XModal id="postModel" v-model="dialogVisible" :title="dialogTitle">
<template #default>
<!-- 对话框(添加 / 修改) -->
<vxe-form
ref="xForm"
v-if="['create', 'update'].includes(actionType)"
:data="formData"
:items="formItems"
:rules="rules"
/>
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailRef"
>
<template #status="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #createTime="{ row }">
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
</Descriptions>
</template>
<template #footer>
<vxe-button
v-if="['create', 'update'].includes(actionType)"
status="primary"
@click="submitForm"
:content="t('action.save')"
/>
<vxe-button @click="dialogVisible = false" :content="t('dialog.close')" />
</template>
</XModal>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import dayjs from 'dayjs'
@ -82,89 +155,3 @@ const submitForm: VxeFormEvents.Submit = async () => {
}
}
</script>
<template>
<ContentWrap>
<vxe-grid ref="xGrid" v-bind="gridOptions">
<template #toolbar_buttons>
<el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
<Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
</el-button>
</template>
<template #status_default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #action_default="{ row }">
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleUpdate(row.id)"
>
<Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:update']"
@click="handleDetail(row)"
>
<Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
</el-button>
<el-button
link
type="primary"
v-hasPermi="['system:post:delete']"
@click="handleDelete(row.id)"
>
<Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
</el-button>
</template>
</vxe-grid>
</ContentWrap>
<vxe-modal
v-model="dialogVisible"
id="postModel"
:title="dialogTitle"
width="800"
height="400"
min-width="460"
min-height="320"
show-zoom
resize
transfer
show-footer
>
<template #default>
<!-- 对话框(添加 / 修改) -->
<vxe-form
ref="xForm"
v-if="['create', 'update'].includes(actionType)"
:data="formData"
:items="formItems"
:rules="rules"
/>
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailRef"
>
<template #status="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
<template #createTime="{ row }">
<span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</template>
</Descriptions>
</template>
<template #footer>
<vxe-button
v-if="['create', 'update'].includes(actionType)"
status="primary"
@click="submitForm"
>
{{ t('action.save') }}
</vxe-button>
<vxe-button @click="dialogVisible = false">{{ t('dialog.close') }}</vxe-button>
</template>
</vxe-modal>
</template>