修改工单: 右键 给角色分配权限
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m45s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m45s
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
fontWeight: '500'
|
fontWeight: '500'
|
||||||
}"
|
}"
|
||||||
@row-click="handleRowClick"
|
@row-click="handleRowClick"
|
||||||
|
@row-contextmenu="handleRowContextmenu"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
>
|
>
|
||||||
<!-- 序号列 -->
|
<!-- 序号列 -->
|
||||||
@@ -174,6 +175,7 @@
|
|||||||
'update:currentPage',
|
'update:currentPage',
|
||||||
'update:pageSize',
|
'update:pageSize',
|
||||||
'row-click',
|
'row-click',
|
||||||
|
'row-contextmenu',
|
||||||
'size-change',
|
'size-change',
|
||||||
'current-change',
|
'current-change',
|
||||||
'selection-change'
|
'selection-change'
|
||||||
@@ -274,6 +276,11 @@
|
|||||||
emit('row-click', row, column, event)
|
emit('row-click', row, column, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 行右键事件
|
||||||
|
const handleRowContextmenu = (row: any, column: any, event: any) => {
|
||||||
|
emit('row-contextmenu', row, column, event)
|
||||||
|
}
|
||||||
|
|
||||||
// 选择变化事件
|
// 选择变化事件
|
||||||
const handleSelectionChange = (selection: any) => {
|
const handleSelectionChange = (selection: any) => {
|
||||||
emit('selection-change', selection)
|
emit('selection-change', selection)
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
@refresh="handleRefresh"
|
@refresh="handleRefresh"
|
||||||
>
|
>
|
||||||
<template #left>
|
<template #left>
|
||||||
<ElButton @click="showDialog('add')" v-permission="'enterprise_customer:add'">新增企业客户</ElButton>
|
<ElButton @click="showDialog('add')" v-permission="'enterprise_customer:add'"
|
||||||
|
>新增企业客户</ElButton
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
</ArtTableHeader>
|
</ArtTableHeader>
|
||||||
|
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
:marginTop="10"
|
:marginTop="10"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
|
@row-contextmenu="handleRowContextMenu"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||||
@@ -455,11 +458,20 @@
|
|||||||
{
|
{
|
||||||
prop: 'operation',
|
prop: 'operation',
|
||||||
label: '操作',
|
label: '操作',
|
||||||
width: 200,
|
width: 280,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
formatter: (row: EnterpriseItem) => {
|
formatter: (row: EnterpriseItem) => {
|
||||||
const buttons = []
|
const buttons = []
|
||||||
|
|
||||||
|
if (hasAuth('enterprise_customer:look_customer')) {
|
||||||
|
buttons.push(
|
||||||
|
h(ArtButtonTable, {
|
||||||
|
text: '账号列表',
|
||||||
|
onClick: () => viewCustomerAccounts(row)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (hasAuth('enterprise_customer:card_authorization')) {
|
if (hasAuth('enterprise_customer:card_authorization')) {
|
||||||
buttons.push(
|
buttons.push(
|
||||||
h(ArtButtonTable, {
|
h(ArtButtonTable, {
|
||||||
@@ -469,12 +481,8 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只要有编辑、账号列表、修改密码权限之一,就显示更多操作按钮
|
// 只要有编辑、修改密码权限之一,就显示更多操作按钮
|
||||||
if (
|
if (hasAuth('enterprise_customer:edit') || hasAuth('enterprise_customer:update_pwd')) {
|
||||||
hasAuth('enterprise_customer:edit') ||
|
|
||||||
hasAuth('enterprise_customer:look_customer') ||
|
|
||||||
hasAuth('enterprise_customer:update_pwd')
|
|
||||||
) {
|
|
||||||
buttons.push(
|
buttons.push(
|
||||||
h(ArtButtonTable, {
|
h(ArtButtonTable, {
|
||||||
text: '更多操作',
|
text: '更多操作',
|
||||||
@@ -756,13 +764,6 @@
|
|||||||
const enterpriseOperationMenuItems = computed((): MenuItemType[] => {
|
const enterpriseOperationMenuItems = computed((): MenuItemType[] => {
|
||||||
const items: MenuItemType[] = []
|
const items: MenuItemType[] = []
|
||||||
|
|
||||||
if (hasAuth('enterprise_customer:look_customer')) {
|
|
||||||
items.push({
|
|
||||||
key: 'accountList',
|
|
||||||
label: '账号列表'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasAuth('enterprise_customer:edit')) {
|
if (hasAuth('enterprise_customer:edit')) {
|
||||||
items.push({
|
items.push({
|
||||||
key: 'edit',
|
key: 'edit',
|
||||||
@@ -793,9 +794,6 @@
|
|||||||
if (!currentOperatingEnterprise.value) return
|
if (!currentOperatingEnterprise.value) return
|
||||||
|
|
||||||
switch (item.key) {
|
switch (item.key) {
|
||||||
case 'accountList':
|
|
||||||
viewCustomerAccounts(currentOperatingEnterprise.value)
|
|
||||||
break
|
|
||||||
case 'edit':
|
case 'edit':
|
||||||
showDialog('edit', currentOperatingEnterprise.value)
|
showDialog('edit', currentOperatingEnterprise.value)
|
||||||
break
|
break
|
||||||
@@ -804,4 +802,12 @@
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理表格行右键菜单
|
||||||
|
const handleRowContextMenu = (row: EnterpriseItem, column: any, event: MouseEvent) => {
|
||||||
|
// 如果用户有编辑或修改密码权限,显示右键菜单
|
||||||
|
if (hasAuth('enterprise_customer:edit') || hasAuth('enterprise_customer:update_pwd')) {
|
||||||
|
showEnterpriseOperationMenu(event, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-contextmenu="handleRowContextMenu"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<ElTableColumn type="selection" width="55" />
|
<ElTableColumn type="selection" width="55" />
|
||||||
@@ -1073,32 +1074,18 @@
|
|||||||
{
|
{
|
||||||
prop: 'operation',
|
prop: 'operation',
|
||||||
label: '操作',
|
label: '操作',
|
||||||
width: 200,
|
width: 120,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
formatter: (row: Device) => {
|
formatter: (row: Device) => {
|
||||||
const buttons = []
|
|
||||||
|
|
||||||
if (hasAuth('devices:look_binding')) {
|
|
||||||
buttons.push(
|
|
||||||
h(ArtButtonTable, {
|
|
||||||
text: '查看卡片',
|
|
||||||
onClick: () => handleViewCards(row)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show "更多操作" only if user has at least one operation permission
|
// Show "更多操作" only if user has at least one operation permission
|
||||||
const hasAnyOperationPermission = hasAuth('devices:delete')
|
const hasAnyOperationPermission = hasAuth('devices:delete') || hasAuth('devices:look_binding')
|
||||||
if (hasAnyOperationPermission) {
|
if (hasAnyOperationPermission) {
|
||||||
buttons.push(
|
return h(ArtButtonTable, {
|
||||||
h(ArtButtonTable, {
|
text: '更多操作',
|
||||||
text: '更多操作',
|
onContextmenu: (e: MouseEvent) => showDeviceOperationMenu(e, row.device_no)
|
||||||
onContextmenu: (e: MouseEvent) => showDeviceOperationMenu(e, row.device_no)
|
})
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
return h('div', { style: 'display: flex; gap: 0; align-items: center;' }, buttons)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@@ -1432,6 +1419,9 @@
|
|||||||
// 设备操作路由
|
// 设备操作路由
|
||||||
const handleDeviceOperation = (command: string, deviceNo: string) => {
|
const handleDeviceOperation = (command: string, deviceNo: string) => {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
case 'view-cards':
|
||||||
|
handleViewCardsByDeviceNo(deviceNo)
|
||||||
|
break
|
||||||
case 'reboot':
|
case 'reboot':
|
||||||
handleRebootDevice(deviceNo)
|
handleRebootDevice(deviceNo)
|
||||||
break
|
break
|
||||||
@@ -1453,6 +1443,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 通过设备号查看卡片
|
||||||
|
const handleViewCardsByDeviceNo = (deviceNo: string) => {
|
||||||
|
const device = deviceList.value.find(d => d.device_no === deviceNo)
|
||||||
|
if (device) {
|
||||||
|
handleViewCards(device)
|
||||||
|
} else {
|
||||||
|
ElMessage.error('未找到该设备')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 通过设备号删除设备
|
// 通过设备号删除设备
|
||||||
const handleDeleteDeviceByNo = async (deviceNo: string) => {
|
const handleDeleteDeviceByNo = async (deviceNo: string) => {
|
||||||
// 先根据设备号找到设备对象
|
// 先根据设备号找到设备对象
|
||||||
@@ -1628,7 +1628,17 @@
|
|||||||
|
|
||||||
// 设备操作菜单项配置
|
// 设备操作菜单项配置
|
||||||
const deviceOperationMenuItems = computed((): MenuItemType[] => {
|
const deviceOperationMenuItems = computed((): MenuItemType[] => {
|
||||||
const items: MenuItemType[] = [
|
const items: MenuItemType[] = []
|
||||||
|
|
||||||
|
// 添加查看卡片到菜单最前面
|
||||||
|
if (hasAuth('devices:look_binding')) {
|
||||||
|
items.push({
|
||||||
|
key: 'view-cards',
|
||||||
|
label: '查看卡片'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push(
|
||||||
{
|
{
|
||||||
key: 'reboot',
|
key: 'reboot',
|
||||||
label: '重启设备'
|
label: '重启设备'
|
||||||
@@ -1649,7 +1659,7 @@
|
|||||||
key: 'set-wifi',
|
key: 'set-wifi',
|
||||||
label: '设置WiFi'
|
label: '设置WiFi'
|
||||||
}
|
}
|
||||||
]
|
)
|
||||||
|
|
||||||
if (hasAuth('devices:delete')) {
|
if (hasAuth('devices:delete')) {
|
||||||
items.push({
|
items.push({
|
||||||
@@ -1676,6 +1686,11 @@
|
|||||||
|
|
||||||
handleDeviceOperation(item.key, deviceNo)
|
handleDeviceOperation(item.key, deviceNo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理表格行右键菜单
|
||||||
|
const handleRowContextMenu = (row: Device, column: any, event: MouseEvent) => {
|
||||||
|
showDeviceOperationMenu(event, row.device_no)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -43,7 +43,12 @@
|
|||||||
批量设置套餐系列
|
批量设置套餐系列
|
||||||
</ElButton>
|
</ElButton>
|
||||||
<ElButton
|
<ElButton
|
||||||
v-if="hasAuth('iot_card:batch_recharge') || hasAuth('iot_card:network_distribution') || hasAuth('iot_card:network_recycle') || hasAuth('iot_card:change_package')"
|
v-if="
|
||||||
|
hasAuth('iot_card:batch_recharge') ||
|
||||||
|
hasAuth('iot_card:network_distribution') ||
|
||||||
|
hasAuth('iot_card:network_recycle') ||
|
||||||
|
hasAuth('iot_card:change_package')
|
||||||
|
"
|
||||||
type="info"
|
type="info"
|
||||||
@contextmenu.prevent="showMoreMenu"
|
@contextmenu.prevent="showMoreMenu"
|
||||||
>
|
>
|
||||||
@@ -65,6 +70,7 @@
|
|||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
|
@row-contextmenu="handleRowContextMenu"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<ElTableColumn type="selection" width="55" />
|
<ElTableColumn type="selection" width="55" />
|
||||||
@@ -537,7 +543,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<ElDescriptions :column="1" border>
|
<ElDescriptions :column="1" border>
|
||||||
<ElDescriptionsItem label="实名链接">
|
<ElDescriptionsItem label="实名链接">
|
||||||
<a :href="realnameLinkData.link" target="_blank" style="color: var(--el-color-primary)">
|
<a
|
||||||
|
:href="realnameLinkData.link"
|
||||||
|
target="_blank"
|
||||||
|
style="color: var(--el-color-primary)"
|
||||||
|
>
|
||||||
{{ realnameLinkData.link }}
|
{{ realnameLinkData.link }}
|
||||||
</a>
|
</a>
|
||||||
</ElDescriptionsItem>
|
</ElDescriptionsItem>
|
||||||
@@ -1090,19 +1100,13 @@
|
|||||||
{
|
{
|
||||||
prop: 'operation',
|
prop: 'operation',
|
||||||
label: '操作',
|
label: '操作',
|
||||||
width: 200,
|
width: 120,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
formatter: (row: StandaloneIotCard) => {
|
formatter: (row: StandaloneIotCard) => {
|
||||||
return h('div', { style: 'display: flex; gap: 0; align-items: center;' }, [
|
return h(ArtButtonTable, {
|
||||||
h(ArtButtonTable, {
|
text: '更多操作',
|
||||||
text: '查询流量',
|
onContextmenu: (e: MouseEvent) => showCardOperationMenu(e, row.iccid)
|
||||||
onClick: () => showFlowUsageDialog(row.iccid)
|
})
|
||||||
}),
|
|
||||||
h(ArtButtonTable, {
|
|
||||||
text: '更多操作',
|
|
||||||
onContextmenu: (e: MouseEvent) => showCardOperationMenu(e, row.iccid)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
@@ -1169,7 +1173,6 @@
|
|||||||
getTableData()
|
getTableData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 表格选择变化
|
// 表格选择变化
|
||||||
const handleSelectionChange = (selection: StandaloneIotCard[]) => {
|
const handleSelectionChange = (selection: StandaloneIotCard[]) => {
|
||||||
selectedCards.value = selection
|
selectedCards.value = selection
|
||||||
@@ -1421,7 +1424,7 @@
|
|||||||
const params: any = {
|
const params: any = {
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 20,
|
page_size: 20,
|
||||||
status: 1 // 只获取启用的
|
status: 1 // 只获取启用的
|
||||||
}
|
}
|
||||||
if (seriesName) {
|
if (seriesName) {
|
||||||
params.series_name = seriesName
|
params.series_name = seriesName
|
||||||
@@ -1546,6 +1549,10 @@
|
|||||||
|
|
||||||
// 卡操作菜单项配置
|
// 卡操作菜单项配置
|
||||||
const cardOperationMenuItems = computed((): MenuItemType[] => [
|
const cardOperationMenuItems = computed((): MenuItemType[] => [
|
||||||
|
{
|
||||||
|
key: 'query-flow',
|
||||||
|
label: '查询流量'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'realname-status',
|
key: 'realname-status',
|
||||||
label: '查询实名状态'
|
label: '查询实名状态'
|
||||||
@@ -1609,7 +1616,11 @@
|
|||||||
const iccid = currentOperatingIccid.value
|
const iccid = currentOperatingIccid.value
|
||||||
if (!iccid) return
|
if (!iccid) return
|
||||||
|
|
||||||
handleCardOperation(item.key, iccid)
|
if (item.key === 'query-flow') {
|
||||||
|
showFlowUsageDialog(iccid)
|
||||||
|
} else {
|
||||||
|
handleCardOperation(item.key, iccid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网卡分销 - 正在开发中
|
// 网卡分销 - 正在开发中
|
||||||
@@ -1807,6 +1818,11 @@
|
|||||||
// 用户取消
|
// 用户取消
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理表格行右键菜单
|
||||||
|
const handleRowContextMenu = (row: StandaloneIotCard, column: any, event: MouseEvent) => {
|
||||||
|
showCardOperationMenu(event, row.iccid)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -567,7 +567,7 @@
|
|||||||
{
|
{
|
||||||
prop: 'order_no',
|
prop: 'order_no',
|
||||||
label: t('orderManagement.table.orderNo'),
|
label: t('orderManagement.table.orderNo'),
|
||||||
minWidth: 180
|
minWidth: 220
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'order_type',
|
prop: 'order_type',
|
||||||
@@ -628,7 +628,7 @@
|
|||||||
{
|
{
|
||||||
prop: 'operation',
|
prop: 'operation',
|
||||||
label: t('orderManagement.table.operation'),
|
label: t('orderManagement.table.operation'),
|
||||||
width: 180,
|
width: 160,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
formatter: (row: Order) => {
|
formatter: (row: Order) => {
|
||||||
return h('div', { style: 'display: flex; gap: 8px;' }, [
|
return h('div', { style: 'display: flex; gap: 8px;' }, [
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
|
@row-contextmenu="handleRowContextMenu"
|
||||||
>
|
>
|
||||||
<template #default>
|
<template #default>
|
||||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||||
@@ -942,11 +943,13 @@
|
|||||||
const shopOperationMenuItems = computed((): MenuItemType[] => {
|
const shopOperationMenuItems = computed((): MenuItemType[] => {
|
||||||
const items: MenuItemType[] = []
|
const items: MenuItemType[] = []
|
||||||
|
|
||||||
|
// 默认角色
|
||||||
items.push({
|
items.push({
|
||||||
key: 'defaultRoles',
|
key: 'defaultRoles',
|
||||||
label: '默认角色'
|
label: '默认角色'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 编辑
|
||||||
if (hasAuth('shop:edit')) {
|
if (hasAuth('shop:edit')) {
|
||||||
items.push({
|
items.push({
|
||||||
key: 'edit',
|
key: 'edit',
|
||||||
@@ -954,6 +957,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
if (hasAuth('shop:delete')) {
|
if (hasAuth('shop:delete')) {
|
||||||
items.push({
|
items.push({
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
@@ -972,6 +976,14 @@
|
|||||||
shopOperationMenuRef.value?.show(e)
|
shopOperationMenuRef.value?.show(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理表格行右键菜单
|
||||||
|
const handleRowContextMenu = (row: ShopResponse, column: any, event: MouseEvent) => {
|
||||||
|
// 如果用户有编辑或删除权限,显示右键菜单
|
||||||
|
if (hasAuth('shop:edit') || hasAuth('shop:delete')) {
|
||||||
|
showShopOperationMenu(event, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理店铺操作菜单选择
|
// 处理店铺操作菜单选择
|
||||||
const handleShopOperationMenuSelect = (item: MenuItemType) => {
|
const handleShopOperationMenuSelect = (item: MenuItemType) => {
|
||||||
if (!currentOperatingShop.value) return
|
if (!currentOperatingShop.value) return
|
||||||
|
|||||||
@@ -120,8 +120,11 @@
|
|||||||
:props="{ children: 'children', label: 'label' }"
|
:props="{ children: 'children', label: 'label' }"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
|
check-strictly
|
||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
:default-expand-all="false"
|
:default-expand-all="false"
|
||||||
|
:check-on-click-node="false"
|
||||||
|
@check="handleLeftTreeCheck"
|
||||||
class="permission-tree"
|
class="permission-tree"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
@@ -130,9 +133,6 @@
|
|||||||
<ElTag :type="data.perm_type === 1 ? 'info' : 'success'" size="small">
|
<ElTag :type="data.perm_type === 1 ? 'info' : 'success'" size="small">
|
||||||
{{ data.perm_type === 1 ? '菜单' : '按钮' }}
|
{{ data.perm_type === 1 ? '菜单' : '按钮' }}
|
||||||
</ElTag>
|
</ElTag>
|
||||||
<ElTag :type="data.status === 1 ? 'success' : 'info'" size="small">
|
|
||||||
{{ data.status === 1 ? '启用' : '禁用' }}
|
|
||||||
</ElTag>
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</ElTree>
|
</ElTree>
|
||||||
@@ -180,15 +180,12 @@
|
|||||||
<ElTag :type="data.perm_type === 1 ? 'info' : 'success'" size="small">
|
<ElTag :type="data.perm_type === 1 ? 'info' : 'success'" size="small">
|
||||||
{{ data.perm_type === 1 ? '菜单' : '按钮' }}
|
{{ data.perm_type === 1 ? '菜单' : '按钮' }}
|
||||||
</ElTag>
|
</ElTag>
|
||||||
<ElTag :type="data.status === 1 ? 'success' : 'info'" size="small">
|
|
||||||
{{ data.status === 1 ? '启用' : '禁用' }}
|
|
||||||
</ElTag>
|
|
||||||
</span>
|
</span>
|
||||||
<ElButton
|
<ElButton
|
||||||
type="danger"
|
type="danger"
|
||||||
size="small"
|
size="small"
|
||||||
link
|
link
|
||||||
@click="removeSinglePermission(data.id)"
|
@click="removeSinglePermission(data)"
|
||||||
>
|
>
|
||||||
移除
|
移除
|
||||||
</ElButton>
|
</ElButton>
|
||||||
@@ -255,6 +252,7 @@
|
|||||||
const allPermissionsMap = ref<Map<number, any>>(new Map()) // 所有权限的映射表
|
const allPermissionsMap = ref<Map<number, any>>(new Map()) // 所有权限的映射表
|
||||||
const leftTreeFilter = ref('') // 左侧树搜索关键字
|
const leftTreeFilter = ref('') // 左侧树搜索关键字
|
||||||
const rightTreeFilter = ref('') // 右侧树搜索关键字
|
const rightTreeFilter = ref('') // 右侧树搜索关键字
|
||||||
|
const isHandlingCheck = ref(false) // 标志位:是否正在处理勾选事件
|
||||||
|
|
||||||
// 搜索表单初始值
|
// 搜索表单初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
@@ -456,15 +454,134 @@
|
|||||||
return data.label.toLowerCase().includes(value.toLowerCase())
|
return data.label.toLowerCase().includes(value.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建权限树数据结构
|
// 获取节点的所有子节点ID(包括子菜单和按钮)
|
||||||
|
const getAllChildrenIds = (node: any): number[] => {
|
||||||
|
const ids: number[] = []
|
||||||
|
const traverse = (n: any) => {
|
||||||
|
if (n.children && n.children.length > 0) {
|
||||||
|
n.children.forEach((child: any) => {
|
||||||
|
ids.push(child.id)
|
||||||
|
traverse(child)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traverse(node)
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在树数据中查找节点
|
||||||
|
const findNodeInTree = (treeData: any[], nodeId: number): any => {
|
||||||
|
for (const node of treeData) {
|
||||||
|
if (node.id === nodeId) return node
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
const found = findNodeInTree(node.children, nodeId)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新父节点的勾选状态(计算应该半选的父节点)
|
||||||
|
const updateParentCheckStatus = (checkedKeys: number[]) => {
|
||||||
|
const checkedSet = new Set(checkedKeys)
|
||||||
|
const parentIdsToHalfCheck = new Set<number>()
|
||||||
|
|
||||||
|
// 递归检查节点的子节点勾选状态
|
||||||
|
const checkNode = (node: any): { allChecked: boolean; someChecked: boolean } => {
|
||||||
|
if (!node.children || node.children.length === 0) {
|
||||||
|
// 叶子节点
|
||||||
|
return {
|
||||||
|
allChecked: checkedSet.has(node.id),
|
||||||
|
someChecked: checkedSet.has(node.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有子节点的节点
|
||||||
|
let allChecked = true
|
||||||
|
let someChecked = false
|
||||||
|
|
||||||
|
for (const child of node.children) {
|
||||||
|
const childStatus = checkNode(child)
|
||||||
|
if (!childStatus.allChecked) {
|
||||||
|
allChecked = false
|
||||||
|
}
|
||||||
|
if (childStatus.someChecked) {
|
||||||
|
someChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前节点被勾选
|
||||||
|
const currentNodeChecked = checkedSet.has(node.id)
|
||||||
|
|
||||||
|
// 判断父节点应该显示的状态:
|
||||||
|
// 1. 如果有子节点被勾选(someChecked=true),但不是全部子节点被勾选或当前节点未被勾选 -> 半选
|
||||||
|
// 2. 如果所有子节点都被勾选且当前节点也被勾选 -> 全选
|
||||||
|
if (someChecked) {
|
||||||
|
if (!allChecked || !currentNodeChecked) {
|
||||||
|
// 子节点部分被勾选,或者当前节点未被勾选 -> 半选
|
||||||
|
parentIdsToHalfCheck.add(node.id)
|
||||||
|
}
|
||||||
|
// 如果 allChecked && currentNodeChecked,则为全选,不需要设置半选
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
allChecked: allChecked && currentNodeChecked,
|
||||||
|
someChecked: someChecked || currentNodeChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查所有顶层节点
|
||||||
|
availablePermissions.value.forEach(node => checkNode(node))
|
||||||
|
|
||||||
|
return Array.from(parentIdsToHalfCheck)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理左侧树的勾选事件
|
||||||
|
const handleLeftTreeCheck = (data: any, checked: any) => {
|
||||||
|
if (isHandlingCheck.value) return
|
||||||
|
|
||||||
|
isHandlingCheck.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取当前勾选的keys
|
||||||
|
const currentChecked = checked.checkedKeys as number[]
|
||||||
|
|
||||||
|
// 计算应该半选的父节点
|
||||||
|
const halfCheckedIds = updateParentCheckStatus(currentChecked)
|
||||||
|
|
||||||
|
// 使用内部API直接操作树的半选状态
|
||||||
|
nextTick(() => {
|
||||||
|
if (leftTreeRef.value && leftTreeRef.value.store) {
|
||||||
|
// 清除所有半选状态
|
||||||
|
Object.values(leftTreeRef.value.store.nodesMap).forEach((node: any) => {
|
||||||
|
node.indeterminate = false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置应该半选的节点
|
||||||
|
halfCheckedIds.forEach((id: number) => {
|
||||||
|
const node = leftTreeRef.value.store.nodesMap[id]
|
||||||
|
if (node) {
|
||||||
|
node.indeterminate = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
isHandlingCheck.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建权限树数据结构(只包含启用的权限)
|
||||||
const buildTreeData = (treeNodes: PermissionTreeNode[]): any[] => {
|
const buildTreeData = (treeNodes: PermissionTreeNode[]): any[] => {
|
||||||
return treeNodes.map((node) => ({
|
return treeNodes
|
||||||
id: node.id,
|
.filter((node) => node.status === 1) // 只显示启用的权限
|
||||||
label: node.perm_name,
|
.map((node) => ({
|
||||||
perm_type: node.perm_type,
|
id: node.id,
|
||||||
status: node.status ?? 1,
|
label: node.perm_name,
|
||||||
children: node.children && node.children.length > 0 ? buildTreeData(node.children) : undefined
|
perm_type: node.perm_type,
|
||||||
}))
|
status: node.status ?? 1,
|
||||||
|
children: node.children && node.children.length > 0 ? buildTreeData(node.children) : undefined
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建权限映射表(包括所有节点和子节点)
|
// 构建权限映射表(包括所有节点和子节点)
|
||||||
@@ -582,12 +699,50 @@
|
|||||||
return leftTreeRef.value.getCheckedKeys(false)
|
return leftTreeRef.value.getCheckedKeys(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取左侧树勾选的节点(包括半选节点,用于提交服务器)
|
// 获取节点的所有父节点ID
|
||||||
|
const getParentNodeIds = (nodeId: number): number[] => {
|
||||||
|
const parentIds: number[] = []
|
||||||
|
|
||||||
|
// 在原始权限树中递归查找父节点
|
||||||
|
const findInTree = (treeNodes: PermissionTreeNode[], targetId: number, parentId?: number): number | null => {
|
||||||
|
for (const node of treeNodes) {
|
||||||
|
if (node.id === targetId) {
|
||||||
|
return parentId || null
|
||||||
|
}
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
const found = findInTree(node.children, targetId, node.id)
|
||||||
|
if (found !== null) return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归向上查找所有父节点
|
||||||
|
const findAllParents = (currentId: number) => {
|
||||||
|
const parentId = findInTree(originalPermissionTree.value, currentId)
|
||||||
|
if (parentId) {
|
||||||
|
parentIds.push(parentId)
|
||||||
|
findAllParents(parentId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findAllParents(nodeId)
|
||||||
|
return parentIds
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取左侧树勾选的节点(包括必要的父节点,用于提交服务器)
|
||||||
const getLeftCheckedKeysWithHalf = (): number[] => {
|
const getLeftCheckedKeysWithHalf = (): number[] => {
|
||||||
if (!leftTreeRef.value) return []
|
if (!leftTreeRef.value) return []
|
||||||
const checkedKeys = leftTreeRef.value.getCheckedKeys(false)
|
const checkedKeys = leftTreeRef.value.getCheckedKeys(false)
|
||||||
const halfCheckedKeys = leftTreeRef.value.getHalfCheckedKeys()
|
const parentIds = new Set<number>()
|
||||||
return [...checkedKeys, ...halfCheckedKeys]
|
|
||||||
|
// 为每个勾选的节点找到所有父节点
|
||||||
|
checkedKeys.forEach((key: number) => {
|
||||||
|
const parents = getParentNodeIds(key)
|
||||||
|
parents.forEach(parentId => parentIds.add(parentId))
|
||||||
|
})
|
||||||
|
|
||||||
|
return [...checkedKeys, ...Array.from(parentIds)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加权限
|
// 添加权限
|
||||||
@@ -625,8 +780,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查节点是否有子节点(子菜单或按钮)
|
||||||
|
const hasChildren = (data: any): boolean => {
|
||||||
|
return data.children && data.children.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
// 移除单个权限
|
// 移除单个权限
|
||||||
const removeSinglePermission = async (permId: number) => {
|
const removeSinglePermission = async (data: any) => {
|
||||||
|
// 检查是否有子菜单或按钮
|
||||||
|
if (hasChildren(data)) {
|
||||||
|
ElMessage.warning('该权限下还有子菜单或按钮,请先移除子项后再移除此权限')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 保存右侧树的展开节点
|
// 保存右侧树的展开节点
|
||||||
const expandedKeys = rightTreeRef.value?.store?.nodesMap
|
const expandedKeys = rightTreeRef.value?.store?.nodesMap
|
||||||
@@ -635,10 +801,10 @@
|
|||||||
.map((key) => Number(key))
|
.map((key) => Number(key))
|
||||||
: []
|
: []
|
||||||
|
|
||||||
await RoleService.removePermission(currentRoleId.value, permId)
|
await RoleService.removePermission(currentRoleId.value, data.id)
|
||||||
|
|
||||||
// 更新已选权限列表
|
// 更新已选权限列表
|
||||||
selectedPermissions.value = selectedPermissions.value.filter((id) => id !== permId)
|
selectedPermissions.value = selectedPermissions.value.filter((id) => id !== data.id)
|
||||||
|
|
||||||
// 重新构建左右两侧树
|
// 重新构建左右两侧树
|
||||||
const fullTreeData = buildTreeData(originalPermissionTree.value)
|
const fullTreeData = buildTreeData(originalPermissionTree.value)
|
||||||
|
|||||||
Reference in New Issue
Block a user