From 192c6f1d2612997f18bfdd4e172bfe368cf02776 Mon Sep 17 00:00:00 2001
From: sexygoat <1538832180@qq.com>
Date: Tue, 3 Feb 2026 17:20:50 +0800
Subject: [PATCH] =?UTF-8?q?fetch(modify):=E5=AE=8C=E5=96=84=E6=8C=89?=
=?UTF-8?q?=E9=92=AE=E6=9D=83=E9=99=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../add-button-permissions/proposal.md | 162 +++++++++++++++
.../changes/add-button-permissions/tasks.md | 190 ++++++++++++++++++
.../account-management/account/index.vue | 49 +++--
.../enterprise-customer/index.vue | 70 ++++---
.../platform-account/index.vue | 62 ++++--
.../account-management/shop-account/index.vue | 36 +++-
.../authorization-records/index.vue | 23 ++-
.../asset-management/device-list/index.vue | 106 ++++++----
.../asset-management/device-task/index.vue | 9 +-
.../iot-card-management/index.vue | 67 ++++--
.../asset-management/iot-card-task/index.vue | 9 +-
.../finance/carrier-management/index.vue | 36 +++-
.../commission/agent-commission/index.vue | 17 +-
.../commission/my-commission/index.vue | 5 +-
.../commission/withdrawal-settings/index.vue | 5 +-
.../order-management/order-list/index.vue | 4 +-
.../package-assign/index.vue | 50 +++--
.../package-management/package-list/index.vue | 38 +++-
.../package-series/index.vue | 37 +++-
.../series-assign/index.vue | 37 +++-
src/views/product/shop/index.vue | 69 +++++--
src/views/system/permission/index.vue | 36 +++-
22 files changed, 885 insertions(+), 232 deletions(-)
create mode 100644 openspec/changes/add-button-permissions/proposal.md
create mode 100644 openspec/changes/add-button-permissions/tasks.md
diff --git a/openspec/changes/add-button-permissions/proposal.md b/openspec/changes/add-button-permissions/proposal.md
new file mode 100644
index 0000000..0d4fbab
--- /dev/null
+++ b/openspec/changes/add-button-permissions/proposal.md
@@ -0,0 +1,162 @@
+# Proposal: Add Button Permissions
+
+## Summary
+
+为系统中的所有页面添加按钮级权限控制,根据用户权限动态显示或隐藏按钮和操作。权限系统已实现(`hasAuth` 和 `v-permission` 指令),本提案专注于将权限控制应用到所有相关页面。
+
+## Motivation
+
+当前系统已经实现了基于角色的权限控制(RBAC),但大多数页面的按钮和操作缺少权限控制。这意味着所有用户都能看到所有按钮,即使他们没有权限执行相应操作。需要在UI层面根据用户权限隐藏无权限的按钮,提升用户体验和系统安全性。
+
+### Problems Solved
+
+1. **安全性提升**: 防止用户看到或尝试执行无权限操作
+2. **用户体验优化**: 只显示用户有权限的功能,避免混淆
+3. **权限一致性**: 统一所有页面的权限控制实现方式
+
+### Related Work
+
+- 权限系统基础设施已实现 (`useAuth.ts`, `permission.ts`)
+- 系统/角色页面 (`/system/role`) 已实现权限控制,作为最佳实践参考
+- 权限配置已由后端API提供(见文档中的权限JSON结构)
+
+## Proposed Solution
+
+### Approach
+
+按业务模块逐步为所有页面添加按钮权限控制,使用已有的权限基础设施:
+
+1. **表格操作按钮**: 使用 `v-permission` 指令
+2. **表格内操作**: 使用 `hasAuth()` 函数动态渲染
+3. **状态切换控件**: 使用 `disabled: !hasAuth()` 控制禁用状态
+
+### Affected Modules
+
+根据提供的权限JSON,需要添加权限控制的模块包括:
+
+1. **套餐管理** (`/package-management`)
+ - 套餐系列 (`package-series`)
+ - 套餐列表 (`package-list`)
+ - 单套餐分配 (`package-assign`)
+ - 套餐系列分配 (`series-assign`)
+
+2. **店铺管理** (`/shop-management`)
+ - 店铺列表 (`list`)
+
+3. **账号管理** (`/account-management`)
+ - 账号列表 (`account`)
+ - 平台账号 (`platform-account`)
+ - 代理账号 (`shop-account`)
+
+4. **资产管理** (`/asset-management`)
+ - IoT卡管理 (`iot-card-management`)
+ - IoT卡任务 (`iot-card-task`)
+ - 设备任务 (`device-task`)
+ - 设备管理 (`devices`)
+ - 授权记录 (`authorization-records`)
+
+5. **账户管理** (`/account`)
+ - 企业客户 (`enterprise-customer`)
+ - 运营商管理 (`carrier-management`)
+ - 订单管理 (`orders`)
+ - 佣金管理 (`commission/*`)
+
+### Implementation Pattern
+
+基于 `/system/role` 页面的实现,统一使用以下模式:
+
+```vue
+
+
+
+
+
+ 新增
+
+
+
+
+
+
+
+
+```
+
+### Permission Mapping
+
+权限代码 (`perm_code`) 与操作的映射关系:
+
+- `module:add` -> 新增按钮
+- `module:edit` -> 编辑按钮
+- `module:delete` -> 删除按钮
+- `module:update_status` -> 状态切换
+- `module:update_xxx` -> 特定更新操作(如修改密码、修改成本价等)
+- `module:xxx` -> 特殊操作(如分配权限、查看客户、卡授权等)
+
+## Impact Analysis
+
+### Benefits
+
+1. **提升安全性**: UI层面防止无权限操作
+2. **改善UX**: 用户只看到有权限的功能
+3. **统一体验**: 所有页面使用相同的权限控制模式
+
+### Risks
+
+1. **向后兼容性**: 现有页面需要更新,可能影响用户使用习惯
+2. **测试覆盖**: 需要测试各种权限组合的显示效果
+
+### Mitigations
+
+1. 分模块逐步rollout,优先完成核心模块
+2. 权限配置由后端控制,前端只负责显示/隐藏
+3. 保持API层面的权限检查,前端权限控制仅为UI优化
+
+## Success Criteria
+
+- [ ] 所有模块的按钮根据权限正确显示/隐藏
+- [ ] 状态切换控件根据权限正确启用/禁用
+- [ ] 表格操作列根据权限动态渲染按钮
+- [ ] 权限变更后UI立即响应
+- [ ] 无权限用户看不到任何无权限操作
+
+## Dependencies
+
+- 依赖后端 API 返回正确的权限数据
+- 依赖 `useUserStore` 中的权限数据管理
+- 依赖已实现的 `useAuth` 和 `v-permission` 指令
+
+## Timeline
+
+预计需要 5-7 个工作日完成所有模块的权限控制集成。
+
+## References
+
+- 权限文档: `docs/在页面上新增按钮权限.md`
+- 参考实现: `src/views/system/role/index.vue`
+- 权限指令: `src/directives/permission.ts`
+- 权限组合式函数: `src/composables/useAuth.ts`
diff --git a/openspec/changes/add-button-permissions/tasks.md b/openspec/changes/add-button-permissions/tasks.md
new file mode 100644
index 0000000..c6ad50d
--- /dev/null
+++ b/openspec/changes/add-button-permissions/tasks.md
@@ -0,0 +1,190 @@
+# Tasks: Add Button Permissions
+
+## Overview
+
+将按钮权限控制系统地应用到所有相关页面。每个任务包含引入 `useAuth`、添加 `v-permission` 指令,以及在表格操作列中使用 `hasAuth()` 进行权限判断。
+
+## Tasks
+
+### 1. 套餐管理模块 (Package Management) ✅
+
+- [x] **套餐系列页面** (`/package-management/package-series`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增套餐系列"按钮添加 `v-permission="'package_series:add'"`
+ - ✅ 为状态切换添加 `disabled: !hasAuth('package_series:update_status')`
+ - ✅ 在操作列中使用 `hasAuth()` 判断编辑权限 (`package_series:edit`)和删除权限 (`package_series:delete`)
+
+- [x] **套餐列表页面** (`/package-management/package-list`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增套餐"按钮添加 `v-permission="'package:add'"`
+ - ✅ 为上/下架切换添加 `disabled: !hasAuth('package:update_away')`
+ - ✅ 为状态切换添加 `disabled: !hasAuth('package:update_status')`
+ - ✅ 在操作列中使用 `hasAuth()` 判断编辑 (`package:edit`) 和删除 (`package:delete`) 权限
+
+- [x] **单套餐分配页面** (`/package-management/package-assign`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增分配"按钮添加 `v-permission="'package_assign:add'"`
+ - ✅ 为状态切换添加 `disabled: !hasAuth('package_assign:update_status')`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 修改成本价: `package_assign:update_cost`
+ - 编辑: `package_assign:edit`
+ - 删除: `package_assign:delete`
+
+- [x] **套餐系列分配页面** (`/package-management/series-assign`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增系列分配"按钮添加 `v-permission="'series_assign:add'"`
+ - ✅ 为状态切换添加 `disabled: !hasAuth('series_assign:update_status')`
+ - ✅ 在操作列中使用 `hasAuth()` 判断编辑 (`series_assign:edit`) 和删除 (`series_assign:delete`) 权限
+
+### 2. 店铺管理模块 (Shop Management) ✅
+
+- [x] **店铺列表页面** (`/product/shop`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增店铺"按钮添加 `v-permission="'shop:add'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 查看客户: `shop:look_customer`
+ - 编辑: `shop:edit`
+ - 删除: `shop:delete`
+ - ✅ 在右键菜单中使用 `hasAuth()` 动态生成菜单项
+
+### 3. 账号管理模块 (Account Management) ✅
+
+- [x] **账号列表页面** (`/account-management/account`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增账号"按钮添加 `v-permission="'account:add'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 分配角色: `account:patch_role`
+ - 编辑: `account:edit`
+ - 删除: `account:delete`
+
+- [x] **平台账号页面** (`/account-management/platform-account`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增平台账号"按钮添加 `v-permission="'platform_account:add'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 分配角色: `platform_account:patch_role`
+ - 修改密码: `platform_account:update_psd`
+ - 编辑: `platform_account:edit`
+ - 删除: `platform_account:delete`
+
+- [x] **代理账号页面** (`/account-management/shop-account`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增代理账号"按钮添加 `v-permission="'shop_account:add'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 修改密码: `shop_account:update_psd`
+ - 编辑: `shop_account:edit`
+
+### 4. 资产管理模块 (Asset Management) ✅
+
+- [x] **IoT卡管理页面** (`/asset-management/iot-card-management`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为表格头部操作按钮添加权限控制:
+ - 批量分配: `v-permission="'iot_card:batch_allocation'"`
+ - 批量回收: `v-permission="'iot_card:batch_recycle'"`
+ - 批量设置套餐系列: `v-permission="'iot_card:batch_setting'"`
+ - 批量充值: `v-permission="'iot_card:batch_recharge'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 网卡分销: `iot_card:network_distribution`
+ - 网卡回收: `iot_card:network_recycle`
+ - 变更套餐: `iot_card:change_package`
+ - ✅ 验证权限控制正确工作
+
+- [x] **IoT卡任务页面** (`/asset-management/iot-card-task`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"批量导入IoT卡"按钮添加 `v-permission="'lot_task:bulk_import'"`
+ - ✅ 验证权限控制正确工作
+
+- [x] **设备任务页面** (`/asset-management/device-task`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"批量导入设备"按钮添加 `v-permission="'device_task:bulk_import'"`
+ - ✅ 验证权限控制正确工作
+
+- [x] **设备管理页面** (`/asset-management/device-list`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为表格头部操作按钮添加权限控制:
+ - 批量分配: `v-permission="'devices:batch_allocation'"`
+ - 批量回收: `v-permission="'devices:batch_recycle'"`
+ - 批量设置套餐系列: `v-permission="'devices:batch_setting'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 查看绑定的卡: `devices:look_binding`
+ - 删除设备: `devices:delete`
+ - ✅ 验证权限控制正确工作
+
+- [x] **授权记录页面** (`/asset-management/authorization-records`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 在操作列中使用 `hasAuth()` 判断修改备注权限 (`authorization_records:update_remark`)
+ - ✅ 验证权限控制正确工作
+
+### 5. 账户管理模块 (Account Module) ✅
+
+- [x] **企业客户页面** (`/account/enterprise-customer`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增企业客户"按钮添加 `v-permission="'enterprise_customer:add'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断以下权限:
+ - 编辑: `enterprise_customer:edit`
+ - 查看客户: `enterprise_customer:look_customer`
+ - 卡授权: `enterprise_customer:card_authorization`
+ - 修改密码: `enterprise_customer:update_pwd`
+ - ✅ 验证权限控制正确工作
+
+- [x] **运营商管理页面** (`/account/carrier-management`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增运营商"按钮添加 `v-permission="'carrier:add'"`
+ - ✅ 为状态切换添加 `disabled: !hasAuth('carrier:update_status')`
+ - ✅ 在操作列中使用 `hasAuth()` 判断编辑 (`carrier:edit`) 和删除 (`carrier:delete`) 权限
+ - ✅ 验证权限控制正确工作
+
+- [x] **订单管理页面** (`/account/orders`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"创建订单"按钮添加 `v-permission="'orders:add'"`
+ - ✅ 验证权限控制正确工作
+
+- [x] **提现配置页面** (`/account/commission/withdrawal-settings`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增配置"按钮添加 `v-permission="'withdrawal_settings:add'"`
+ - ✅ 验证权限控制正确工作
+
+- [x] **我的佣金页面** (`/account/commission/my-commission`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"发起提现"按钮添加 `v-permission="'my_commission:add'"`
+ - ✅ 验证权限控制正确工作
+
+- [x] **代理商佣金管理页面** (`/account/commission/agent-commission`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 在操作列中使用 `hasAuth()` 判断查看详情权限 (`agent_commission:detail`)
+ - ✅ 验证权限控制正确工作
+
+### 6. 系统管理模块 (System Management) ✅
+
+- [x] **权限管理页面** (`/system/permission`)
+ - ✅ 引入 `useAuth` composable
+ - ✅ 为"新增权限"按钮添加 `v-permission="'permission:add'"`
+ - ✅ 在操作列中使用 `hasAuth()` 判断编辑 (`permission:edit`) 和删除 (`permission:delete`) 权限
+ - ✅ 验证权限控制正确工作
+
+### 7. 测试与文档
+
+- [ ] **权限控制测试**
+ - 创建不同权限组合的测试用户
+ - 验证每个页面的按钮显示/隐藏符合预期
+ - 验证状态切换的启用/禁用符合预期
+ - 测试权限动态变更后UI的响应
+
+- [ ] **文档更新**
+ - 更新开发文档,说明权限控制的实现方式
+ - 添加权限代码与功能的映射表
+ - 提供新页面添加权限控制的指南
+
+## Validation
+
+每个任务完成后需验证:
+
+1. **功能正确性**: 有权限时按钮/操作可见且可用
+2. **权限隔离**: 无权限时按钮/操作不可见或禁用
+3. **用户体验**: 权限控制不影响正常操作流程
+4. **性能**: 权限检查不影响页面渲染性能
+
+## Dependencies
+
+- 所有任务依赖权限基础设施 (`useAuth`, `v-permission`)
+- 任务可并行执行,建议按模块分批进行
+- 优先级: 核心模块 (账号、套餐、资产) > 其他模块
diff --git a/src/views/account-management/account/index.vue b/src/views/account-management/account/index.vue
index fcf4dc7..d746079 100644
--- a/src/views/account-management/account/index.vue
+++ b/src/views/account-management/account/index.vue
@@ -17,7 +17,7 @@
@refresh="handleRefresh"
>
- 新增账号
+ 新增账号
@@ -117,6 +117,7 @@
import { ElMessageBox, ElMessage } from 'element-plus'
import type { FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { AccountService } from '@/api/modules/account'
import { RoleService } from '@/api/modules/role'
@@ -128,6 +129,8 @@
defineOptions({ name: 'Account' }) // 定义组件名称,用于 KeepAlive 缓存控制
+ const { hasAuth } = useAuth()
+
const dialogType = ref('add')
const dialogVisible = ref(false)
const roleDialogVisible = ref(false)
@@ -375,20 +378,36 @@
width: 200,
fixed: 'right',
formatter: (row: any) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- icon: '',
- onClick: () => showRoleDialog(row)
- }),
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deleteAccount(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('account:patch_role')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ icon: '',
+ onClick: () => showRoleDialog(row)
+ })
+ )
+ }
+
+ if (hasAuth('account:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('account:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deleteAccount(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/account-management/enterprise-customer/index.vue b/src/views/account-management/enterprise-customer/index.vue
index 19e2bfa..5abeb8e 100644
--- a/src/views/account-management/enterprise-customer/index.vue
+++ b/src/views/account-management/enterprise-customer/index.vue
@@ -19,7 +19,7 @@
@refresh="handleRefresh"
>
- 新增企业客户
+ 新增企业客户
@@ -213,6 +213,7 @@
import type { EnterpriseItem, ShopResponse } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import CustomerAccountDialog from '@/components/business/CustomerAccountDialog.vue'
import { formatDateTime } from '@/utils/business/format'
@@ -220,6 +221,8 @@
defineOptions({ name: 'EnterpriseCustomer' })
+ const { hasAuth } = useAuth()
+
const router = useRouter()
const dialogVisible = ref(false)
@@ -450,28 +453,49 @@
width: 340,
fixed: 'right',
formatter: (row: EnterpriseItem) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- text: '编辑',
- iconClass: BgColorEnum.SECONDARY,
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- text: '查看客户',
- iconClass: BgColorEnum.PRIMARY,
- onClick: () => viewCustomerAccounts(row)
- }),
- h(ArtButtonTable, {
- text: '卡授权',
- iconClass: BgColorEnum.PRIMARY,
- onClick: () => manageCards(row)
- }),
- h(ArtButtonTable, {
- text: '修改密码',
- iconClass: BgColorEnum.WARNING,
- onClick: () => showPasswordDialog(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('enterprise_customer:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '编辑',
+ iconClass: BgColorEnum.SECONDARY,
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('enterprise_customer:look_customer')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '查看客户',
+ iconClass: BgColorEnum.PRIMARY,
+ onClick: () => viewCustomerAccounts(row)
+ })
+ )
+ }
+
+ if (hasAuth('enterprise_customer:card_authorization')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '卡授权',
+ iconClass: BgColorEnum.PRIMARY,
+ onClick: () => manageCards(row)
+ })
+ )
+ }
+
+ if (hasAuth('enterprise_customer:update_pwd')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '修改密码',
+ iconClass: BgColorEnum.WARNING,
+ onClick: () => showPasswordDialog(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/account-management/platform-account/index.vue b/src/views/account-management/platform-account/index.vue
index 03a6c36..a160f11 100644
--- a/src/views/account-management/platform-account/index.vue
+++ b/src/views/account-management/platform-account/index.vue
@@ -18,7 +18,7 @@
@refresh="handleRefresh"
>
- 新增平台账号
+ 新增平台账号
@@ -164,6 +164,7 @@
import { FormInstance, ElMessage, ElMessageBox, ElTag, ElSwitch } from 'element-plus'
import type { FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { AccountService, RoleService, ShopService } from '@/api/modules'
import type { SearchFormItem } from '@/types'
@@ -173,6 +174,8 @@
defineOptions({ name: 'PlatformAccount' }) // 定义组件名称,用于 KeepAlive 缓存控制
+ const { hasAuth } = useAuth()
+
const dialogType = ref('add')
const dialogVisible = ref(false)
const roleDialogVisible = ref(false)
@@ -443,24 +446,45 @@
width: 240,
fixed: 'right',
formatter: (row: PlatformAccount) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- icon: '',
- onClick: () => showRoleDialog(row)
- }),
- h(ArtButtonTable, {
- icon: '',
- onClick: () => showPasswordDialog(row)
- }),
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deleteAccount(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('platform_account:patch_role')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ icon: '',
+ onClick: () => showRoleDialog(row)
+ })
+ )
+ }
+
+ if (hasAuth('platform_account:update_psd')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ icon: '',
+ onClick: () => showPasswordDialog(row)
+ })
+ )
+ }
+
+ if (hasAuth('platform_account:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('platform_account:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deleteAccount(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/account-management/shop-account/index.vue b/src/views/account-management/shop-account/index.vue
index e20db7d..1d245b2 100644
--- a/src/views/account-management/shop-account/index.vue
+++ b/src/views/account-management/shop-account/index.vue
@@ -17,7 +17,7 @@
@refresh="handleRefresh"
>
- 新增代理账号
+ 新增代理账号
@@ -125,6 +125,7 @@
import { FormInstance, ElMessage, ElMessageBox, ElSwitch, ElSelect, ElOption } from 'element-plus'
import type { FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { AccountService, ShopService } from '@/api/modules'
import type { SearchFormItem } from '@/types'
@@ -134,6 +135,8 @@
defineOptions({ name: 'ShopAccount' }) // 定义组件名称,用于 KeepAlive 缓存控制
+ const { hasAuth } = useAuth()
+
const dialogType = ref('add')
const dialogVisible = ref(false)
const passwordDialogVisible = ref(false)
@@ -342,16 +345,27 @@
width: 120,
fixed: 'right',
formatter: (row: PlatformAccount) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- icon: '',
- onClick: () => showPasswordDialog(row)
- }),
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('shop_account:update_psd')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ icon: '',
+ onClick: () => showPasswordDialog(row)
+ })
+ )
+ }
+
+ if (hasAuth('shop_account:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/asset-management/authorization-records/index.vue b/src/views/asset-management/authorization-records/index.vue
index 8cc7fac..5c822f2 100644
--- a/src/views/asset-management/authorization-records/index.vue
+++ b/src/views/asset-management/authorization-records/index.vue
@@ -102,6 +102,7 @@
import type { FormInstance, FormRules } from 'element-plus'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import { formatDateTime } from '@/utils/business/format'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import type {
@@ -113,6 +114,7 @@
defineOptions({ name: 'AuthorizationRecords' })
+ const { hasAuth } = useAuth()
const router = useRouter()
const loading = ref(false)
const detailDialogVisible = ref(false)
@@ -297,16 +299,25 @@
width: 150,
fixed: 'right',
formatter: (row: AuthorizationItem) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
+ const buttons = []
+
+ buttons.push(
h(ArtButtonTable, {
text: '详情',
onClick: () => viewDetail(row)
- }),
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showRemarkDialog(row)
})
- ])
+ )
+
+ if (hasAuth('authorization_records:update_remark')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showRemarkDialog(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue
index edb7b9a..f67bfaa 100644
--- a/src/views/asset-management/device-list/index.vue
+++ b/src/views/asset-management/device-list/index.vue
@@ -21,13 +21,23 @@
type="primary"
@click="handleBatchAllocate"
:disabled="!selectedDevices.length"
+ v-permission="'devices:batch_allocation'"
>
批量分配
-
+
批量回收
-
+
批量设置套餐系列
@@ -546,6 +556,7 @@
} from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
@@ -555,6 +566,7 @@
defineOptions({ name: 'DeviceList' })
+ const { hasAuth } = useAuth()
const router = useRouter()
const loading = ref(false)
const allocateLoading = ref(false)
@@ -1064,16 +1076,29 @@
width: 200,
fixed: 'right',
formatter: (row: Device) => {
- return h('div', { style: 'display: flex; gap: 0; align-items: center;' }, [
- h(ArtButtonTable, {
- text: '查看卡片',
- onClick: () => handleViewCards(row)
- }),
- h(ArtButtonTable, {
- text: '更多操作',
- onContextmenu: (e: MouseEvent) => showDeviceOperationMenu(e, row.device_no)
- })
- ])
+ 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
+ const hasAnyOperationPermission = hasAuth('devices:delete')
+ if (hasAnyOperationPermission) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '更多操作',
+ onContextmenu: (e: MouseEvent) => showDeviceOperationMenu(e, row.device_no)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 0; align-items: center;' }, buttons)
}
}
])
@@ -1602,32 +1627,39 @@
}
// 设备操作菜单项配置
- const deviceOperationMenuItems = computed((): MenuItemType[] => [
- {
- key: 'reboot',
- label: '重启设备'
- },
- {
- key: 'reset',
- label: '恢复出厂'
- },
- {
- key: 'speed-limit',
- label: '设置限速'
- },
- {
- key: 'switch-card',
- label: '切换SIM卡'
- },
- {
- key: 'set-wifi',
- label: '设置WiFi'
- },
- {
- key: 'delete',
- label: '删除设备'
+ const deviceOperationMenuItems = computed((): MenuItemType[] => {
+ const items: MenuItemType[] = [
+ {
+ key: 'reboot',
+ label: '重启设备'
+ },
+ {
+ key: 'reset',
+ label: '恢复出厂'
+ },
+ {
+ key: 'speed-limit',
+ label: '设置限速'
+ },
+ {
+ key: 'switch-card',
+ label: '切换SIM卡'
+ },
+ {
+ key: 'set-wifi',
+ label: '设置WiFi'
+ }
+ ]
+
+ if (hasAuth('devices:delete')) {
+ items.push({
+ key: 'delete',
+ label: '删除设备'
+ })
}
- ])
+
+ return items
+ })
// 显示设备操作菜单
const showDeviceOperationMenu = (e: MouseEvent, deviceNo: string) => {
diff --git a/src/views/asset-management/device-task/index.vue b/src/views/asset-management/device-task/index.vue
index de329aa..0cb9997 100644
--- a/src/views/asset-management/device-task/index.vue
+++ b/src/views/asset-management/device-task/index.vue
@@ -18,7 +18,12 @@
@refresh="handleRefresh"
>
-
+
批量导入设备
@@ -215,6 +220,7 @@
import type { UploadInstance } from 'element-plus'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import { formatDateTime } from '@/utils/business/format'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { StorageService } from '@/api/modules/storage'
@@ -222,6 +228,7 @@
defineOptions({ name: 'DeviceTask' })
+ const { hasAuth } = useAuth()
const loading = ref(false)
const tableRef = ref()
const uploadRef = ref()
diff --git a/src/views/asset-management/iot-card-management/index.vue b/src/views/asset-management/iot-card-management/index.vue
index c87c137..252b545 100644
--- a/src/views/asset-management/iot-card-management/index.vue
+++ b/src/views/asset-management/iot-card-management/index.vue
@@ -22,6 +22,7 @@
type="success"
:disabled="selectedCards.length === 0"
@click="showAllocateDialog"
+ v-permission="'iot_card:batch_allocation'"
>
批量分配
@@ -29,6 +30,7 @@
type="warning"
:disabled="selectedCards.length === 0"
@click="showRecallDialog"
+ v-permission="'iot_card:batch_recycle'"
>
批量回收
@@ -36,10 +38,17 @@
type="primary"
:disabled="selectedCards.length === 0"
@click="showSeriesBindingDialog"
+ v-permission="'iot_card:batch_setting'"
>
批量设置套餐系列
- 更多操作
+
+ 更多操作
+
@@ -595,6 +604,7 @@
import QRCode from 'qrcode'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import { formatDateTime } from '@/utils/business/format'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
@@ -611,6 +621,7 @@
defineOptions({ name: 'StandaloneCardList' })
+ const { hasAuth } = useAuth()
const router = useRouter()
const loading = ref(false)
const allocateDialogVisible = ref(false)
@@ -1513,28 +1524,44 @@
}
// 更多操作菜单项配置
- const moreMenuItems = computed((): MenuItemType[] => [
- {
- key: 'distribution',
- label: '网卡分销'
- },
- {
- key: 'recharge',
- label: '批量充值'
- },
- {
- key: 'recycle',
- label: '网卡回收'
- },
- {
+ const moreMenuItems = computed((): MenuItemType[] => {
+ const items: MenuItemType[] = []
+
+ if (hasAuth('iot_card:network_distribution')) {
+ items.push({
+ key: 'distribution',
+ label: '网卡分销'
+ })
+ }
+
+ if (hasAuth('iot_card:batch_recharge')) {
+ items.push({
+ key: 'recharge',
+ label: '批量充值'
+ })
+ }
+
+ if (hasAuth('iot_card:network_recycle')) {
+ items.push({
+ key: 'recycle',
+ label: '网卡回收'
+ })
+ }
+
+ items.push({
key: 'download',
label: '批量下载'
- },
- {
- key: 'changePackage',
- label: '变更套餐'
+ })
+
+ if (hasAuth('iot_card:change_package')) {
+ items.push({
+ key: 'changePackage',
+ label: '变更套餐'
+ })
}
- ])
+
+ return items
+ })
// 卡操作菜单项配置
const cardOperationMenuItems = computed((): MenuItemType[] => [
diff --git a/src/views/asset-management/iot-card-task/index.vue b/src/views/asset-management/iot-card-task/index.vue
index d4a5cd9..f6a835d 100644
--- a/src/views/asset-management/iot-card-task/index.vue
+++ b/src/views/asset-management/iot-card-task/index.vue
@@ -18,7 +18,12 @@
@refresh="handleRefresh"
>
-
+
批量导入IoT卡
@@ -212,6 +217,7 @@
import type { UploadInstance } from 'element-plus'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import { formatDateTime } from '@/utils/business/format'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { StorageService } from '@/api/modules/storage'
@@ -220,6 +226,7 @@
defineOptions({ name: 'IotCardTask' })
+ const { hasAuth } = useAuth()
const loading = ref(false)
const tableRef = ref()
const uploadRef = ref()
diff --git a/src/views/finance/carrier-management/index.vue b/src/views/finance/carrier-management/index.vue
index a9cba39..e9d202b 100644
--- a/src/views/finance/carrier-management/index.vue
+++ b/src/views/finance/carrier-management/index.vue
@@ -19,7 +19,7 @@
@refresh="handleRefresh"
>
- 新增运营商
+ 新增运营商
@@ -104,6 +104,7 @@
import type { Carrier, CarrierType } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import {
@@ -116,6 +117,7 @@
defineOptions({ name: 'CarrierManagement' })
+ const { hasAuth } = useAuth()
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
@@ -258,6 +260,7 @@
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
+ disabled: !hasAuth('carrier:update_status'),
'onUpdate:modelValue': (val: string | number | boolean) =>
handleStatusChange(row, val as number)
})
@@ -275,16 +278,27 @@
width: 150,
fixed: 'right',
formatter: (row: any) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deleteCarrier(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('carrier:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('carrier:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deleteCarrier(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/finance/commission/agent-commission/index.vue b/src/views/finance/commission/agent-commission/index.vue
index aa68d79..0979dc0 100644
--- a/src/views/finance/commission/agent-commission/index.vue
+++ b/src/views/finance/commission/agent-commission/index.vue
@@ -232,6 +232,7 @@
WithdrawalRequestItem
} from '@/types/api/commission'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime, formatMoney } from '@/utils/business/format'
import {
@@ -243,6 +244,8 @@
defineOptions({ name: 'AgentCommission' })
+ const { hasAuth } = useAuth()
+
const route = useRoute()
// 主表格状态
@@ -380,10 +383,16 @@
width: 120,
fixed: 'right',
formatter: (row: ShopCommissionSummaryItem) => {
- return h(ArtButtonTable, {
- icon: '',
- onClick: () => showDetail(row)
- })
+ const buttons = []
+ if (hasAuth('agent_commission:detail')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ icon: '',
+ onClick: () => showDetail(row)
+ })
+ )
+ }
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/finance/commission/my-commission/index.vue b/src/views/finance/commission/my-commission/index.vue
index bcf01f9..df726d5 100644
--- a/src/views/finance/commission/my-commission/index.vue
+++ b/src/views/finance/commission/my-commission/index.vue
@@ -106,7 +106,7 @@
style="margin-top: 20px"
>
- 发起提现
+ 发起提现
@@ -296,10 +296,13 @@
} from '@/config/constants/commission'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import { formatDateTime, formatMoney } from '@/utils/business/format'
defineOptions({ name: 'MyCommission' })
+ const { hasAuth } = useAuth()
+
// 标签页
const activeTab = ref('commission')
diff --git a/src/views/finance/commission/withdrawal-settings/index.vue b/src/views/finance/commission/withdrawal-settings/index.vue
index f4fab5f..ad7d505 100644
--- a/src/views/finance/commission/withdrawal-settings/index.vue
+++ b/src/views/finance/commission/withdrawal-settings/index.vue
@@ -80,7 +80,7 @@
@refresh="handleRefresh"
>
- 新增配置
+ 新增配置
@@ -155,10 +155,13 @@
import type { FormInstance, FormRules } from 'element-plus'
import type { WithdrawalSettingItem } from '@/types/api/commission'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import { formatDateTime, formatMoney, formatFeeRate } from '@/utils/business/format'
defineOptions({ name: 'CommissionWithdrawalSettings' })
+ const { hasAuth } = useAuth()
+
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
diff --git a/src/views/order-management/order-list/index.vue b/src/views/order-management/order-list/index.vue
index 19efcb5..43bc3a1 100644
--- a/src/views/order-management/order-list/index.vue
+++ b/src/views/order-management/order-list/index.vue
@@ -18,7 +18,7 @@
@refresh="handleRefresh"
>
- {{ t('orderManagement.createOrder') }}
+ {{ t('orderManagement.createOrder') }}
@@ -275,12 +275,14 @@
} from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
defineOptions({ name: 'OrderList' })
const { t } = useI18n()
+ const { hasAuth } = useAuth()
const loading = ref(false)
const createLoading = ref(false)
diff --git a/src/views/package-management/package-assign/index.vue b/src/views/package-management/package-assign/index.vue
index 94296d4..ad7cb9f 100644
--- a/src/views/package-management/package-assign/index.vue
+++ b/src/views/package-management/package-assign/index.vue
@@ -18,7 +18,7 @@
@refresh="handleRefresh"
>
- 新增分配
+ 新增分配
@@ -174,6 +174,7 @@
import type { ShopPackageAllocationResponse, PackageResponse, ShopResponse } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import {
@@ -185,6 +186,8 @@
defineOptions({ name: 'PackageAssign' })
+ const { hasAuth } = useAuth()
+
const dialogVisible = ref(false)
const costPriceDialogVisible = ref(false)
const loading = ref(false)
@@ -386,6 +389,7 @@
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
+ disabled: !hasAuth('package_assign:update_status'),
'onUpdate:modelValue': (val: string | number | boolean) =>
handleStatusChange(row, val as number)
})
@@ -403,20 +407,36 @@
width: 230,
fixed: 'right',
formatter: (row: ShopPackageAllocationResponse) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- text: '修改成本价',
- onClick: () => showCostPriceDialog(row)
- }),
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deleteAllocation(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('package_assign:update_cost')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '修改成本价',
+ onClick: () => showCostPriceDialog(row)
+ })
+ )
+ }
+
+ if (hasAuth('package_assign:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('package_assign:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deleteAllocation(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/package-management/package-list/index.vue b/src/views/package-management/package-list/index.vue
index 3935bfb..813d358 100644
--- a/src/views/package-management/package-list/index.vue
+++ b/src/views/package-management/package-list/index.vue
@@ -18,7 +18,7 @@
@refresh="handleRefresh"
>
- 新增套餐
+ 新增套餐
@@ -176,6 +176,7 @@
import type { PackageResponse, SeriesSelectOption } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import {
@@ -196,6 +197,8 @@
defineOptions({ name: 'PackageList' })
+ const { hasAuth } = useAuth()
+
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
@@ -438,6 +441,7 @@
activeText: '上架',
inactiveText: '下架',
inlinePrompt: true,
+ disabled: !hasAuth('package:update_away'),
'onUpdate:modelValue': (val: string | number | boolean) =>
handleShelfStatusChange(row, val ? 1 : 2)
})
@@ -456,6 +460,7 @@
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
+ disabled: !hasAuth('package:update_status'),
'onUpdate:modelValue': (val: string | number | boolean) =>
handleStatusChange(row, val as number)
})
@@ -473,16 +478,27 @@
width: 150,
fixed: 'right',
formatter: (row: PackageResponse) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deletePackage(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('package:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('package:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deletePackage(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/package-management/package-series/index.vue b/src/views/package-management/package-series/index.vue
index 42b5758..72421a3 100644
--- a/src/views/package-management/package-series/index.vue
+++ b/src/views/package-management/package-series/index.vue
@@ -18,7 +18,7 @@
@refresh="handleRefresh"
>
- 新增套餐系列
+ 新增套餐系列
@@ -98,6 +98,7 @@
import type { PackageSeriesResponse } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import {
@@ -110,6 +111,8 @@
defineOptions({ name: 'PackageSeries' })
+ const { hasAuth } = useAuth()
+
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
@@ -229,6 +232,7 @@
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
+ disabled: !hasAuth('package_series:update_status'),
'onUpdate:modelValue': (val: string | number | boolean) =>
handleStatusChange(row, val as number)
})
@@ -252,16 +256,27 @@
width: 150,
fixed: 'right',
formatter: (row: PackageSeriesResponse) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deleteSeries(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('package_series:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('package_series:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deleteSeries(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/package-management/series-assign/index.vue b/src/views/package-management/series-assign/index.vue
index b0f2a80..ad6af27 100644
--- a/src/views/package-management/series-assign/index.vue
+++ b/src/views/package-management/series-assign/index.vue
@@ -18,7 +18,7 @@
@refresh="handleRefresh"
>
- 新增系列分配
+ 新增系列分配
@@ -266,6 +266,7 @@
} from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import {
@@ -277,6 +278,8 @@
defineOptions({ name: 'SeriesAssign' })
+ const { hasAuth } = useAuth()
+
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
@@ -511,6 +514,7 @@
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
+ disabled: !hasAuth('series_assign:update_status'),
'onUpdate:modelValue': (val: string | number | boolean) =>
handleStatusChange(row, val as number)
})
@@ -528,16 +532,27 @@
width: 150,
fixed: 'right',
formatter: (row: ShopSeriesAllocationResponse) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deleteAllocation(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('series_assign:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('series_assign:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deleteAllocation(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
diff --git a/src/views/product/shop/index.vue b/src/views/product/shop/index.vue
index de85867..b55fb36 100644
--- a/src/views/product/shop/index.vue
+++ b/src/views/product/shop/index.vue
@@ -17,7 +17,7 @@
@refresh="handleRefresh"
>
- 新增店铺
+ 新增店铺
@@ -310,6 +310,7 @@
} from 'element-plus'
import type { FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
@@ -323,6 +324,8 @@
defineOptions({ name: 'Shop' })
+ const { hasAuth } = useAuth()
+
const dialogType = ref('add')
const dialogVisible = ref(false)
const customerAccountDialogVisible = ref(false)
@@ -606,16 +609,28 @@
width: 200,
fixed: 'right',
formatter: (row: ShopResponse) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- text: '查看客户',
- onClick: () => viewCustomerAccounts(row)
- }),
- h(ArtButtonTable, {
- text: '更多操作',
- onContextmenu: (e: MouseEvent) => showShopOperationMenu(e, row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('shop:look_customer')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '查看客户',
+ onClick: () => viewCustomerAccounts(row)
+ })
+ )
+ }
+
+ // 只要有编辑或删除权限之一,就显示更多操作按钮
+ if (hasAuth('shop:edit') || hasAuth('shop:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ text: '更多操作',
+ onContextmenu: (e: MouseEvent) => showShopOperationMenu(e, row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])
@@ -874,20 +889,30 @@
}
// 店铺操作菜单项配置
- const shopOperationMenuItems = computed((): MenuItemType[] => [
- {
+ const shopOperationMenuItems = computed((): MenuItemType[] => {
+ const items: MenuItemType[] = []
+
+ items.push({
key: 'defaultRoles',
label: '默认角色'
- },
- {
- key: 'edit',
- label: '编辑'
- },
- {
- key: 'delete',
- label: '删除'
+ })
+
+ if (hasAuth('shop:edit')) {
+ items.push({
+ key: 'edit',
+ label: '编辑'
+ })
}
- ])
+
+ if (hasAuth('shop:delete')) {
+ items.push({
+ key: 'delete',
+ label: '删除'
+ })
+ }
+
+ return items
+ })
// 显示店铺操作右键菜单
const showShopOperationMenu = (e: MouseEvent, row: ShopResponse) => {
diff --git a/src/views/system/permission/index.vue b/src/views/system/permission/index.vue
index 6ad647a..c6e30d1 100644
--- a/src/views/system/permission/index.vue
+++ b/src/views/system/permission/index.vue
@@ -17,7 +17,7 @@
@refresh="handleRefresh"
>
- 新增权限
+ 新增权限
@@ -125,6 +125,7 @@
import type { CreatePermissionParams, PermissionTreeNode } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
+ import { useAuth } from '@/composables/useAuth'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import {
PermissionType,
@@ -138,6 +139,8 @@
defineOptions({ name: 'Permission' })
+ const { hasAuth } = useAuth()
+
// 搜索表单初始值
const initialSearchState = {
perm_name: '',
@@ -299,16 +302,27 @@
width: 120,
fixed: 'right',
formatter: (row: PermissionTreeNode) => {
- return h('div', { style: 'display: flex; gap: 8px;' }, [
- h(ArtButtonTable, {
- type: 'edit',
- onClick: () => showDialog('edit', row)
- }),
- h(ArtButtonTable, {
- type: 'delete',
- onClick: () => deletePermission(row)
- })
- ])
+ const buttons = []
+
+ if (hasAuth('permission:edit')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'edit',
+ onClick: () => showDialog('edit', row)
+ })
+ )
+ }
+
+ if (hasAuth('permission:delete')) {
+ buttons.push(
+ h(ArtButtonTable, {
+ type: 'delete',
+ onClick: () => deletePermission(row)
+ })
+ )
+ }
+
+ return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
}
}
])