<template>
<div v-bind="$attrs">
<div class="title flex flex-jus-sp flex-align-c"><span>常用功能</span>
<a v-if="!loading" @click="openModal()">
<Icon icon="ant-design:setting-outlined" />
</a>
</div>
<a-spin :spinning="loading">
<div class="content flex flex-wrap-w" v-if="menu.length">
<div class="menu-item" v-for="item in menu" :key="item.id" @click="handleHref(item.menuHref, item.menuType)">
<span>{{ item.menuName }}</span></div>
</div>
<a-empty description="暂无配置" v-else></a-empty>
</a-spin>
<a-modal v-model:visible="visible" title="菜单配置" width="800px" @ok="handleSave">
<a-spin :spinning="spinning">
<a-tabs v-model:activeKey="activeKey" @change="checkTab" type="card">
<a-tab-pane key="1" tab="全部功能"></a-tab-pane>
<a-tab-pane key="2" tab="已选功能"></a-tab-pane>
</a-tabs>
<div style="padding:0 16px 15px;" v-if="record.length && activeKey == '1'">
<div class="flex">
<div class="modal-title modal-title-0">模块</div>
<div class="modal-title modal-title-1">子模块</div>
<div class="modal-title modal-title-2">功能</div>
</div>
<div class="flex">
<div class="modal modal-0">
<div class="li" :class="{ active: index == active[0] }" v-for="(item, index) in record" :key="index">
<a-checkbox :checked="item.checked" :indeterminate="item.indeterminate"
@change="handleChange($event, item, index)"></a-checkbox> <span class="choose"
@click="handleClickChoose(0, index)">{{ item.menuName }}</span>
</div>
</div>
<div class="modal modal-1">
<div class="li" :class="{ active: index == active[1] }" v-for="(item, index) in record[active[0]].childList"
:key="index"><a-checkbox :checked="item.checked" :indeterminate="item.indeterminate"
:disabled="!item.page.length && !item.ctrl.length"
@change="handleChange($event, item, index)"></a-checkbox> <span class="choose"
@click="handleClickChoose(1, index)">{{ item.menuName }}</span>
</div>
</div>
<div class="modal modal-2">
<div class="modal-title">页面</div>
<div class="li" v-for="(item, index) in record[active[0]].childList[active[1]].page" :key="index">
<a-checkbox v-model:checked="item.checked" @change="handleChange($event, item, index)">{{ item.menuName
}}</a-checkbox>
</div>
</div>
<div class="modal modal-3">
<div class="modal-title">操作</div>
<div class="li" v-for="(item, index) in record[active[0]].childList[active[1]].ctrl" :key="index">
<a-checkbox v-model:checked="item.checked" @change="handleChange($event, item, index)">{{ item.menuName
}}</a-checkbox>
</div>
</div>
</div>
</div>
<div style="padding:0 10px 15px;" v-show="activeKey == '2'">
<BasicTable @register="registerTable">
<template #choose="{ record }"><a-checkbox v-model:checked="record.checked"></a-checkbox></template>
</BasicTable>
</div>
</a-spin>
</a-modal>
<component :is="inputFormComponent" v-model:visible="drawerVisible" @register="registerDrawer" />
<component :is="inputFormComponent" v-model:visible="modalVisible" @register="registerModal" />
</div>
</template>
<script lang="ts">
export default defineComponent({
name: 'homeCommonExtension',
});
</script>
<script lang="ts" setup>
import { defineComponent, ref, shallowRef } from 'vue';
import { Icon } from '/@/components/Icon';
import { BasicTable, useTable } from '/@/components/Table';
import { hpMenuSetListData, hpMenuSetSave } from '/@/api/hp/menu/hpMenuSet'
import { useMessage } from '/@/hooks/web/useMessage';
import { useDrawer } from '/@/components/Drawer';
import { useModal } from '/@/components/Modal';
import { useGo } from '/@/hooks/web/usePage';
import { dynamicImport } from '/@/router/helper/routeHelper';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import qs from 'qs';
const go = useGo();
const { showMessage } = useMessage();
const visible = ref(false)
const loading = ref(true)
const spinning = ref(false)
const record = ref<any>([])
const active = ref([0, 0])
const activeKey = ref("1")
const menu = ref<any>([])
init()
async function init() {
getUserInfo()
let menu = sessionStorage.getItem("menuItem")
if (menu) { record.value = JSON.parse(menu) }
else {
spinning.value = true;
record.value = await getData(0, 0)
sessionStorage.setItem("menuItem", JSON.stringify(record.value))
spinning.value = false;
}
defaultChecked()
}
function checkTab() {
if (activeKey.value == "2") {
let arr = getChecked()
setTableData(arr)
} else {
let arr = getDataSource().filter(item => item.checked)
menu.value = arr
defaultChecked()
}
}
function checkAll(item: any) {
let val = { checked: false, indeterminate: false }
if (item.childList && item.level == 0) {
let count = 0, total = 0;
for (let i in item.childList) {
let data = foreachAll(item.childList[i])
count += data.count
total += data.total
}
val = { checked: count > 0 && total == count ? true : false, indeterminate: count > 0 && total != count ? true : false }
}
if (item.level == 1) {
let { count, total } = foreachAll(item)
val = { checked: count > 0 && total == count ? true : false, indeterminate: count > 0 && total != count ? true : false }
}
return val
}
function foreachAll(item) {
let total = item.page.length + item.ctrl.length;
let count = 0;
if (item.page.length + item.ctrl.length > 0) {
count += item.page.filter(item => item.checked).length
count += item.ctrl.filter(item => item.checked).length
}
return { count, total }
}
function chooseAll(item: any, bool) {
if (item.childList) {
item.childList.forEach(items => {
chooseAll(items, bool)
})
}
if (item.page) {
item.page.forEach(items => {
items.checked = bool
})
}
if (item.ctrl) {
item.ctrl.forEach(items => {
items.checked = bool
})
}
}
function calcChecked(arr) {
arr.forEach(item => {
let { checked, indeterminate } = checkAll(item)
item.checked = checked
item.indeterminate = indeterminate
if (item.childList) {
item.childList.forEach(items => {
let { checked, indeterminate } = checkAll(items)
items.checked = checked
items.indeterminate = indeterminate
})
}
})
return arr
}
async function getUserInfo() {
loading.value = true
let res = await hpMenuSetListData({ isAll: 0 })
menu.value = res
loading.value = false
}
async function getData(parentCode: number, level: number) {
try {
// 获取所有
let res = await hpMenuSetListData({ isAll: 1, parentCode })
if (res.length) {
for (let i = 0; i < res.length; i++) {
let item: any = res[i]
item.level = level;
if (level == 0) {
item.childList = await getData(item.id, level + 1)
}
if (level == 1) {
let list = await getData(item.id, level + 1)
item.page = list?.filter(item => item.menuType == 2)
item.ctrl = list?.filter(item => item.menuType == 3)
}
}
return res
} else {
return []
}
} catch (e) { }
}
async function handleClickChoose(level, index) {
if (level == 0) {
active.value[1] = 0
}
active.value[level] = index
}
function handleChange(e, item, index) {
if (item.level > 1) {
let type = item.menuType == 3 ? "ctrl" : "page"
record.value[active.value[0]].childList[active.value[1]][type][index] = item
} else {
chooseAll(item, e.target.checked)
}
record.value = calcChecked(record.value)
}
const [registerDrawer, { setDrawerData }] = useDrawer();
const [registerModal, { setModalData }] = useModal();
const inputFormComponent = shallowRef<Nullable<any>>(null);
const drawerVisible = ref<Boolean>(false);
const modalVisible = ref<Boolean>(false);
function handleHref(url, menuType) {
let idx = url.indexOf('?');
let paramStr = idx == -1 ? '' : url.substring(idx + 1);
let compStr = idx == -1 ? url : url.substring(0, idx);
let isTab = paramStr && paramStr.indexOf('type=tab') >= 0 ? true : false
let isModal = paramStr && paramStr.indexOf('type=modal') >= 0 ? true : false
//开启TAB
if (isTab || menuType == '2') {
go(url)
return
}
//抽屉
// component
let component: ReturnType<typeof defineComponent>;
if (compStr && compStr != '') {
const imp = dynamicImport(compStr);
if (imp) component = createAsyncComponent(imp);
}
// params
let params = {};
if (paramStr && paramStr != '') {
params = qs.parse(paramStr);
}
// open
if (component) {
inputFormComponent.value = component;
drawerVisible.value = false;
modalVisible.value = false;
if (isModal) {
modalVisible.value = true;
setModalData(params)
} else {
drawerVisible.value = true;
setDrawerData(params)
}
}
}
function openModal() {
// push(href)
visible.value = true
if (!spinning.value) defaultChecked()
}
// 初始化弹窗默认选中模块
function defaultChecked() {
record.value.forEach(item => {
if (item.childList) {
item.childList.forEach(items => {
items.page.forEach(p => {
let obj = menu.value.find(m => m.id == p.id)
p.checked = obj ? true : false
})
items.ctrl.forEach(p => {
let obj = menu.value.find(m => m.id == p.id)
p.checked = obj ? true : false
})
let { checked, indeterminate } = checkAll(items)
items.checked = checked
items.indeterminate = indeterminate
})
}
let { checked, indeterminate } = checkAll(item)
item.checked = checked
item.indeterminate = indeterminate
})
}
// 获取弹窗选中模块
function getChecked() {
let ids: any = []
record.value.forEach(item => {
if (item.childList) {
item.childList.forEach(items => {
items.page.forEach(p => {
if (p.checked) ids.push(Object.assign({}, p))
})
items.ctrl.forEach(p => {
if (p.checked) ids.push(Object.assign({}, p))
})
})
}
})
return ids
}
// 配置常用功能
async function handleSave() {
spinning.value = true
let ids: any = []
if (activeKey.value == "1") ids = getChecked().map(item => item.id)
if (activeKey.value == "2") ids = getDataSource().filter(item => item.checked).map(item => item.id)
try {
let res = await hpMenuSetSave({ menuCodes: ids.join(",") })
showMessage(res.message);
setTimeout(() => {
visible.value = false
activeKey.value = "1"
getUserInfo()
}, 500)
} catch (err) { } finally {
spinning.value = false
}
}
// 初始化表格
const [registerTable, { setTableData, getDataSource }] = useTable({
columns: [
{
title: '功能',
dataIndex: 'menuName',
sorter: false,
width: 130,
align: 'center',
},
{
title: '功能类型',
dataIndex: 'menuType',
sorter: false,
width: 130,
align: 'center',
customRender: ({ record }) => {
return record.menuType == 2 ? "页面" : "操作"
}
},
{
title: '所属模块',
dataIndex: 'treeNames',
sorter: false,
width: 130,
align: 'center',
customRender: ({ record }) => {
let arr = record.treeNames.split("/").splice(0, 2)
return arr.join("-")
}
},
{
title: '操作',
dataIndex: 'choose',
sorter: false,
width: 50,
align: 'center',
slot: "choose"
},
],
useSearchForm: false,
pagination: false,
scroll: { y: 360 }
});
</script>
<style lang="less" scoped>
.content {
max-height: 180px;
overflow: auto;
}
.menu-item {
display: flex;
justify-content: center;
align-items: center;
width: 84px;
height: 60px;
padding: 3px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
margin-bottom: 10px;
user-select: none;
cursor: pointer;
>span {
text-align: center;
line-height: 1.2;
}
&:hover {
background: #fafafa;
}
}
.modal-title {
text-align: center;
font-weight: bold;
padding: 10px 0;
border-left: 1px solid #ddd;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
&:last-child {
border-right: 1px solid #ddd;
}
}
.modal {
height: 360px;
padding: 6px;
border-left: 1px solid #ddd;
border-bottom: 1px solid #ddd;
&:last-child {
border-right: 1px solid #ddd;
}
.modal-title {
border: none;
padding: 2px;
}
.li {
margin-bottom: 4px;
.choose {
cursor: pointer;
}
&.active .choose {
color: #2a50ec;
font-weight: bold;
}
}
}
.modal-0,
.modal-title-0 {
flex: 1
}
.modal-1,
.modal-title-1 {
width: 160px
}
.modal-title-2 {
width: 440px;
}
.modal-2 {
width: 220px
}
.modal-3 {
width: 220px
}
.ant-tabs.ant-tabs-card {
background: transparent;
}
</style>

