feat 商户用户和权限相关界面

This commit is contained in:
DaxPay
2024-08-28 15:03:28 +08:00
parent 50455059cf
commit ea6dc1569d
8 changed files with 1098 additions and 10 deletions

View File

@@ -1,17 +1,28 @@
import { defHttp } from '@/utils/http/axios'
import { PageResult, Result } from '#/axios'
import { UserInfo } from '@/views/iam/user/User.api'
import { Role } from '@/views/iam/role/Role.api'
/**
* 用户列表
*/
export function findAll(params) {
export function page(params) {
return defHttp.get<Result<PageResult<UserInfo[]>>>({
url: '/merchant/user/page',
params,
})
}
/**
* 用户详情
*/
export function get(id) {
return defHttp.get<Result<UserInfo>>({
url: '/merchant/user/findById',
params: { id },
})
}
/**
* 添加用户
*/
@@ -23,21 +34,71 @@ export function add(params) {
}
/**
* 修用户
* 修用户
*/
export function edit(params) {
export function update(params) {
return defHttp.post<Result>({
url: '/merchant/user/update',
data: params,
})
}
/**
* 重置密码
*/
export function restartPassword(userId, newPassword) {
return defHttp.post({
url: '/merchant/user/restartPassword',
data: { userId, newPassword },
})
}
/**
* 批量重置密码
*/
export function restartPasswordBatch(userIds, newPassword) {
return defHttp.post({
url: '/merchant/user/restartPasswordBatch',
data: { userIds, newPassword },
})
}
/**
* 分配角色
*/
export function assignRole(params) {
export function addUserRole(params) {
return defHttp.post<Result>({
url: '/merchant/user/assignRole',
data: params,
})
}
/**
* 分配角色 批量
*/
export function addUserRoleBatch(data) {
return defHttp.post({
url: `/merchant/user/assignRoleBatch`,
data: data,
})
}
/**
* 获取用户拥有角色id集合
*/
export function getRoleIds(userId) {
return defHttp.get<Result<string[]>>({
url: `/merchant/user/findRoleIdsByUser`,
params: { userId },
})
}
/**
* 获取用户拥有角色集合
*/
export function getRoles(userId) {
return defHttp.get<Result<Role[]>>({
url: `/merchant/user/findRolesByUser`,
params: { userId },
})
}

View File

@@ -1,5 +1,232 @@
<script setup lang="ts"></script>
<template>
<div>
<div class="m-3 p-3 pt-5 bg-white">
<b-query
:query-params="model.queryParam"
:fields="fields"
@query="queryPage"
@reset="resetQueryParams"
/>
</div>
<div class="m-3 p-3 bg-white">
<vxe-toolbar ref="xToolbar" custom :refresh="{ queryMethod: queryPage }">
<template #buttons>
<a-space>
<a-button type="primary" pre-icon="ant-design:plus-outlined" @click="add"
>新建</a-button
>
<a-dropdown v-if="batchOperateFlag">
<a-button post-icon="ant-design:down-outlined"> 批量操作 </a-button>
<template #overlay>
<a-menu>
<a-menu-item>
<a @click="assignRolesBatch()">角色分配</a>
</a-menu-item>
<!-- <a-menu-item>-->
<!-- <a @click="lockUserConfirmBatch(true)">锁定账号</a>-->
<!-- </a-menu-item>-->
<!-- <a-menu-item>-->
<!-- <a @click="lockUserConfirmBatch(false)">解锁账号</a>-->
<!-- </a-menu-item>-->
<a-menu-item>
<a @click="resetPwdBatch()">重置密码</a>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-space>
</template>
</vxe-toolbar>
<div class="h-65vh">
<vxe-table
ref="xTable"
height="auto"
size="medium"
@checkbox-all="selectAllEvent"
@checkbox-change="selectChangeEvent"
:loading="loading"
:data="pagination.records"
>
<vxe-column type="checkbox" width="50" />
<vxe-column field="name" title="姓名" />
<vxe-column field="account" title="账号" />
<vxe-column field="phone" title="手机号" />
<vxe-column field="email" title="邮箱" />
<vxe-column field="avatar" title="头像" align="center">
<template #default="{ row }">
<Avatar size="large" :src="getFileUrl(row.avatar)" />
</template>
</vxe-column>
<template></template>
<vxe-column field="status" title="用户状态" align="center">
<template #default="{ row }">
{{ dictConvert('UserStatusCode', row.status) || '无' }}
</template>
</vxe-column>
<vxe-column fixed="right" width="170" :showOverflow="false" title="操作">
<template #default="{ row }">
<a href="javascript:" @click="show(row)">查看</a>
<a-divider type="vertical" />
<a href="javascript:" @click="edit(row)">编辑</a>
<a-divider type="vertical" />
<a-dropdown>
<a> 更多 <icon icon="ant-design:down-outlined" :size="12" /> </a>
<template #overlay>
<a-menu>
<a-menu-item>
<a-link @click="assignRoles(row)">角色分配</a-link>
</a-menu-item>
<a-menu-item>
<a-link @click="resetPwd(row)">重置密码</a-link>
</a-menu-item>
<a-menu-item v-if="[1, 3].includes(row.status)">
<a-link v-if="row.status === 1" @click="lockUserConfirm(row.id, true)"
>封禁账号</a-link
>
<a-link v-if="row.status === 3" @click="lockUserConfirm(row.id, false)"
>解锁账号</a-link
>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</vxe-column>
</vxe-table>
</div>
<vxe-pager
size="medium"
:loading="loading"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total"
@page-change="handleTableChange"
/>
<UserAdd ref="userAdd" @ok="queryPage" />
<UserEdit ref="userEdit" @ok="queryPage" />
<UserShow ref="userShow" />
<UserResetPwd ref="userResetPwd" />
<UserRoleAssign ref="userRoleAssign" />
</div>
</div>
</template>
<style scoped lang="less"></style>
<script lang="ts" setup>
import BQuery from '/@/components/Bootx/Query/BQuery.vue'
import { Avatar } from 'ant-design-vue'
import useTablePage from '@/hooks/bootx/useTablePage'
import { useMessage } from '@/hooks/web/useMessage'
import { onMounted, ref } from 'vue'
import { QueryField, STRING } from '@/components/Bootx/Query/Query'
import { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'
import { useDict } from '@/hooks/bootx/useDict'
import { Icon } from '@/components/Icon'
import UserShow from './UserShow.vue'
import UserEdit from './UserEdit.vue'
import UserAdd from './UserAdd.vue'
import UserResetPwd from './UserResetPwd.vue'
import UserRoleAssign from './UserRoleAssign.vue'
import ALink from '@/components/Link/Link.vue'
import { useFilePlatform } from '@/hooks/bootx/useFilePlatform'
import { page } from './MerchantUser.api'
// 使用hooks
const {
handleTableChange,
pageQueryResHandel,
resetQueryParams,
pagination,
pages,
model,
loading,
batchOperateFlag,
} = useTablePage(queryPage)
const { dictConvert } = useDict()
const { getFileUrl } = useFilePlatform()
// 查询条件
const fields = [
{ field: 'account', type: STRING, name: '账号', placeholder: '请输入要查询的账号' },
{ field: 'name', type: STRING, name: '姓名', placeholder: '请输入要查询的姓名' },
] as QueryField[]
const xTable = ref<VxeTableInstance>()
const xToolbar = ref<VxeToolbarInstance>()
let userAdd = ref<any>()
let userEdit = ref<any>()
let userShow = ref<any>()
let userRoleAssign = ref<any>()
let userResetPwd = ref<any>()
onMounted(() => {
vxeBind()
queryPage()
})
function vxeBind() {
xTable.value?.connect(xToolbar.value as VxeToolbarInstance)
}
// 分页查询
function queryPage() {
loading.value = true
page({
...model.queryParam,
...pages,
}).then(({ data }) => {
pageQueryResHandel(data)
})
return Promise.resolve()
}
/**
* 选中全部
*/
function selectAllEvent() {
const records = xTable.value?.getCheckboxRecords()
batchOperateFlag.value = !!records?.length
}
/**
* 选中事件
*/
function selectChangeEvent() {
const records = xTable.value?.getCheckboxRecords()
batchOperateFlag.value = !!records?.length
}
// 分配角色
function assignRoles(record) {
userRoleAssign.value.init(false, record.id)
}
// 批量分配角色
function assignRolesBatch() {
const userIds = xTable.value?.getCheckboxRecords().map((o) => o.id)
userRoleAssign.value.init(true, userIds)
}
function add() {
userAdd.value.init()
}
/**
* 查看信息
*/
function show(record) {
userShow.value.init(record.id)
}
/**
* 信息编辑
*/
function edit(record) {
userEdit.value.init(record.id)
}
/**
* 重置密码
*/
function resetPwd(record) {
userResetPwd.value.init(false, record.id)
}
/**
* 重置密码
*/
function resetPwdBatch() {
const userIds = xTable.value?.getCheckboxRecords().map((o) => o.id)
userResetPwd.value.init(true, userIds)
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,184 @@
<template>
<basic-drawer
showFooter
title="添加用户"
v-bind="$attrs"
:width="modalWidth"
:open="visible"
:mask-closable="false"
@close="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="用户账号" name="account" validate-first>
<a-input v-model:value="form.account" placeholder="请输入用户账号" />
</a-form-item>
<a-form-item label="用户名称" name="name">
<a-input v-model:value="form.name" placeholder="请输入用户名称" />
</a-form-item>
<a-form-item label="登录密码" name="password" validate-first>
<strength-meter
v-model:value="form.password"
placeholder="请输入登录密码"
@change="validateToNextPassword"
/>
</a-form-item>
<a-form-item label="确认密码" name="confirmPassword" validate-first>
<a-input-password v-model:value="form.confirmPassword" placeholder="请重新输入登录密码" />
</a-form-item>
<a-form-item label="手机号" validate-first name="phone">
<a-input v-model:value="form.phone" placeholder="请输入用户手机号" />
</a-form-item>
<a-form-item label="邮箱" validate-first name="email">
<a-input v-model:value="form.email" placeholder="请输入用户邮箱" />
</a-form-item>
</a-form>
</a-spin>
<template #footer>
<a-space>
<a-button key="cancel" @click="handleCancel">取消</a-button>
<a-button key="forward" :loading="confirmLoading" type="primary" @click="handleOk"
>保存</a-button
>
</a-space>
</template>
</basic-drawer>
</template>
<script lang="ts" setup>
import { BasicDrawer } from '@/components/Drawer'
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { nextTick, reactive, ref } from 'vue'
import StrengthMeter from '/@/components/StrengthMeter/src/StrengthMeter.vue'
import { validateEmail, validateMobile } from '@/utils/validate'
import { existsAccount, existsEmail, existsPhone } from '@/api/sys/userAssist'
import { add } from './MerchantUser.api'
import { useMessage } from '@/hooks/web/useMessage'
import { UserInfo } from '@/views/iam/user/User.api'
const { handleCancel, labelCol, wrapperCol, modalWidth, confirmLoading, visible } = useFormEdit()
const { createMessage } = useMessage()
const formRef = ref<FormInstance>()
let form = ref<UserInfo>({
name: '',
account: '',
phone: '',
email: '',
password: '',
confirmPassword: '',
})
// 校验
const rules = reactive({
name: [{ required: true, message: '请输入用户名称' }],
account: [
{ required: true, message: '请输入用户账号' },
{ validator: checkAccount, trigger: 'blur' },
],
password: [{ required: true, message: '请输入登录密码!' }],
confirmPassword: [
{ required: true, message: '请重新输入登录密码!' },
{ validator: compareToFirstPassword },
],
phone: [{ validator: validatePhone }, { validator: validateBindPhone, trigger: 'blur' }],
email: [{ validator: validateEmailRule }, { validator: validateBindEmail, trigger: 'blur' }],
} as Record<string, Rule[]>)
let confirmDirty = ref(false)
// 事件
const emits = defineEmits(['ok'])
function init() {
visible.value = true
confirmLoading.value = false
nextTick(() => {
formRef.value?.resetFields()
})
}
function handleOk() {
formRef.value?.validate().then(async () => {
confirmLoading.value = true
await add(form.value).finally(() => (confirmLoading.value = false))
createMessage.success('添加用户成功')
confirmLoading.value = false
emits('ok')
handleCancel()
})
}
/**
* 用户名检查
*/
async function checkAccount() {
const { data } = await existsAccount(form.value.account)
return data ? Promise.reject('用户名已存在!') : Promise.resolve()
}
/**
* 密码检查
*/
function validateToNextPassword() {
if (confirmDirty.value) {
formRef.value?.validateFields(['confirmPassword'])
}
}
function compareToFirstPassword() {
if (form.value.confirmPassword !== form.value.password) {
confirmDirty.value = true
return Promise.reject('密码不一致')
} else {
return Promise.resolve()
}
}
/**
* 校验邮箱号
*/
function validateEmailRule() {
const { email } = form.value
if (!email) {
return Promise.resolve()
}
const { msg, result } = validateEmail(email)
return result ? Promise.resolve() : Promise.reject(msg)
}
/**
* 后台校验邮箱号是否被使用
*/
async function validateBindEmail() {
const { email } = form.value
const { data } = await existsEmail(email)
return data ? Promise.reject('邮箱已被使用') : Promise.resolve()
}
/**
* 校验手机号
*/
function validatePhone() {
const { phone } = form.value
if (!phone) {
return Promise.resolve()
}
const { msg, result } = validateMobile(phone)
return result ? Promise.resolve() : Promise.reject(msg)
}
/**
* 后台校验手机号是否被使用
*/
async function validateBindPhone() {
const { phone } = form.value
const { data } = await existsPhone(phone)
return data ? Promise.reject('手机号已被使用') : Promise.resolve()
}
defineExpose({ init })
</script>
<style scoped></style>

View File

@@ -0,0 +1,170 @@
<template>
<basic-drawer
showFooter
title="用户信息修改"
v-bind="$attrs"
:width="modalWidth"
:open="visible"
:mask-closable="false"
@ok="handleOk"
@close="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-form
class="small-from-item"
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="用户账号" validate-first name="account">
<a-input v-model:value="form.account" placeholder="请输入用户账号" />
</a-form-item>
<a-form-item label="用户名称" name="name">
<a-input v-model:value="form.name" placeholder="请输入用户名称" />
</a-form-item>
<a-form-item label="手机号" validate-first name="phone">
<a-input v-model:value="form.phone" placeholder="请输入用户手机号" />
</a-form-item>
<a-form-item label="邮箱" validate-first name="email">
<a-input v-model:value="form.email" placeholder="请输入用户邮箱" />
</a-form-item>
</a-form>
</a-spin>
</basic-drawer>
</template>
<script lang="ts" setup>
import { BasicDrawer } from '@/components/Drawer'
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { computed, nextTick, ref } from 'vue'
import { existsEmailNotId, existsPhoneNotId, existsAccountNotId } from '@/api/sys/userAssist'
import { validateEmail, validateMobile } from '@/utils/validate'
import { useMessage } from '@/hooks/web/useMessage'
import { get, update } from './MerchantUser.api'
import { UserInfo } from '@/views/iam/user/User.api'
const { handleCancel, diffForm, labelCol, wrapperCol, modalWidth, confirmLoading, visible } =
useFormEdit()
const { createMessage } = useMessage()
const formRef = ref<FormInstance>()
let form = ref<UserInfo>({
id: null,
name: '',
account: '',
phone: '',
email: '',
})
let rawForm: any = {}
// 校验
const rules = computed(() => {
return {
name: [{ required: true, message: '请输入名称' }],
account: [
{ required: true, message: '请输入账号' },
{ validator: checkAccount, trigger: 'blur' },
],
phone:
rawForm.phone !== form.value.phone
? [{ validator: validatePhone }, { validator: validateBindPhone, trigger: 'blur' }]
: [],
email:
rawForm.email !== form.value.email
? [{ validator: validateEmailRule }, { validator: validateBindEmail, trigger: 'blur' }]
: [],
} as Record<string, Rule[]>
})
// 事件
const emits = defineEmits(['ok'])
/**
* 入口
*/
function init(id) {
visible.value = true
confirmLoading.value = true
nextTick(() => {
formRef.value?.resetFields()
})
get(id).then(({ data }) => {
rawForm = { ...data }
form.value = data
confirmLoading.value = false
})
}
/**
* 更新
*/
function handleOk() {
formRef.value?.validate().then(async () => {
confirmLoading.value = true
await update({
...form.value,
...diffForm(form.value, rawForm, 'password'),
}).finally(() => (confirmLoading.value = false))
createMessage.success('用户更改成功')
confirmLoading.value = false
emits('ok')
handleCancel()
})
}
/**
* 用户名检查
*/
async function checkAccount() {
const { data } = await existsAccountNotId(form.value.account, form.value.id)
return data ? Promise.reject('用户名已存在!') : Promise.resolve()
}
/**
* 校验邮箱号
*/
function validateEmailRule() {
const { email } = form.value
if (!email) {
return Promise.resolve()
}
const { msg, result } = validateEmail(email)
return result ? Promise.resolve() : Promise.reject(msg)
}
/**
* 后台校验邮箱号是否被使用
*/
async function validateBindEmail() {
const { email, id } = form.value
if (rawForm.email === email) {
return Promise.resolve()
}
const { data } = await existsEmailNotId(email, id)
return data ? Promise.reject('邮箱已被使用') : Promise.resolve()
}
/**
* 校验手机号
*/
function validatePhone() {
const { phone } = form.value
if (!phone) {
return Promise.resolve()
}
const { msg, result } = validateMobile(phone)
return result ? Promise.resolve() : Promise.reject(msg)
}
/**
* 后台校验手机号是否被使用
*/
async function validateBindPhone() {
const { phone, id } = form.value
const { data } = await existsPhoneNotId(phone, id)
return data ? Promise.reject('手机号已被使用') : Promise.resolve()
}
defineExpose({ init })
</script>
<style scoped></style>

View File

@@ -0,0 +1,92 @@
<template>
<basic-modal
v-bind="$attrs"
title="重置密码"
:width="modalWidth"
:open="visible"
:confirmLoading="confirmLoading"
:maskClosable="false"
@cancel="handleCancel"
@ok="handleOk"
>
<a-spin :spinning="confirmLoading">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:validate-trigger="['blur', 'change']"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item name="newPassword" label="重置密码">
<a-input-password v-model:value="form.newPassword" placeholder="请输入密码" />
</a-form-item>
</a-form>
</a-spin>
</basic-modal>
</template>
<script setup lang="ts">
import BasicModal from '@/components/Modal/src/BasicModal.vue'
import useFormEdit from '@/hooks/bootx/useFormEdit'
import { restartPassword, restartPasswordBatch } from './MerchantUser.api'
import { useMessage } from '@/hooks/web/useMessage'
import { Rule } from 'ant-design-vue/lib/form'
import { ref } from 'vue'
const { handleCancel, labelCol, wrapperCol, modalWidth, confirmLoading, visible } = useFormEdit()
const { createConfirm, createMessage } = useMessage()
// 用户id
const userId = ref('')
// 用户ids
const userIds = ref<string[]>([])
const batch = ref(false)
let form = ref<any>({
newPassword: '',
})
let formRef = ref<any>()
const rules = {
newPassword: [{ required: true, message: '重置密码不得为空' }],
} as Record<string, Rule[]>
/**
* 初始化
*/
function init(b: boolean, userIdOrIds: string | string[]) {
visible.value = true
batch.value = b
if (!b) {
userId.value = userIdOrIds as string
} else {
userIds.value = userIdOrIds as never[]
}
}
function handleOk() {
formRef.value?.validate().then(async () => {
createConfirm({
iconType: 'info',
title: '警告',
content: '是否重置用户的密码',
onOk: async () => {
confirmLoading.value = true
if (batch.value) {
await restartPasswordBatch(userIds.value, form.value.newPassword).finally(
() => (confirmLoading.value = false),
)
} else {
await restartPassword(userId.value, form.value.newPassword).finally(
() => (confirmLoading.value = false),
)
}
createMessage.success('保存成功')
handleCancel()
},
})
})
}
defineExpose({ init })
</script>
<style scoped lang="less"></style>

View File

@@ -0,0 +1,269 @@
<template>
<basic-drawer
showFooter
v-bind="$attrs"
:title="modal.title"
width="40%"
:open="modal.visible"
@close="handleCancel"
>
<a-spin :spinning="modal.loading">
<a-input
style="margin-bottom: 8px"
placeholder="筛选"
allowClear
v-model:value="modal.searchName"
@change="search"
/>
<a-tree
:checkable="true"
:checkStrictly="true"
v-model:checkedKeys="modal.checkedKeys"
v-model:expandedKeys="modal.expandedKeys"
:auto-expand-parent="true"
:tree-data="modal.treeData"
@check="onCheck"
@expand="onExpand"
>
<template #title="{ title }">
<span v-if="title?.toLowerCase()?.indexOf(modal.searchName.toLowerCase()) > -1">
{{ searchRenderStart(title, modal.searchName) }}
<span style="color: #f50">
{{ searchRenderMiddle(title, modal.searchName) }}
</span>
{{ searchRenderEnd(title, modal.searchName) }}
</span>
<span v-else>{{ title }}</span>
</template>
</a-tree>
</a-spin>
<template #footer>
<a-dropdown style="margin-left: 5px" :trigger="['click']" placement="top">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="checkALL">全部勾选</a-menu-item>
<a-menu-item key="2" @click="cancelCheckALL">取消全选</a-menu-item>
<a-menu-item key="3" @click="expandAll">展开所有</a-menu-item>
<a-menu-item key="4" @click="closeAll">合并所有</a-menu-item>
</a-menu>
</template>
<a-button post-icon="ant-design:up-outlined"> 操作 </a-button>
</a-dropdown>
<a-button @click="modal.visible = false" style="margin-right: 0.8rem">取消</a-button>
<a-button
@click="handleSubmit()"
type="primary"
:loading="modal.loading"
style="margin-right: 0.8rem"
>保存</a-button
>
</template>
</basic-drawer>
</template>
<script setup lang="ts">
import { BasicDrawer } from '@/components/Drawer'
import { reactive } from 'vue'
import { Tree, treeDataTranslate } from '@/utils/dataUtil'
import XEUtils from 'xe-utils'
import { RoleTree, tree as roleTree } from '@/views/iam/role/Role.api'
import { useMessage } from '@/hooks/web/useMessage'
import { addUserRole, addUserRoleBatch, getRoleIds } from './MerchantUser.api'
const { createConfirm, createMessage } = useMessage()
const modal = reactive({
title: '角色分配',
visible: false,
batch: false,
loading: false,
searchName: '',
// 所有的key
allTreeKeys: [] as string[],
// 展开的key
expandedKeys: [] as string[],
// 被选中的key
checkedKeys: [] as string[],
// 后台获取的角色树
treeData: [] as Tree[],
// 转换后的树数据
treeList: [] as RoleTree[],
// 用户id
userId: '',
// 用户ids
userIds: [],
// 角色Ids
roleIds: [],
})
/**
* 初始化
*/
async function init(batch: boolean, userIdOrIds: string | string[]) {
modal.visible = true
modal.batch = batch
modal.loading = true
// 初始化角色树
await initRoles()
if (!batch) {
modal.userId = userIdOrIds as string
// 初始化分配角色
await initAssign()
} else {
modal.userIds = userIdOrIds as never[]
}
modal.loading = false
}
/**
* 初始化菜单
*/
async function initRoles() {
modal.searchName = ''
modal.expandedKeys = []
// 角色树
await roleTree().then((res) => {
modal.treeData = treeDataTranslate(res.data, 'id', 'name')
generateTreeList(res.data)
})
}
/**
* 初始化权限码分配信息
*/
async function initAssign() {
// 当前用户已经选择的角色Id
await getRoleIds(modal.userId).then((res) => {
modal.checkedKeys = res.data
})
// 选中的的key值
modal.allTreeKeys = modal.treeList.map((item) => item.id) as string[]
}
/**
* 保存
*/
function handleSubmit() {
createConfirm({
iconType: 'info',
title: '警告',
content: '是否要保存角色分配信息',
onOk: async () => {
modal.loading = true
if (modal.batch) {
await addUserRoleBatch({
userIds: modal.userIds,
roleIds: modal.checkedKeys,
})
} else {
await addUserRole({
userId: modal.userId,
roleIds: modal.checkedKeys,
})
}
createMessage.success('保存成功')
modal.loading = false
modal.visible = false
},
})
}
/**
* 取消
*/
function handleCancel() {
modal.visible = false
}
/**
* 树数据铺平
*/
function generateTreeList(treeData) {
if (!treeData) {
return
}
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i]
modal.treeList.push(node)
if (node.children) {
generateTreeList(node.children)
}
}
}
/**
* 搜索
*/
function search() {
const value = XEUtils.toValueString(modal.searchName).toLowerCase()
modal.expandedKeys = modal.treeList
.map((node) => {
if (
modal.searchName &&
node.pid &&
XEUtils.toValueString(node.name)?.toLowerCase()?.indexOf(value) > -1
) {
return node.pid
}
})
.filter((item, i, self) => item && self.indexOf(item) === i) as string[]
}
/**
* 渲染搜索项目数据开始段
*/
function searchRenderStart(title, searchName) {
return title.substring(0, title.toLowerCase().indexOf(searchName.toLowerCase()))
}
/**
* 渲染搜索项目数据中间段
*/
function searchRenderMiddle(title, searchName) {
return title.substring(
title.toLowerCase().indexOf(searchName.toLowerCase()),
title.toLowerCase().indexOf(searchName.toLowerCase()) + searchName.length,
)
}
/**
* 渲染搜索项目数据结束段
*/
function searchRenderEnd(title, searchName) {
return title.substring(
title.toLowerCase().indexOf(searchName.toLowerCase()) + searchName.length,
)
}
/**
* 展开/收起节点时触发
*/
function onExpand(keys) {
modal.expandedKeys = keys
}
/**
* 点击复选框触发
*/
function onCheck(key) {
modal.checkedKeys = key.checked
}
/**
* 展开全部
*/
function expandAll() {
modal.expandedKeys = modal.allTreeKeys
}
/**
* 合并全部
*/
function closeAll() {
modal.expandedKeys = []
}
/**
* 全选
*/
function checkALL() {
modal.checkedKeys = modal.allTreeKeys
}
/**
* 全不选
*/
function cancelCheckALL() {
modal.checkedKeys = []
}
defineExpose({ init })
</script>
<style scoped lang="less"></style>

View File

@@ -0,0 +1,85 @@
<template>
<basic-drawer
show-footer
title="用户信息"
v-bind="$attrs"
:width="modalWidth"
:open="visible"
:mask-closable="false"
@close="handleCancel"
>
<a-spin :spinning="confirmLoading">
<a-form
shio
class="small-from-item"
ref="formRef"
:model="form"
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="用户账号" name="account">
<a-input disabled v-model:value="form.account" />
</a-form-item>
<a-form-item label="用户名称" name="name">
<a-input disabled v-model:value="form.name" />
</a-form-item>
<a-form-item label="手机号" name="phone">
<a-input disabled v-model:value="form.phone" />
</a-form-item>
<a-form-item label="邮箱">
<a-input disabled v-model:value="form.email" />
</a-form-item>
<a-form-item label="管理员">
<a-tag v-if="form.administrator" color="green"></a-tag>
<a-tag v-else></a-tag>
</a-form-item>
<a-form-item label="用户状态">
<a-tag>{{ dictConvert('UserStatusCode', form.status) || '无' }}</a-tag>
</a-form-item>
<a-form-item label="角色列表">
<a-tag color="green" v-for="o in roles" :key="o.id">{{ o.name }}</a-tag>
</a-form-item>
</a-form>
</a-spin>
<template #footer>
<a-button key="cancel" @click="handleCancel">取消</a-button>
</template>
</basic-drawer>
</template>
<script lang="ts" setup>
import useFormEdit from '@/hooks/bootx/useFormEdit'
import BasicDrawer from '/@/components/Drawer/src/BasicDrawer.vue'
import { get, getRoles } from './MerchantUser.api'
import { useDict } from '@/hooks/bootx/useDict'
import { ref } from 'vue'
import { Role } from '@/views/iam/role/Role.api'
import { UserInfo } from '@/views/iam/user/User.api'
const { handleCancel, labelCol, wrapperCol, modalWidth, confirmLoading, visible } = useFormEdit()
const { dictConvert } = useDict()
let form = ref<UserInfo>({
name: '',
account: '',
})
let roles = ref<Role[]>([])
async function init(id) {
confirmLoading.value = true
visible.value = true
Promise.all([
get(id).then((res) => {
form.value = res.data
}),
getRoles(id).then((res) => {
roles.value = res.data
}),
]).then(() => (confirmLoading.value = false))
}
defineExpose({ init })
</script>
<style scoped></style>

View File

@@ -111,8 +111,8 @@
<UserAdd ref="userAdd" @ok="queryPage" />
<UserEdit ref="userEdit" @ok="queryPage" />
<UserShow ref="userShow" />
<UserRoleAssign ref="userRoleAssign" />
<UserResetPwd ref="userResetPwd" />
<UserRoleAssign ref="userRoleAssign" />
</div>
</div>
</template>
@@ -134,7 +134,7 @@
import UserResetPwd from './UserResetPwd.vue'
import UserRoleAssign from './role/UserRoleAssign.vue'
import ALink from '@/components/Link/Link.vue'
import { useFilePlatform } from "@/hooks/bootx/useFilePlatform";
import { useFilePlatform } from '@/hooks/bootx/useFilePlatform'
// 使用hooks
const {
@@ -149,7 +149,7 @@
} = useTablePage(queryPage)
const { createConfirm } = useMessage()
const { dictConvert } = useDict()
const {getFileUrl} = useFilePlatform()
const { getFileUrl } = useFilePlatform()
// 查询条件
const fields = [