S11-10 Vue-项目:mr_vue3_ts_cms2
[TOC]
User
页面布局
<div class="user">
<div class="user-search">
<UserSearch />
</div>
<div class="content">
<UserContent />
</div>
</div>
组件:UserSearch
使用组件
<template>
<div class="user">
<div class="user-search">
+ <UserSearch />
</div>
<div class="content">
<UserContent />
</div>
</div>
</template>
<script setup lang="ts">
+ import UserSearch from './cpns/UserSearch/UserSearch.vue'
import UserContent from './cpns/UserContent/UserContent.vue'
</script>
页面布局
注意: 在element-plus中允许将多行的el-col
放到一个el-row
中,配合:span
属性,当span
满24份时会自动换行
<div class="user-search">
<el-form label-width="80px">
<el-row :gutter="120">
<el-col :span="8">
<el-form-item label="用户名">
<el-input placeholder="请输入用户名" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="真实姓名">
<el-input placeholder="请输入真实姓名" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="手机号码">
<el-input placeholder="请输入手机号码" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="状态">
+ <el-select class="m-2" placeholder="Select" style="width: 100%">
<el-option label="启用" :value="1" />
<el-option label="禁用" :value="0" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="创建时间">
+ <el-date-picker
type="daterange"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item class="btns">
<el-button icon="Refresh">重置</el-button>
<el-button icon="Search" type="primary">查询</el-button>
</el-form-item>
</el-form>
</div>
element国际化@
重置、查询按钮
<el-form-item class="btns">
+ <el-button icon="Refresh">重置</el-button>
+ <el-button icon="Search" type="primary">查询</el-button>
</el-form-item>
重置功能
1、绑定表单数据
+ <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="realname">
+ <el-input v-model="searchForm.realname" placeholder="请输入真实姓名" />
</el-form-item>
</el-col>
</el-row>
</el-form>
/* 表单数据 */
const searchForm = reactive({
name: '',
realname: '',
cellphone: '',
enable: 1,
creatAt: []
})
2、重置表单
<el-button icon="Refresh" @click="hdlReset">重置</el-button>
<el-form label-width="80px" :model="searchForm" ref="searchFormRef">
const searchFormRef = ref<InstanceType<typeof ElForm>>()
/* 重置搜索表单 */
function hdlReset() {
searchFormRef.value?.resetFields()
}
3、注意: 如果想让resetFields起作用,需要添加prop属性
+ <el-form-item label="用户名" prop="name"> <!-- prop="name" -->
<el-input v-model="searchForm.name" placeholder="请输入用户名" />
</el-form-item>
查询功能
<el-button icon="Search" type="primary" @click="hdlQuery">查询</el-button>
/* 根据搜索项查询 */
function hdlQuery() {
console.log('根据搜索项查询')
}
组件:UserContent
使用组件
<template>
<div class="user">
<div class="user-search">
<UserSearch />
</div>
<div class="content">
+ <UserContent />
</div>
</div>
</template>
<script setup lang="ts">
import UserSearch from './cpns/UserSearch/UserSearch.vue'
+ import UserContent from './cpns/UserContent/UserContent.vue'
</script>
页面布局
<div class="user-content">
<div class="header">header</div>
<div class="form">form</div>
<div class="navigation">navigation</div>
</div>
表格-请求用户列表数据
1、在services中发送网络请求
/* 请求用户列表数据 */
export function postUserList() {
return mrRequest.post({
url: '/users/list',
data: {
offset: 0,
size: 10
}
})
}
2、在pinia中调用网络请求,并保存返回结果
import { postUserList } from '@/service/main/system'
import { defineStore } from 'pinia'
interface ISystemState {
userList: any[]
totalCount: number
}
const useSystemStore = defineStore('system', {
+ state: (): ISystemState => ({
userList: [],
totalCount: 0
}),
actions: {
/* 请求用户列表数据 */
+ async postUserListAction() {
const res = await postUserList()
this.userList = res.data.list
this.totalCount = res.data.totalCount
}
}
})
export default useSystemStore
3、在UserContent组件中调用Action,
import useSystemStore from '@/store/main/system'
const systemStore = useSystemStore()
systemStore.postUserListAction()
表格-展示用户列表
1、获取数据
import useSystemStore from '@/store/main/system'
import { storeToRefs } from 'pinia'
const systemStore = useSystemStore()
systemStore.postUserListAction()
/* 获取用户列表 */
+ const { userList, totalCount } = storeToRefs(systemStore)
2、使用el-table展示数据
<el-table :data="userList" 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="150px" />
<el-table-column align="center" prop="realname" label="真实姓名" width="150px" />
<el-table-column align="center" prop="cellphone" label="手机号码" width="150px" />
<el-table-column align="center" prop="enable" label="状态" width="60px" />
<el-table-column align="center" prop="createAt" label="创建时间" />
<el-table-column align="center" prop="updateAt" label="更新时间" />
<el-table-column align="center" label="操作" width="150px"></el-table-column>
</el-table>
4、调整表格样式-宽度、居中、高度、按钮样式
表格样式-宽度、居中
<el-table-column align="center" type="index" label="序号" width="60px" />
<el-table-column align="center" prop="name" label="用户名" width="150px" />
<el-table-column align="center" prop="realname" label="真实姓名" width="150px" />
<el-table-column align="center" prop="cellphone" label="手机号码" width="150px" />
<el-table-column align="center" prop="enable" label="状态" width="60px">
表格样式-高度
.el-table {
:deep(.el-table__cell) {
padding: 12px 0;
}
}
表格样式-按钮样式、给按钮添加图标、样式
<el-table-column align="center" label="操作" width="150px">
<div class="btns">
+ <el-button type="primary" text>
<el-icon><Edit /></el-icon>
<span>编辑</span>
</el-button>
+ <el-button type="danger" text>
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-button>
</div>
</el-table-column>
表格-展示启用
知识点: 作用域插槽
视频: [220715] D064-18 Vue3-(理解)组件插槽-作用域插槽的使用.mp4
<el-table-column align="center" prop="enable" label="状态" width="60px">
+ <template #default="scope">
<div class="enable">
+ <el-button size="small" plain :type="scope.row.enable ? 'primary' : 'danger'">
+ {{ scope.row.enable ? '启用' : '禁用' }}
</el-button>
</div>
</template>
</el-table-column>
表格-时间格式化
知识点: 插件:dayjs
1、安装
npm i dayjs
2、封装dayjs
import dayjs from 'dayjs'
+ import utc from 'dayjs/plugin/utc'
// 继承utc
+ dayjs.extend(utc)
export function formatUTC(utcString: string, format: string = 'YYYY-MM-DD HH:mm:ss') {
+ return dayjs.utc(utcString).format(format)
}
3、转成东八区时间
export function formatUTC(utcString: string, format: string = 'YYYY-MM-DD HH:mm:ss') {
+ return dayjs.utc(utcString).utcOffset(8).format(format)
}
4、使用封装的方法进行格式化
<script setup lang="ts">
+ import { formatUTC } from '@/utils/format
</script>
<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>
分页-展示
<div class="navigation">
+ <el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30]"
small="small"
layout="total, sizes, prev, pager, next, jumper"
:total="totalCount"
@size-change="hdlSizeChange"
@current-change="hdlCurrentChange"
/>
</div>
/* 分页 */
const currentPage = ref(1)
const pageSize = ref(10)
function hdlSizeChange() {}
function hdlCurrentChange() {}
分页-切换页码重新请求数据
1、修改网络请求函数
/* 请求用户列表数据 */
+ export function postUserList(query: any) {
return mrRequest.post({
url: '/users/list',
+ data: query
})
}
2、在store中调用请求函数
const useSystemStore = defineStore('system', {
state: (): ISystemState => ({
userList: [],
totalCount: 0
}),
actions: {
+ async postUserListAction(query: any) {
const res = await postUserList(query)
this.userList = res.data.list
this.totalCount = res.data.totalCount
}
}
})
3、在组件中封装调用请求的函数
/* 发送网络请求 */
function fetchUserList(searchForm: ISearchForm = {}) {
const offset = (currentPage.value - 1) * pageSize.value
const size = pageSize.value
const query = { offset, size }
+ const finalQuery = { ...query, ...searchForm }
systemStore.postUserListAction(finalQuery)
}
4、页码、每页大小发生变化时,调用fetchUserListData
/* 分页事件 */
function hdlSizeChange() {
fetchUserList()
}
function hdlPageChange() {
fetchUserList()
}
点击查询发送请求
难点: 查询和重置按钮属于UserSearch
,而数据请求时发生在UserContent
组件中,二者是兄弟组件
知识点:
- defineEmits(string:[]):
返回:emit
,定义向外发射的事件
1、发射事件queryClick
到外部
const emits = defineEmits(['search-form'])
/* 表单数据 */
const searchForm = reactive<ISearchForm>({
name: '',
realname: '',
cellphone: '',
enable: 1,
createAt: ''
})
/* 根据搜索项查询 */
function hdlQuery() {
emits('search-form', searchForm)
}
2、在外部监听事件,并接收数据
<UserSearch @search-form="hdlSearchForm" />
3、通过绑定ref调用另一个组件中的方法
<UserContent ref="contentRef" />
/* 调用UserContent组件中的方法,查询数据 */
const contentRef = ref<InstanceType<typeof UserContent>>()
function hdlSearchForm(searchForm: ISearchForm) {
if (contentRef.value) contentRef.value.fetchUserList(searchForm)
}
4、在另一个组件中暴露将被调用的方法,并根据传递的formData数据发送请求
defineExpose({ fetchUserList })
/* 发送网络请求 */
function fetchUserList(searchForm: ISearchForm = {}) {
const offset = (currentPage.value - 1) * pageSize.value
const size = pageSize.value
const query = { offset, size }
const finalQuery = { ...query, ...searchForm }
systemStore.postUserListAction(finalQuery)
}
点击重置发送请求
1、发射事件resetClick
到外部
/* 重置搜索表单 */
function hdlReset() {
+ emits('search-form', {})
searchFormRef.value?.resetFields()
}
2、在外部监听事件,并接收数据,重新发送请求
<UserSearch @search-form="hdlSearchForm" />
/* 调用UserContent组件中的方法,查询数据 */
const contentRef = ref<InstanceType<typeof UserContent>>()
function hdlSearchForm(searchForm: ISearchForm) {
if (contentRef.value) contentRef.value.fetchUserList(searchForm)
}
删除-根据id删除数据
1、监听删除按钮点击事件
+ <el-button type="danger" text @click="() => hdlDeleteUser(scope.row.id)">
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-button>
2、添加作用域插槽,获取scope
<el-table-column align="center" label="操作" width="140px">
+ <template #default="scope">
<div class="btns">
<el-button type="primary" text>
<el-icon><Edit /></el-icon>
<span>编辑</span>
</el-button>
+ <el-button type="danger" text @click="() => hdlDeleteUser(scope.row.id)">
<el-icon><Delete /></el-icon>
<span>删除</span>
</el-button>
</div>
</template>
</el-table-column>
3、根据id发送请求删除数据
/* 根据id删除用户 */
function hdlDeleteUser(id: number) {
+ systemStore.delUserByIdAction(id)
}
4、在store中发送调用请求函数
/* 根据id删除用户 */
async delUserByIdAction(id: number) {
+ await delUserById(id)
ElMessage.success('哈哈,删除成功~')
this.postUserListAction({ offset: 0, size: 5 })
},
5、在service中定义发送网络请求函数
/* 根据id删除用户 */
export function delUserById(id: number) {
return mrRequest.delete({
url: `/users/${id}`
})
}
6、删除成功后重新请求数据
/* 根据id删除用户 */
async delUserByIdAction(id: number) {
await delUserById(id)
ElMessage.success('哈哈,删除成功~')
+ this.postUserListAction({ offset: 0, size: 5 })
},
新增用户
1、监听新增按钮点击
<el-button class="btn" type="primary" @click="hdlAddUser">新增用户</el-button>
组件:UserModal
使用组件
<div class="user-content">
+ <UserModal />
</div>
<script setup lang="ts">
+ import UserModal from '../UserModal/UserModal.vue'
</script>
页面布局
<div class="user-modal">
<el-dialog v-model="modalVisiable" title="新增用户" width="30%" center>
<span>
表单部分
</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="modalVisiable = false">取消</el-button>
<el-button type="primary" @click="modalVisiable = false">确定</el-button>
</span>
</template>
</el-dialog>
</div>
显示、隐藏对话框
1、组件UserContent中
const emit = defineEmits(['change-visiable'])
/* 新增用户 */
function hdlAddUser() {
emit('change-visiable')
}
2、组件User中
<UserContent ref="contentRef" @change-visiable="hdlChangeVisiable" />
<UserModal ref="modalRef" />
/* 修改对话框是否显示 */
const modalRef = ref<InstanceType<typeof UserModal>>()
function hdlChangeVisiable() {
if (modalRef.value) modalRef.value.changeModalVisiable()
}
3、组件UserModal中
defineExpose({ changeModalVisiable })
const modalVisiable = ref(false)
/* 修改对话框是否显示 */
function changeModalVisiable() {
modalVisiable.value = true
}
表单布局
<div class="form">
<el-form label-position="right" label-width="100px" size="large">
<el-form-item label="用户名" prop="name">
<el-input placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="真实姓名" prop="realname">
<el-input placeholder="请输入真实姓名" />
</el-form-item>
<el-form-item label="密码" prop="password" show-password>
<el-input placeholder="请输入密码" />
</el-form-item>
<el-form-item label="电话号码" prop="cellphone">
<el-input placeholder="请输入电话号码" />
</el-form-item>
<el-form-item label="选择角色" prop="role">
<el-input placeholder="请选择角色" />
</el-form-item>
<el-form-item label="选择部门" prop="department">
<el-input placeholder="请选择部门" />
</el-form-item>
</el-form>
</div>
/* 表单数据 */
const addUserForm = reactive({
name: '',
realname: '',
password: '',
cellphone: '',
roleId: '',
departmentId: ''
})
角色和部门数据
注意: 由于角色和部门数据可能会在其他许多页面都有使用,应该提取出来,放在main/main.ts
中
1、在service中发送网络请求
/* 获取角色列表 */
export function postRoleLists() {
return mrRequest.post({
url: '/role/list'
})
}
/* 获取部门列表 */
export function postDepartmentLists() {
return mrRequest.post({
url: '/department/list'
})
}
2、在store中调用网络请求
import { postDepartmentLists, postRoleLists } from '@/service/main/main'
import { defineStore } from 'pinia'
interface IMainState {
roleLists: any[]
departmentLists: any[]
}
const useMainStore = defineStore('main', {
+ state: (): IMainState => ({
roleLists: [],
departmentLists: []
}),
actions: {
+ async postRoleListsAction() {
+ const res = await postRoleLists()
this.roleLists = res.data.list
},
+ async postDepartmentListsAction() {
+ const res = await postDepartmentLists()
this.departmentLists = res.data.list
}
}
})
export default useMainStore
3、在组件中发起action
// src\views\Main\Main.vue
/* 发送网络请求 */
mainStore.postRoleListsAction()
mainStore.postDepartmentListsAction()
展示角色和部门
1、从store中获取数据
/* 获取store中数据 */
const { roleLists, departmentLists } = storeToRefs(mainStore)
2、遍历展示数据
<el-form-item label="选择角色" prop="roleId">
<el-select v-model="addUserForm.roleId" class="m-2" placeholder="Select">
<el-option
+ v-for="item in roleLists"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="选择部门" prop="departmentId">
<el-select v-model="addUserForm.departmentId" class="m-2" placeholder="Select">
<el-option
+ v-for="item in departmentLists"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id"
/>
</el-select>
</el-form-item>
点击确定添加用户
1、监听按钮点击
<template #footer>
<span class="dialog-footer">
<el-button @click="modalVisiable = false">取消</el-button>
+ <el-button type="primary" @click="hdlAddUser">确定</el-button>
</span>
</template>
2、在service中发送添加用户的网络请求
/* 新增用户 */
export function addUser(userInfo: any) {
return mrRequest.post({
url: '/users',
data: userInfo
})
}
3、在store中调用网络请求
/* 新增用户 */
async addUserAction(userInfo: any) {
+ await addUser(userInfo)
}
4、在组件中,调用action,创建新用户
/* 添加用户 */
const formRef = ref<InstanceType<typeof ElForm>>()
function hdlAddUser() {
modalVisiable.value = false
// 验证表单
+ formRef.value?.validate((valid: any) => {
if (valid) {
// 验证成功
+ systemStore.addUserAction(addUserForm)
} else {
// 验证失败
ElMessage.error('呜呼,验证失败,请重新来过~')
}
})
}
5、新增用户后,重新请求用户数据
/* 新增用户 */
async addUserAction(userInfo: any) {
await addUser(userInfo)
+ this.postUserListAction({ offset: 0, size: 5 })
}
编辑用户
1、监听编辑按钮点击
+ <el-button type="primary" text @click="() => hdlEditUser(scope.row)">
<el-icon><Edit /></el-icon>
<span>编辑</span>
</el-button>
2、向外暴露事件,并传递数据
const emit = defineEmits(['change-visiable', 'edit-click'])
/* 编辑用户 */
function hdlEditUser(userItem: any) {
emit('edit-click', userItem)
}
3、在User父组件中,监听edit-click
事件,并调用弹出框组件中的方法
<UserContent
ref="contentRef"
@change-visiable="hdlChangeVisiable"
+ @edit-click="hdlEditClick"
/>
/* 调用模态组件内函数,修改用户 */
function hdlEditClick(userItem: any) {
if (modalRef.value) modalRef.value.changeModalVisiable(userItem)
}
4、在UserModal组件中,回显当前编辑的用户
/* 修改对话框是否显示 */
const isEdit = ref(false)
function changeModalVisiable(userItem: any = null) {
modalVisiable.value = true
userId.value = userItem?.id
if (userItem) {
// 编辑状态
isEdit.value = true
// 遍历回显
+ for (const key in userForm) {
+ userForm[key] = userItem[key]
+ }
}
}
注意: formData需要定义为any类型
/* 表单数据 */
+ const userForm = reactive<any>({ // reactive<any>
name: '',
realname: '',
password: '',
cellphone: '',
roleId: '',
departmentId: ''
})
5、不显示密码表单
- 全局记录isNew的值
/* 修改对话框是否显示 */
+ const isEdit = ref(false)
const userInfo = ref()
function changeModalVisiable(userItem: any = null) {
modalVisiable.value = true
userId.value = userItem?.id
if (userItem) {
// 编辑状态
+ isEdit.value = true
for (const key in userForm) {
userForm[key] = userItem[key]
}
userInfo.value = userForm
} else {
// 新增状态
+ isEdit.value = false
for (const key in userForm) {
userForm[key] = ''
}
userInfo.value = null
}
}
- 根据isNew的值,显示、隐藏密码表单
<el-form-item v-if="!isEdit" label="密码" prop="password">
<el-input v-model="userForm.password" placeholder="请输入密码" show-password />
</el-form-item>
6、在新增用户的情况下,初始化清空所有表单
function changeModalVisiable(userItem: any = null) {
modalVisiable.value = true
userId.value = userItem?.id
if (userItem) {
// 编辑状态
isEdit.value = true
for (const key in userForm) {
userForm[key] = userItem[key]
}
} else {
// 新增状态
isEdit.value = false
+ for (const key in userForm) {
+ userForm[key] = ''
+ }
}
}
点击确定编辑用户
1、service
/* 编辑用户 */
export function editUser(id: number, userInfo: any) {
return mrRequest.patch({
url: `/users/${id}`,
data: userInfo
})
}
2、store
/* 修改用户 */
async editUserAction(id: number, userInfo: any) {
await editUser(id, userInfo)
this.postUserListAction({ offset: 0, size: 5 })
}
3、组件
- 保存userInfo
+ const userInfo = ref()
function changeModalVisiable(userItem: any = null) {
modalVisiable.value = true
userId.value = userItem?.id
if (userItem) {
// 编辑状态
isEdit.value = true
for (const key in userForm) {
userForm[key] = userItem[key]
}
+ userInfo.value = userForm
} else {
// 新增状态
isEdit.value = false
for (const key in userForm) {
userForm[key] = ''
}
+ userInfo.value = null
}
}
- 调用action,执行编辑操作
function hdlSubmitUser() {
modalVisiable.value = false
// 验证表单
formRef.value?.validate((valid: any) => {
if (valid) {
// 验证成功
if (isEdit.value) {
+ systemStore.editUserAction(userId.value, userInfo)
+ ElMessage.success('哈哈,修改用户成功~')
} else {
systemStore.addUserAction(userInfo)
ElMessage.success('哈哈,新增用户成功~')
}
} else {
// 验证失败
ElMessage.error('呜呼,验证失败,请重新来过~')
}
})
}
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、页面组件为响应式布局