配置引导页完成一部分

This commit is contained in:
aixianling
2022-07-08 18:01:58 +08:00
parent d12705b4f6
commit 86451a245f
9 changed files with 531 additions and 237 deletions

View File

@@ -1,175 +1,29 @@
<template>
<section class="AppMenuManager">
<ai-list>
<ai-title slot="title" title="菜单配置" isShowBottomBorder/>
<template #content>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="el-icon-circle-plus" @click="addRootMenu">添加一级目录</el-button>
</template>
<template #right>
<el-input size="small" v-model="search" clearable @change="$refs.MenuTree.filter(search)"
placeholder="菜单名称"/>
<el-button icon="iconfont iconResetting" @click="getData">刷新</el-button>
</template>
</ai-search-bar>
<el-row type="flex" class="headerRow">
<div class="menuName" v-text="`菜单名称`"/>
<el-row type="flex" align="middle" class="info">
<div class="style" v-text="`图标`"/>
<div class="type" v-text="`菜单类型`"/>
<div class="component" v-text="`应用模块`"/>
<div class="status" v-text="`是否显示`"/>
<div class="showIndex" v-text="`排序`"/>
</el-row>
<div class="operation" v-text="`操作`"/>
</el-row>
<el-scrollbar>
<el-tree ref="MenuTree" :data="treeData" :props="{children:'subSet'}" highlight-current node-key="id"
:filter-node-method="handleSearch">
<el-row type="flex" align="middle" slot-scope="{node,data}" class="menuItem">
<div class="menuName" v-text="data.name"/>
<el-row type="flex" align="middle" class="info">
<div class="style" :class="data.style"/>
<div class="type" v-text="dict.getLabel('menuType',data.type)"/>
<div class="component" v-text="data.component"/>
<div class="status" v-text="dict.getLabel('yesOrNo',data.status)"/>
<div class="showIndex" v-text="data.showIndex"/>
</el-row>
<el-row type="flex" align="middle" class="operation">
<div v-if="node.isLeaf" class="opBtn del" v-text="`删除`" @click="handleDelete(data)"/>
<div v-if="data.type<2" class="opBtn" v-text="`添加下级`" @click="addMenu(data)"/>
<div class="opBtn" v-text="`编辑`" @click="handleEdit(data)"/>
</el-row>
</el-row>
</el-tree>
</el-scrollbar>
</template>
</ai-list>
<ai-dialog :visible.sync="dialog" title="菜单设置" width="500px" @onConfirm="handleSubmit"
@closed="form={},selected={}">
<el-form ref="MenuForm" :model="form" size="small" label-width="100px" :rules="rules">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="菜单类型" prop="type">
<ai-select v-model="form.type" clearable :selectList="dict.getDict('menuType')"/>
</el-form-item>
<template v-if="form.type==0">
<el-form-item label="菜单图标" prop="style">
<el-input v-model="form.style" placeholder="请输入" clearable/>
</el-form-item>
</template>
<template v-if="form.type==1">
<el-form-item label="菜单应用" prop="component">
<el-input v-model="form.component" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="路径(path)" prop="path">
<el-input v-model="form.path" placeholder="请输入" clearable/>
</el-form-item>
</template>
<template v-if="form.type==2">
<el-form-item label="权限码" prop="permission">
<el-input v-model="form.permission" placeholder="请输入" clearable/>
</el-form-item>
</template>
<el-form-item label="显示菜单" prop="status">
<ai-select v-model="form.status" clearable :selectList="dict.getDict('yesOrNo')"/>
</el-form-item>
<el-form-item v-if="form.type<2" label="排序" prop="showIndex">
<el-input v-model="form.showIndex" placeholder="请输入" clearable/>
</el-form-item>
</el-form>
</ai-dialog>
<component :is="currentPage" v-bind="$props"/>
</section>
</template>
<script>
import List from "./list";
import IntroPage from "./introPage";
export default {
name: "AppMenuManager",
components: {IntroPage, List},
label: "菜单管理",
props: {
instance: Function,
dict: {default: () => ({})}
},
data() {
return {
treeData: [],
dialog: false,
form: {},
selected: {},
rules: {
name: [{required: true, message: "请输入 菜单名称"}],
type: [{required: true, message: "请选择 菜单类型"}],
status: [{required: true, message: "请选择 显示菜单"}],
showIndex: [{required: true, message: "请输入 排序"}],
permission: [{required: true, message: "请输入 权限码"}],
},
search: ""
}
},
methods: {
getData() {
return this.instance.post("/admin/menu/menuTree").then(res => {
if (res?.data) {
this.treeData = res.data
}
})
},
handleSubmit() {
this.$refs.MenuForm.validate(v => {
if (v) {
this.instance.post("/admin/menu/addOrUpdate", this.form).then(res => {
if (res?.code == 0) {
this.$message.success("提交成功!")
this.dialog = false
if (!!this.form.id) {
let node = this.$refs.MenuTree.getNode(this.form)
node.data = this.form
} else if (!!this.form.parentId) {
this.$refs.MenuTree.append(this.form, this.selected)
} else this.getData()
}
})
}
})
},
handleDelete(data) {
let {id} = data
this.$confirm("是否要删除该菜单").then(() => {
this.instance.post("/admin/menu/delete", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.$message.success("删除成功!")
this.dialog = false
this.$refs.MenuTree.remove(data)
}
})
}).catch(() => 0)
},
addRootMenu(row) {
this.dialog = true
this.selected = row
},
addMenu(row) {
this.dialog = true
this.form = {parentId: row.id}
this.selected = row
},
handleEdit(row) {
this.dialog = true
this.form = JSON.parse(JSON.stringify(row))
this.selected = row
},
handleSearch(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
computed: {
currentPage() {
const {hash} = this.$route
return hash == "#intro" ? IntroPage : List
}
},
created() {
this.getData()
this.dict.load("yesOrNo", "menuType")
this.dict.load("menuType")
}
}
</script>
@@ -177,85 +31,5 @@ export default {
<style lang="scss" scoped>
.AppMenuManager {
height: 100%;
::v-deep .ai-list__content--right-wrapper {
height: 100%;
display: flex;
flex-direction: column;
.el-tree {
width: 100%;
height: 100%;
font-size: 14px;
.menuItem {
flex: 1;
min-width: 0;
}
.el-tree-node__content {
border-bottom: 1px solid #d0d4dc;
}
}
.el-scrollbar {
flex: 1;
min-height: 0;
.el-scrollbar__wrap {
overflow-x: auto;
}
}
.headerRow {
background: #f3f4f5;
color: #666;
font-weight: bold;
align-items: center;
height: 40px;
.menuName {
padding-left: 16px;
}
}
.info {
gap: 16px;
text-align: center;
.showIndex, .status, .type, .style {
width: 80px;
}
.component {
width: 300px;
}
}
.operation {
width: 200px;
flex-shrink: 0;
justify-content: flex-end;
text-align: center;
.opBtn {
cursor: pointer;
width: 60px;
font-size: 14px;
color: #26f;
&.del {
color: #f46;
}
}
}
.menuName {
flex: 1;
min-width: 0;
}
}
}
</style>

View File

@@ -0,0 +1,78 @@
<template>
<section class="introPage">
<ai-detail list>
<ai-title slot="title" title="引导页配置" isShowBottomBorder>
<template #rightBtn>
<el-row type="flex">
<component-lib @select="handleAddComp"/>
<el-button type="text" @click="submit">保存</el-button>
<el-button type="text" @click="">取消</el-button>
</el-row>
</template>
</ai-title>
<template #content>
<ai-drag v-for="comp in configs" :key="comp.value" :w="comp.width" :h="comp.height" parent>
<comp-render :ops="comp"/>
</ai-drag>
<div v-if="!hasConfigs" class="empty">编辑区域</div>
</template>
</ai-detail>
</section>
</template>
<script>
import AiDrag from "../../components/AiDrag";
import ComponentLib from "./tools/componentLib";
import AiEditBtn from "../../components/AiEditBtn";
import CompRender from "./tools/compRender";
export default {
name: "introPage",
components: {CompRender, AiEditBtn, ComponentLib, AiDrag},
props: {
instance: Function,
dict: {default: () => ({})}
},
computed: {
hasConfigs: v => v.configs?.length > 0
},
data() {
return {
form: {},
configs: []
}
},
methods: {
handleAddComp(comp) {
console.log(comp)
this.configs.push({...comp})
},
submit() {
}
},
created() {
}
}
</script>
<style lang="scss" scoped>
.introPage {
height: 100%;
.empty {
width: 100%;
min-height: calc(100vh - 180px);
display: flex;
justify-content: center;
align-items: center;
font-size: 48px;
color: #999;
border: 1px dashed #ddd;
}
::v-deep.ai-detail__content--wrapper {
min-height: 100%;
}
}
</style>

View File

@@ -0,0 +1,265 @@
<template>
<section class="list">
<ai-list>
<ai-title slot="title" title="菜单配置" isShowBottomBorder/>
<template #content>
<ai-search-bar>
<template #left>
<el-button type="primary" icon="el-icon-circle-plus" @click="addRootMenu">添加一级目录</el-button>
</template>
<template #right>
<el-input size="small" v-model="search" clearable @change="$refs.MenuTree.filter(search)"
placeholder="菜单名称"/>
<el-button icon="iconfont iconResetting" @click="getData">刷新</el-button>
</template>
</ai-search-bar>
<el-row type="flex" class="headerRow">
<div class="menuName" v-text="`菜单名称`"/>
<el-row type="flex" align="middle" class="info">
<div class="style" v-text="`图标`"/>
<div class="type" v-text="`菜单类型`"/>
<div class="component" v-text="`应用模块`"/>
<div class="status" v-text="`是否显示`"/>
<div class="showIndex" v-text="`排序`"/>
</el-row>
<div class="operation" v-text="`操作`"/>
</el-row>
<el-scrollbar>
<el-tree ref="MenuTree" :data="treeData" :props="{children:'subSet'}" highlight-current node-key="id"
:filter-node-method="handleSearch">
<el-row type="flex" align="middle" slot-scope="{node,data}" class="menuItem">
<div class="menuName" v-text="data.name"/>
<el-row type="flex" align="middle" class="info">
<div class="style" :class="data.style"/>
<div class="type" v-text="dict.getLabel('menuType',data.type)"/>
<div class="component" v-text="data.component"/>
<div class="status" v-text="dict.getLabel('yesOrNo',data.status)"/>
<div class="showIndex" v-text="data.showIndex"/>
</el-row>
<el-row type="flex" align="middle" class="operation">
<div v-if="node.isLeaf" class="opBtn del" v-text="`删除`" @click="handleDelete(data)"/>
<div v-if="data.component&&data.type==1" class="opBtn" v-text="`引导页`" @click="$router.push({hash:'#intro',query:{id:data.id}})"/>
<div v-if="data.type<2" class="opBtn" v-text="`添加下级`" @click="addMenu(data)"/>
<div class="opBtn" v-text="`编辑`" @click="handleEdit(data)"/>
</el-row>
</el-row>
</el-tree>
</el-scrollbar>
</template>
</ai-list>
<ai-dialog :visible.sync="dialog" title="菜单设置" width="500px" @onConfirm="handleSubmit"
@closed="form={},selected={}">
<el-form ref="MenuForm" :model="form" size="small" label-width="100px" :rules="rules">
<el-form-item label="菜单名称" prop="name">
<el-input v-model="form.name" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="菜单类型" prop="type">
<ai-select v-model="form.type" clearable :selectList="dict.getDict('menuType')"/>
</el-form-item>
<template v-if="form.type==0">
<el-form-item label="菜单图标" prop="style">
<el-input v-model="form.style" placeholder="请输入" clearable/>
</el-form-item>
</template>
<template v-if="form.type==1">
<el-form-item label="菜单应用" prop="component">
<el-input v-model="form.component" placeholder="请输入" clearable/>
</el-form-item>
<el-form-item label="路径(path)" prop="path">
<el-input v-model="form.path" placeholder="请输入" clearable/>
</el-form-item>
</template>
<template v-if="form.type==2">
<el-form-item label="权限码" prop="permission">
<el-input v-model="form.permission" placeholder="请输入" clearable/>
</el-form-item>
</template>
<el-form-item label="显示菜单" prop="status">
<ai-select v-model="form.status" clearable :selectList="dict.getDict('yesOrNo')"/>
</el-form-item>
<el-form-item v-if="form.type<2" label="排序" prop="showIndex">
<el-input v-model="form.showIndex" placeholder="请输入" clearable/>
</el-form-item>
</el-form>
</ai-dialog>
</section>
</template>
<script>
export default {
name: "list",
props: {
instance: Function,
dict: {default: () => ({})}
},
data() {
return {
treeData: [],
dialog: false,
form: {},
selected: {},
rules: {
name: [{required: true, message: "请输入 菜单名称"}],
type: [{required: true, message: "请选择 菜单类型"}],
status: [{required: true, message: "请选择 显示菜单"}],
showIndex: [{required: true, message: "请输入 排序"}],
permission: [{required: true, message: "请输入 权限码"}],
},
search: ""
}
},
methods: {
getData() {
return this.instance.post("/admin/menu/menuTree").then(res => {
if (res?.data) {
this.treeData = res.data
}
})
},
handleSubmit() {
this.$refs.MenuForm.validate(v => {
if (v) {
this.instance.post("/admin/menu/addOrUpdate", this.form).then(res => {
if (res?.code == 0) {
this.$message.success("提交成功!")
this.dialog = false
if (!!this.form.id) {
let node = this.$refs.MenuTree.getNode(this.form)
node.data = this.form
} else if (!!this.form.parentId) {
this.$refs.MenuTree.append(this.form, this.selected)
} else this.getData()
}
})
}
})
},
handleDelete(data) {
let {id} = data
this.$confirm("是否要删除该菜单").then(() => {
this.instance.post("/admin/menu/delete", null, {
params: {id}
}).then(res => {
if (res?.code == 0) {
this.$message.success("删除成功!")
this.dialog = false
this.$refs.MenuTree.remove(data)
}
})
}).catch(() => 0)
},
addRootMenu(row) {
this.dialog = true
this.selected = row
},
addMenu(row) {
this.dialog = true
this.form = {parentId: row.id}
this.selected = row
},
handleEdit(row) {
this.dialog = true
this.form = JSON.parse(JSON.stringify(row))
this.selected = row
},
handleSearch(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
}
},
created() {
this.getData()
}
}
</script>
<style lang="scss" scoped>
.list {
height: 100%;
::v-deep .ai-list__content--right-wrapper {
height: calc(100% - 10px);
display: flex;
flex-direction: column;
.el-tree {
width: 100%;
height: 100%;
font-size: 14px;
.menuItem {
flex: 1;
min-width: 0;
}
.el-tree-node:nth-of-type(2n) {
background: rgba(#26f, .05);
}
.el-tree-node__content {
border-bottom: 1px solid #d0d4dc;
}
}
.el-scrollbar {
flex: 1;
min-height: 0;
.el-scrollbar__wrap {
overflow-x: auto;
}
}
.headerRow {
background: #f3f4f5;
color: #666;
font-weight: bold;
align-items: center;
height: 40px;
.menuName {
padding-left: 16px;
}
}
.info {
gap: 16px;
text-align: center;
.showIndex, .status, .type, .style {
width: 80px;
}
.component {
width: 300px;
}
}
.operation {
width: 300px;
flex-shrink: 0;
justify-content: flex-end;
text-align: center;
.opBtn {
cursor: pointer;
width: 60px;
font-size: 14px;
color: #26f;
&.del {
color: #f46;
}
}
}
.menuName {
flex: 1;
min-width: 0;
}
}
}
</style>

View File

@@ -0,0 +1,22 @@
<template>
<section class="compRender">
</section>
</template>
<script>
export default {
name: "compRender",
data() {
return {}
},
methods: {},
created() {
}
}
</script>
<style lang="scss" scoped>
.compRender {
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<section class="componentLib">
<el-button type="text" @click="drawer=true">组件库</el-button>
<el-drawer title="组件库" :visible.sync="drawer" direction="ltr" :modal="false" size="100%">
<el-row class="item" type="flex" align="middle" v-for="item in list" :key="item.value" @click.native="handleClick">
<i class="icon" :class="item.icon"/>
<div class="fill mar-l8" v-text="item.label"/>
</el-row>
</el-drawer>
</section>
</template>
<script>
export default {
name: "componentLib",
data() {
return {
drawer: true,
list: [
{value: 'text', icon: 'iconfont iconrich_text', label: "文字组件", width: 300, height: 32},
{value: 'image', icon: 'el-icon-picture', label: "图片组件", width: 300, height: 300},
{value: 'startBtn', icon: 'el-icon-video-play', label: "开始使用", width: 80, height: 32},
]
}
},
methods: {
handleClick(item) {
this.$emit('select', item)
}
},
created() {
}
}
</script>
<style lang="scss" scoped>
.componentLib {
.mar-l8 {
margin-left: 8px;
}
.item {
font-size: 16px;
margin: 8px;
padding: 4px;
border: 1px dashed #ddd;
cursor: pointer;
&:hover {
opacity: .8;
}
.icon {
font-size: 20px;
}
}
::v-deep.el-drawer__wrapper {
position: fixed !important;
top: 96px;
bottom: 0;
left: 0;
width: 200px;
}
}
</style>

View File

@@ -0,0 +1,22 @@
<template>
<section class="fieldPane">
<el-drawer></el-drawer>
</section>
</template>
<script>
export default {
name: "fieldPane",
data() {
return {}
},
methods: {},
created() {
}
}
</script>
<style lang="scss" scoped>
.fieldPane {
}
</style>

View File

@@ -0,0 +1,35 @@
<template>
<section class="AiDrag">
<vue-draggable-resizable v-bind="$attrs">
<slot/>
</vue-draggable-resizable>
</section>
</template>
<script>
import 'vue-draggable-resizable/dist/VueDraggableResizable.css'
import VueDraggableResizable from 'vue-draggable-resizable'
export default {
name: "AiDrag",
components: {VueDraggableResizable},
props: {
type: {default: "show"} //show:只拖拽
}
}
</script>
<style lang="scss" scoped>
.AiDrag {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
::v-deep.vdr {
pointer-events: auto;
}
}
</style>

View File

@@ -0,0 +1,31 @@
<template>
<section class="AiEditBtn">
<el-button v-if="!edit" type="text" @click="handleOper('edit')">编辑</el-button>
<template v-else>
<el-button type="text" @click="handleOper('submit')">保存</el-button>
<el-button type="text" @click="handleOper('cancel')">取消</el-button>
</template>
</section>
</template>
<script>
export default {
name: "AiEditBtn",
data() {
return {
edit: false
}
},
methods: {
handleOper(event) {
this.edit = !this.edit
this.$emit(event)
}
}
}
</script>
<style lang="scss" scoped>
.AiEditBtn {
}
</style>

View File

@@ -7,6 +7,6 @@
"dist"
],
"publishConfig": {
"registry": "http://192.168.1.87:4873/"
"registry": "http://cli.sinoecare.net"
}
}