fetch(modify):修改账号列表中的分配角色弹窗
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m33s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m33s
This commit is contained in:
@@ -422,7 +422,7 @@
|
|||||||
"agent": "代理商管理",
|
"agent": "代理商管理",
|
||||||
"customerAccount": "客户账号",
|
"customerAccount": "客户账号",
|
||||||
"enterpriseCustomer": "企业客户",
|
"enterpriseCustomer": "企业客户",
|
||||||
"enterpriseCustomerAccounts": "企业客户账号列表",
|
"enterpriseCustomerAccounts": "客户账号列表",
|
||||||
"enterpriseCards": "企业卡管理",
|
"enterpriseCards": "企业卡管理",
|
||||||
"customerCommission": "客户账号佣金"
|
"customerCommission": "客户账号佣金"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,27 +82,107 @@
|
|||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
|
||||||
<!-- 分配角色对话框 -->
|
<!-- 分配角色对话框 -->
|
||||||
<ElDialog v-model="roleDialogVisible" title="分配角色" width="500px">
|
<ElDialog v-model="roleDialogVisible" width="900px">
|
||||||
<ElCheckboxGroup v-model="selectedRoles">
|
<template #header>
|
||||||
<div v-for="role in allRoles" :key="role.id" style="margin-bottom: 12px">
|
<div class="dialog-header">
|
||||||
<ElCheckbox :value="role.id">
|
<span class="dialog-title">分配角色</span>
|
||||||
{{ role.role_name }}
|
<div class="account-info">
|
||||||
|
<span class="account-name">{{ currentAccountName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="role-transfer-container">
|
||||||
|
<!-- 左侧:可分配角色列表 -->
|
||||||
|
<div class="transfer-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<span class="panel-title">可分配角色</span>
|
||||||
|
<ElInput
|
||||||
|
v-model="leftRoleFilter"
|
||||||
|
placeholder="搜索角色"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 180px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<ElCheckboxGroup v-model="rolesToAdd" class="role-list">
|
||||||
|
<div
|
||||||
|
v-for="role in filteredAvailableRoles"
|
||||||
|
:key="role.ID"
|
||||||
|
class="role-item"
|
||||||
|
>
|
||||||
|
<ElCheckbox :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
||||||
|
<span class="role-info">
|
||||||
|
<span>{{ role.role_name }}</span>
|
||||||
<ElTag
|
<ElTag
|
||||||
:type="role.role_type === 1 ? 'primary' : 'success'"
|
:type="role.role_type === 1 ? 'primary' : 'success'"
|
||||||
size="small"
|
size="small"
|
||||||
style="margin-left: 8px"
|
|
||||||
>
|
>
|
||||||
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
</ElTag>
|
</ElTag>
|
||||||
|
</span>
|
||||||
</ElCheckbox>
|
</ElCheckbox>
|
||||||
</div>
|
</div>
|
||||||
</ElCheckboxGroup>
|
</ElCheckboxGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中间:操作按钮 -->
|
||||||
|
<div class="transfer-buttons">
|
||||||
|
<ElButton
|
||||||
|
type="primary"
|
||||||
|
:icon="'ArrowRight'"
|
||||||
|
@click="addRoles"
|
||||||
|
:disabled="rolesToAdd.length === 0"
|
||||||
|
>
|
||||||
|
添加
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:已分配角色列表 -->
|
||||||
|
<div class="transfer-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<span class="panel-title">已分配角色</span>
|
||||||
|
<ElInput
|
||||||
|
v-model="rightRoleFilter"
|
||||||
|
placeholder="搜索角色"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 180px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="role-list">
|
||||||
|
<div
|
||||||
|
v-for="role in filteredAssignedRoles"
|
||||||
|
:key="role.ID"
|
||||||
|
class="role-item assigned-role-item"
|
||||||
|
>
|
||||||
|
<span class="role-info">
|
||||||
|
<span>{{ role.role_name }}</span>
|
||||||
|
<ElTag
|
||||||
|
:type="role.role_type === 1 ? 'primary' : 'success'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
|
</ElTag>
|
||||||
|
</span>
|
||||||
|
<ElButton
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
@click="removeSingleRole(role.ID)"
|
||||||
|
>
|
||||||
|
移除
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<ElButton @click="roleDialogVisible = false">取消</ElButton>
|
<ElButton @click="roleDialogVisible = false">关闭</ElButton>
|
||||||
<ElButton type="primary" @click="handleAssignRoles" :loading="roleSubmitLoading"
|
|
||||||
>提交</ElButton
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
@@ -122,7 +202,7 @@
|
|||||||
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
|
||||||
import { AccountService } from '@/api/modules/account'
|
import { AccountService } from '@/api/modules/account'
|
||||||
import { RoleService } from '@/api/modules/role'
|
import { RoleService } from '@/api/modules/role'
|
||||||
import { ShopService } from '@/api/modules'
|
import { ShopService, EnterpriseService } from '@/api/modules'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
import type { PlatformRole } from '@/types/api'
|
import type { PlatformRole } from '@/types/api'
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
@@ -139,8 +219,12 @@
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const roleSubmitLoading = ref(false)
|
const roleSubmitLoading = ref(false)
|
||||||
const currentAccountId = ref<number>(0)
|
const currentAccountId = ref<number>(0)
|
||||||
|
const currentAccountName = ref<string>('')
|
||||||
const selectedRoles = ref<number[]>([])
|
const selectedRoles = ref<number[]>([])
|
||||||
const allRoles = ref<PlatformRole[]>([])
|
const allRoles = ref<PlatformRole[]>([])
|
||||||
|
const rolesToAdd = ref<number[]>([])
|
||||||
|
const leftRoleFilter = ref('')
|
||||||
|
const rightRoleFilter = ref('')
|
||||||
|
|
||||||
// 定义表单搜索初始值
|
// 定义表单搜索初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
@@ -239,6 +323,22 @@
|
|||||||
placeholder: '请输入店铺名称搜索'
|
placeholder: '请输入店铺名称搜索'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '关联企业',
|
||||||
|
prop: 'enterprise_id',
|
||||||
|
type: 'select',
|
||||||
|
options: enterpriseList.value.map((enterprise) => ({
|
||||||
|
label: enterprise.enterprise_name,
|
||||||
|
value: enterprise.id
|
||||||
|
})),
|
||||||
|
config: {
|
||||||
|
clearable: true,
|
||||||
|
filterable: true,
|
||||||
|
remote: true,
|
||||||
|
remoteMethod: handleEnterpriseSearch,
|
||||||
|
placeholder: '请输入企业名称搜索'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '状态',
|
label: '状态',
|
||||||
prop: 'status',
|
prop: 'status',
|
||||||
@@ -436,6 +536,7 @@
|
|||||||
getAccountList()
|
getAccountList()
|
||||||
loadAllRoles()
|
loadAllRoles()
|
||||||
loadShopList()
|
loadShopList()
|
||||||
|
loadEnterpriseList()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 加载所有角色列表
|
// 加载所有角色列表
|
||||||
@@ -450,10 +551,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算属性:过滤后的可分配角色
|
||||||
|
const filteredAvailableRoles = computed(() => {
|
||||||
|
if (!leftRoleFilter.value) return allRoles.value
|
||||||
|
const keyword = leftRoleFilter.value.toLowerCase()
|
||||||
|
return allRoles.value.filter((role) => role.role_name.toLowerCase().includes(keyword))
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算属性:过滤后的已分配角色
|
||||||
|
const filteredAssignedRoles = computed(() => {
|
||||||
|
const assignedRolesList = allRoles.value.filter((role) => selectedRoles.value.includes(role.ID))
|
||||||
|
if (!rightRoleFilter.value) return assignedRolesList
|
||||||
|
const keyword = rightRoleFilter.value.toLowerCase()
|
||||||
|
return assignedRolesList.filter((role) => role.role_name.toLowerCase().includes(keyword))
|
||||||
|
})
|
||||||
|
|
||||||
// 显示分配角色对话框
|
// 显示分配角色对话框
|
||||||
const showRoleDialog = async (row: any) => {
|
const showRoleDialog = async (row: any) => {
|
||||||
currentAccountId.value = row.id
|
currentAccountId.value = row.id
|
||||||
|
currentAccountName.value = row.username
|
||||||
selectedRoles.value = []
|
selectedRoles.value = []
|
||||||
|
rolesToAdd.value = []
|
||||||
|
leftRoleFilter.value = ''
|
||||||
|
rightRoleFilter.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 每次打开对话框时重新加载最新的角色列表
|
// 每次打开对话框时重新加载最新的角色列表
|
||||||
@@ -464,7 +584,8 @@
|
|||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
// 提取角色ID数组
|
// 提取角色ID数组
|
||||||
const roles = res.data || []
|
const roles = res.data || []
|
||||||
selectedRoles.value = roles.map((role: any) => role.id)
|
// 兼容 ID 和 id 两种字段名
|
||||||
|
selectedRoles.value = roles.map((role: any) => role.ID || role.id)
|
||||||
// 数据加载完成后再打开对话框
|
// 数据加载完成后再打开对话框
|
||||||
roleDialogVisible.value = true
|
roleDialogVisible.value = true
|
||||||
}
|
}
|
||||||
@@ -473,19 +594,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交分配角色
|
// 批量添加角色
|
||||||
const handleAssignRoles = async () => {
|
const addRoles = async () => {
|
||||||
roleSubmitLoading.value = true
|
if (rolesToAdd.value.length === 0) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AccountService.assignRolesToAccount(currentAccountId.value, selectedRoles.value)
|
// 将选中的角色添加到已分配列表
|
||||||
ElMessage.success('分配角色成功')
|
const newRoles = [...new Set([...selectedRoles.value, ...rolesToAdd.value])]
|
||||||
roleDialogVisible.value = false
|
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
||||||
|
|
||||||
|
selectedRoles.value = newRoles
|
||||||
|
rolesToAdd.value = []
|
||||||
|
|
||||||
|
ElMessage.success('角色添加成功')
|
||||||
// 刷新列表以更新角色显示
|
// 刷新列表以更新角色显示
|
||||||
await getAccountList()
|
await getAccountList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error('添加角色失败:', error)
|
||||||
} finally {
|
}
|
||||||
roleSubmitLoading.value = false
|
}
|
||||||
|
|
||||||
|
// 移除单个角色
|
||||||
|
const removeSingleRole = async (roleId: number) => {
|
||||||
|
try {
|
||||||
|
// 从已分配列表中移除该角色
|
||||||
|
const newRoles = selectedRoles.value.filter((id) => id !== roleId)
|
||||||
|
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
||||||
|
|
||||||
|
selectedRoles.value = newRoles
|
||||||
|
|
||||||
|
ElMessage.success('角色移除成功')
|
||||||
|
// 刷新列表以更新角色显示
|
||||||
|
await getAccountList()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('移除角色失败:', error)
|
||||||
|
ElMessage.error('角色移除失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,10 +763,156 @@
|
|||||||
const handleShopSearch = (query: string) => {
|
const handleShopSearch = (query: string) => {
|
||||||
loadShopList(query)
|
loadShopList(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载企业列表
|
||||||
|
const loadEnterpriseList = async (keyword: string = '') => {
|
||||||
|
try {
|
||||||
|
const res = await EnterpriseService.getEnterprises({
|
||||||
|
page: 1,
|
||||||
|
page_size: 20, // 默认加载20条
|
||||||
|
status: 1, // 只加载启用的企业
|
||||||
|
enterprise_name: keyword || undefined // 根据企业名称搜索
|
||||||
|
})
|
||||||
|
if (res.code === 0) {
|
||||||
|
enterpriseList.value = res.data.items || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取企业列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 企业搜索处理
|
||||||
|
const handleEnterpriseSearch = (query: string) => {
|
||||||
|
loadEnterpriseList(query)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.account-page {
|
.account-page {
|
||||||
// 账号管理页面样式
|
// 账号管理页面样式
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.dialog-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.account-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-transfer-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px 0;
|
||||||
|
min-height: 500px;
|
||||||
|
|
||||||
|
.transfer-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 380px;
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
.role-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.role-item {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
border-color: var(--el-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-checkbox) {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.el-checkbox__label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.assigned-role-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.role-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 10px;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
v-model:filter="searchForm"
|
v-model:filter="searchForm"
|
||||||
:items="searchFormItems"
|
:items="searchFormItems"
|
||||||
:show-expand="false"
|
:show-expand="false"
|
||||||
:label-width="90"
|
label-width="90"
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
></ArtSearchBar>
|
></ArtSearchBar>
|
||||||
@@ -210,7 +210,7 @@
|
|||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { EnterpriseService, ShopService } from '@/api/modules'
|
import { EnterpriseService, ShopService } from '@/api/modules'
|
||||||
import { ElMessage, ElMessageBox, ElTag, ElSwitch } from 'element-plus'
|
import { ElMessage, ElSwitch } from 'element-plus'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import type { EnterpriseItem, ShopResponse } from '@/types/api'
|
import type { EnterpriseItem, ShopResponse } from '@/types/api'
|
||||||
import type { SearchFormItem } from '@/types'
|
import type { SearchFormItem } from '@/types'
|
||||||
@@ -220,8 +220,6 @@
|
|||||||
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
|
||||||
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
import { BgColorEnum } from '@/enums/appEnum'
|
|
||||||
import { RoutesAlias } from '@/router/routesAlias'
|
|
||||||
|
|
||||||
defineOptions({ name: 'EnterpriseCustomer' })
|
defineOptions({ name: 'EnterpriseCustomer' })
|
||||||
|
|
||||||
@@ -807,9 +805,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.enterprise-customer-page {
|
|
||||||
// 可以在这里添加企业客户页面特定样式
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -114,18 +114,107 @@
|
|||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
|
||||||
<!-- 分配角色对话框 -->
|
<!-- 分配角色对话框 -->
|
||||||
<ElDialog v-model="roleDialogVisible" title="分配角色" width="500px">
|
<ElDialog v-model="roleDialogVisible" width="900px">
|
||||||
<ElCheckboxGroup v-model="selectedRoles">
|
<template #header>
|
||||||
<div v-for="role in allRoles" :key="role.ID" style="margin-bottom: 12px">
|
<div class="dialog-header">
|
||||||
<ElCheckbox :label="role.ID">{{ role.role_name }}</ElCheckbox>
|
<span class="dialog-title">分配角色</span>
|
||||||
|
<div class="account-info">
|
||||||
|
<span class="account-name">{{ currentAccountName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="role-transfer-container">
|
||||||
|
<!-- 左侧:可分配角色列表 -->
|
||||||
|
<div class="transfer-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<span class="panel-title">可分配角色</span>
|
||||||
|
<ElInput
|
||||||
|
v-model="leftRoleFilter"
|
||||||
|
placeholder="搜索角色"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 180px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<ElCheckboxGroup v-model="rolesToAdd" class="role-list">
|
||||||
|
<div
|
||||||
|
v-for="role in filteredAvailableRoles"
|
||||||
|
:key="role.ID"
|
||||||
|
class="role-item"
|
||||||
|
>
|
||||||
|
<ElCheckbox :label="role.ID" :disabled="selectedRoles.includes(role.ID)">
|
||||||
|
<span class="role-info">
|
||||||
|
<span>{{ role.role_name }}</span>
|
||||||
|
<ElTag
|
||||||
|
:type="role.role_type === 1 ? 'primary' : 'success'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
|
</ElTag>
|
||||||
|
</span>
|
||||||
|
</ElCheckbox>
|
||||||
</div>
|
</div>
|
||||||
</ElCheckboxGroup>
|
</ElCheckboxGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中间:操作按钮 -->
|
||||||
|
<div class="transfer-buttons">
|
||||||
|
<ElButton
|
||||||
|
type="primary"
|
||||||
|
:icon="'ArrowRight'"
|
||||||
|
@click="addRoles"
|
||||||
|
:disabled="rolesToAdd.length === 0"
|
||||||
|
>
|
||||||
|
添加
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:已分配角色列表 -->
|
||||||
|
<div class="transfer-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<span class="panel-title">已分配角色</span>
|
||||||
|
<ElInput
|
||||||
|
v-model="rightRoleFilter"
|
||||||
|
placeholder="搜索角色"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 180px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="role-list">
|
||||||
|
<div
|
||||||
|
v-for="role in filteredAssignedRoles"
|
||||||
|
:key="role.ID"
|
||||||
|
class="role-item assigned-role-item"
|
||||||
|
>
|
||||||
|
<span class="role-info">
|
||||||
|
<span>{{ role.role_name }}</span>
|
||||||
|
<ElTag
|
||||||
|
:type="role.role_type === 1 ? 'primary' : 'success'"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ role.role_type === 1 ? '平台角色' : '客户角色' }}
|
||||||
|
</ElTag>
|
||||||
|
</span>
|
||||||
|
<ElButton
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
@click="removeSingleRole(role.ID)"
|
||||||
|
>
|
||||||
|
移除
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<ElButton @click="roleDialogVisible = false">取消</ElButton>
|
<ElButton @click="roleDialogVisible = false">关闭</ElButton>
|
||||||
<ElButton type="primary" @click="handleAssignRoles" :loading="roleSubmitLoading"
|
|
||||||
>提交</ElButton
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
@@ -185,8 +274,12 @@
|
|||||||
const roleSubmitLoading = ref(false)
|
const roleSubmitLoading = ref(false)
|
||||||
const passwordSubmitLoading = ref(false)
|
const passwordSubmitLoading = ref(false)
|
||||||
const currentAccountId = ref<number>(0)
|
const currentAccountId = ref<number>(0)
|
||||||
|
const currentAccountName = ref<string>('')
|
||||||
const selectedRoles = ref<number[]>([])
|
const selectedRoles = ref<number[]>([])
|
||||||
const allRoles = ref<PlatformRole[]>([])
|
const allRoles = ref<PlatformRole[]>([])
|
||||||
|
const rolesToAdd = ref<number[]>([])
|
||||||
|
const leftRoleFilter = ref('')
|
||||||
|
const rightRoleFilter = ref('')
|
||||||
|
|
||||||
// 定义表单搜索初始值
|
// 定义表单搜索初始值
|
||||||
const initialSearchState = {
|
const initialSearchState = {
|
||||||
@@ -528,10 +621,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算属性:过滤后的可分配角色
|
||||||
|
const filteredAvailableRoles = computed(() => {
|
||||||
|
if (!leftRoleFilter.value) return allRoles.value
|
||||||
|
const keyword = leftRoleFilter.value.toLowerCase()
|
||||||
|
return allRoles.value.filter((role) => role.role_name.toLowerCase().includes(keyword))
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算属性:过滤后的已分配角色
|
||||||
|
const filteredAssignedRoles = computed(() => {
|
||||||
|
const assignedRolesList = allRoles.value.filter((role) => selectedRoles.value.includes(role.ID))
|
||||||
|
if (!rightRoleFilter.value) return assignedRolesList
|
||||||
|
const keyword = rightRoleFilter.value.toLowerCase()
|
||||||
|
return assignedRolesList.filter((role) => role.role_name.toLowerCase().includes(keyword))
|
||||||
|
})
|
||||||
|
|
||||||
// 显示分配角色对话框
|
// 显示分配角色对话框
|
||||||
const showRoleDialog = async (row: PlatformAccount) => {
|
const showRoleDialog = async (row: PlatformAccount) => {
|
||||||
currentAccountId.value = row.id
|
currentAccountId.value = row.id
|
||||||
|
currentAccountName.value = row.username
|
||||||
selectedRoles.value = []
|
selectedRoles.value = []
|
||||||
|
rolesToAdd.value = []
|
||||||
|
leftRoleFilter.value = ''
|
||||||
|
rightRoleFilter.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 每次打开对话框时重新加载最新的角色列表
|
// 每次打开对话框时重新加载最新的角色列表
|
||||||
@@ -542,7 +654,8 @@
|
|||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
// 提取角色ID数组
|
// 提取角色ID数组
|
||||||
const roles = res.data || []
|
const roles = res.data || []
|
||||||
selectedRoles.value = roles.map((role: any) => role.id)
|
// 兼容 ID 和 id 两种字段名
|
||||||
|
selectedRoles.value = roles.map((role: any) => role.ID || role.id)
|
||||||
// 数据加载完成后再打开对话框
|
// 数据加载完成后再打开对话框
|
||||||
roleDialogVisible.value = true
|
roleDialogVisible.value = true
|
||||||
}
|
}
|
||||||
@@ -551,19 +664,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交分配角色
|
// 批量添加角色
|
||||||
const handleAssignRoles = async () => {
|
const addRoles = async () => {
|
||||||
roleSubmitLoading.value = true
|
if (rolesToAdd.value.length === 0) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AccountService.assignRolesToAccount(currentAccountId.value, selectedRoles.value)
|
// 将选中的角色添加到已分配列表
|
||||||
ElMessage.success('分配角色成功')
|
const newRoles = [...new Set([...selectedRoles.value, ...rolesToAdd.value])]
|
||||||
roleDialogVisible.value = false
|
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
||||||
|
|
||||||
|
selectedRoles.value = newRoles
|
||||||
|
rolesToAdd.value = []
|
||||||
|
|
||||||
|
ElMessage.success('角色添加成功')
|
||||||
// 刷新列表以更新角色显示
|
// 刷新列表以更新角色显示
|
||||||
await getAccountList()
|
await getAccountList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error('添加角色失败:', error)
|
||||||
} finally {
|
ElMessage.error('角色添加失败')
|
||||||
roleSubmitLoading.value = false
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除单个角色
|
||||||
|
const removeSingleRole = async (roleId: number) => {
|
||||||
|
try {
|
||||||
|
// 从已分配列表中移除该角色
|
||||||
|
const newRoles = selectedRoles.value.filter((id) => id !== roleId)
|
||||||
|
await AccountService.assignRolesToAccount(currentAccountId.value, newRoles)
|
||||||
|
|
||||||
|
selectedRoles.value = newRoles
|
||||||
|
|
||||||
|
ElMessage.success('角色移除成功')
|
||||||
|
// 刷新列表以更新角色显示
|
||||||
|
await getAccountList()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('移除角色失败:', error)
|
||||||
|
ElMessage.error('角色移除失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,4 +904,128 @@
|
|||||||
.platform-account-page {
|
.platform-account-page {
|
||||||
// 平台账号管理页面样式
|
// 平台账号管理页面样式
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.dialog-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.account-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--el-text-color-regular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-transfer-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px 0;
|
||||||
|
min-height: 500px;
|
||||||
|
|
||||||
|
.transfer-panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 380px;
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
.role-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.role-item {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
border-color: var(--el-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-checkbox) {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.el-checkbox__label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.assigned-role-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.role-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.transfer-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 10px;
|
||||||
|
|
||||||
|
.el-button {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
:currentPage="pagination.page"
|
:currentPage="pagination.page"
|
||||||
:pageSize="pagination.pageSize"
|
:pageSize="pagination.pageSize"
|
||||||
:total="pagination.total"
|
:total="pagination.total"
|
||||||
:marginTop="10"
|
:marginTop="10"v
|
||||||
height="60vh"
|
height="60vh"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h, computed } from 'vue'
|
import { h, computed, ref, reactive, onMounted } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ElMessage, ElTag, ElIcon, ElButton, ElCard } from 'element-plus'
|
import { ElMessage, ElTag, ElIcon, ElButton, ElCard } from 'element-plus'
|
||||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||||
|
|||||||
Reference in New Issue
Block a user