Compare commits

...

2 Commits

@ -1,9 +1,11 @@
<template> <template>
<!-- 对话框组件 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="!dataForm.areaId ? '新增' : '修改'" :title="!dataForm.areaId ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<!-- 表单组件 -->
<el-form <el-form
ref="dataFormRef" ref="dataFormRef"
:model="dataForm" :model="dataForm"
@ -11,6 +13,7 @@
label-width="100px" label-width="100px"
@keyup.enter="onSubmit()" @keyup.enter="onSubmit()"
> >
<!-- 地区名称输入项 -->
<el-form-item <el-form-item
label="地区名称" label="地区名称"
prop="areaName" prop="areaName"
@ -22,6 +25,7 @@
show-word-limit show-word-limit
/> />
</el-form-item> </el-form-item>
<!-- 上级地区选择项 -->
<el-form-item <el-form-item
label="上级地区" label="上级地区"
prop="parentId" prop="parentId"
@ -37,6 +41,7 @@
/> />
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 对话框底部操作按钮 -->
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button <el-button
@ -59,7 +64,12 @@
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { treeDataTranslate } from '@/utils' import { treeDataTranslate } from '@/utils'
import { Debounce } from '@/utils/debounce' import { Debounce } from '@/utils/debounce'
import { ref, reactive, nextTick, defineEmits, defineExpose } from 'vue'
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
//
const dataRule = reactive({ const dataRule = reactive({
areaName: [ areaName: [
{ required: true, message: '区域名称不能为空', trigger: 'blur' }, { required: true, message: '区域名称不能为空', trigger: 'blur' },
@ -67,6 +77,7 @@ const dataRule = reactive({
] ]
}) })
//
const visible = ref(false) const visible = ref(false)
const areaList = ref([]) const areaList = ref([])
const dataForm = ref({ const dataForm = ref({
@ -81,61 +92,76 @@ const categoryTreeProps = reactive({
checkStrictly: true checkStrictly: true
}) })
const selectedOptions = ref([]) const selectedOptions = ref([])
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
// areaId
const init = (areaId) => { const init = (areaId) => {
dataForm.value.areaId = areaId || 0 dataForm.value.areaId = areaId || 0 // ID
visible.value = true visible.value = true //
selectedOptions.value = [] selectedOptions.value = [] //
// 使nextTickDOM
nextTick(() => { nextTick(() => {
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields() //
if (dataForm.value.areaId) { if (dataForm.value.areaId) {
// areaId
http({ http({
url: http.adornUrl('/admin/area/info/' + dataForm.value.areaId), url: http.adornUrl('/admin/area/info/' + dataForm.value.areaId),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}).then(({ data }) => {
dataForm.value = data //
selectedOptions.value = dataForm.value.parentId //
// categoryTreePropsreactive
categoryTreeProps.areaId = dataForm.value.areaId
categoryTreeProps.areaName = dataForm.value.areaName
}) })
.then(({ data }) => {
dataForm.value = data
selectedOptions.value = dataForm.value.parentId
categoryTreeProps.areaId = dataForm.value.areaId
categoryTreeProps.areaName = dataForm.value.areaName
})
} }
//
http({ http({
url: http.adornUrl('/admin/area/list'), url: http.adornUrl('/admin/area/list'),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}).then(({ data }) => {
areaList.value = treeDataTranslate(data, 'areaId', 'parentId')
}) })
.then(({ data }) => {
areaList.value = treeDataTranslate(data, 'areaId', 'parentId')
})
}) })
} }
// init
defineExpose({ init }) defineExpose({ init })
//
const page = { const page = {
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 20 // pageSize: 20 //
} }
/** /**
* 表单提交 * 表单提交
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => {
//
dataFormRef.value?.validate((valid) => { dataFormRef.value?.validate((valid) => {
if (valid) { if (valid) {
// HTTP
http({ http({
url: http.adornUrl('/admin/area'), url: http.adornUrl('/admin/area'),
method: dataForm.value.areaId ? 'put' : 'post', method: dataForm.value.areaId ? 'put' : 'post', //
data: http.adornData(dataForm.value) data: http.adornData(dataForm.value)
}).then(() => { }).then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
visible.value = false visible.value = false //
emit('refreshDataList', page) emit('refreshDataList', page) //
} }
}) })
}) })
@ -143,8 +169,9 @@ const onSubmit = Debounce(() => {
}) })
}) })
//
const handleChange = (val) => { const handleChange = (val) => {
// parentIdID
dataForm.value.parentId = val[val.length - 1] dataForm.value.parentId = val[val.length - 1]
} }
</script> </script>

@ -1,10 +1,13 @@
<template> <template>
<!-- 地区管理模块 -->
<div class="mod-sys-area"> <div class="mod-sys-area">
<!-- 地区名称输入框用于过滤树状结构中的节点 -->
<el-input <el-input
v-model="areaName" v-model="areaName"
class="area-search-input" class="area-search-input"
placeholder="地区关键词" placeholder="地区关键词"
/> />
<!-- 新增按钮点击时触发新增或更新操作 -->
<el-button <el-button
type="primary" type="primary"
class="area-add-btn" class="area-add-btn"
@ -13,17 +16,20 @@
新增 新增
</el-button> </el-button>
<!-- 树形控件展示地区层级结构 -->
<el-tree <el-tree
ref="tree2Ref" ref="tree2Ref"
:data="areaTreeData" :data="areaTreeData"
node-key="areaId" node-key="areaId"
:filter-node-method="filterNode" :filter-node-method="filterNode"
:props="props" :props="props"
:expand-on-click-node="false" :expand-on-click-node="false"
> >
<template #default="{ node, data }"> <!-- 自定义节点内容模板 -->
<span class="addr-name">{{ node.label }}</span> <template #default="{ node, data }">
<span> <span class="addr-name">{{ node.label }}</span> <!-- 显示地区名称 -->
<span>
<!-- 修改按钮点击时触发修改操作 -->
<el-button <el-button
type="text" type="text"
icon="el-icon-edit" icon="el-icon-edit"
@ -31,6 +37,7 @@
> >
修改 修改
</el-button> </el-button>
<!-- 删除按钮点击时触发删除操作 -->
<el-button <el-button
type="text" type="text"
icon="el-icon-delete" icon="el-icon-delete"
@ -39,48 +46,59 @@
删除 删除
</el-button> </el-button>
</span> </span>
</template> </template>
</el-tree> </el-tree>
<!-- 添加或更新对话框组件 -->
<add-or-update <add-or-update
v-if="addOrUpdateVisible" v-if="addOrUpdateVisible"
ref="addOrUpdateRef" ref="addOrUpdateRef"
@refresh-data-list="getDataList" @refresh-data-list="getDataList"
@close="addOrUpdateVisible=false" @close="addOrUpdateVisible=false"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import AddOrUpdate from './add-or-update.vue' import AddOrUpdate from './add-or-update.vue' //
import { treeDataTranslate } from '@/utils' import { treeDataTranslate } from '@/utils' //
import { ref, watch, onMounted, reactive, nextTick } from 'vue'
//
const areaName = ref('') const areaName = ref('')
//
const props = { const props = {
id: 'areaId', id: 'areaId',
label: 'areaName', label: 'areaName',
children: 'children' children: 'children'
} }
const tree2Ref = ref(null) //
watch( watch(
() => areaName, () => areaName.value,
(val) => { (val) => {
tree2Ref.value?.filter(val) tree2Ref.value?.filter(val)
} }
) )
//
onMounted(() => { onMounted(() => {
getDataList(page) getDataList(page)
}) })
//
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
}) })
//
const areaTreeData = ref(null) const areaTreeData = ref(null)
//
const getDataList = (pageParam, params) => { const getDataList = (pageParam, params) => {
http({ http({
url: http.adornUrl('/admin/area/list'), url: http.adornUrl('/admin/area/list'),
@ -90,38 +108,46 @@ const getDataList = (pageParam, params) => {
size: pageParam == null ? page.pageSize : pageParam.pageSize size: pageParam == null ? page.pageSize : pageParam.pageSize
}, params)) }, params))
}).then(({ data }) => { }).then(({ data }) => {
// areaTreeData
areaTreeData.value = treeDataTranslate(data, 'areaId', 'parentId') areaTreeData.value = treeDataTranslate(data, 'areaId', 'parentId')
}) })
} }
//
const addOrUpdateRef = ref(null) const addOrUpdateRef = ref(null)
//
const addOrUpdateVisible = ref(false) const addOrUpdateVisible = ref(false)
/** /**
* 新增 / 修改 * 触发新增或更新操作
* @param id * @param id 可选参数若提供则表示更新指定ID的地区信息
*/ */
const onAddOrUpdate = (id) => { const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true addOrUpdateVisible.value = true
nextTick(() => { nextTick(() => {
addOrUpdateRef.value?.init(id) addOrUpdateRef.value?.init(id) //
}) })
} }
/** /**
* 删除 * 执行删除操作
* @param areaId * @param areaId 要删除的地区ID
*/ */
const onDelete = (areaId) => { const onDelete = (areaId) => {
//
ElMessageBox.confirm('确定进行删除操作?', '提示', { ElMessageBox.confirm('确定进行删除操作?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
http({ http({
url: http.adornUrl('/admin/area/' + areaId), url: http.adornUrl('/admin/area/' + areaId),
method: 'delete', method: 'delete',
data: http.adornData({}) data: http.adornData({})
}).then(() => { }).then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
@ -131,35 +157,37 @@ const onDelete = (areaId) => {
} }
}) })
}) })
}).catch(() => { }) }).catch(() => { /* 用户取消删除 */ })
} }
// eslint-disable-next-line no-unused-vars
// onAddOrUpdate
const update = (node, data) => { const update = (node, data) => {
onAddOrUpdate(data.areaId) onAddOrUpdate(data.areaId)
} }
// eslint-disable-next-line no-unused-vars // onDelete
const remove = (node, data) => { const remove = (node, data) => {
onDelete(data.areaId) onDelete(data.areaId)
} }
//
const filterNode = (value, data) => { const filterNode = (value, data) => {
if (!value) return true if (!value) return true //
return data.areaName.indexOf(value) !== -1 return data.areaName.indexOf(value) !== -1 //
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.mod-sys-area { .mod-sys-area {
.area-search-input { .area-search-input {
width: 30%; width: 30%; // 30%
} }
.area-add-btn { .area-add-btn {
float: right; float: right; //
} }
} }
/* 使用:deep选择器穿透样式作用于子组件 */
:deep(.el-tree-node) .addr-name { :deep(.el-tree-node) .addr-name {
width: 100%; width: 100%; //
} }
</style> </style>

@ -1,89 +1,111 @@
<template> <template>
<!-- 对话框组件 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="!dataForm.id ? '新增' : '修改'" :title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-form <!-- 表单组件 -->
ref="dataFormRef" <el-form
:model="dataForm" ref="dataFormRef"
:rules="dataRule" :model="dataForm"
label-width="80px" :rules="dataRule"
@keyup.enter="onSubmit()" label-width="80px"
> @keyup.enter="onSubmit()"
<el-form-item >
label="参数名" <!-- 参数名输入项 -->
prop="paramKey" <el-form-item
> label="参数名"
<el-input prop="paramKey"
v-model="dataForm.paramKey" >
placeholder="参数名" <el-input
/> v-model="dataForm.paramKey"
</el-form-item> placeholder="参数名"
<el-form-item />
label="参数值" </el-form-item>
prop="paramValue" <!-- 参数值输入项 -->
> <el-form-item
<el-input label="参数值"
v-model="dataForm.paramValue" prop="paramValue"
placeholder="参数值" >
/> <el-input
</el-form-item> v-model="dataForm.paramValue"
<el-form-item placeholder="参数值"
label="备注" />
prop="remark" </el-form-item>
> <!-- 备注输入项 -->
<el-input <el-form-item
v-model="dataForm.remark" label="备注"
placeholder="备注" prop="remark"
/> >
</el-form-item> <el-input
</el-form> v-model="dataForm.remark"
<template #footer> placeholder="备注"
/>
</el-form-item>
</el-form>
<!-- 对话框底部操作按钮 -->
<template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button> <!-- -->
<el-button <el-button type="primary" @click="onSubmit()"></el-button> <!-- 提交表单 -->
type="primary"
@click="onSubmit()"
>确定</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Debounce } from '@/utils/debounce' import { Debounce } from '@/utils/debounce'
import { ref, reactive, nextTick, defineEmits, defineExpose } from 'vue'
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
//
const visible = ref(false) const visible = ref(false)
// 使reactive
const dataForm = reactive({ const dataForm = reactive({
id: 0, id: 0, // ID0
paramKey: '', paramKey: '', //
paramValue: '', paramValue: '', //
remark: '' remark: '' //
}) })
//
const dataRule = { const dataRule = {
paramKey: [ paramKey: [
{ required: true, message: '参数名不能为空', trigger: 'blur' }, { required: true, message: '参数名不能为空', trigger: 'blur' }, //
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的参数名', trigger: 'blur' } { pattern: /\s\S+|S+\s|\S/, message: '请输入正确的参数名', trigger: 'blur' } //
], ],
paramValue: [ paramValue: [
{ required: true, message: '参数值不能为空', trigger: 'blur' }, { required: true, message: '参数值不能为空', trigger: 'blur' }, //
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的参数值', trigger: 'blur' } { pattern: /\s\S+|S+\s|\S/, message: '请输入正确的参数值', trigger: 'blur' } //
] ]
} }
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
// id
const init = (id) => { const init = (id) => {
dataForm.id = id || 0 dataForm.id = id || 0 // ID
visible.value = true visible.value = true //
// 使nextTickDOM
nextTick(() => { nextTick(() => {
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields() //
if (dataForm.id) { if (dataForm.id) {
// id
http({ http({
url: http.adornUrl(`/sys/config/info/${dataForm.id}`), url: http.adornUrl(`/sys/config/info/${dataForm.id}`),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}).then(({ data }) => { }).then(({ data }) => {
//
dataForm.paramKey = data.paramKey dataForm.paramKey = data.paramKey
dataForm.paramValue = data.paramValue dataForm.paramValue = data.paramValue
dataForm.remark = data.remark dataForm.remark = data.remark
@ -91,16 +113,21 @@ const init = (id) => {
} }
}) })
} }
// init
defineExpose({ init }) defineExpose({ init })
/** /**
* 表单提交 * 表单提交
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => {
//
dataFormRef.value?.validate((valid) => { dataFormRef.value?.validate((valid) => {
if (valid) { if (valid) {
// HTTP
http({ http({
url: http.adornUrl('/sys/config'), url: http.adornUrl('/sys/config'),
method: dataForm.id ? 'put' : 'post', method: dataForm.id ? 'put' : 'post', //
data: http.adornData({ data: http.adornData({
id: dataForm.id || undefined, id: dataForm.id || undefined,
paramKey: dataForm.paramKey, paramKey: dataForm.paramKey,
@ -108,13 +135,14 @@ const onSubmit = Debounce(() => {
remark: dataForm.remark remark: dataForm.remark
}) })
}).then(() => { }).then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
visible.value = false visible.value = false //
emit('refreshDataList') emit('refreshDataList') //
} }
}) })
}) })

@ -1,135 +1,164 @@
<template> <template>
<!-- 配置管理模块 -->
<div class="mod-config"> <div class="mod-config">
<!-- AvueCrud表格组件 -->
<avue-crud <avue-crud
ref="crudRef" ref="crudRef"
:page="page" :page="page"
:data="dataList" :data="dataList"
:option="tableOption" :option="tableOption"
@search-change="onSearch" @search-change="onSearch"
@selection-change="selectionChange" @selection-change="selectionChange"
@on-load="getDataList" @on-load="getDataList"
> >
<template #menu-left> <!-- 自定义左侧菜单按钮 -->
<el-button <template #menu-left>
type="primary" <el-button
icon="el-icon-plus" type="primary"
@click.stop="onAddOrUpdate()" icon="el-icon-plus"
> @click.stop="onAddOrUpdate()"
新增 >
</el-button> 新增
</el-button>
<el-button <el-button
type="danger" type="danger"
:disabled="dataListSelections.length <= 0" :disabled="dataListSelections.length <= 0"
@click="onDelete()" @click="onDelete()"
> >
批量删除 批量删除
</el-button> </el-button>
</template> </template>
<template #menu="scope">
<el-button <!-- 自定义行内操作按钮 -->
type="primary" <template #menu="scope">
icon="el-icon-edit" <el-button
@click.stop="onAddOrUpdate(scope.row.id)" type="primary"
> icon="el-icon-edit"
编辑 @click.stop="onAddOrUpdate(scope.row.id)"
</el-button> >
<el-button 编辑
type="danger" </el-button>
icon="el-icon-delete" <el-button
@click.stop="onDelete(scope.row.id)" type="danger"
> icon="el-icon-delete"
删除 @click.stop="onDelete(scope.row.id)"
</el-button> >
</template> 删除
</el-button>
</template>
</avue-crud> </avue-crud>
<!-- 弹窗, 新增 / 修改 --> <!-- 添加或更新对话框组件 -->
<add-or-update <add-or-update
v-if="addOrUpdateVisible" v-if="addOrUpdateVisible"
ref="addOrUpdateRef" ref="addOrUpdateRef"
@refresh-data-list="getDataList" @refresh-data-list="getDataList"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { tableOption } from '@/crud/sys/config.js' import { tableOption } from '@/crud/sys/config.js' //
import { ref, reactive, nextTick, defineExpose } from 'vue'
//
const dataList = ref([]) const dataList = ref([])
//
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
}) })
/** /**
* 获取数据列表 * 获取数据列表方法
* @param pageParam 可选参数用于指定分页信息
* @param params 搜索条件参数
* @param done 分页加载完成回调函数
*/ */
const getDataList = (pageParam, params, done) => { const getDataList = (pageParam, params, done) => {
http({ http({
url: http.adornUrl('/sys/config/page'), url: http.adornUrl('/sys/config/page'), // API
method: 'get', method: 'get', //
params: http.adornParams( params: http.adornParams(
Object.assign( Object.assign(
{ {
current: pageParam == null ? page.currentPage : pageParam.currentPage, current: pageParam == null ? page.currentPage : pageParam.currentPage,
size: pageParam == null ? page.pageSize : pageParam.pageSize size: pageParam == null ? page.pageSize : pageParam.pageSize
}, },
params params //
) )
) )
}) })
.then(({ data }) => { .then(({ data }) => {
dataList.value = data.records dataList.value = data.records //
page.total = data.total page.total = data.total //
if (done) done() if (done) done() //
}) })
} }
/** /**
* 条件查询 * 条件查询方法
* @param params 搜索条件参数
* @param done 分页加载完成回调函数
*/ */
const onSearch = (params, done) => { const onSearch = (params, done) => {
getDataList(page, params, done) getDataList(page, params, done) //
} }
//
const dataListSelections = ref([]) const dataListSelections = ref([])
/** /**
* 多选变化 * 多选变化方法
* @param val 选中的数据项
*/ */
const selectionChange = (val) => { const selectionChange = (val) => {
dataListSelections.value = val dataListSelections.value = val //
} }
//
const addOrUpdateVisible = ref(false) const addOrUpdateVisible = ref(false)
//
const addOrUpdate = ref(null) const addOrUpdate = ref(null)
/** /**
* 新增 / 修改 * 触发新增或修改操作
* @param id 可选参数若提供则表示更新指定ID的配置信息
*/ */
const onAddOrUpdate = (id) => { const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true addOrUpdateVisible.value = true //
nextTick(() => { nextTick(() => {
addOrUpdate.value?.init(id) addOrUpdate.value?.init(id) //
}) })
} }
/** /**
* 删除 * 删除配置信息
* @param id 可选参数若提供则表示删除指定ID的配置信息
*/ */
const onDelete = (id) => { const onDelete = (id) => {
const ids = id ? [id] : dataListSelections.value?.map(item => { const ids = id ? [id] : dataListSelections.value?.map(item => item.id) // ID
return item.id
}) //
ElMessageBox.confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', { ElMessageBox.confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}) })
.then(() => { .then(() => {
// HTTP
http({ http({
url: http.adornUrl('/sys/config'), url: http.adornUrl('/sys/config'),
method: 'delete', method: 'delete',
data: http.adornData(ids, false) data: http.adornData(ids, false)
}) })
.then(() => { .then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
@ -139,6 +168,6 @@ const onDelete = (id) => {
} }
}) })
}) })
}).catch(() => { }) }).catch(() => { /* 用户取消删除 */ })
} }
</script> </script>

@ -1,57 +1,70 @@
<template> <template>
<!-- 日志管理模块 -->
<div class="mod-log"> <div class="mod-log">
<!-- AvueCrud表格组件用于展示日志数据 -->
<avue-crud <avue-crud
ref="crudRef" ref="crudRef" <!-- 引用表格实例 -->
:page="page" :page="page" <!-- 分页信息 -->
:data="dataList" :data="dataList" <!-- 表格数据 -->
:option="tableOption" :option="tableOption" <!-- 表格选项配置 -->
@search-change="onSearch" @search-change="onSearch" <!-- 搜索条件变化事件 -->
@on-load="getDataList" @on-load="getDataList" <!-- 加载数据事件 -->
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { tableOption } from '@/crud/sys/log.js' import { tableOption } from '@/crud/sys/log.js' //
import { ref, reactive, onMounted } from 'vue'
//
const dataList = ref([]) const dataList = ref([])
//
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
}) })
//
onMounted(() => { onMounted(() => {
getDataList() getDataList()
}) })
/** /**
* 获取数据列表 * 获取数据列表方法
* @param pageParam 可选参数用于指定分页信息
* @param params 搜索条件参数
* @param done 分页加载完成回调函数
*/ */
const getDataList = (pageParam, params, done) => { const getDataList = (pageParam, params, done) => {
http({ http({
url: http.adornUrl('/sys/log/page'), url: http.adornUrl('/sys/log/page'), // API
method: 'get', method: 'get', //
params: http.adornParams( params: http.adornParams(
Object.assign( Object.assign(
{ {
current: pageParam == null ? page.currentPage : pageParam.currentPage, current: pageParam == null ? page.currentPage : pageParam.currentPage,
size: pageParam == null ? page.pageSize : pageParam.pageSize size: pageParam == null ? page.pageSize : pageParam.pageSize
}, },
params params //
) )
) )
}) })
.then(({ data }) => { .then(({ data }) => {
dataList.value = data.records dataList.value = data.records //
page.total = data.total page.total = data.total //
if (done) done() if (done) done() //
}) })
} }
/** /**
* 条件查询 * 条件查询方法
* @param params 搜索条件参数
* @param done 分页加载完成回调函数
*/ */
const onSearch = (params, done) => { const onSearch = (params, done) => {
getDataList(page, params, done) getDataList(page, params, done) //
} }
</script> </script>

@ -1,162 +1,182 @@
<template> <template>
<!-- 对话框组件 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="!dataForm.id ? '新增' : '修改'" :title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-form <!-- 表单组件 -->
ref="dataFormRef" <el-form
:model="dataForm" ref="dataFormRef"
:rules="dataRule" :model="dataForm"
label-width="80px" :rules="dataRule"
@keyup.enter="onSubmit()" label-width="80px"
> @keyup.enter="onSubmit()"
<el-form-item >
label="类型" <!-- 类型选择项 -->
prop="type" <el-form-item
> label="类型"
<el-radio-group v-model="dataForm.type"> prop="type"
<el-radio >
v-for="(type, index) in dataForm.typeList" <el-radio-group v-model="dataForm.type">
:key="index" <el-radio
:label="index" v-for="(type, index) in dataForm.typeList"
> :key="index"
{{ type }} :label="index"
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="dataForm.typeList[dataForm.type] + '名称'"
prop="name"
>
<el-input
v-model="dataForm.name"
:placeholder="dataForm.typeList[dataForm.type] + '名称'"
/>
</el-form-item>
<el-form-item label="上级菜单">
<el-cascader
v-model="selectedMenu"
expand-trigger="hover"
:options="menuList"
:props="menuListTreeProps"
change-on-select
:clearable="true"
@change="handleSelectMenuChange"
/>
</el-form-item>
<el-form-item
v-if="dataForm.type === 1"
label="菜单路由"
prop="url"
>
<el-input
v-model="dataForm.url"
placeholder="菜单路由"
/>
</el-form-item>
<el-form-item
v-if="dataForm.type !== 0"
label="授权标识"
prop="perms"
> >
{{ type }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 动态标签根据类型改变 -->
<el-form-item
:label="dataForm.typeList[dataForm.type] + '名称'"
prop="name"
>
<el-input
v-model="dataForm.name"
:placeholder="dataForm.typeList[dataForm.type] + '名称'"
/>
</el-form-item>
<!-- 上级菜单选择 -->
<el-form-item label="上级菜单">
<el-cascader
v-model="selectedMenu"
expand-trigger="hover"
:options="menuList"
:props="menuListTreeProps"
change-on-select
:clearable="true"
@change="handleSelectMenuChange"
/>
</el-form-item>
<!-- 菜单路由输入仅当类型为1时显示 -->
<el-form-item
v-if="dataForm.type === 1"
label="菜单路由"
prop="url"
>
<el-input
v-model="dataForm.url"
placeholder="菜单路由"
/>
</el-form-item>
<!-- 授权标识输入除类型0外显示 -->
<el-form-item
v-if="dataForm.type !== 0"
label="授权标识"
prop="perms"
>
<el-input
v-model="dataForm.perms"
placeholder="多个用逗号分隔, 如: user:list,user:create"
/>
</el-form-item>
<!-- 排序号输入除类型2外显示 -->
<el-form-item
v-if="dataForm.type !== 2"
label="排序号"
prop="orderNum"
>
<el-input-number
v-model="dataForm.orderNum"
controls-position="right"
:min="0"
label="排序号"
/>
</el-form-item>
<!-- 菜单图标选择除类型2外显示 -->
<el-form-item
v-if="dataForm.type !== 2"
label="菜单图标"
prop="icon"
>
<el-row>
<el-col :span="22">
<el-input <el-input
v-model="dataForm.perms" ref="iconInputRef"
placeholder="多个用逗号分隔, 如: user:list,user:create" v-model="dataForm.icon"
:virtual-ref="iconListPopoverRef"
placeholder="菜单图标名称"
clearable
/> />
</el-form-item> <el-popover
<el-form-item ref="iconListPopoverRef"
v-if="dataForm.type !== 2" style="width: 390px"
label="排序号" :virtual-ref="iconInputRef"
prop="orderNum" placement="bottom-start"
> trigger="click"
<el-input-number :popper-style="iconPopoverClass"
v-model="dataForm.orderNum" virtual-triggering
controls-position="right" >
:min="0" <el-button
label="排序号" v-for="(item, index) in iconList"
/> :key="index"
</el-form-item> style="padding: 8px; margin: 8px 0 0 8px"
<el-form-item :class="{ 'is-active': item === dataForm.icon }"
v-if="dataForm.type !== 2" @click="iconActiveHandle(item)"
label="菜单图标"
prop="icon"
>
<el-row>
<el-col :span="22">
<el-input
ref="iconInputRef"
v-model="dataForm.icon"
:virtual-ref="iconListPopoverRef"
placeholder="菜单图标名称"
clearable
/>
<el-popover
ref="iconListPopoverRef"
style="width: 390px"
:virtual-ref="iconInputRef"
placement="bottom-start"
trigger="click"
:popper-style="iconPopoverClass"
virtual-triggering
>
<el-button
v-for="(item, index) in iconList"
:key="index"
style="padding: 8px; margin: 8px 0 0 8px"
:class="{ 'is-active': item === dataForm.icon }"
@click="iconActiveHandle(item)"
>
<svg-icon
:icon-class="`${item}`"
/>
</el-button>
</el-popover>
</el-col>
<el-col
:span="2"
class="icon-list__tips"
> >
<el-tooltip <svg-icon
placement="top" :icon-class="`${item}`"
effect="light" />
> </el-button>
<template #content> </el-popover>
<div>全站推荐使用SVG Sprite, 详细请参考:icons/index.js 描述</div> </el-col>
</template> <el-col
<i class="el-icon-warning" /> :span="2"
</el-tooltip> class="icon-list__tips"
</el-col> >
</el-row> <el-tooltip
</el-form-item> placement="top"
</el-form> effect="light"
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button
type="primary"
@click="onSubmit()"
> >
确定 <template #content>
</el-button> <div>全站推荐使用SVG Sprite, 详细请参考:icons/index.js 描述</div>
</template>
<i class="el-icon-warning" />
</el-tooltip>
</el-col>
</el-row>
</el-form-item>
</el-form>
<!-- 对话框底部操作按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <!-- -->
<el-button type="primary" @click="onSubmit()"></el-button> <!-- 提交表单 -->
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { treeDataTranslate, idList } from '@/utils' import { treeDataTranslate, idList } from '@/utils'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { ref, reactive, computed, onMounted, nextTick, defineEmits, defineExpose } from 'vue'
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
//
const iconInputRef = ref(null) const iconInputRef = ref(null)
const iconListPopoverRef = ref(null) const iconListPopoverRef = ref(null)
const iconPopoverClass = computed(() => {
return {
width: '396px'
}
})
//
const iconPopoverClass = computed(() => ({
width: '396px'
}))
//
const visible = ref(false) const visible = ref(false)
// 使reactive
const dataForm = reactive({ const dataForm = reactive({
id: 0, id: 0,
type: 1, type: 1,
@ -169,15 +189,19 @@ const dataForm = reactive({
icon: '', icon: '',
iconList: [] iconList: []
}) })
//
const menuList = ref([]) const menuList = ref([])
const selectedMenu = ref([]) const selectedMenu = ref([])
//
const menuListTreeProps = { const menuListTreeProps = {
value: 'menuId', value: 'menuId',
label: 'name', label: 'name',
checkStrictly: true checkStrictly: true
} }
// eslint-disable-next-line no-unused-vars // URL
const validateUrl = (rule, value, callback) => { const validateUrl = (rule, value, callback) => {
if (dataForm.type === 1 && !/\S/.test(value)) { if (dataForm.type === 1 && !/\S/.test(value)) {
callback(new Error('菜单URL不能为空')) callback(new Error('菜单URL不能为空'))
@ -185,6 +209,8 @@ const validateUrl = (rule, value, callback) => {
callback() callback()
} }
} }
//
const dataRule = ref({ const dataRule = ref({
name: [ name: [
{ required: true, message: '菜单名称不能为空', trigger: 'blur' }, { required: true, message: '菜单名称不能为空', trigger: 'blur' },
@ -195,10 +221,11 @@ const dataRule = ref({
] ]
}) })
//
onMounted(() => { onMounted(() => {
onLoadIcons() onLoadIcons()
}) })
const iconList = []
/** /**
* 加载图标 * 加载图标
*/ */
@ -206,35 +233,42 @@ const onLoadIcons = () => {
const icons = import.meta.glob('@/icons/svg/*.svg') const icons = import.meta.glob('@/icons/svg/*.svg')
for (const icon in icons) { for (const icon in icons) {
const iconName = icon.split('/src/icons/svg/')[1].split('.svg')[0] const iconName = icon.split('/src/icons/svg/')[1].split('.svg')[0]
iconList.push(iconName) dataForm.iconList.push(iconName)
} }
} }
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
/**
* 初始化方法接收id作为参数
* @param id 菜单项ID
*/
const init = (id) => { const init = (id) => {
dataForm.id = id || 0 dataForm.id = id || 0 // ID
http({ http({
url: http.adornUrl('/sys/menu/list'), url: http.adornUrl('/sys/menu/list'),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}) })
.then(({ data }) => { .then(({ data }) => {
menuList.value = treeDataTranslate(data, 'menuId') menuList.value = treeDataTranslate(data, 'menuId') //
}) })
.then(() => { .then(() => {
visible.value = true visible.value = true //
nextTick(() => { nextTick(() => {
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields() //
}) })
}) })
.then(() => { .then(() => {
if (dataForm.id) { if (dataForm.id) {
// // id
http({ http({
url: http.adornUrl(`/sys/menu/info/${dataForm.id}`), url: http.adornUrl(`/sys/menu/info/${dataForm.id}`),
method: 'get', method: 'get',
params: http.adornParams() params: http.adornParams()
}).then(({ data }) => { }).then(({ data }) => {
//
dataForm.id = data.menuId dataForm.id = data.menuId
dataForm.type = data.type dataForm.type = data.type
dataForm.name = data.name dataForm.name = data.name
@ -246,31 +280,39 @@ const init = (id) => {
selectedMenu.value = idList(menuList.value, data.parentId, 'menuId', 'children').reverse() selectedMenu.value = idList(menuList.value, data.parentId, 'menuId', 'children').reverse()
}) })
} else { } else {
selectedMenu.value = [] selectedMenu.value = [] //
} }
}) })
} }
defineExpose({ init }) defineExpose({ init })
/**
* 上级菜单选择变化处理
* @param val 选中的菜单路径
*/
const handleSelectMenuChange = (val) => { const handleSelectMenuChange = (val) => {
dataForm.parentId = val[val.length - 1] dataForm.parentId = val[val.length - 1] // parentIdID
} }
/** /**
* 图标选中 * 图标选中处理
* @param iconName * @param iconName 选中的图标名称
*/ */
const iconActiveHandle = (iconName) => { const iconActiveHandle = (iconName) => {
dataForm.icon = iconName dataForm.icon = iconName //
} }
/** /**
* 表单提交 * 表单提交
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => {
//
dataFormRef.value?.validate((valid) => { dataFormRef.value?.validate((valid) => {
if (valid) { if (valid) {
// HTTP
http({ http({
url: http.adornUrl('/sys/menu'), url: http.adornUrl('/sys/menu'),
method: dataForm.id ? 'put' : 'post', method: dataForm.id ? 'put' : 'post', //
data: http.adornData({ data: http.adornData({
menuId: dataForm.id || undefined, menuId: dataForm.id || undefined,
type: dataForm.type, type: dataForm.type,
@ -283,13 +325,14 @@ const onSubmit = Debounce(() => {
}) })
}) })
.then(() => { .then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
visible.value = false visible.value = false //
emit('refreshDataList') emit('refreshDataList') //
} }
}) })
}) })

@ -1,194 +1,211 @@
<template> <template>
<!-- 菜单管理模块 -->
<div class="mod-menu"> <div class="mod-menu">
<!-- 搜索表单 -->
<el-form <el-form
:inline="true" :inline="true"
:model="dataForm" :model="dataForm"
> >
<el-form-item> <el-form-item>
<el-button <!-- 新增按钮只有有权限时显示 -->
v-if="isAuth('sys:menu:save')" <el-button
type="primary" v-if="isAuth('sys:menu:save')"
@click="onAddOrUpdate()" type="primary"
> @click="onAddOrUpdate()"
新增 >
</el-button> 新增
</el-form-item> </el-button>
</el-form-item>
</el-form> </el-form>
<!-- 数据表格 -->
<el-table <el-table
:data="dataList" :data="dataList"
border border
style="width: 100%;" style="width: 100%;"
row-key="menuId" row-key="menuId"
> >
<el-table-column <!-- 名称列 -->
prop="name" <el-table-column
header-align="center" prop="name"
tree-key="menuId" header-align="center"
width="150" tree-key="menuId"
label="名称" width="150"
/> label="名称"
<el-table-column />
header-align="center"
align="center" <!-- 图标列 -->
label="图标" <el-table-column
> header-align="center"
<template #default="scope"> align="center"
<svg-icon label="图标"
:icon-class="`icon-${scope.row.icon}`" >
/> <template #default="scope"> <!-- 自定义列内容 -->
</template> <svg-icon
</el-table-column> :icon-class="`icon-${scope.row.icon}`"
<el-table-column
prop="type"
header-align="center"
align="center"
label="类型"
>
<template #default="scope">
<el-tag
v-if="scope.row.type === 0"
>
目录
</el-tag>
<el-tag
v-else-if="scope.row.type === 1"
type="success"
>
菜单
</el-tag>
<el-tag
v-else-if="scope.row.type === 2"
type="info"
>
按钮
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="orderNum"
header-align="center"
align="center"
label="排序号"
/> />
<el-table-column </template>
prop="url" </el-table-column>
header-align="center"
align="center" <!-- 类型列 -->
width="150" <el-table-column
:show-overflow-tooltip="true" prop="type"
label="菜单URL" header-align="center"
> align="center"
<template #default="scope"> label="类型"
{{ scope.row.url || '-' }} >
</template> <template #default="scope"> <!-- 自定义列内容 -->
</el-table-column> <el-tag v-if="scope.row.type === 0"></el-tag> <!-- 根据类型动态渲染标签 -->
<el-table-column <el-tag v-else-if="scope.row.type === 1" type="success">菜单</el-tag>
prop="perms" <el-tag v-else-if="scope.row.type === 2" type="info">按钮</el-tag>
header-align="center" </template>
align="center" </el-table-column>
width="150"
:show-overflow-tooltip="true" <!-- 排序号列 -->
label="授权标识" <el-table-column
prop="orderNum"
header-align="center"
align="center"
label="排序号"
/>
<!-- 菜单URL列 -->
<el-table-column
prop="url"
header-align="center"
align="center"
width="150"
:show-overflow-tooltip="true"
label="菜单URL"
>
<template #default="scope"> <!-- 自定义列内容 -->
{{ scope.row.url || '-' }} <!-- 如果没有值则显示'-' -->
</template>
</el-table-column>
<!-- 授权标识列 -->
<el-table-column
prop="perms"
header-align="center"
align="center"
width="150"
:show-overflow-tooltip="true"
label="授权标识"
>
<template #default="scope"> <!-- 自定义列内容 -->
{{ scope.row.perms || '-' }} <!-- 如果没有值则显示'-' -->
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作"
>
<template #default="scope"> <!-- 自定义列内容 -->
<!-- 修改按钮只有有权限时显示 -->
<el-button
v-if="isAuth('sys:menu:update')"
type="text"
@click="onAddOrUpdate(scope.row.menuId)"
> >
<template #default="scope"> 修改
{{ scope.row.perms || '-' }} </el-button>
</template> <!-- 删除按钮只有有权限时显示 -->
</el-table-column> <el-button
<el-table-column v-if="isAuth('sys:menu:delete')"
fixed="right" type="text"
header-align="center" @click="onDelete(scope.row.menuId)"
align="center"
width="150"
label="操作"
> >
<template #default="scope"> 删除
<el-button </el-button>
v-if="isAuth('sys:menu:update')" </template>
type="text" </el-table-column>
@click="onAddOrUpdate(scope.row.menuId)"
>
修改
</el-button>
<el-button
v-if="isAuth('sys:menu:delete')"
type="text"
@click="onDelete(scope.row.menuId)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table> </el-table>
<!-- 弹窗, 新增 / 修改 --> <!-- 弹窗, 新增 / 修改 -->
<add-or-update <add-or-update
v-if="addOrUpdateVisible" v-if="addOrUpdateVisible"
ref="addOrUpdateRef" ref="addOrUpdateRef"
@refresh-data-list="getDataList" @refresh-data-list="getDataList"
@close="addOrUpdateVisible=false" @close="addOrUpdateVisible=false"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { treeDataTranslate, isAuth } from '@/utils' import { ref, onMounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { treeDataTranslate, isAuth } from '@/utils' //
import AddOrUpdate from './add-or-update.vue' import { ElMessage, ElMessageBox } from 'element-plus' //
import AddOrUpdate from './add-or-update.vue' // /
//
const dataForm = ref({}) const dataForm = ref({})
//
onMounted(() => { onMounted(() => {
getDataList() getDataList()
}) })
//
const dataList = ref([]) const dataList = ref([])
/** /**
* 获取数据列表 * 获取数据列表方法
*/ */
const getDataList = () => { const getDataList = () => {
http({ http({
url: http.adornUrl('/sys/menu/table'), url: http.adornUrl('/sys/menu/table'), // API
method: 'get', method: 'get', //
params: http.adornParams() params: http.adornParams() //
}).then(({ data }) => { }).then(({ data }) => {
dataList.value = treeDataTranslate(data, 'menuId') dataList.value = treeDataTranslate(data, 'menuId') //
}) })
} }
// /
const addOrUpdateRef = ref(null) const addOrUpdateRef = ref(null)
//
const addOrUpdateVisible = ref(false) const addOrUpdateVisible = ref(false)
/** /**
* 新增 / 修改 * 新增 / 修改菜单项
* @param id * @param id 菜单项ID可选如果没有传入则表示新增
*/ */
const onAddOrUpdate = (id) => { const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true addOrUpdateVisible.value = true //
nextTick(() => { nextTick(() => {
addOrUpdateRef.value?.init(id) addOrUpdateRef.value?.init(id) //
}) })
} }
/** /**
* 删除 * 删除菜单项
* @param id * @param id 菜单项ID
*/ */
const onDelete = (id) => { const onDelete = (id) => {
//
ElMessageBox.confirm(`确定对[id=${id}]进行[删除]操作?`, '提示', { ElMessageBox.confirm(`确定对[id=${id}]进行[删除]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
http({ http({
url: http.adornUrl(`/sys/menu/${id}`), url: http.adornUrl(`/sys/menu/${id}`),
method: 'delete', method: 'delete',
data: http.adornData() data: http.adornData()
}).then(() => { }).then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
getDataList() getDataList() //
} }
}) })
}) })

@ -1,136 +1,180 @@
<template> <template>
<!-- 角色管理对话框 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="!dataForm.id ? '新增' : '修改'" :title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-form <!-- 表单 -->
ref="dataFormRef" <el-form
:model="dataForm" ref="dataFormRef"
:rules="dataRule" :model="dataForm"
label-width="80px" :rules="dataRule"
@keyup.enter="onSubmit()" label-width="80px"
> @keyup.enter="onSubmit()"
<el-form-item >
label="角色名称" <!-- 角色名称输入框 -->
prop="roleName" <el-form-item
> label="角色名称"
<el-input prop="roleName"
v-model="dataForm.roleName" >
placeholder="角色名称" <el-input
/> v-model="dataForm.roleName"
</el-form-item> placeholder="角色名称"
<el-form-item />
label="备注" </el-form-item>
prop="remark"
> <!-- 备注输入框 -->
<el-input <el-form-item
v-model="dataForm.remark" label="备注"
placeholder="备注" prop="remark"
/> >
</el-form-item> <el-input
<el-form-item label="授权"> v-model="dataForm.remark"
<el-tree placeholder="备注"
ref="menuListTreeRef" />
:data="menuList" </el-form-item>
:props="menuListTreeProps"
node-key="menuId" <!-- 授权菜单树 -->
show-checkbox <el-form-item label="授权">
/> <el-tree
</el-form-item> ref="menuListTreeRef"
</el-form> :data="menuList"
<template #footer> :props="menuListTreeProps"
node-key="menuId"
show-checkbox
/>
</el-form-item>
</el-form>
<!-- 对话框底部按钮组 -->
<template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button> <!-- -->
<el-button <el-button type="primary" @click="onSubmit()"></el-button> <!-- 确认按钮 -->
type="primary"
@click="onSubmit()"
>确定</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ref, reactive, nextTick } from 'vue'
import { treeDataTranslate } from '@/utils' import { ElMessage } from 'element-plus' //
import { Debounce } from '@/utils/debounce' import { treeDataTranslate } from '@/utils' //
import { Debounce } from '@/utils/debounce' //
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
const tempKey = -666666 // key) tree. #
// keytree
const tempKey = -666666
//
const visible = ref(false) const visible = ref(false)
//
const menuList = ref([]) const menuList = ref([])
//
const menuListTreeProps = { const menuListTreeProps = {
label: 'name', label: 'name', //
children: 'children' children: 'children' //
} }
// 使reactive使
const dataForm = reactive({ const dataForm = reactive({
id: 0, id: 0, // ID0
roleName: '', roleName: '', //
remark: '' remark: '' //
}) })
// 使reactive使
const dataRule = reactive({ const dataRule = reactive({
roleName: [ roleName: [ //
{ required: true, message: '角色名称不能为空', trigger: 'blur' }, { required: true, message: '角色名称不能为空', trigger: 'blur' }, //
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的角色名称', trigger: 'blur' } { pattern: /\s\S+|\S+\s|\S/, message: '请输入正确的角色名称', trigger: 'blur' } //
], ],
remark: [ remark: [ //
{ required: false, pattern: /\s\S+|S+\s|\S/, message: '输入格式有误', trigger: 'blur' } { required: false, pattern: /\s\S+|\S+\s|\S/, message: '输入格式有误', trigger: 'blur' } //
] ]
}) })
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
//
const menuListTreeRef = ref(null) const menuListTreeRef = ref(null)
/**
* 初始化方法接收可选的角色ID参数
* @param id 角色ID可选
*/
const init = (id) => { const init = (id) => {
dataForm.id = id || 0 dataForm.id = id || 0 // id
//
http({ http({
url: http.adornUrl('/sys/menu/table'), url: http.adornUrl('/sys/menu/table'), // API
method: 'get', method: 'get', //
params: http.adornParams() params: http.adornParams() //
}) })
.then(({ data }) => { .then(({ data }) => {
menuList.value = treeDataTranslate(data, 'menuId', 'parentId') menuList.value = treeDataTranslate(data, 'menuId', 'parentId') //
}) })
.then(() => { .then(() => {
visible.value = true visible.value = true //
nextTick(() => { return nextTick() // DOM
dataFormRef.value?.resetFields()
menuListTreeRef.value?.setCheckedKeys([])
})
}) })
.then(() => { .then(() => {
if (dataForm.id) { dataFormRef.value?.resetFields() //
menuListTreeRef.value?.setCheckedKeys([]) //
})
.then(() => {
if (dataForm.id) { //
http({ http({
url: http.adornUrl(`/sys/role/info/${dataForm.id}`), url: http.adornUrl(`/sys/role/info/${dataForm.id}`), // API
method: 'get', method: 'get', //
params: http.adornParams() params: http.adornParams() //
}) })
.then(({ data }) => { .then(({ data }) => {
dataForm.roleName = data.roleName dataForm.roleName = data.roleName //
dataForm.remark = data.remark dataForm.remark = data.remark //
// key
const idx = data.menuIdList.indexOf(tempKey) const idx = data.menuIdList.indexOf(tempKey)
if (idx !== -1) { if (idx !== -1) {
data.menuIdList.splice(idx, data.menuIdList.length - idx) data.menuIdList.splice(idx, data.menuIdList.length - idx)
} }
//
menuListTreeRef.value?.setCheckedKeys(data.menuIdList) menuListTreeRef.value?.setCheckedKeys(data.menuIdList)
}) })
} }
}) })
} }
// init便
defineExpose({ init }) defineExpose({ init })
/** /**
* 表单提交 * 提交表单方法
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => { // 使
dataFormRef.value?.validate((valid) => { dataFormRef.value?.validate((valid) => { //
if (valid) { if (valid) { //
http({ http({
url: http.adornUrl('/sys/role'), url: http.adornUrl('/sys/role'), // API
method: dataForm.id ? 'put' : 'post', method: dataForm.id ? 'put' : 'post', // id
data: http.adornData({ data: http.adornData({
roleId: dataForm.id || undefined, roleId: dataForm.id || undefined, // ID
roleName: dataForm.roleName, roleName: dataForm.roleName, //
remark: dataForm.remark, remark: dataForm.remark, //
menuIdList: [].concat(menuListTreeRef.value?.getCheckedKeys(), [tempKey], menuListTreeRef.value?.getHalfCheckedKeys()) // ID
menuIdList: [].concat(
menuListTreeRef.value?.getCheckedKeys(),
[tempKey],
menuListTreeRef.value?.getHalfCheckedKeys()
)
}) })
}) })
.then(() => { .then(() => {
@ -139,13 +183,12 @@ const onSubmit = Debounce(() => {
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
visible.value = false visible.value = false //
emit('refreshDataList') emit('refreshDataList') //
} }
}) })
}) })
} }
}) })
}) })
</script> </script>

@ -1,153 +1,186 @@
<template> <template>
<!-- 角色管理模块 -->
<div class="mod-role"> <div class="mod-role">
<!-- 使用avue-crud创建CRUD表格 -->
<avue-crud <avue-crud
ref="crudRef" ref="crudRef"
:page="page" :page="page"
:data="dataList" :data="dataList"
:option="tableOption" :option="tableOption"
@search-change="onSearch" @search-change="onSearch"
@selection-change="selectionChange" @selection-change="selectionChange"
@on-load="getDataList" @on-load="getDataList"
> >
<template #menu-left> <!-- 左侧菜单栏 -->
<el-button <template #menu-left>
v-if="isAuth('sys:role:save')" <!-- 新增按钮只有有权限时显示 -->
type="primary" <el-button
icon="el-icon-plus" v-if="isAuth('sys:role:save')"
@click.stop="onAddOrUpdate()" type="primary"
> icon="el-icon-plus"
新增 @click.stop="onAddOrUpdate()"
</el-button> >
新增
<el-button </el-button>
v-if="isAuth('sys:role:delete')"
type="danger" <!-- 批量删除按钮只有有权限且选择了项目时显示 -->
:disabled="dataListSelections.length <= 0" <el-button
@click="onDelete()" v-if="isAuth('sys:role:delete')"
> type="danger"
批量删除 :disabled="dataListSelections.length <= 0"
</el-button> @click="onDelete()"
</template> >
批量删除
<template #menu="scope"> </el-button>
<el-button </template>
v-if="isAuth('sys:role:update')"
type="primary" <!-- 行内菜单 -->
icon="el-icon-edit" <template #menu="scope"> <!-- 自定义行内菜单 -->
@click.stop="onAddOrUpdate(scope.row.roleId)" <!-- 编辑按钮只有有权限时显示 -->
> <el-button
编辑 v-if="isAuth('sys:role:update')"
</el-button> type="primary"
icon="el-icon-edit"
<el-button @click.stop="onAddOrUpdate(scope.row.roleId)"
v-if="isAuth('sys:role:delete')" >
type="danger" 编辑
icon="el-icon-delete" </el-button>
@click.stop="onDelete(scope.row.roleId)"
> <!-- 删除按钮只有有权限时显示 -->
删除 <el-button
</el-button> v-if="isAuth('sys:role:delete')"
</template> type="danger"
icon="el-icon-delete"
@click.stop="onDelete(scope.row.roleId)"
>
删除
</el-button>
</template>
</avue-crud> </avue-crud>
<!-- 弹窗, 新增 / 修改 --> <!-- 弹窗, 新增 / 修改 -->
<add-or-update <add-or-update
v-if="addOrUpdateVisible" v-if="addOrUpdateVisible"
ref="addOrUpdateRef" ref="addOrUpdateRef"
@refresh-data-list="getDataList" @refresh-data-list="getDataList"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { isAuth } from '@/utils' import { ref, reactive } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { isAuth } from '@/utils' //
import { tableOption } from '@/crud/sys/role.js' import { ElMessage, ElMessageBox } from 'element-plus' //
import AddOrUpdate from './add-or-update.vue' import { tableOption } from '@/crud/sys/role.js' //
import AddOrUpdate from './add-or-update.vue' // /
//
const dataList = ref([]) const dataList = ref([])
//
const dataListSelections = ref([]) const dataListSelections = ref([])
// 使reactive使
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
}) })
/** /**
* 获取数据列表 * 获取数据列表方法
* @param pageParam 分页参数可选
* @param params 搜索参数可选
* @param done 加载完成回调可选
*/ */
const getDataList = (pageParam, params, done) => { const getDataList = (pageParam, params, done) => {
http({ http({
url: http.adornUrl('/sys/role/page'), url: http.adornUrl('/sys/role/page'), // API
method: 'get', method: 'get', //
params: http.adornParams( params: http.adornParams(
Object.assign( Object.assign(
{ {
current: pageParam == null ? page.currentPage : pageParam.currentPage, current: pageParam == null ? page.currentPage : pageParam.currentPage, //
size: pageParam == null ? page.pageSize : pageParam.pageSize size: pageParam == null ? page.pageSize : pageParam.pageSize //
}, },
params params //
) )
) )
}) })
.then(({ data }) => { .then(({ data }) => {
dataList.value = data.records dataList.value = data.records //
page.total = data.total page.total = data.total //
if (done) { if (done) {
done() done() //
} }
}) })
} }
/** /**
* 条件查询 * 条件查询方法
* @param params 搜索参数
* @param done 加载完成回调可选
*/ */
const onSearch = (params, done) => { const onSearch = (params, done) => {
getDataList(page, params, done) getDataList(page, params, done)
} }
/** /**
* 多选变化 * 多选变化方法
* @param val 选择的数据项
*/ */
const selectionChange = (val) => { const selectionChange = (val) => {
dataListSelections.value = val dataListSelections.value = val //
} }
//
const addOrUpdateVisible = ref(false) const addOrUpdateVisible = ref(false)
//
const addOrUpdateRef = ref(null) const addOrUpdateRef = ref(null)
/** /**
* 新增 / 修改 * 新增 / 修改角色信息
* @param id 角色ID可选如果没有传入则表示新增
*/ */
const onAddOrUpdate = (id) => { const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true addOrUpdateVisible.value = true //
nextTick(() => { nextTick(() => {
addOrUpdateRef.value?.init(id) addOrUpdateRef.value?.init(id) //
}) })
} }
/** /**
* 删除 * 删除角色信息
* @param id 角色ID可选如果传入则为单个删除否则为批量删除
*/ */
const onDelete = (id) => { const onDelete = (id) => {
const ids = id ? [id] : dataListSelections.value?.map(item => { const ids = id ? [id] : dataListSelections.value?.map(item => item.roleId) // ID
return item.roleId
}) //
ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', { ElMessageBox.confirm(`确定进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}) })
.then(() => { .then(() => {
// HTTP
http({ http({
url: http.adornUrl('/sys/role'), url: http.adornUrl('/sys/role'),
method: 'delete', method: 'delete',
data: http.adornData(ids, false) data: http.adornData(ids, false) // ID
}) })
.then(() => { .then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
getDataList() getDataList() //
} }
}) })
}) })
}).catch(() => { }) }).catch(() => { /* 用户取消删除操作 */ })
} }
</script> </script>

@ -1,231 +1,264 @@
<template> <template>
<!-- 用户管理对话框 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="!dataForm.id ? '新增' : '修改'" :title="!dataForm.id ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-form <!-- 表单 -->
ref="dataFormRef" <el-form
:model="dataForm" ref="dataFormRef"
:rules="dataRule" :model="dataForm"
label-width="80px" :rules="dataRule"
@keyup.enter="onSubmit()" label-width="80px"
@keyup.enter="onSubmit()"
>
<!-- 用户名输入框 -->
<el-form-item
label="用户名"
prop="userName"
>
<el-input
v-model="dataForm.userName"
placeholder="登录帐号"
/>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item
label="密码"
prop="password"
:class="{ 'is-required': !dataForm.id }"
>
<el-input
v-model="dataForm.password"
type="password"
placeholder="密码"
/>
</el-form-item>
<!-- 确认密码输入框 -->
<el-form-item
label="确认密码"
prop="comfirmPassword"
:class="{ 'is-required': !dataForm.id }"
>
<el-input
v-model="dataForm.comfirmPassword"
type="password"
placeholder="确认密码"
/>
</el-form-item>
<!-- 邮箱输入框 -->
<el-form-item
label="邮箱"
prop="email"
>
<el-input
v-model="dataForm.email"
placeholder="邮箱"
/>
</el-form-item>
<!-- 手机号输入框 -->
<el-form-item
label="手机号"
prop="mobile"
>
<el-input
v-model="dataForm.mobile"
maxlength="11"
placeholder="手机号"
/>
</el-form-item>
<!-- 角色选择 -->
<el-form-item
label="角色"
prop="roleIdList"
>
<el-checkbox-group v-model="dataForm.roleIdList"> <!-- ID -->
<el-checkbox
v-for="role in roleList"
:key="role.roleId"
:label="role.roleId"
> >
<el-form-item {{ role.roleName }} <!-- 显示角色名称 -->
label="用户名" </el-checkbox>
prop="userName" </el-checkbox-group>
> </el-form-item>
<el-input
v-model="dataForm.userName" <!-- 状态选择 -->
placeholder="登录帐号" <el-form-item
/> label="状态"
</el-form-item> prop="status"
<el-form-item >
label="密码" <el-radio-group v-model="dataForm.status"> <!-- -->
prop="password" <el-radio :label="0">禁用</el-radio> <!-- -->
:class="{ 'is-required': !dataForm.id }" <el-radio :label="1">正常</el-radio> <!-- -->
> </el-radio-group>
<el-input </el-form-item>
v-model="dataForm.password" </el-form>
type="password"
placeholder="密码" <!-- 对话框底部按钮组 -->
/> <template #footer>
</el-form-item>
<el-form-item
label="确认密码"
prop="comfirmPassword"
:class="{ 'is-required': !dataForm.id }"
>
<el-input
v-model="dataForm.comfirmPassword"
type="password"
placeholder="确认密码"
/>
</el-form-item>
<el-form-item
label="邮箱"
prop="email"
>
<el-input
v-model="dataForm.email"
placeholder="邮箱"
/>
</el-form-item>
<el-form-item
label="手机号"
prop="mobile"
>
<el-input
v-model="dataForm.mobile"
maxlength="11"
placeholder="手机号"
/>
</el-form-item>
<el-form-item
label="角色"
prop="roleIdList"
>
<el-checkbox-group v-model="dataForm.roleIdList">
<el-checkbox
v-for="role in roleList"
:key="role.roleId"
:label="role.roleId"
>
{{ role.roleName }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item
label="状态"
prop="status"
>
<el-radio-group v-model="dataForm.status">
<el-radio :label="0">
禁用
</el-radio>
<el-radio :label="1">
正常
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button> <!-- -->
<el-button <el-button type="primary" @click="onSubmit()"></el-button> <!-- 确认按钮 -->
type="primary"
@click="onSubmit()"
>确定</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ref, reactive, nextTick } from 'vue'
import { isEmail, isMobile } from '@/utils/validate' import { ElMessage } from 'element-plus' //
import { Debounce } from '@/utils/debounce' import { isEmail, isMobile } from '@/utils/validate' //
import { encrypt } from '@/utils/crypto' import { Debounce } from '@/utils/debounce' //
import { encrypt } from '@/utils/crypto' //
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
//
const visible = ref(false) const visible = ref(false)
// 使reactive使
const dataForm = reactive({ const dataForm = reactive({
id: 0, id: 0, // ID0
userName: '', userName: '', //
password: '', password: '', //
comfirmPassword: '', comfirmPassword: '', //
email: '', email: '', //
mobile: '', mobile: '', //
roleIdList: [], roleIdList: [], // ID
status: 1 status: 1 // 1
}) })
// eslint-disable-next-line no-unused-vars //
const validatePassword = (rule, value, callback) => { const validatePassword = (rule, value, callback) => {
if (!dataForm.id && !/\S/.test(value)) { if (!dataForm.id && !/\S/.test(value)) { //
callback(new Error('密码不能为空')) callback(new Error('密码不能为空'))
} else { } else {
callback() callback() //
} }
} }
// eslint-disable-next-line no-unused-vars
//
const validateComfirmPassword = (rule, value, callback) => { const validateComfirmPassword = (rule, value, callback) => {
if (!dataForm.id && !/\S/.test(value)) { if (!dataForm.id && !/\S/.test(value)) { //
dataForm.password = '' dataForm.password = ''
callback(new Error('确认密码不能为空')) callback(new Error('确认密码不能为空'))
} else if (dataForm.password !== value) { } else if (dataForm.password !== value) { //
callback(new Error('确认密码与密码输入不一致')) callback(new Error('确认密码与密码输入不一致'))
} else { } else {
callback() callback() //
} }
} }
// eslint-disable-next-line no-unused-vars
//
const validateEmail = (rule, value, callback) => { const validateEmail = (rule, value, callback) => {
if (!isEmail(value)) { if (!isEmail(value)) { //
callback(new Error('邮箱格式错误')) callback(new Error('邮箱格式错误'))
} else { } else {
callback() callback() //
} }
} }
// eslint-disable-next-line no-unused-vars
//
const validateMobile = (rule, value, callback) => { const validateMobile = (rule, value, callback) => {
if (!isMobile(value)) { if (!isMobile(value)) { //
callback(new Error('手机号格式错误')) callback(new Error('手机号格式错误'))
} else { } else {
callback() callback() //
} }
} }
const dataRule = {
// 使reactive使
const dataRule = reactive({
userName: [ userName: [
{ required: true, message: '用户名不能为空', trigger: 'blur' }, { required: true, message: '用户名不能为空', trigger: 'blur' }, //
{ pattern: /\s\S+|S+\s|\S/, message: '请输入正确的用户名', trigger: 'blur' } { pattern: /\s\S+|\S+\s|\S/, message: '请输入正确的用户名', trigger: 'blur' } //
], ],
password: [ password: [
{ validator: validatePassword, trigger: 'blur' } { validator: validatePassword, trigger: 'blur' } //
], ],
comfirmPassword: [ comfirmPassword: [
{ validator: validateComfirmPassword, trigger: 'blur' } { validator: validateComfirmPassword, trigger: 'blur' } //
], ],
email: [ email: [
{ required: true, message: '邮箱不能为空', trigger: 'blur' }, { required: true, message: '邮箱不能为空', trigger: 'blur' }, //
{ validator: validateEmail, trigger: 'blur' } { validator: validateEmail, trigger: 'blur' } //
], ],
mobile: [ mobile: [
{ required: true, message: '手机号不能为空', trigger: 'blur' }, { required: true, message: '手机号不能为空', trigger: 'blur' }, //
{ validator: validateMobile, trigger: 'blur' } { validator: validateMobile, trigger: 'blur' } //
] ]
} })
//
const roleList = ref([]) const roleList = ref([])
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
/**
* 初始化方法接收可选的用户ID参数
* @param id 用户ID可选
*/
const init = (id) => { const init = (id) => {
dataForm.id = id || 0 dataForm.id = id || 0 // id
//
http({ http({
url: http.adornUrl('/sys/role/list'), url: http.adornUrl('/sys/role/list'), // API
method: 'get', method: 'get', //
params: http.adornParams() params: http.adornParams() //
}).then(({ data }) => { }).then(({ data }) => {
roleList.value = data roleList.value = data //
}).then(() => { }).then(() => {
visible.value = true visible.value = true //
nextTick(() => { return nextTick() // DOM
dataFormRef.value?.resetFields()
})
}).then(() => { }).then(() => {
if (dataForm.id) { dataFormRef.value?.resetFields() //
}).then(() => {
if (dataForm.id) { //
http({ http({
url: http.adornUrl(`/sys/user/info/${dataForm.id}`), url: http.adornUrl(`/sys/user/info/${dataForm.id}`), // API
method: 'get', method: 'get', //
params: http.adornParams() params: http.adornParams() //
}).then(({ data }) => { }).then(({ data }) => {
dataForm.userName = data.username dataForm.userName = data.username //
dataForm.email = data.email dataForm.email = data.email //
dataForm.mobile = data.mobile dataForm.mobile = data.mobile //
dataForm.roleIdList = data.roleIdList dataForm.roleIdList = data.roleIdList // ID
dataForm.status = data.status dataForm.status = data.status //
}) })
} }
}) })
} }
// init便
defineExpose({ init }) defineExpose({ init })
/** /**
* 表单提交 * 提交表单方法
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => { // 使
dataFormRef.value?.validate((valid) => { dataFormRef.value?.validate((valid) => { //
if (valid) { if (valid) { //
http({ http({
url: http.adornUrl('/sys/user'), url: http.adornUrl('/sys/user'), // API
method: dataForm.id ? 'put' : 'post', method: dataForm.id ? 'put' : 'post', // id
data: http.adornData({ data: http.adornData({
userId: dataForm.id || undefined, userId: dataForm.id || undefined, // ID
username: dataForm.userName, username: dataForm.userName, //
password: encrypt(dataForm.password), password: encrypt(dataForm.password), //
email: dataForm.email, email: dataForm.email, //
mobile: dataForm.mobile, mobile: dataForm.mobile, //
status: dataForm.status, status: dataForm.status, //
roleIdList: dataForm.roleIdList roleIdList: dataForm.roleIdList // ID
}) })
}).then(() => { }).then(() => {
ElMessage({ ElMessage({
@ -233,13 +266,12 @@ const onSubmit = Debounce(() => {
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
visible.value = false visible.value = false //
emit('refreshDataList') emit('refreshDataList') //
} }
}) })
}) })
} }
}) })
}) })
</script> </script>

@ -1,155 +1,187 @@
<template> <template>
<!-- 用户管理模块 -->
<div class="mod-user"> <div class="mod-user">
<!-- 使用avue-crud创建CRUD表格 -->
<avue-crud <avue-crud
ref="crudRef" ref="crudRef"
:page="page" :page="page"
:data="dataList" :data="dataList"
:option="tableOption" :option="tableOption"
@search-change="onSearch" @search-change="onSearch"
@selection-change="selectionChange" @selection-change="selectionChange"
@on-load="getDataList" @on-load="getDataList"
> >
<template #menu-left> <!-- 左侧菜单栏 -->
<el-button <template #menu-left>
v-if="isAuth('sys:user:save')" <!-- 新增按钮只有有权限时显示 -->
type="primary" <el-button
icon="el-icon-plus" v-if="isAuth('sys:user:save')"
@click.stop="onAddOrUpdate()" type="primary"
> icon="el-icon-plus"
新增 @click.stop="onAddOrUpdate()"
</el-button>
<el-button
v-if="isAuth('sys:user:delete')"
type="danger"
:disabled="dataListSelections.length <= 0"
@click="onDelete()"
>
批量删除
</el-button>
</template>
<template
#menu="scope"
> >
<el-button 新增
v-if="isAuth('sys:user:update')" </el-button>
type="primary"
icon="el-icon-edit" <!-- 批量删除按钮只有有权限且选择了项目时显示 -->
@click.stop="onAddOrUpdate(scope.row.userId)" <el-button
> v-if="isAuth('sys:user:delete')"
编辑 type="danger"
</el-button> :disabled="dataListSelections.length <= 0"
@click="onDelete()"
<el-button >
v-if="isAuth('sys:user:delete')" 批量删除
type="danger" </el-button>
icon="el-icon-delete" </template>
@click.stop="onDelete(scope.row.userId)"
> <!-- 行内菜单 -->
删除 <template #menu="scope"> <!-- 自定义行内菜单 -->
</el-button> <!-- 编辑按钮只有有权限时显示 -->
</template> <el-button
v-if="isAuth('sys:user:update')"
type="primary"
icon="el-icon-edit"
@click.stop="onAddOrUpdate(scope.row.userId)"
>
编辑
</el-button>
<!-- 删除按钮只有有权限时显示 -->
<el-button
v-if="isAuth('sys:user:delete')"
type="danger"
icon="el-icon-delete"
@click.stop="onDelete(scope.row.userId)"
>
删除
</el-button>
</template>
</avue-crud> </avue-crud>
<!-- 弹窗, 新增 / 修改 --> <!-- 弹窗, 新增 / 修改 -->
<add-or-update <add-or-update
v-if="addOrUpdateVisible" v-if="addOrUpdateVisible"
ref="addOrUpdateRef" ref="addOrUpdateRef"
@refresh-data-list="getDataList" @refresh-data-list="getDataList"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { isAuth } from '@/utils' import { ref, reactive, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { isAuth } from '@/utils' //
import { tableOption } from '@/crud/sys/user.js' import { ElMessage, ElMessageBox } from 'element-plus' //
import AddOrUpdate from './add-or-update.vue' import { tableOption } from '@/crud/sys/user.js' //
import AddOrUpdate from './add-or-update.vue' // /
//
const dataList = ref([]) const dataList = ref([])
//
const dataListLoading = ref(false) const dataListLoading = ref(false)
//
const dataListSelections = ref([]) const dataListSelections = ref([])
//
const addOrUpdateVisible = ref(false) const addOrUpdateVisible = ref(false)
// 使reactive使
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
}) })
/** /**
* 获取数据列表 * 获取数据列表方法
* @param pageParam 分页参数可选
* @param params 搜索参数可选
* @param done 加载完成回调可选
*/ */
const getDataList = (pageParam, params, done) => { const getDataList = (pageParam, params, done) => {
dataListLoading.value = true dataListLoading.value = true // true
http({ http({
url: http.adornUrl('/sys/user/page'), url: http.adornUrl('/sys/user/page'), // API
method: 'get', method: 'get', //
params: http.adornParams( params: http.adornParams(
Object.assign( Object.assign(
{ {
current: pageParam == null ? page.currentPage : pageParam.currentPage, current: pageParam == null ? page.currentPage : pageParam.currentPage, //
size: pageParam == null ? page.pageSize : pageParam.pageSize size: pageParam == null ? page.pageSize : pageParam.pageSize //
}, },
params params //
) )
) )
}).then(({ data }) => { }).then(({ data }) => {
dataList.value = data.records dataList.value = data.records //
page.total = data.total page.total = data.total //
dataListLoading.value = false dataListLoading.value = false // false
if (done) done() if (done) done() //
}) })
} }
/** /**
* 条件查询 * 条件查询方法
* @param params 搜索参数
* @param done 加载完成回调可选
*/ */
const onSearch = (params, done) => { const onSearch = (params, done) => {
getDataList(page, params, done) getDataList(page, params, done)
} }
/** /**
* 多选变化 * 多选变化方法
* @param val 选择的数据项
*/ */
const selectionChange = (val) => { const selectionChange = (val) => {
dataListSelections.value = val dataListSelections.value = val //
} }
//
const addOrUpdateRef = ref(null) const addOrUpdateRef = ref(null)
/** /**
* 新增 / 修改 * 新增 / 修改用户信息
* @param id 用户ID可选如果没有传入则表示新增
*/ */
const onAddOrUpdate = (id) => { const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true addOrUpdateVisible.value = true //
nextTick(() => { nextTick(() => {
addOrUpdateRef.value?.init(id) addOrUpdateRef.value?.init(id) //
}) })
} }
/** /**
* 删除 * 删除用户信息
* @param id 用户ID可选如果传入则为单个删除否则为批量删除
*/ */
const onDelete = (id) => { const onDelete = (id) => {
const userIds = id ? [id] : dataListSelections.value?.map(item => { const userIds = id ? [id] : dataListSelections.value?.map(item => item.userId) // ID
return item.userId
}) //
ElMessageBox.confirm(`确定对[id=${userIds.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', { ElMessageBox.confirm(`确定对[id=${userIds.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
http({ http({
url: http.adornUrl('/sys/user'), url: http.adornUrl('/sys/user'),
method: 'delete', method: 'delete',
data: http.adornData(userIds, false) data: http.adornData(userIds, false) // ID
}).then(() => { }).then(() => {
//
ElMessage({ ElMessage({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
getDataList() getDataList() //
} }
}) })
}) })
}).catch(() => { }) }).catch(() => { /* 用户取消删除操作 */ })
} }
</script> </script>

@ -1,139 +1,152 @@
<template> <template>
<!-- 用户管理对话框 -->
<el-dialog <el-dialog
v-model="visible" v-model="visible"
:title="!dataForm.userId ? '新增' : '修改'" :title="!dataForm.userId ? '新增' : '修改'"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-form <!-- 表单 -->
ref="dataFormRef" <el-form
:model="dataForm" ref="dataFormRef"
:rules="dataRule" :model="dataForm"
label-width="80px" :rules="dataRule"
@keyup.enter="onSubmit()" label-width="80px"
> @keyup.enter="onSubmit()"
<el-form-item >
label="用户头像" <!-- 用户头像展示 -->
prop="pic" <el-form-item
> label="用户头像"
<img prop="pic"
v-if="dataForm.pic" >
:src="dataForm.pic" <img
class="image" v-if="dataForm.pic"
alt="" :src="dataForm.pic"
> class="image"
<div v-else> alt=""
/>
</div> <div v-else></div> <!-- -->
</el-form-item> </el-form-item>
<el-form-item
label="用户昵称" <!-- 用户昵称输入框 -->
prop="nickName" <el-form-item
> label="用户昵称"
<el-input prop="nickName"
v-model="dataForm.nickName" >
:disabled="true" <el-input
placeholder="用户昵称" v-model="dataForm.nickName"
/> :disabled="true"
</el-form-item> placeholder="用户昵称"
<el-form-item />
label="状态" </el-form-item>
prop="status"
> <!-- 状态选择 -->
<el-radio-group v-model="dataForm.status"> <el-form-item
<el-radio :label="0"> label="状态"
禁用 prop="status"
</el-radio> >
<el-radio :label="1"> <el-radio-group v-model="dataForm.status"> <!-- -->
正常 <el-radio :label="0">禁用</el-radio> <!-- -->
</el-radio> <el-radio :label="1">正常</el-radio> <!-- -->
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer>
<span <!-- 对话框底部按钮组 -->
class="dialog-footer" <template #footer>
> <span class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button> <!-- -->
<el-button <el-button type="primary" @click="onSubmit()"></el-button> <!-- 确认按钮 -->
type="primary"
@click="onSubmit()"
>确定</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup>
import { ElMessage } from 'element-plus' import { ref, reactive, nextTick } from 'vue'
import { Debounce } from '@/utils/debounce' import { ElMessage } from 'element-plus' //
import { Debounce } from '@/utils/debounce' //
//
const emit = defineEmits(['refreshDataList']) const emit = defineEmits(['refreshDataList'])
//
const visible = ref(false) const visible = ref(false)
// 使ref使
const dataForm = ref({ const dataForm = ref({
userId: 0, userId: 0, // ID0
nickName: '', nickName: '', //
pic: '', pic: '', //
status: 1 status: 1 // 1
}) })
// 使reactive使
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
}) })
// 使
const dataRule = { const dataRule = {
nickName: [ nickName: [
{ required: true, message: '用户名不能为空', trigger: 'blur' } { required: true, message: '用户名不能为空', trigger: 'blur' } //
] ]
} }
//
const dataFormRef = ref(null) const dataFormRef = ref(null)
/**
* 初始化方法接收可选的用户ID参数
* @param id 用户ID可选
*/
const init = (id) => { const init = (id) => {
dataForm.value.userId = id || 0 dataForm.value.userId = id || 0 // id
visible.value = true visible.value = true //
nextTick(() => { nextTick(() => {
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields() //
}) })
if (dataForm.value.userId) { if (dataForm.value.userId) { //
http({ http({
url: http.adornUrl(`/admin/user/info/${dataForm.value.userId}`), url: http.adornUrl(`/admin/user/info/${dataForm.value.userId}`), // API
method: 'get', method: 'get', //
params: http.adornParams() params: http.adornParams() //
}).then(({ data }) => {
dataForm.value = data //
}) })
.then(({ data }) => {
dataForm.value = data
})
} }
} }
// init便
defineExpose({ init }) defineExpose({ init })
/** /**
* 表单提交 * 提交表单方法
*/ */
const onSubmit = Debounce(() => { const onSubmit = Debounce(() => { // 使
dataFormRef.value?.validate(valid => { dataFormRef.value?.validate((valid) => { //
if (valid) { if (valid) { //
http({ http({
url: http.adornUrl('/admin/user'), url: http.adornUrl('/admin/user'), // API
method: dataForm.value.userId ? 'put' : 'post', method: dataForm.value.userId ? 'put' : 'post', // userId
data: http.adornData({ data: http.adornData({
userId: dataForm.value.userId || undefined, userId: dataForm.value.userId || undefined, // ID
nickName: dataForm.value.nickName, nickName: dataForm.value.nickName, //
status: dataForm.value.status status: dataForm.value.status //
}) })
}) }).then(() => {
.then(() => { ElMessage({
ElMessage({ message: '操作成功',
message: '操作成功', type: 'success',
type: 'success', duration: 1500,
duration: 1500, onClose: () => {
onClose: () => { visible.value = false //
visible.value = false emit('refreshDataList', page) //
emit('refreshDataList', page) }
}
})
}) })
})
} }
}) })
}) })
</script> </script>

@ -1,120 +1,139 @@
<template> <template>
<!-- 用户管理模块 -->
<div class="mod-user"> <div class="mod-user">
<!-- 使用avue-crud创建CRUD表格 -->
<avue-crud <avue-crud
ref="crudRef" ref="crudRef"
:page="page" :page="page"
:data="dataList" :data="dataList"
:option="tableOption" :option="tableOption"
@search-change="onSearch" @search-change="onSearch"
@selection-change="selectionChange" @selection-change="selectionChange"
@on-load="getDataList" @on-load="getDataList"
> >
<template #pic="scope"> <!-- 自定义列模板用户头像 -->
<span <template #pic="scope"> <!-- 自定义用户头像列 -->
v-if="scope.row.pic" <span v-if="scope.row.pic" class="avue-crud__img">
class="avue-crud__img" <el-icon><Document /></el-icon> <!-- -->
>
<el-icon><Document /></el-icon>
</span> </span>
<span v-else>-</span> <span v-else>-</span> <!-- "-" -->
</template> </template>
<template #status="scope"> <!-- 自定义列模板状态 -->
<el-tag <template #status="scope"> <!-- 自定义状态列 -->
v-if="scope.row.status === 0" <el-tag v-if="scope.row.status === 0" type="danger">
type="danger" 禁用
> </el-tag>
禁用 <el-tag v-else>
</el-tag> 正常
<el-tag v-else> </el-tag>
正常 </template>
</el-tag>
</template>
<template #menu="scope"> <!-- 自定义行内菜单 -->
<el-button <template #menu="scope"> <!-- 自定义行内菜单 -->
v-if="isAuth('admin:user:update')" <!-- 编辑按钮只有有权限时显示 -->
type="primary" <el-button
icon="el-icon-edit" v-if="isAuth('admin:user:update')"
@click.stop="onAddOrUpdate(scope.row.userId)" type="primary"
> icon="el-icon-edit"
编辑 @click.stop="onAddOrUpdate(scope.row.userId)"
</el-button> >
</template> 编辑
</el-button>
</template>
</avue-crud> </avue-crud>
<!-- 弹窗, 新增 / 修改 --> <!-- 弹窗, 新增 / 修改 -->
<add-or-update <add-or-update
v-if="addOrUpdateVisible" v-if="addOrUpdateVisible"
ref="addOrUpdateRef" ref="addOrUpdateRef"
@refresh-data-list="getDataList" @refresh-data-list="getDataList"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import { isAuth } from '@/utils' import { ref, reactive, nextTick } from 'vue'
import { tableOption } from '@/crud/user/user.js' import { isAuth } from '@/utils' //
import AddOrUpdate from './add-or-update.vue' import { tableOption } from '@/crud/user/user.js' //
import AddOrUpdate from './add-or-update.vue' // /
//
const dataList = ref([]) const dataList = ref([])
//
const dataListLoading = ref(false) const dataListLoading = ref(false)
//
const dataListSelections = ref([]) const dataListSelections = ref([])
//
const addOrUpdateVisible = ref(false) const addOrUpdateVisible = ref(false)
// 使reactive使
const page = reactive({ const page = reactive({
total: 0, // total: 0, //
currentPage: 1, // currentPage: 1, //
pageSize: 10 // pageSize: 10 //
}) })
/** /**
* 获取数据列表 * 获取数据列表方法
* @param pageParam 分页参数可选
* @param params 搜索参数可选
* @param done 加载完成回调可选
*/ */
const getDataList = (pageParam, params, done) => { const getDataList = (pageParam, params, done) => {
dataListLoading.value = true dataListLoading.value = true // true
http({ http({
url: http.adornUrl('/admin/user/page'), url: http.adornUrl('/admin/user/page'), // API
method: 'get', method: 'get', //
params: http.adornParams( params: http.adornParams(
Object.assign( Object.assign(
{ {
current: pageParam == null ? page.currentPage : pageParam.currentPage, current: pageParam == null ? page.currentPage : pageParam.currentPage, //
size: pageParam == null ? page.pageSize : pageParam.pageSize size: pageParam == null ? page.pageSize : pageParam.pageSize //
}, },
params params //
) )
) )
}).then(({ data }) => {
dataList.value = data.records //
page.total = data.total //
dataListLoading.value = false // false
if (done) done() //
}) })
.then(({ data }) => {
dataList.value = data.records
page.total = data.total
dataListLoading.value = false
if (done) done()
})
} }
//
const addOrUpdateRef = ref(null) const addOrUpdateRef = ref(null)
/** /**
* 新增 / 修改 * 新增 / 修改用户信息
* @param id * @param id 用户ID可选如果没有传入则表示新增
*/ */
const onAddOrUpdate = (id) => { const onAddOrUpdate = (id) => {
addOrUpdateVisible.value = true addOrUpdateVisible.value = true //
nextTick(() => { nextTick(() => {
addOrUpdateRef.value?.init(id) addOrUpdateRef.value?.init(id) //
}) })
} }
/** /**
* 条件查询 * 条件查询方法
* @param params 搜索参数
* @param done 加载完成回调可选
*/ */
const onSearch = (params, done) => { const onSearch = (params, done) => {
getDataList(page, params, done) getDataList(page, params, done)
} }
/** /**
* 多选变化 * 多选变化方法
* @param val 选择的数据项
*/ */
const selectionChange = (val) => { const selectionChange = (val) => {
dataListSelections.value = val dataListSelections.value = val //
} }
</script> </script>

Loading…
Cancel
Save