S11-11 Vue-项目:mr_vue3_ts_cms2
[TOC]
Department
组件:PageSearch
使用组件
<template>
<div class="department">
+ <PageSearch @search-form="hdlSearchForm" />
</div>
</template>
<script setup lang="ts">
+ import PageSearch from './cpns/PageSearch/PageSearch.vue'
</script>
页面布局
<div class="page-search">
<el-form label-width="80px" :model="searchForm" ref="searchFormRef">
<el-row :gutter="120">
<el-col :span="8">
+ <el-form-item label="部门名称" prop="name">
<el-input v-model="searchForm.name" placeholder="请输入部门名称" />
</el-form-item>
</el-col>
<el-col :span="8">
+ <el-form-item label="部门领导" prop="leader">
<el-input v-model="searchForm.leader" placeholder="请输入部门领导" />
</el-form-item>
</el-col>
<el-col :span="8">
+ <el-form-item label="创建时间" prop="createAt">
<el-date-picker
v-model="searchForm.createAt"
type="daterange"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item class="btns">
<el-button icon="Refresh" @click="hdlReset">重置</el-button>
<el-button icon="Search" type="primary" @click="hdlQuery">查询</el-button>
</el-form-item>
</el-form>
</div>
组件:PageContent
使用组件
<template>
<div class="department">
<PageSearch @search-form="hdlSearchForm" />
+ <PageContent ref="contentRef" @change-visiable="hdlChangeVisiable" @edit-click="hdlEditClick" />
</div>
</template>
<script setup lang="ts">
import PageSearch from './cpns/PageSearch/PageSearch.vue'
+ import PageContent from './cpns/PageContent/PageContent.vue'
</script>
提取page的请求方法
1、service
/* 请求页面列表数据 */
export function postPageList(pageName: string, query: any) {
return mrRequest.post({
url: `/${pageName}/list`,
data: query
})
}
2、store
interface ISystemState {
userList: any[]
totalCount: number
+ pageList: any[]
+ pageTotalCount: number
}
state: (): ISystemState => ({
userList: [],
totalCount: 0,
+ pageList: [],
+ pageTotalCount: 0
}),
actions: {
/* 统一接口 */
/* 请求页面列表数据 */
+ async postPageListAction(pageName: string, query: any) {
const res = await postPageList(pageName, query)
this.pageList = res.data.list
this.pageTotalCount = res.data.totalCount
},
}
3、组件
/* 发送网络请求 */
function fetchPageList(searchForm: any = {}) {
const offset = (currentPage.value - 1) * pageSize.value
const size = pageSize.value
const query = { offset, size }
const finalQuery = { ...query, ...searchForm }
+ systemStore.postPageListAction('department', finalQuery)
}
fetchPageList()
4、组件获取store数据
/* 获取用户列表 */
const { pageList, pageTotalCount } = storeToRefs(systemStore)
5、组件展示数据
<div class="form">
+ <el-table :data="pageList" border style="width: 100%">
<el-table-column align="center" type="selection" />
<el-table-column align="center" type="index" label="序号" width="60px" />
+ <el-table-column align="center" prop="name" label="部门名称" width="180px" />
+ <el-table-column align="center" prop="leader" label="部门领导" width="180px" />
+ <el-table-column align="center" prop="parentId" label="上级部门" width="150px" />
<el-table-column align="center" prop="createAt" label="创建时间">
<template #default="scope">{{ formatUTC(scope.row.createAt) }}</template>
</el-table-column>
<el-table-column align="center" prop="updateAt" label="更新时间">
<template #default="scope">{{ formatUTC(scope.row.updateAt) }}</template>
</el-table-column>
<el-table-column align="center" label="操作" width="140px">
<template #default="scope">
<div class="btns">
<el-button type="primary" text @click="() => hdlEditItem(scope.row)">
<el-icon><Edit /></el-icon>
<span>编辑</span>
</el-button>
<el-button type="danger" text @click="() => hdlDeletePage(scope.row.id)">
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="navigation">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[5, 10, 20]"
small="small"
layout="total, sizes, prev, pager, next, jumper"
+ :total="pageTotalCount"
@size-change="hdlSizeChange"
@current-change="hdlPageChange"
/>
查询功能
在父组件中监听查询按钮点击事件,并调用子组件的方法执行查询
<!-- @search-form="hdlSearchForm" -->
<!-- ref="contentRef" -->
<div class="department">
+ <PageSearch @search-form="hdlSearchForm" />
+ <PageContent ref="contentRef"/>
</div>
/* 调用UserContent组件中的方法,查询数据 */
const contentRef = ref<InstanceType<typeof PageContent>>()
function hdlSearchForm(searchForm: any) {
if (contentRef.value) contentRef.value.fetchPageList(searchForm)
}
重置功能
1、在子组件中发送事件
/* 重置搜索表单 */
function hdlReset() {
emits('search-form', {})
searchFormRef.value?.resetFields()
}
2、在父组件中监听重置按钮点击事件,并调用子组件的方法执行重置
<PageSearch @search-form="hdlSearchForm" />
/* 调用UserContent组件中的方法,查询数据 */
const contentRef = ref<InstanceType<typeof PageContent>>()
function hdlSearchForm(searchForm: any) {
if (contentRef.value) contentRef.value.fetchPageList(searchForm)
}
删除功能
1、service
/* 根据id删除用户 */
export function delPageById(pageName: string, id: number) {
return mrRequest.delete({
url: `/${pageName}/${id}`
})
}
2、store
/* 根据id删除 */
async delPageByIdAction(pageName: string, id: number) {
await delPageById(pageName, id)
ElMessage.success('哈哈,删除成功~')
this.postPageListAction(pageName, { offset: 0, size: 5 })
},
3、组件
<el-button type="danger" text @click="() => hdlDeletePage(scope.row.id)">
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-button>
/* 根据id删除用户 */
function hdlDeletePage(id: number) {
systemStore.delPageByIdAction('department', id)
}
新增功能
1、在父组件中监听PageContent中的点击事件
<PageContent ref="contentRef" @change-visiable="hdlChangeVisiable" />
<PageModal ref="modalRef" />
/* 修改对话框是否显示 */
const modalRef = ref<InstanceType<typeof PageModal>>()
function hdlChangeVisiable() {
if (modalRef.value) modalRef.value.changeModalVisiable()
}
2、见:组件PageModal
组件:PageModal
使用组件
<div class="department">
<PageSearch @search-form="hdlSearchForm" />
<PageContent ref="contentRef" @change-visiable="hdlChangeVisiable" @edit-click="hdlEditClick" />
+ <PageModal ref="modalRef" />
</div>
修改PageModal
1、表单数据
/* 表单数据 */
const pageForm = reactive<any>({
name: '',
leader: '',
parentId: ''
})
2、模板
<div class="user-modal">
<el-dialog v-model="modalVisiable" :title="isEdit ? '编辑用户' : '新增用户'" width="30%" center>
<div class="form">
<el-form
+ :model="pageForm"
:rules="formRules"
label-position="right"
label-width="100px"
size="large"
ref="formRef"
>
+ <el-form-item label="部门名称" prop="name">
<el-input v-model="pageForm.name" placeholder="请输入部门名称" />
</el-form-item>
+ <el-form-item label="部门领导" prop="realname">
<el-input v-model="pageForm.leader" placeholder="请输入部门领导" />
</el-form-item>
+ <el-form-item label="上级部门" prop="parentId">
<el-select
v-model="pageForm.parentId"
class="m-2"
placeholder="请选择上级部门"
style="width: 100%"
>
<el-option
v-for="item in departmentLists"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="modalVisiable = false">取消</el-button>
<el-button type="primary" @click="hdlSubmitUser">确定</el-button>
</span>
</template>
</el-dialog>
</div>
3、从store获取数据
/* 获取store中数据 */
const { departmentLists } = storeToRefs(mainStore)
点击确定创建部门
1、service
/* 新增用户 */
export function addPage(pageName: string, pageInfo: any) {
return mrRequest.post({
url: `/${pageName}`,
data: pageInfo
})
}
2、store
/* 新增 */
async addPageAction(pageName: string, pageInfo: any) {
await addPage(pageName, pageInfo)
this.postPageListAction(pageName, { offset: 0, size: 5 })
},
3、组件
<el-button type="primary" @click="hdlSubmitUser">确定</el-button>
/* 添加、编辑用户 */
const formRef = ref<InstanceType<typeof ElForm>>()
function hdlSubmitUser() {
modalVisiable.value = false
pageInfo.value = pageForm
// 验证表单
formRef.value?.validate((valid: any) => {
if (valid) {
// 验证成功
if (isEdit.value) {
console.log('pageId', pageId.value, 'pageInfo', pageInfo.value)
systemStore.editPageAction('department', pageId.value, pageInfo.value)
ElMessage.success('哈哈,修改用户成功~')
} else {
console.log('pageInfo', pageInfo.value)
+ systemStore.addPageAction('department', pageInfo.value)
ElMessage.success('哈哈,新增用户成功~')
}
} else {
// 验证失败
ElMessage.error('呜呼,验证失败,请重新来过~')
}
})
}
编辑功能
1、service
/* 编辑用户 */
export function editPage(pageName: string, id: number, pageInfo: any) {
return mrRequest.patch({
url: `/${pageName}/${id}`,
data: pageInfo
})
}
2、store
/* 编辑 */
async editPageAction(pageName: string, id: number, pageInfo: any) {
await editPage(pageName, id, pageInfo)
this.postPageListAction(pageName, { offset: 0, size: 5 })
}
3、组件
/* 添加、编辑用户 */
const formRef = ref<InstanceType<typeof ElForm>>()
function hdlSubmitUser() {
modalVisiable.value = false
pageInfo.value = pageForm
// 验证表单
formRef.value?.validate((valid: any) => {
if (valid) {
// 验证成功
if (isEdit.value) {
+ systemStore.editPageAction('department', pageId.value, pageInfo.value)
ElMessage.success('哈哈,修改用户成功~')
} else {
systemStore.addPageAction('department', pageInfo.value)
ElMessage.success('哈哈,新增用户成功~')
}
} else {
// 验证失败
ElMessage.error('呜呼,验证失败,请重新来过~')
}
})
}
抽取
抽取:PageSearch
使用组件
import PageSearch from '@/components/PageSearch/PageSearch.vue'
配置
1、定义配置
/deparment/config/search.config.ts
const searchConfig = {
formItems: [
{ type: 'input', prop: 'name', label: '部门名称', placeholder: '请输入部门名称' },
{ type: 'input', prop: 'leader', label: '部门领导', placeholder: '请输入部门领导' },
{ type: 'date-picker', prop: 'createAt', label: '创建时间' }
]
}
export default searchConfig
2、传递配置
import searchConfig from './config/search.config'
<div class="department">
<PageSearch :search-config="searchConfig" @search-form="hdlSearchForm" />
</div>
3、在组件内部接收searchConfig
export interface IProps {
searchConfig: {
formItems: any[]
}
}
defineProps<IProps>()
根据配置渲染模板
1、根据配置,初始化search表单列表
/* 表单数据 */
const initForm: any = {}
for (const item of props.searchConfig.formItems) {
initForm[item.prop] = item.initValue ?? ''
}
const searchForm = reactive<any>(initForm)
2、遍历配置项,渲染search
<el-form label-width="80px" :model="searchForm" ref="searchFormRef">
<el-row :gutter="120">
+ <template v-for="item in searchConfig.formItems" :key="item.prop">
<el-col :span="8">
<el-form-item :label="item.label" :prop="item.prop">
+ <template v-if="item.type === 'input'">
<el-input v-model="searchForm[item.prop]" :placeholder="item.placeholder" />
</template>
+ <template v-if="item.type === 'date-picker'">
<el-date-picker
v-model="searchForm[item.prop]"
type="daterange"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
</template>
</el-form-item>
</el-col>
</template>
</el-row>
<el-form-item class="btns">
<el-button icon="Refresh" @click="hdlReset">重置</el-button>
<el-button icon="Search" type="primary" @click="hdlQuery">查询</el-button>
</el-form-item>
</el-form>
2、select类型
{
type: 'select',
prop: 'enable',
label: '状态',
placeholder: '请选择状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
}
<template v-else-if="item.type === 'select'">
<el-select
v-model="searchForm[item.prop]"
class="m-2"
:placeholder="item.placeholder"
style="width: 100%"
>
+ <el-option v-for="value in item.options" :key="value.value" v-bind="value" />
</el-select>
</template>
可选配置
1、labelWidth
抽取:PageContent
使用组件
import PageContent from '@/components/PageContent/PageContent.vue'
header-配置
1、定义配置
const contentConfig = {
pageName: 'department',
header: {
title: '部门列表',
btnTitle: '新增部门'
}
}
export default contentConfig
2、传递配置
<template>
<div class="department">
<PageSearch :search-config="searchConfig" @search-form="hdlSearchForm" />
+ <PageContent
+ ref="contentRef"
+ :content-config="contentConfig"
+ @change-visiable="hdlChangeVisiable"
+ @edit-click="hdlEditClick"
+ />
</div>
</template>
<script setup lang="ts">
+ import contentConfig from './config/content.config'
</script>
3、组件内部接收配置
export interface IProps {
contentConfig: {
pageName: string
header: {
title: string
btnTitle: string
}
}
}
const props = defineProps<IProps>()
header-渲染模板
<div class="header">
+ <h3 class="title">{{ contentConfig.header.title }}</h3>
<el-button class="btn" type="primary" @click="hdlAddItem">
+ {{ contentConfig.header.btnTitle }}
</el-button>
</div>
table-配置
1、定义配置
const contentConfig = {
pageName: 'department',
header: {
title: '部门列表',
btnTitle: '新增部门'
},
+ formItems: [
{ type: 'selection', label: '选择', width: '50px' },
{ type: 'index', label: '序号', width: '60px' },
{ type: 'normal', label: '部门名称', prop: 'name', width: '180px' },
{ type: 'normal', label: '部门领导', prop: 'leader', width: '180px' },
{ type: 'normal', label: '上级部门', prop: 'parentId', width: '150px' },
{ type: 'timer', label: '创建时间', prop: 'createAt' },
{ type: 'timer', label: '更新时间', prop: 'updateAt' },
{ type: 'handler', label: '操作', width: '140px' }
]
}
export default contentConfig
带插槽的时间
{ type: 'timer', label: '创建时间', prop: 'createAt' },
{ type: 'timer', label: '更新时间', prop: 'updateAt' },
操作类型
{ type: 'handler', label: '操作', width: '140px' }
2、传递配置
<PageContent
ref="contentRef"
+ :content-config="contentConfig"
@change-visiable="hdlChangeVisiable"
@edit-click="hdlEditClick"
/>
3、组件内部接收配置
export interface IProps {
contentConfig: {
pageName: string
header: {
title: string
btnTitle: string
}
formItems: any[]
}
}
const props = defineProps<IProps>()
table-渲染模板
1、基础模板
<div class="form">
<el-table :data="pageList" border style="width: 100%">
<template v-for="item in contentConfig.formItems" :key="item.prop">
+ <template v-if="item.type === 'timer'">
<el-table-column align="center" v-bind="item">
<template #default="scope">{{ formatUTC(scope.row[item.prop]) }}</template>
</el-table-column>
</template>
+ <template v-else-if="item.type === 'handler'">
<el-table-column align="center" v-bind="item">
<template #default="scope">
<div class="btns">
<el-button type="primary" text @click="() => hdlEditItem(scope.row)">
<el-icon><Edit /></el-icon>
<span>编辑</span>
</el-button>
<el-button type="danger" text @click="() => hdlDeletePage(scope.row.id)">
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-button>
</div>
</template>
</el-table-column>
</template>
+ <template v-else>
<el-table-column align="center" v-bind="item" />
</template>
</template>
</el-table>
</div>
2、时间类型-timer
+ <template v-if="item.type === 'timer'">
<el-table-column align="center" v-bind="item">
<template #default="scope">{{ formatUTC(scope.row[item.prop]) }}</template>
</el-table-column>
</template>
3、操作类型-handler
+ <template v-else-if="item.type === 'handler'">
<el-table-column align="center" v-bind="item">
<template #default="scope">
<div class="btns">
<el-button type="primary" text @click="() => hdlEditItem(scope.row)">
<el-icon><Edit /></el-icon>
<span>编辑</span>
</el-button>
<el-button type="danger" text @click="() => hdlDeletePage(scope.row.id)">
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-button>
</div>
</template>
</el-table-column>
</template>
定制插槽
1、修改类型为custom,并指定插槽名
{ type: 'custom', label: '上级部门', prop: 'parentId', width: '150px', slotName: 'parent' },
{ type: 'custom', label: '上级领导', prop: 'leader', width: '150px', slotName: 'leader' },
2、在模板中添加具名插槽
<!-- 定制插槽 -->
<template v-else-if="item.type === 'custom'">
<el-table-column align="center" v-bind="item">
<template #default="scope">
+ <slot :name="item.slotName" v-bind="scope" :prop="item.prop"></slot>
</template>
</el-table-column>
</template>
3、使用插槽,自定义数据
<PageContent
ref="contentRef"
:content-config="contentConfig"
@change-visiable="hdlChangeVisiable"
@edit-click="hdlEditClick"
>
+ <template #parent="scope">
+ <div style="color: red">{{ scope.row[scope.prop] }}</div>
</template>
+ <template #leader="scope">
+ <div style="color: green">{{ scope.row[scope.prop] }}</div>
</template>
</PageContent>
4、动态决定插槽中的数据
<template #default="scope">
<slot :name="item.slotName"
v-bind="scope"
+ :prop="item.prop">
</slot>
</template>
<template #parent="scope">
+ <div style="color: red">{{ scope.row[scope.prop] }}</div>
</template>
动态pageName
1、传递的配置中包含pageName
const contentConfig = {
+ pageName: 'department',
header: {
title: '部门列表',
btnTitle: '新增部门'
},
}
export interface IProps {
contentConfig: {
+ pageName: string
header: {
title: string
btnTitle: string
}
formItems: any[]
}
}
const props = defineProps<IProps>()
2、根据传递的pageName发送请求
/* 发送网络请求 */
function fetchPageList(searchForm: any = {}) {
const offset = (currentPage.value - 1) * pageSize.value
const size = pageSize.value
const query = { offset, size }
const finalQuery = { ...query, ...searchForm }
+ systemStore.postPageListAction(props.contentConfig.pageName, finalQuery)
}
fetchPageList()
/* 根据id删除用户 */
function hdlDeletePage(id: number) {
+ systemStore.delPageByIdAction(props.contentConfig.pageName, id)
}
抽取:PageModal
使用组件
import PageModal from '@/components/PageModal/PageModal.vue'
配置
1、定义配置
const modalConfig = {
pageName: 'department',
header: {
addTitle: '新建部门',
editTitle: '编辑部门'
},
formItems: [
{ type: 'input', label: '部门名称', prop: 'name', placeholder: '请输入部门名称' },
{ type: 'input', label: '部门领导', prop: 'leader', placeholder: '请输入部门领导' },
{ type: 'select', label: '上级部门', prop: 'parentId', placeholder: '请选择上级部门' }
]
}
export default modalConfig
2、传递配置
<div class="department">
<PageSearch :search-config="searchConfig" @search-form="hdlSearchForm" />
<PageContent
ref="contentRef"
:content-config="contentConfig"
@change-visiable="hdlChangeVisiable"
@edit-click="hdlEditClick"
>
</PageContent>
+ <PageModal :modal-config="modalConfig" ref="modalRef" />
</div>
<script setup lang="ts">
+ import modalConfig from './config/modal.config'
</script>
3、接收配置
export interface IProps {
modalConfig: {
pageName: string
header: {
addTitle: string
editTitle: string
}
formItems: any[]
}
}
const props = defineProps<IProps>()
渲染模板
1、header
<el-dialog
v-model="modalVisiable"
+ :title="isEdit ? modalConfig.header.editTitle : modalConfig.header.addTitle"
width="30%"
center
>
<div class="form">
...
</el-dialog>
2、表单
<el-form
:model="pageForm"
:rules="formRules"
label-position="right"
label-width="100px"
size="large"
ref="formRef"
>
+ <template v-for="item in modalConfig.formItems" :key="item.prop">
<el-form-item v-bind="item">
+ <template v-if="item.type === 'input'">
<el-input v-model="pageForm[item.prop]" :placeholder="item.placeholder" />
</template>
+ <template v-else-if="item.type === 'select'">
<el-select
v-model="pageForm[item.prop]"
class="m-2"
:placeholder="item.placeholder"
style="width: 100%"
>
<el-option
++ v-for="value in item?.options"
++ :key="value.value"
++ :label="value.label"
++ :value="value.value"
/>
</el-select>
</template>
</el-form-item>
</template>
</el-form>
初始化formData
/* 表单数据 */
const initForm: any = {}
for (const item of props.modalConfig.formItems) {
initForm[item.prop] = item.initValue ?? ''
}
const pageForm = reactive<any>(initForm)
设置初始化值
动态options数据
0、数据
entireDepartments:
1、初始化config中的options为空数组
{
type: 'select',
label: '上级部门',
prop: 'parentId',
placeholder: '请选择上级部门',
+ options: []
}
2、在传递config过程中对config进行修改
注意: 需要对entireDepartments数据进行转换后才能使用
/* 为modalConfig添加动态options数据 */
const mainStore = useMainStore()
const modalConfigRef = computed(() => {
const { departmentLists } = mainStore
// 1. 整理departmentLists数据
const departments = departmentLists.map((item) => {
return { label: item.name, value: item.id }
})
// 2. 动态添加departments到item.options中
for (const item of modalConfig.formItems) {
if (item.prop === 'parentId') {
item.options.push(...departments)
}
}
return modalConfig
})
<PageModal :modal-config="modalConfigRef" ref="modalRef" />
动态pageName
1、传递的配置中包含pageName
const modalConfig: IModalConfig = {
+ pageName: 'department',
header: {
addTitle: '新建部门',
editTitle: '编辑部门'
},
}
export interface IModalConfig {
+ pageName: string
header: {
addTitle: string
editTitle: string
}
formItems: any[]
}
/* define函数 */
export interface IModalProps {
+ modalConfig: IModalConfig
}
const props = defineProps<IModalProps>()
2、根据传递的pageName发送请求
/* 添加、编辑用户 */
const formRef = ref<InstanceType<typeof ElForm>>()
function hdlSubmitUser() {
modalVisiable.value = false
pageInfo.value = pageForm
// 验证表单
formRef.value?.validate((valid: any) => {
if (valid) {
// 验证成功
if (isEdit.value) {
+ systemStore.editPageAction(props.modalConfig.pageName, pageId.value, pageInfo.value)
ElMessage.success('哈哈,修改用户成功~')
} else {
+ systemStore.addPageAction(props.modalConfig.pageName, pageInfo.value)
ElMessage.success('哈哈,新增用户成功~')
}
} else {
// 验证失败
ElMessage.error('呜呼,验证失败,请重新来过~')
}
})
}
Hooks抽取
将不需要修改的代码逻辑部分抽取到Hooks中
重置、查询功能
import { ref } from 'vue'
// import type PageContent from '@/components/PageContent/PageContent.vue'
function usePageContent() {
/* 调用UserContent组件中的方法,查询、重置数据 */
const contentRef = ref<any>()
function hdlSearchForm(searchForm: any) {
contentRef.value?.fetchPageList(searchForm)
}
return {
contentRef,
hdlSearchForm
}
}
export default usePageContent
使用hooks
const { contentRef, hdlSearchForm } = usePageContent()
新增、修改功能
import { ref } from 'vue'
// import type PageModal from '@/components/PageModal/PageModal.vue'
function usePageModal() {
/* 修改对话框是否显示 */
// const modalRef = ref<InstanceType<typeof PageModal>>()
const modalRef = ref<any>()
function hdlChangeVisiable() {
modalRef.value?.changeModalVisiable()
}
/* 调用模态组件内函数,修改用户 */
function hdlEditClick(pageItem: any) {
modalRef.value?.changeModalVisiable(pageItem)
}
return {
modalRef,
hdlChangeVisiable,
hdlEditClick
}
}
export default usePageModal
使用hooks
const { modalRef, hdlChangeVisiable, hdlEditClick } = usePageModal()
Role
组件:PageSearch
使用组件
<div class="role">
+ <PageSearch />
</div>
<script setup lang="ts">
+ import PageSearch from '@/components/PageSearch/PageSearch.vue'
</script>
搜索配置
const searchConfig = {
pageName: 'role',
formItems: [
{ type: 'input', label: '角色名称', prop: 'name', placeholder: '请输入角色名称' },
{ type: 'input', label: '权限介绍', prop: 'intro', placeholder: '请输入权限介绍' },
{ type: 'date-picker', label: '创建时间', prop: 'createAt' }
]
}
export default searchConfig
使用配置
<template>
<div class="role">
+ <PageSearch :search-config="searchConfig" />
</div>
<script setup lang="ts">
import PageSearch from '@/components/PageSearch/PageSearch.vue'
+ import searchConfig from './config/search.config'
</script>
组件:PageContent
使用组件
<template>
<div class="role">
<PageSearch :search-config="searchConfig" @search-form="hdlSearchForm" />
+ <PageContent/>
</div>
</template>
<script setup lang="ts">
import PageSearch from '@/components/PageSearch/PageSearch.vue'
+ import PageContent from '@/components/PageContent/PageContent.vue'
</script>
内容配置
const contentConfig = {
pageName: 'role',
header: {
title: '角色列表',
btnTitle: '新增角色'
},
formItems: [
{ type: 'selection', label: '选择', width: '50px' },
{ type: 'index', label: '序号', width: '60px' },
{ type: 'normal', label: '角色名称', prop: 'name', width: '180px' },
{ type: 'normal', label: '权限介绍', prop: 'intro', width: '180px' },
{ type: 'timer', label: '创建时间', prop: 'createAt' },
{ type: 'timer', label: '更新时间', prop: 'updateAt' },
{ type: 'handler', label: '操作', width: '140px' }
]
}
export default contentConfig
使用配置
<template>
<div class="role">
<PageSearch :search-config="searchConfig" @search-form="hdlSearchForm" />
<PageContent
+ :content-config="contentConfig"
/>
</div>
</template>
<script setup lang="ts">
import PageSearch from '@/components/PageSearch/PageSearch.vue'
import PageContent from '@/components/PageContent/PageContent.vue'
import searchConfig from './config/search.config'
+ import contentConfig from './config/content.config'
</script>
组件:PageModal
使用组件
<template>
<div class="role">
<PageSearch :search-config="searchConfig" @search-form="hdlSearchForm" />
<PageContent
:content-config="contentConfig"
ref="contentRef"
@change-visiable="hdlChangeVisiable"
@edit-click="hdlEditClick"
/>
+ <PageModal />
</div>
</template>
<script setup lang="ts">
import PageSearch from '@/components/PageSearch/PageSearch.vue'
import PageContent from '@/components/PageContent/PageContent.vue'
+ import PageModal from '@/components/PageModal/PageModal.vue'
</script>
配置
const modalConfig = {
pageName: 'role',
header: {
addTitle: '新增角色',
editTitle: '修改角色'
},
formItems: [
{ type: 'input', label: '角色名称', prop: 'name', placeholder: '请输入角色名称' },
{ type: 'input', label: '权限介绍', prop: 'intro', placeholder: '请输入权限介绍' }
]
}
export default modalConfig
使用配置
<template>
<div class="role">
+ <PageModal :modal-config="modalConfig" ref="modalRef" />
</div>
</template>
<script setup lang="ts">
import PageModal from '@/components/PageModal/PageModal.vue'
+ import modalConfig from './config/modal.config'
</script>
事件逻辑
// 事件函数
const { contentRef, hdlSearchForm } = usePageContent()
const { modalRef, hdlChangeVisiable, hdlEditClick } = usePageModal()
<div class="role">
+ <PageSearch :search-config="searchConfig" @search-form="hdlSearchForm" />
<PageContent
:content-config="contentConfig"
+ ref="contentRef"
+ @change-visiable="hdlChangeVisiable"
+ @edit-click="hdlEditClick"
/>
+ <PageModal :modal-config="modalConfig" ref="modalRef" />
</div>
Menu
组件:PageContent
数据:
使用组件
<template>
<div class="menu">
+ <PageContent />
</div>
</template>
<script setup lang="ts">
+ import PageContent from '@/components/PageContent/PageContent.vue'
</script>
配置
const contentConfig = {
pageName: 'menu',
header: {
title: '菜单列表',
btnTitle: '新建菜单'
},
formItems: [
{ type: 'normal', label: '菜单名称', prop: 'name', width: '150px' },
{ type: 'normal', label: '级别', prop: 'type', width: '80px' },
{ type: 'normal', label: '菜单url', prop: 'url', width: '180px' },
{ type: 'normal', label: '菜单icon', prop: 'icon', width: '190px' },
{ type: 'normal', label: '排序', prop: 'sort', width: '80px' },
{ type: 'normal', label: '权限', prop: 'permission', width: '180px' },
{ type: 'timer', label: '创建时间', prop: 'createAt' },
{ type: 'timer', label: '更新时间', prop: 'updateAt' },
{ type: 'handler', label: '操作', width: '140px' }
]
}
export default contentConfig
使用配置
<template>
<div class="menu">
+ <PageContent :content-config="contentConfig" />
</div>
</template>
<script setup lang="ts">
import PageContent from '@/components/PageContent/PageContent.vue'
+ import contentConfig from './config/content.config'
</script>
菜单子树展开
1、基本使用
注意: 当数据中子数据通过children
区分时,tree-props
属性可以省略
<el-table
:data="pageList"
border
style="width: 100%"
+ row-key="id"
+ :tree-props="{ children: 'chidren', hasChildren: 'hasChildren' }"
>
2、*注意:*如果想让子菜单树显示,在配置时,不能添加type: 'normal'
formItems: [
+ { label: '菜单名称', prop: 'name', width: '150px' },
+ { label: '级别', prop: 'type', width: '80px' },
+ { label: '菜单url', prop: 'url', width: '180px' },
+ { label: '菜单icon', prop: 'icon', width: '190px' },
+ { label: '排序', prop: 'sort', width: '80px' },
+ { label: '权限', prop: 'permission', width: '180px' },
{ type: 'timer', label: '创建时间', prop: 'createAt' },
{ type: 'timer', label: '更新时间', prop: 'updateAt' },
{ type: 'handler', label: '操作', width: '140px' }
],
3、动态定义row-key
const contentConfig: IContentConfig = {
pageName: 'menu',
header: {
title: '菜单列表',
btnTitle: '新建菜单'
},
formItems: [
...
],
+ childrenTree: {
+ rowKey: 'id',
+ treeProps: {
+ children: 'children',
+ hasChildren: 'hasChildren'
+ }
+ }
}
<el-table
:data="pageList"
border
style="width: 100%"
+ v-bind="contentConfig.childrenTree"
>
权限管理
分配权限
新建角色时分配权限
定义配置
formItems: [
{ type: 'input', label: '角色名称', prop: 'name', placeholder: '请输入角色名称' },
{ type: 'input', label: '权限介绍', prop: 'intro', placeholder: '请输入权限介绍' },
+ { type: 'custom', label: '分配权限', slotName: 'menuList' }
]
自定义插槽
<template v-else-if="item.type === 'custom'">
<slot :name="item.slotName"></slot>
</template>
请求完整菜单树数据
1、service
/* 获取权限列表 */
export function postMenuLists() {
return mrRequest.post({
url: '/menu/list'
})
}
2、store
async postMenuListsAction() {
const res = await postMenuLists()
this.menuLists = res.data.list
}
3、组件Role中
const mainStore = useMainStore()
const { menuLists } = storeToRefs(mainStore)
const defaultProps = {
children: 'children',
label: 'name'
}
<PageModal :modal-config="modalConfig" :other-info="otherInfo" ref="modalRef">
+ <template #menuList>
<el-tree
+ :data="menuLists"
+ show-checkbox
+ node-key="id"
+ :props="defaultProps"
ref="treeRef"
@check="hdlSelectChecked"
/>
</template>
</PageModal>
创建角色时带权限
1、点击权限树后获取选中项的id
<el-tree
:data="menuLists"
show-checkbox
node-key="id"
:props="defaultProps"
ref="treeRef"
+ @check="hdlSelectChecked"
/>
/* 获取选中的权限节点 */
const otherInfo = ref({})
function hdlSelectChecked(param1: any, param2: any) {
const menuList = [...param2.checkedKeys, ...param2.halfCheckedKeys]
otherInfo.value = { menuList }
}
hdlSelectChecked
的2个参数:
2、传递额外的数据otherInfo到PageModal组件中
<PageModal :modal-config="modalConfig"
+ :other-info="otherInfo"
ref="modalRef">
3、在PageModal组件中接收数据
export interface IModalProps {
modalConfig: IModalConfig
+ otherInfo?: any
}
const props = defineProps<IModalProps>()
4、合并otherInfo和formData
function hdlSubmitUser() {
modalVisiable.value = false
+ pageInfo.value = { ...pageForm, ...props.otherInfo }
console.log('pageInfo', pageInfo.value)
// 验证表单
formRef.value?.validate((valid: any) => {
if (valid) {
// 验证成功
if (isEdit.value) {
+ systemStore.editPageAction(props.modalConfig.pageName, pageId.value, pageInfo.value)
ElMessage.success('哈哈,修改用户成功~')
} else {
+ systemStore.addPageAction(props.modalConfig.pageName, pageInfo.value)
ElMessage.success('哈哈,新增用户成功~')
}
} else {
// 验证失败
ElMessage.error('呜呼,验证失败,请重新来过~')
}
})
}
权限菜单回显
1、绑定ElTree的ref
<el-tree
:data="menuLists"
show-checkbox
node-key="id"
:props="defaultProps"
+ ref="treeRef"
@check="hdlSelectChecked"
/>
2、在Role组件中定义回调函数,并将其传递给usePageModal中,目的是为了获取数据itemData
+ const { modalRef, hdlChangeVisiable, hdlEditClick } = usePageModal(editCB)
/* 点击编辑,回显权限 */
const treeRef = ref<InstanceType<typeof ElTree>>()
+ function editCB(pageItem: any) {
// console.log('pageItem: ', pageItem.menuList)
nextTick(() => {
const checkedKeys = mapMenuListToIds(pageItem.menuList)
treeRef.value?.setCheckedKeys(checkedKeys)
})
}
3、在Hook中接收回调函数
function usePageModal(editCB: (pageItem: any) => void) {
/* 修改对话框是否显示 */
// const modalRef = ref<InstanceType<typeof PageModal>>()
const modalRef = ref<any>()
function hdlChangeVisiable() {
modalRef.value?.changeModalVisiable()
}
/* 调用模态组件内函数,修改用户 */
function hdlEditClick(pageItem: any) {
modalRef.value?.changeModalVisiable(pageItem)
+ if (editCB) editCB(pageItem)
}
return {
modalRef,
hdlChangeVisiable,
hdlEditClick
}
}
4、定义一个获取数组中id的工具函数
/**
* 根据权限列表获取其所有根节点id
* @param menuList 权限列表
* @returns 权限列表的所有根节点id
*/
export function mapMenuListToIds(menuList: any[]) {
const ids: number[] = []
function recurseGetId(menuList: any[]) {
for (const item of menuList) {
if (item.children) {
recurseGetId(item.children)
} else {
ids.push(item.id)
}
}
}
recurseGetId(menuList)
console.log('ids', ids)
return ids
}
5、使用获取到的id,调用ElTree的setCheckedKeys
方法,给控件添加初始值
/* 点击编辑,回显权限 */
const treeRef = ref<InstanceType<typeof ElTree>>()
function editCB(pageItem: any) {
// console.log('pageItem: ', pageItem.menuList)
+ nextTick(() => {
+ const checkedKeys = mapMenuListToIds(pageItem.menuList)
+ treeRef.value?.setCheckedKeys(checkedKeys)
})
}
新增重置权限菜单
思路: 在新增角色时,同样添加一个callback,并在setCheckedKeys
时传递一个空数组
按钮权限
获取用户所有按钮权限
1、定义获取userMenus中所有permissions的工具函数
2、使用工具函数,在@store/login/login.ts
中的loginAction
和loadLocalCacheAction
中都获取permissions
根据权限展示按钮
1、在PageContent中获取对应的增删改查的权限
2、根据权限展示、隐藏按钮
3、封装权限判断
4、使用封装的hook
5、如果没有query权限,不展示PageSearch组件
PageSearch.vue
为user页面添加按钮权限
新建编辑删除后重置page
思路: 由于新建、编辑、删除都会在systemStore中执行action,因此可以通过在PageContent页面中监听store中是否有执行这些对应的action来决定是否要重置page
在action执行成功之后再重置page
Dashboard
顶部数字展示
1、布局
组件:CountCard
使用组件
页面布局
悬浮提示
动态渲染数据
1、组件内定义类型,并设置默认值
2、渲染组件
请求数据
1、service
2、main
3、组件-发起请求
4、组件-从store中获取数据
5、遍历数据
数据动画效果
1、动画插件:npm i countup.js
2、使用countup添加动画
3、添加人民币符号
组件:ChartCard
使用组件
页面布局
ECharts
API
- echarts
echarts.init(dom, theme?, opt?)
echarts.registerMap(mapName, opt | geoJSON)
- echartsInstance
echartsInstance.setOption()
安装
依赖包: echarts
安装: pnpm add echarts
基本使用
1、指定echarts的容器
2、引入echarts
封装Echarts
1、目录结构
组件:BaseEchart
使用组件
组件:PieEchart
1、使用组件
2、使用base-echart
组件,并传入option
3、option配置
4、修改BaseEchart,使用传递进来的option
5、统一导出
组件:LineEchart
1、使用组件
2、配置
3、请求商品销量数据
见:动态渲染数据
4、转化请求的商品销量数据
5、传递数据
组件:RoseEchart
1、使用组件
2、页面布局
3、修改BaseEchart
组件:BarEchart
1、store
2、组件
3、渲染数据
4、配置
组件:MapEchart
1、注册map
2、页面布局
3、获取城市经纬度工具函数
4、经纬度数据
动态渲染数据
1、service
2、store
3、组件Dashboard,获取到的数据需要通过map转化一下
4、组件PieEchart
在pie-echart
组件中,可以使用computed
监听props.pieData
数据的变化
5、组件BaseEchart中,使用watchEffect()监听options数据的变化,重新设置echart
页面缩放
1、window缩放时,echart也同时跟随缩放
2、页面组件为响应式布局