This commit is contained in:
@@ -86,9 +86,18 @@
|
||||
<span class="dialog-title">分配角色</span>
|
||||
<div class="account-info">
|
||||
<span class="account-name">{{ currentAccountName }}</span>
|
||||
<ElTag v-if="currentAccountType === 1" type="danger" size="small" style="margin-left: 8px">
|
||||
超级管理员不能分配角色
|
||||
</ElTag>
|
||||
<ElTag v-if="currentAccountType === 2" type="info" size="small" style="margin-left: 8px">
|
||||
平台用户只能分配一个平台角色
|
||||
</ElTag>
|
||||
<ElTag v-if="currentAccountType === 3" type="warning" size="small" style="margin-left: 8px">
|
||||
代理账号只能分配一个客户角色
|
||||
</ElTag>
|
||||
<ElTag v-if="currentAccountType === 4" type="warning" size="small" style="margin-left: 8px">
|
||||
企业账号只能分配一个客户角色
|
||||
</ElTag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -549,9 +558,15 @@
|
||||
let roles = allRoles.value
|
||||
|
||||
// 根据账号类型过滤角色
|
||||
if (currentAccountType.value === 3) {
|
||||
if (currentAccountType.value === 1) {
|
||||
// 超级管理员:不能分配任何角色
|
||||
return []
|
||||
} else if (currentAccountType.value === 3) {
|
||||
// 代理账号:只显示客户角色
|
||||
roles = roles.filter((role) => role.role_type === 2)
|
||||
} else if (currentAccountType.value === 4) {
|
||||
// 企业账号:只显示客户角色
|
||||
roles = roles.filter((role) => role.role_type === 2)
|
||||
} else if (currentAccountType.value === 2) {
|
||||
// 平台用户:只显示平台角色
|
||||
roles = roles.filter((role) => role.role_type === 1)
|
||||
@@ -605,21 +620,15 @@
|
||||
if (rolesToAdd.value.length === 0) return
|
||||
|
||||
try {
|
||||
let newRoles: number[]
|
||||
|
||||
// 代理账号只能分配一个角色,会覆盖之前的角色
|
||||
if (currentAccountType.value === 3) {
|
||||
if (rolesToAdd.value.length > 1) {
|
||||
ElMessage.warning('代理账号只能分配一个角色')
|
||||
return
|
||||
}
|
||||
// 只保留新选择的一个角色
|
||||
newRoles = rolesToAdd.value
|
||||
} else {
|
||||
// 其他账号类型可以分配多个角色
|
||||
newRoles = [...new Set([...selectedRoles.value, ...rolesToAdd.value])]
|
||||
// 所有账号只能分配一个角色
|
||||
if (rolesToAdd.value.length > 1) {
|
||||
ElMessage.warning('只能分配一个角色')
|
||||
return
|
||||
}
|
||||
|
||||
// 新角色会替换之前的角色
|
||||
const newRoles = rolesToAdd.value
|
||||
|
||||
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
||||
|
||||
selectedRoles.value = newRoles
|
||||
|
||||
@@ -458,7 +458,7 @@
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 280,
|
||||
width: 190,
|
||||
fixed: 'right',
|
||||
formatter: (row: EnterpriseItem) => {
|
||||
const buttons = []
|
||||
@@ -481,16 +481,6 @@
|
||||
)
|
||||
}
|
||||
|
||||
// 只要有编辑、修改密码权限之一,就显示更多操作按钮
|
||||
if (hasAuth('enterprise_customer:edit') || hasAuth('enterprise_customer:update_pwd')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '更多操作',
|
||||
onContextmenu: (e: MouseEvent) => showEnterpriseOperationMenu(e, row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +358,10 @@
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
<div v-if="deviceCards.length === 0" style="text-align: center; padding: 20px; color: #909399">
|
||||
<div
|
||||
v-if="deviceCards.length === 0"
|
||||
style="text-align: center; padding: 20px; color: #909399"
|
||||
>
|
||||
暂无绑定的卡片
|
||||
</div>
|
||||
</div>
|
||||
@@ -374,7 +377,12 @@
|
||||
|
||||
<!-- 绑定卡弹窗 -->
|
||||
<ElDialog v-model="bindCardDialogVisible" title="绑定卡到设备" width="500px">
|
||||
<ElForm ref="bindCardFormRef" :model="bindCardForm" :rules="bindCardRules" label-width="100px">
|
||||
<ElForm
|
||||
ref="bindCardFormRef"
|
||||
:model="bindCardForm"
|
||||
:rules="bindCardRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<ElFormItem label="IoT卡" prop="iot_card_id">
|
||||
<ElSelect
|
||||
v-model="bindCardForm.iot_card_id"
|
||||
@@ -396,7 +404,11 @@
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="插槽位置" prop="slot_position">
|
||||
<ElSelect v-model="bindCardForm.slot_position" placeholder="请选择插槽位置" style="width: 100%">
|
||||
<ElSelect
|
||||
v-model="bindCardForm.slot_position"
|
||||
placeholder="请选择插槽位置"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="i in currentDeviceDetail?.max_sim_slots || 4"
|
||||
:key="i"
|
||||
@@ -903,7 +915,9 @@
|
||||
// 重新加载卡列表
|
||||
await loadDeviceCards(currentDeviceDetail.value.id)
|
||||
// 刷新设备详情以更新绑定卡数量
|
||||
const detailRes = await DeviceService.getDeviceByImei(currentDeviceDetail.value.device_no)
|
||||
const detailRes = await DeviceService.getDeviceByImei(
|
||||
currentDeviceDetail.value.device_no
|
||||
)
|
||||
if (detailRes.code === 0 && detailRes.data) {
|
||||
currentDeviceDetail.value = detailRes.data
|
||||
}
|
||||
@@ -929,10 +943,7 @@
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
const res = await DeviceService.unbindCard(
|
||||
currentDeviceDetail.value.id,
|
||||
row.iot_card_id
|
||||
)
|
||||
const res = await DeviceService.unbindCard(currentDeviceDetail.value.id, row.iot_card_id)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('解绑成功')
|
||||
// 重新加载卡列表
|
||||
@@ -1070,23 +1081,6 @@
|
||||
label: '创建时间',
|
||||
width: 180,
|
||||
formatter: (row: Device) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
formatter: (row: Device) => {
|
||||
// Show "更多操作" only if user has at least one operation permission
|
||||
const hasAnyOperationPermission = hasAuth('devices:delete') || hasAuth('devices:look_binding')
|
||||
if (hasAnyOperationPermission) {
|
||||
return h(ArtButtonTable, {
|
||||
text: '更多操作',
|
||||
onContextmenu: (e: MouseEvent) => showDeviceOperationMenu(e, row.device_no)
|
||||
})
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -1105,8 +1099,8 @@
|
||||
try {
|
||||
const res = await ShopService.getShops({
|
||||
page: 1,
|
||||
page_size: 9999, // 获取所有数据用于构建树形结构
|
||||
status: 1 // 只获取启用的店铺
|
||||
page_size: 9999, // 获取所有数据用于构建树形结构
|
||||
status: 1 // 只获取启用的店铺
|
||||
})
|
||||
if (res.code === 0) {
|
||||
shopTreeData.value = buildShopTree(res.data.items || [])
|
||||
@@ -1349,7 +1343,7 @@
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
status: 1 // 只获取启用的
|
||||
status: 1 // 只获取启用的
|
||||
}
|
||||
if (seriesName) {
|
||||
params.series_name = seriesName
|
||||
@@ -1445,7 +1439,7 @@
|
||||
|
||||
// 通过设备号查看卡片
|
||||
const handleViewCardsByDeviceNo = (deviceNo: string) => {
|
||||
const device = deviceList.value.find(d => d.device_no === deviceNo)
|
||||
const device = deviceList.value.find((d) => d.device_no === deviceNo)
|
||||
if (device) {
|
||||
handleViewCards(device)
|
||||
} else {
|
||||
@@ -1456,7 +1450,7 @@
|
||||
// 通过设备号删除设备
|
||||
const handleDeleteDeviceByNo = async (deviceNo: string) => {
|
||||
// 先根据设备号找到设备对象
|
||||
const device = deviceList.value.find(d => d.device_no === deviceNo)
|
||||
const device = deviceList.value.find((d) => d.device_no === deviceNo)
|
||||
if (device) {
|
||||
deleteDevice(device)
|
||||
} else {
|
||||
|
||||
@@ -1096,18 +1096,6 @@
|
||||
label: '创建时间',
|
||||
width: 180,
|
||||
formatter: (row: StandaloneIotCard) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
formatter: (row: StandaloneIotCard) => {
|
||||
return h(ArtButtonTable, {
|
||||
text: '更多操作',
|
||||
onContextmenu: (e: MouseEvent) => showCardOperationMenu(e, row.iccid)
|
||||
})
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
@@ -37,12 +37,36 @@
|
||||
:marginTop="10"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
@row-contextmenu="handleRowContextMenu"
|
||||
>
|
||||
<template #default>
|
||||
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
|
||||
</template>
|
||||
</ArtTable>
|
||||
|
||||
<!-- 右键菜单 -->
|
||||
<div
|
||||
v-if="contextMenuVisible"
|
||||
:style="{
|
||||
position: 'fixed',
|
||||
left: contextMenuPosition.x + 'px',
|
||||
top: contextMenuPosition.y + 'px',
|
||||
zIndex: 9999
|
||||
}"
|
||||
>
|
||||
<ElCard shadow="always" class="context-menu">
|
||||
<div class="context-menu-item" @click="handleViewDetail(contextMenuRow)" v-if="hasAuth('package_series:detail')">
|
||||
<span>详情</span>
|
||||
</div>
|
||||
<div class="context-menu-item" @click="showDialog('edit', contextMenuRow)" v-if="hasAuth('package_series:edit')">
|
||||
<span>编辑</span>
|
||||
</div>
|
||||
<div class="context-menu-item danger" @click="deleteSeries(contextMenuRow)" v-if="hasAuth('package_series:delete')">
|
||||
<span>删除</span>
|
||||
</div>
|
||||
</ElCard>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑对话框 -->
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
@@ -386,6 +410,11 @@
|
||||
const tableRef = ref()
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
// 右键菜单状态
|
||||
const contextMenuVisible = ref(false)
|
||||
const contextMenuPosition = reactive({ x: 0, y: 0 })
|
||||
const contextMenuRow = ref<PackageSeriesResponse | null>(null)
|
||||
|
||||
// 搜索表单初始值
|
||||
const initialSearchState = {
|
||||
series_name: '',
|
||||
@@ -456,8 +485,7 @@
|
||||
{ label: '强充计算类型', prop: 'force_calc_type' },
|
||||
{ label: '时效类型', prop: 'validity_type' },
|
||||
{ label: '状态', prop: 'status' },
|
||||
{ label: '创建时间', prop: 'created_at' },
|
||||
{ label: '操作', prop: 'operation' }
|
||||
{ label: '创建时间', prop: 'created_at' }
|
||||
]
|
||||
|
||||
// 表单验证规则
|
||||
@@ -675,50 +703,34 @@
|
||||
label: '创建时间',
|
||||
width: 180,
|
||||
formatter: (row: PackageSeriesResponse) => formatDateTime(row.created_at)
|
||||
},
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 220,
|
||||
fixed: 'right',
|
||||
formatter: (row: PackageSeriesResponse) => {
|
||||
const buttons = []
|
||||
|
||||
// 详情按钮
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '详情',
|
||||
onClick: () => handleViewDetail(row)
|
||||
})
|
||||
)
|
||||
|
||||
if (hasAuth('package_series:edit')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '编辑',
|
||||
onClick: () => showDialog('edit', row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (hasAuth('package_series:delete')) {
|
||||
buttons.push(
|
||||
h(ArtButtonTable, {
|
||||
text: '删除',
|
||||
onClick: () => deleteSeries(row)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return h('div', { style: 'display: flex; gap: 8px;' }, buttons)
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
getTableData()
|
||||
// 添加全局点击事件监听,关闭右键菜单
|
||||
document.addEventListener('click', closeContextMenu)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
// 移除全局点击事件监听
|
||||
document.removeEventListener('click', closeContextMenu)
|
||||
})
|
||||
|
||||
// 处理右键菜单
|
||||
const handleRowContextMenu = (row: PackageSeriesResponse, column: any, event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
contextMenuRow.value = row
|
||||
contextMenuPosition.x = event.clientX
|
||||
contextMenuPosition.y = event.clientY
|
||||
contextMenuVisible.value = true
|
||||
}
|
||||
|
||||
// 关闭右键菜单
|
||||
const closeContextMenu = () => {
|
||||
contextMenuVisible.value = false
|
||||
}
|
||||
|
||||
// 获取套餐系列列表
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
@@ -1046,4 +1058,35 @@
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
min-width: 120px;
|
||||
padding: 4px 0;
|
||||
background: white;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-primary);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
&.danger {
|
||||
color: var(--el-color-danger);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-danger-light-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -181,7 +181,9 @@
|
||||
:label="role.role_name"
|
||||
:value="role.ID"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div
|
||||
style="display: flex; justify-content: space-between; align-items: center"
|
||||
>
|
||||
<span>{{ role.role_name }}</span>
|
||||
<ElTag type="success" size="small">客户角色</ElTag>
|
||||
</div>
|
||||
@@ -623,7 +625,7 @@
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '操作',
|
||||
width: 200,
|
||||
width: 110,
|
||||
fixed: 'right',
|
||||
formatter: (row: ShopResponse) => {
|
||||
const buttons = []
|
||||
@@ -637,16 +639,6 @@
|
||||
)
|
||||
}
|
||||
|
||||
// 只要有编辑或删除权限之一,就显示更多操作按钮
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -855,9 +847,7 @@
|
||||
{ len: 11, message: '手机号必须为 11 位', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
|
||||
],
|
||||
default_role_id: [
|
||||
{ required: true, message: '请选择默认角色', trigger: 'blur' }
|
||||
]
|
||||
default_role_id: [{ required: true, message: '请选择默认角色', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 提交表单
|
||||
@@ -1048,9 +1038,8 @@
|
||||
const showAddRoleDialog = async () => {
|
||||
addRoleDialogVisible.value = true
|
||||
// 如果已有默认角色,预选第一个
|
||||
selectedRoleId.value = currentDefaultRoles.value.length > 0
|
||||
? currentDefaultRoles.value[0].role_id
|
||||
: undefined
|
||||
selectedRoleId.value =
|
||||
currentDefaultRoles.value.length > 0 ? currentDefaultRoles.value[0].role_id : undefined
|
||||
await loadAvailableRoles()
|
||||
}
|
||||
|
||||
|
||||
@@ -734,15 +734,15 @@
|
||||
const getLeftCheckedKeysWithHalf = (): number[] => {
|
||||
if (!leftTreeRef.value) return []
|
||||
const checkedKeys = leftTreeRef.value.getCheckedKeys(false)
|
||||
const parentIds = new Set<number>()
|
||||
const allIds = new Set<number>(checkedKeys)
|
||||
|
||||
// 为每个勾选的节点找到所有父节点
|
||||
checkedKeys.forEach((key: number) => {
|
||||
const parents = getParentNodeIds(key)
|
||||
parents.forEach(parentId => parentIds.add(parentId))
|
||||
parents.forEach(parentId => allIds.add(parentId))
|
||||
})
|
||||
|
||||
return [...checkedKeys, ...Array.from(parentIds)]
|
||||
return Array.from(allIds)
|
||||
}
|
||||
|
||||
// 添加权限
|
||||
|
||||
Reference in New Issue
Block a user