将开源版本的修改打补丁应用到商业版
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="mdi:chevron-down"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
<div style="margin-left: 10px;margin-top: 5px">当前登录租户: <span class="tenant-name">{{loginTenantName}}</span> </div>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--角色用户表格-->
|
||||
<RoleUserTable @register="roleUserDrawer" :disableUserEdit="true"/>
|
||||
<!--角色编辑抽屉-->
|
||||
<RoleDrawer @register="registerDrawer" @success="reload" :showFooter="showFooter" />
|
||||
<!--角色详情-->
|
||||
<RoleDesc @register="registerDesc"></RoleDesc>
|
||||
</template>
|
||||
<script lang="ts" name="tenant-role-list" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import RoleDesc from './components/RoleDesc.vue';
|
||||
import RoleDrawer from './components/RoleDrawer.vue';
|
||||
import RoleUserTable from './components/RoleUserTable.vue';
|
||||
import { columns, searchFormSchema } from './role.data';
|
||||
import { listByTenant, deleteRole, batchDeleteRole, getExportUrl, getImportUrl } from './role.api';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { getLoginTenantName } from "/@/views/system/tenant/tenant.api";
|
||||
import { tenantSaasMessage } from "@/utils/common/compUtils";
|
||||
|
||||
const showFooter = ref(true);
|
||||
const [roleUserDrawer, { openDrawer: openRoleUserDrawer }] = useDrawer();
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
const [registerDesc, { openDrawer: openRoleDesc }] = useDrawer();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, onImportXls, onExportXls } = useListPage({
|
||||
designScope: 'role-template',
|
||||
tableProps: {
|
||||
title: '租户角色列表',
|
||||
api: listByTenant,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
},
|
||||
rowSelection: null,
|
||||
//自定义默认排序
|
||||
defSort: {
|
||||
column: 'id',
|
||||
order: 'desc',
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: '角色列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
});
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function handleCreate() {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function handleEdit(record: Recordable) {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
function handleDetail(record) {
|
||||
showFooter.value = false;
|
||||
openRoleDesc(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteRole({ id: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteRole({ ids: selectedRowKeys.value }, reload);
|
||||
}
|
||||
/**
|
||||
* 角色用户
|
||||
*/
|
||||
function handleUser(record) {
|
||||
//onSelectChange(selectedRowKeys)
|
||||
openRoleUserDrawer(true, record);
|
||||
}
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '用户',
|
||||
onClick: handleUser.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const loginTenantName = ref<string>('');
|
||||
|
||||
getTenantName();
|
||||
|
||||
async function getTenantName(){
|
||||
loginTenantName.value = await getLoginTenantName();
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
tenantSaasMessage('租户角色')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.tenant-name{
|
||||
text-decoration:underline;
|
||||
margin: 5px;
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="添加所属部门" width="400px" @ok="handleSubmit" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/src/components/Modal';
|
||||
import { BasicForm, useForm } from '/src/components/Form';
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const rowRecord = ref<any>(null);
|
||||
|
||||
const [registerForm, { validate, resetFields, updateSchema }] = useForm({
|
||||
labelWidth: 80,
|
||||
schemas: [
|
||||
{
|
||||
field: 'deptIds',
|
||||
label: '选择部门',
|
||||
component: 'Select',
|
||||
required: true,
|
||||
componentProps: {
|
||||
mode: 'multiple',
|
||||
options: [], // 初始为空,由父组件注入
|
||||
},
|
||||
},
|
||||
],
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
rowRecord.value = data.record;
|
||||
// ✅ 关键:适配你说的“预加载后作为props传入”
|
||||
if (data.deptOptions) {
|
||||
updateSchema({
|
||||
field: 'deptIds',
|
||||
componentProps: { options: data.deptOptions },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
console.log('当前选到的部门Id为', values.deptIds);
|
||||
console.log('接收到的来自table的record为', rowRecord.value);
|
||||
emit('success', {
|
||||
deptIds: values.deptIds,
|
||||
userId: rowRecord.value.userId,
|
||||
roleId: rowRecord.value.roleId,
|
||||
});
|
||||
// closeModal();
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="数据规则配置" width="450px" destroyOnClose>
|
||||
<a-tabs defaultActiveKey="1">
|
||||
<a-tab-pane tab="数据规则" key="1">
|
||||
<a-checkbox-group v-model:value="dataRuleChecked" v-if="dataRuleList.length > 0">
|
||||
<a-row>
|
||||
<a-col :span="24" v-for="(item, index) in dataRuleList" :key="'dr' + index">
|
||||
<a-checkbox :value="item.id">{{ item.ruleName }}</a-checkbox>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="24">
|
||||
<div style="width: 100%; margin-top: 15px">
|
||||
<a-button @click="saveDataRuleForRole" type="primary" size="small"> <Icon icon="ant-design:save-outlined"></Icon>点击保存</a-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-checkbox-group>
|
||||
<div v-else><h3>无配置信息!</h3></div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/src/components/Drawer';
|
||||
import { useMessage } from '/src/hooks/web/useMessage';
|
||||
import { queryDataRule, saveDataRule } from '../role.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const { createMessage } = useMessage();
|
||||
// 声明数据
|
||||
const functionId = ref('');
|
||||
const roleId = ref('');
|
||||
const dataRuleList = ref([]);
|
||||
const dataRuleChecked = ref([]);
|
||||
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
await reset();
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
//权限的id
|
||||
functionId.value = data.functionId;
|
||||
//角色的id
|
||||
roleId.value = data.roleId;
|
||||
//查询数据
|
||||
const res = await queryDataRule({ functionId: unref(functionId), roleId: unref(roleId) });
|
||||
if (res.success) {
|
||||
dataRuleList.value = res.result.datarule;
|
||||
if (res.result.drChecked) {
|
||||
dataRuleChecked.value = res.result.drChecked.split(',');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 重置
|
||||
*/
|
||||
function reset() {
|
||||
functionId.value = '';
|
||||
roleId.value = '';
|
||||
dataRuleList.value = [];
|
||||
dataRuleChecked.value = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
async function saveDataRuleForRole() {
|
||||
if (!unref(dataRuleChecked) || unref(dataRuleChecked).length == 0) {
|
||||
createMessage.warning('请注意,现未勾选任何数据权限!');
|
||||
}
|
||||
let params = {
|
||||
permissionId: unref(functionId),
|
||||
roleId: unref(roleId),
|
||||
dataRuleIds: unref(dataRuleChecked).join(','),
|
||||
};
|
||||
await saveDataRule(params);
|
||||
//关闭弹窗
|
||||
closeDrawer();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="角色详情" width="500px" destroyOnClose>
|
||||
<Description :column="1" :data="roleData" :schema="formDescSchema" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, useAttrs } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/src/components/Drawer';
|
||||
import { formDescSchema } from '../role.data';
|
||||
import { Description, useDescription } from '/@/components/Description/index';
|
||||
const emit = defineEmits(['register']);
|
||||
const attrs = useAttrs();
|
||||
const roleData = ref({});
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
roleData.value = data.record;
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" :title="getTitle" width="500px" @ok="handleSubmit" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref, useAttrs } from 'vue';
|
||||
import { BasicForm, useForm } from '/src/components/Form';
|
||||
import { BasicDrawer, useDrawerInner } from '/src/components/Drawer';
|
||||
import { BasicTree, TreeItem } from '/src/components/Tree';
|
||||
import { formSchema } from '../role.data';
|
||||
import { saveOrUpdateRole } from '../role.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const attrs = useAttrs();
|
||||
const isUpdate = ref(true);
|
||||
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
|
||||
labelWidth: 90,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
resetFields();
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
if (unref(isUpdate)) {
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
//禁用表单
|
||||
setProps({ disabled: !attrs.showFooter });
|
||||
});
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色'));
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setDrawerProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateRole(values, isUpdate.value);
|
||||
closeDrawer();
|
||||
emit('success');
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="首页配置" @ok="handleSubmit" width="40%">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { roleIndexFormSchema } from '../role.data';
|
||||
import { saveOrUpdateRoleIndex, queryIndexByCode } from '../role.api';
|
||||
// Emits声明
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
labelWidth: 150,
|
||||
schemas: roleIndexFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
setFieldsValue({ roleCode: data.roleCode });
|
||||
let res = await queryIndexByCode({ roleCode: data.roleCode });
|
||||
isUpdate.value = !!res.result?.id;
|
||||
if (unref(isUpdate)) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...res.result,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//表单提交事件
|
||||
async function handleSubmit(v) {
|
||||
try {
|
||||
let values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateRoleIndex(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success', { isUpdate: isUpdate.value, values });
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" width="650px" destroyOnClose showFooter>
|
||||
<template #title>
|
||||
角色权限配置
|
||||
<a-dropdown>
|
||||
<Icon icon="ant-design:more-outlined" class="more-icon" />
|
||||
<template #overlay>
|
||||
<a-menu @click="treeMenuClick">
|
||||
<a-menu-item key="checkAll">选择全部</a-menu-item>
|
||||
<a-menu-item key="cancelCheck">取消选择</a-menu-item>
|
||||
<div class="line"></div>
|
||||
<a-menu-item key="openAll">展开全部</a-menu-item>
|
||||
<a-menu-item key="closeAll">折叠全部</a-menu-item>
|
||||
<div class="line"></div>
|
||||
<a-menu-item key="relation">层级关联</a-menu-item>
|
||||
<a-menu-item key="standAlone">层级独立</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<BasicTree
|
||||
ref="treeRef"
|
||||
checkable
|
||||
:treeData="treeData"
|
||||
:checkedKeys="checkedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:selectedKeys="selectedKeys"
|
||||
:clickRowToExpand="false"
|
||||
:checkStrictly="true"
|
||||
title="所拥有的的权限"
|
||||
@check="onCheck"
|
||||
@select="onTreeNodeSelect"
|
||||
>
|
||||
<template #title="{ slotTitle, ruleFlag }">
|
||||
{{ slotTitle }}
|
||||
<Icon v-if="ruleFlag" icon="ant-design:align-left-outlined" style="margin-left: 5px; color: red"></Icon>
|
||||
</template>
|
||||
</BasicTree>
|
||||
<!--右下角按钮-->
|
||||
<template #footer>
|
||||
<!-- <PopConfirmButton title="确定放弃编辑?" @confirm="closeDrawer" okText="确定" cancelText="取消"></PopConfirmButton> -->
|
||||
<a-button @click="closeDrawer">取消</a-button>
|
||||
<a-button @click="handleSubmit(false)" type="primary" :loading="loading" ghost style="margin-right: 0.8rem">仅保存</a-button>
|
||||
<a-button @click="handleSubmit(true)" type="primary" :loading="loading">保存并关闭</a-button>
|
||||
</template>
|
||||
<RoleDataRuleDrawer @register="registerDrawer1" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref, onMounted } from 'vue';
|
||||
import { BasicDrawer, useDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { BasicTree, TreeItem } from '/@/components/Tree';
|
||||
import { PopConfirmButton } from '/@/components/Button';
|
||||
import RoleDataRuleDrawer from './RoleDataRuleDrawer.vue';
|
||||
import { queryTreeListForRole, queryRolePermission, saveRolePermission, queryScopeRolePermission } from '../role.api';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { ROLE_AUTH_CONFIG_KEY } from '/@/enums/cacheEnum';
|
||||
import {ROLE_SCOPE_CONFIG} from '../config/rolePermession'
|
||||
const emit = defineEmits(['register']);
|
||||
//树的信息
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
//树的全部节点信息
|
||||
const allTreeKeys = ref([]);
|
||||
//树的选择节点信息
|
||||
const checkedKeys = ref<any>([]);
|
||||
const defaultCheckedKeys = ref([]);
|
||||
//树的选中的节点信息
|
||||
const selectedKeys = ref([]);
|
||||
const roleId = ref('');
|
||||
//树的实例
|
||||
const treeRef = ref(null);
|
||||
const loading = ref(false);
|
||||
|
||||
//展开折叠的key
|
||||
const expandedKeys = ref<any>([]);
|
||||
//父子节点选中状态是否关联 true不关联,false关联
|
||||
const checkStrictly = ref<boolean>(false);
|
||||
const [registerDrawer1, { openDrawer: openDataRuleDrawer }] = useDrawer();
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
await reset();
|
||||
setDrawerProps({ confirmLoading: false, loading: true });
|
||||
roleId.value = data.roleId;
|
||||
//初始化数据
|
||||
const roleResult = await queryScopeRolePermission({ ids: ROLE_SCOPE_CONFIG.DEFAULT_PERMESSION_IDS });
|
||||
// update-begin--author:liaozhiyang---date:20240228---for:【QQYUN-8355】角色权限配置的菜单翻译
|
||||
treeData.value = translateTitle(roleResult.treeList);
|
||||
// update-end--author:liaozhiyang---date:20240228---for:【QQYUN-8355】角色权限配置的菜单翻译
|
||||
allTreeKeys.value = roleResult.ids;
|
||||
// update-begin--author:liaozhiyang---date:20240531---for:【TV360X-590】角色授权弹窗操作缓存
|
||||
const localData = localStorage.getItem(ROLE_AUTH_CONFIG_KEY);
|
||||
if (localData) {
|
||||
const obj = JSON.parse(localData);
|
||||
obj.level && treeMenuClick({ key: obj.level });
|
||||
obj.expand && treeMenuClick({ key: obj.expand });
|
||||
} else {
|
||||
expandedKeys.value = roleResult.ids;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240531---for:【TV360X-590】角色授权弹窗操作缓存
|
||||
//初始化角色菜单数据
|
||||
const permResult = await queryRolePermission({ roleId: unref(roleId) });
|
||||
checkedKeys.value = permResult;
|
||||
defaultCheckedKeys.value = permResult;
|
||||
setDrawerProps({ loading: false });
|
||||
});
|
||||
/**
|
||||
* 2024-02-28
|
||||
* liaozhiyang
|
||||
* 翻译菜单名称
|
||||
*/
|
||||
function translateTitle(data) {
|
||||
if (data?.length) {
|
||||
data.forEach((item) => {
|
||||
if (item.slotTitle) {
|
||||
const { t } = useI18n();
|
||||
if (item.slotTitle.includes("t('") && t) {
|
||||
item.slotTitle = new Function('t', `return ${item.slotTitle}`)(t);
|
||||
}
|
||||
}
|
||||
if (item.children?.length) {
|
||||
translateTitle(item.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* 点击选中
|
||||
* 2024-04-26
|
||||
* liaozhiyang
|
||||
*/
|
||||
function onCheck(o, e) {
|
||||
// checkStrictly: true=>层级独立,false=>层级关联.
|
||||
if (checkStrictly.value) {
|
||||
checkedKeys.value = o.checked ? o.checked : o;
|
||||
} else {
|
||||
const keys = getNodeAllKey(e.node, 'children', 'key');
|
||||
if (e.checked) {
|
||||
// 反复操作下可能会有重复的keys,得用new Set去重下
|
||||
checkedKeys.value = [...new Set([...checkedKeys.value, ...keys])];
|
||||
} else {
|
||||
const result = removeMatchingItems(checkedKeys.value, keys);
|
||||
checkedKeys.value = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 2024-04-26
|
||||
* liaozhiyang
|
||||
* 删除相匹配数组的项
|
||||
*/
|
||||
function removeMatchingItems(arr1, arr2) {
|
||||
// 使用哈希表记录 arr2 中的元素
|
||||
const hashTable = {};
|
||||
for (const item of arr2) {
|
||||
hashTable[item] = true;
|
||||
}
|
||||
// 使用 filter 方法遍历第一个数组,过滤出不在哈希表中存在的项
|
||||
return arr1.filter((item) => !hashTable[item]);
|
||||
}
|
||||
/**
|
||||
* 2024-04-26
|
||||
* liaozhiyang
|
||||
* 获取当前节点及以下所有子孙级的key
|
||||
*/
|
||||
function getNodeAllKey(node: any, children: any, key: string) {
|
||||
const result: any = [];
|
||||
result.push(node[key]);
|
||||
const recursion = (data) => {
|
||||
data.forEach((item: any) => {
|
||||
result.push(item[key]);
|
||||
if (item[children]?.length) {
|
||||
recursion(item[children]);
|
||||
}
|
||||
});
|
||||
};
|
||||
node[children]?.length && recursion(node[children]);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中节点,打开数据权限抽屉
|
||||
*/
|
||||
function onTreeNodeSelect(key) {
|
||||
if (key && key.length > 0) {
|
||||
selectedKeys.value = key;
|
||||
}
|
||||
openDataRuleDrawer(true, { functionId: unref(selectedKeys)[0], roleId: unref(roleId) });
|
||||
}
|
||||
/**
|
||||
* 数据重置
|
||||
*/
|
||||
function reset() {
|
||||
treeData.value = [];
|
||||
allTreeKeys.value = [];
|
||||
checkedKeys.value = [];
|
||||
defaultCheckedKeys.value = [];
|
||||
selectedKeys.value = [];
|
||||
roleId.value = '';
|
||||
}
|
||||
/**
|
||||
* 获取tree实例
|
||||
*/
|
||||
function getTree() {
|
||||
const tree = unref(treeRef);
|
||||
if (!tree) {
|
||||
throw new Error('tree is null!');
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
/**
|
||||
* 提交
|
||||
*/
|
||||
async function handleSubmit(exit) {
|
||||
let params = {
|
||||
roleId: unref(roleId),
|
||||
permissionIds: unref(getTree().getCheckedKeys()).join(','),
|
||||
lastpermissionIds: unref(defaultCheckedKeys).join(','),
|
||||
};
|
||||
//update-begin-author:taoyan date:2023-2-11 for: issues/352 VUE角色授权重复保存
|
||||
if (loading.value === false) {
|
||||
await doSave(params);
|
||||
} else {
|
||||
console.log('请等待上次执行完毕!');
|
||||
}
|
||||
if (exit) {
|
||||
// 如果关闭
|
||||
closeDrawer();
|
||||
} else {
|
||||
// 没有关闭需要重新获取选中数据
|
||||
const permResult = await queryRolePermission({ roleId: unref(roleId) });
|
||||
defaultCheckedKeys.value = permResult;
|
||||
}
|
||||
}
|
||||
|
||||
// VUE角色授权重复保存 #352
|
||||
async function doSave(params) {
|
||||
loading.value = true;
|
||||
try {
|
||||
await saveRolePermission(params);
|
||||
} catch (e) {
|
||||
loading.value = false;
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
}
|
||||
//update-end-author:taoyan date:2023-2-11 for: issues/352 VUE角色授权重复保存
|
||||
|
||||
/**
|
||||
* 树菜单选择
|
||||
* @param key
|
||||
*/
|
||||
function treeMenuClick({ key }) {
|
||||
if (key === 'checkAll') {
|
||||
checkedKeys.value = allTreeKeys.value;
|
||||
} else if (key === 'cancelCheck') {
|
||||
checkedKeys.value = [];
|
||||
} else if (key === 'openAll') {
|
||||
expandedKeys.value = allTreeKeys.value;
|
||||
saveLocalOperation('expand', 'openAll');
|
||||
} else if (key === 'closeAll') {
|
||||
expandedKeys.value = [];
|
||||
saveLocalOperation('expand', 'closeAll');
|
||||
} else if (key === 'relation') {
|
||||
checkStrictly.value = false;
|
||||
saveLocalOperation('level', 'relation');
|
||||
} else {
|
||||
checkStrictly.value = true;
|
||||
saveLocalOperation('level', 'standAlone');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 2024-05-31
|
||||
* liaozhiyang
|
||||
* 【TV360X-590】角色授权弹窗操作缓存
|
||||
* */
|
||||
const saveLocalOperation = (key, value) => {
|
||||
const localData = localStorage.getItem(ROLE_AUTH_CONFIG_KEY);
|
||||
const obj = localData ? JSON.parse(localData) : {};
|
||||
obj[key] = value;
|
||||
localStorage.setItem(ROLE_AUTH_CONFIG_KEY, JSON.stringify(obj));
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
/** 固定操作按钮 */
|
||||
.jeecg-basic-tree {
|
||||
position: absolute;
|
||||
width: 618px;
|
||||
}
|
||||
//update-begin---author:wangshuai ---date:20230202 for:抽屉弹窗标题图标下拉样式------------
|
||||
.line {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.more-icon {
|
||||
font-size: 20px !important;
|
||||
color: black;
|
||||
display: inline-flex;
|
||||
float: right;
|
||||
margin-right: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
:deep(.jeecg-tree-header) {
|
||||
border-bottom: none;
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20230202 for:抽屉弹窗标题图标下拉样式------------
|
||||
</style>
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<BasicDrawer @register="registerBaseDrawer" title="角色用户" width="800" destroyOnClose>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<template #tableTitle>
|
||||
<!-- <a-button type="primary" @click="handleCreate" v-if="!disableUserEdit"> 新增用户</a-button>-->
|
||||
<a-button type="primary" @click="handleSelect"> 已有用户</a-button>
|
||||
|
||||
<a-dropdown v-if="checkedKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="bx:bx-unlink"></Icon>
|
||||
取消关联
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--用户操作抽屉-->
|
||||
<UserDrawer @register="registerDrawer" @success="handleSuccess" />
|
||||
<!--用户选择弹窗-->
|
||||
<UseSelectModal @register="registerModal" @select="selectOk" />
|
||||
<DeptSelectModal @register="registerDeptModal" @success="handleDeptSuccess" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, unref } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '/src/components/Table';
|
||||
import { BasicDrawer, useDrawer, useDrawerInner } from '/src/components/Drawer';
|
||||
import { useModal } from '/src/components/Modal';
|
||||
import UserDrawer from '@/views/system/user/UserDrawer.vue';
|
||||
import UseSelectModal from './UseSelectModal.vue';
|
||||
import { userList, userListWithDepts, deleteUserRole, batchDeleteUserRole, addUserRole, addDepts, userDepts, saveUserDeptsApi } from '../role.api';
|
||||
import DeptSelectModal from './DeptSelectModal.vue'; // 引入新组件
|
||||
import { userColumns, searchUserFormSchema } from '../role.data';
|
||||
import { getUserRoles } from '@/views/system/user/user.api';
|
||||
import { options } from 'axios';
|
||||
|
||||
const emit = defineEmits(['register', 'hideUserList']);
|
||||
const props = defineProps({
|
||||
disableUserEdit: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const roleId = ref('');
|
||||
const [registerBaseDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
roleId.value = data.id;
|
||||
setProps({ searchInfo: { roleId: data.id } });
|
||||
reload();
|
||||
});
|
||||
//注册drawer
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
//注册drawer
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
// 2. 注册第二个 Modal (部门选择)
|
||||
const [registerDeptModal, { openModal: openDeptModal, closeModal: closeDeptModal }] = useModal();
|
||||
const [registerTable, { reload, updateTableDataRecord, setProps }] = useTable({
|
||||
title: '用户列表',
|
||||
api: userListWithDepts,
|
||||
columns: userColumns,
|
||||
formConfig: {
|
||||
//update-begin---author:wangshuai ---date:20230703 for:【QQYUN-5685】3、租户角色下,查询居左显示
|
||||
labelWidth: 60,
|
||||
//update-end---author:wangshuai ---date:20230703 for:【QQYUN-5685】3、租户角色下,查询居左显示
|
||||
schemas: searchUserFormSchema,
|
||||
autoSubmitOnEnter: true,
|
||||
},
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
clickToRowSelect: false,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
// 【issues/1064】列设置的 cacheKey
|
||||
tableSetting: { fullScreen: true, cacheKey: 'role_user_table' },
|
||||
canResize: false,
|
||||
rowKey: 'id',
|
||||
actionColumn: {
|
||||
width: 180,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 选择列配置
|
||||
*/
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
columnWidth: 50,
|
||||
selectedRowKeys: checkedKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
|
||||
/**
|
||||
* 选择事件
|
||||
*/
|
||||
function onSelectChange(selectedRowKeys: (string | number)[], selectionRows) {
|
||||
checkedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function handleCreate() {
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
selectedroles: [roleId.value],
|
||||
isRole: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
async function handleEdit(record: Recordable) {
|
||||
try {
|
||||
const userRoles = await getUserRoles({ userid: record.id });
|
||||
if (userRoles && userRoles.length > 0) {
|
||||
record.selectedroles = userRoles;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
isRole: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteUserRole({ userId: record.id, roleId: roleId.value }, reload);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteUserRole({ userIds: checkedKeys.value.join(','), roleId: roleId.value }, () => {
|
||||
// update-begin--author:liaozhiyang---date:20240701---for:【TV360X-1655】批量取消关联之后清空选中记录
|
||||
reload();
|
||||
checkedKeys.value = [];
|
||||
// update-end--author:liaozhiyang---date:20240701---for:【TV360X-1655】批量取消关联之后清空选中记录
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess({ isUpdate, values }) {
|
||||
isUpdate ? updateTableDataRecord(values.id, values) : reload();
|
||||
}
|
||||
/**
|
||||
* 选择已有用户
|
||||
*/
|
||||
function handleSelect() {
|
||||
openModal(true);
|
||||
}
|
||||
/**
|
||||
* 添加所属部门按钮点击
|
||||
*/
|
||||
async function handleAddDepts(record: Recordable) {
|
||||
// 1. 在打开前预加载 options,确保渲染时数据已存在
|
||||
// 这里可以根据 record.id 或 roleId 灵活传参
|
||||
console.log('record为', record);
|
||||
// const rId = unref(roleId) ?? '';
|
||||
const userName = record?.username ?? '';
|
||||
|
||||
// 然后进行业务判定
|
||||
// if (!rId || !userName) {
|
||||
// console.warn('参数不完整,停止请求');
|
||||
// return;
|
||||
// }
|
||||
|
||||
try {
|
||||
const res = await userDepts({ userName: userName });
|
||||
console.log('加载出来的res', res);
|
||||
// 2. 格式化为 Select 需要的结构
|
||||
const options = (res.records || res || []).map((item) => ({
|
||||
label: item.departName,
|
||||
value: item.id,
|
||||
}));
|
||||
// 请求成功才打开弹窗
|
||||
if (options && options.length > 0) {
|
||||
// 3. 打开新 Modal 并传入数据
|
||||
openDeptModal(true, {
|
||||
record: record,
|
||||
deptOptions: options,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// 请求失败,给用户一个提示
|
||||
console.error('加载部门数据失败,请稍后再试');
|
||||
console.error('接口报错详情:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 添加已有用户
|
||||
*/
|
||||
async function selectOk(val) {
|
||||
await addUserRole({ roleId: roleId.value, userIdList: val }, reload);
|
||||
}
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
// {
|
||||
// label: '编辑',
|
||||
// onClick: handleEdit.bind(null, record),
|
||||
// ifShow: () => !props.disableUserEdit,
|
||||
// },
|
||||
{
|
||||
label: '添加所属部门',
|
||||
onClick: handleAddDepts.bind(null, record),
|
||||
ifShow: () => !props.disableUserEdit,
|
||||
},
|
||||
{
|
||||
label: '取消关联',
|
||||
popConfirm: {
|
||||
title: '是否确认取消关联',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
// 响应子组件的 emit('success', ...)
|
||||
async function handleDeptSuccess(data) {
|
||||
try {
|
||||
console.log('提交的data为', data);
|
||||
data.roleId = roleId.value;
|
||||
|
||||
// 1. 调用后端接口
|
||||
await saveUserDeptsApi(data);
|
||||
|
||||
// 3. 刷新表格数据
|
||||
reload();
|
||||
|
||||
// 4. 关键:由父组件决定什么时候关闭弹窗
|
||||
closeDeptModal();
|
||||
} catch (error) {
|
||||
// 如果接口报错,由于没有执行 closeModal,弹窗会保持开启,
|
||||
// 子组件的 setModalProps({ confirmLoading: false }) 也会停止转圈,允许用户重试
|
||||
console.error('保存失败:', error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/*update-begin---author:wangshuai ---date:20230703 for:【QQYUN-5685】3、租户角色下,查询居左显示*/
|
||||
:deep(.ant-form-item-control-input-content) {
|
||||
text-align: left;
|
||||
}
|
||||
/*update-end---author:wangshuai ---date:20230703 for:【QQYUN-5685】3、租户角色下,查询居左显示*/
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
title="用户选择列表"
|
||||
width="1000px"
|
||||
@ok="handleSubmit"
|
||||
destroyOnClose
|
||||
@openChange="handleOpenChange"
|
||||
>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref, toRaw } from 'vue';
|
||||
import { BasicModal, useModal, useModalInner } from '/src/components/Modal';
|
||||
import { BasicTable, useTable, TableAction } from '/src/components/Table';
|
||||
import { userColumnsWithoutDept, searchUserFormSchema } from '../role.data';
|
||||
import { listNoCareTenant } from '../../three-admin-user/user.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['select', 'register']);
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner();
|
||||
//注册table数据
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: listNoCareTenant,
|
||||
rowKey: 'id',
|
||||
columns: userColumnsWithoutDept,
|
||||
formConfig: {
|
||||
labelWidth: 60,
|
||||
schemas: searchUserFormSchema,
|
||||
baseRowStyle: { maxHeight: '20px' },
|
||||
autoSubmitOnEnter: true,
|
||||
},
|
||||
striped: true,
|
||||
useSearchForm: true,
|
||||
showTableSetting: false,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
canResize: false,
|
||||
});
|
||||
const [registerDeptModal, { openModal: openDeptModal }] = useModal(); // 注册新 Modal
|
||||
/**
|
||||
* 选择列配置
|
||||
*/
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
columnWidth: 50,
|
||||
selectedRowKeys: checkedKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
/**
|
||||
* 选择事件
|
||||
*/
|
||||
function onSelectChange(selectedRowKeys: (string | number)[]) {
|
||||
checkedKeys.value = selectedRowKeys;
|
||||
}
|
||||
|
||||
const handleOpenChange = (visible) => {
|
||||
// -update-begin--author:liaozhiyang---date:20240702---for:【TV360X-1679】系统角色-角色用户再次打开弹窗重置之前选中的状态
|
||||
if (visible) {
|
||||
checkedKeys.value = [];
|
||||
}
|
||||
// -update-end--author:liaozhiyang---date:20240702---for:【TV360X-1679】系统角色-角色用户再次打开弹窗重置之前选中的状态
|
||||
};
|
||||
|
||||
//提交事件
|
||||
function handleSubmit() {
|
||||
setModalProps({ confirmLoading: true });
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('select', toRaw(unref(checkedKeys)));
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
export const ROLE_SCOPE_CONFIG = {
|
||||
DEFAULT_PERMESSION_IDS: '2035916880060223489',
|
||||
};
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="mdi:chevron-down"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--角色用户表格-->
|
||||
<RoleUserTable @register="roleUserDrawer" />
|
||||
<!--角色编辑抽屉-->
|
||||
<RoleDrawer @register="registerDrawer" @success="reload" :showFooter="showFooter" />
|
||||
<!--角色详情-->
|
||||
<RoleDesc @register="registerDesc"></RoleDesc>
|
||||
<!--角色菜单授权抽屉-->
|
||||
<RolePermissionDrawer @register="rolePermissionDrawer" />
|
||||
<!--角色首页配置-->
|
||||
<RoleIndexModal @register="registerIndexModal" />
|
||||
</template>
|
||||
<script lang="ts" name="system-role" setup>
|
||||
import { ref } from 'vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import RoleDrawer from './components/RoleDrawer.vue';
|
||||
import RoleDesc from './components/RoleDesc.vue';
|
||||
import RolePermissionDrawer from './components/RolePermissionDrawer.vue';
|
||||
import RoleIndexModal from './components/RoleIndexModal.vue';
|
||||
import RoleUserTable from './components/RoleUserTable.vue';
|
||||
import { columns, searchFormSchema } from './role.data';
|
||||
import { list, deleteRole, batchDeleteRole, getExportUrl, getImportUrl } from './role.api';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
const showFooter = ref(true);
|
||||
const [roleUserDrawer, { openDrawer: openRoleUserDrawer }] = useDrawer();
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
const [registerIndexModal, { openModal: openIndexModal }] = useModal();
|
||||
const [rolePermissionDrawer, { openDrawer: openRolePermissionDrawer }] = useDrawer();
|
||||
const [registerDesc, { openDrawer: openRoleDesc }] = useDrawer();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, onImportXls, onExportXls } = useListPage({
|
||||
designScope: 'role-template',
|
||||
tableProps: {
|
||||
title: '系统角色列表',
|
||||
api: list,
|
||||
columns: columns,
|
||||
formConfig: {
|
||||
// update-begin--author:liaozhiyang---date:20230803---for:【QQYUN-5873】查询区域lablel默认居左
|
||||
labelWidth:65,
|
||||
rowProps: { gutter: 24 },
|
||||
// update-end--author:liaozhiyang---date:20230803---for:【QQYUN-5873】查询区域lablel默认居左
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
},
|
||||
rowSelection: null,
|
||||
//自定义默认排序
|
||||
defSort: {
|
||||
column: 'id',
|
||||
order: 'desc',
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: '角色列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
});
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function handleCreate() {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function handleEdit(record: Recordable) {
|
||||
showFooter.value = true;
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
function handleDetail(record) {
|
||||
showFooter.value = false;
|
||||
openRoleDesc(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteRole({ id: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDeleteRole({ ids: selectedRowKeys.value }, reload);
|
||||
}
|
||||
/**
|
||||
* 角色授权弹窗
|
||||
*/
|
||||
function handlePerssion(record) {
|
||||
openRolePermissionDrawer(true, { roleId: record.id });
|
||||
}
|
||||
/**
|
||||
* 首页配置弹窗
|
||||
*/
|
||||
function handleIndexConfig(roleCode) {
|
||||
openIndexModal(true, { roleCode });
|
||||
}
|
||||
/**
|
||||
* 角色用户
|
||||
*/
|
||||
function handleUser(record) {
|
||||
//onSelectChange(selectedRowKeys)
|
||||
openRoleUserDrawer(true, record);
|
||||
}
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '用户',
|
||||
onClick: handleUser.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '授权',
|
||||
onClick: handlePerssion.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '首页配置',
|
||||
// onClick: handleIndexConfig.bind(null, record.roleCode),
|
||||
// },
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,220 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/role/list',
|
||||
listByTenant = '/sys/role/listByTenant',
|
||||
save = '/sys/role/add',
|
||||
edit = '/sys/role/edit',
|
||||
deleteRole = '/sys/role/delete',
|
||||
deleteBatch = '/sys/role/deleteBatch',
|
||||
exportXls = '/sys/role/exportXls',
|
||||
importExcel = '/sys/role/importExcel',
|
||||
isRoleExist = '/sys/role/checkRoleCode',
|
||||
queryTreeListForRole = '/sys/role/queryTreeList',
|
||||
queryRolePermission = '/sys/permission/queryRolePermission',
|
||||
queryScopeRolePermission = '/sys/role/queryTreeListByIds',
|
||||
saveRolePermission = '/sys/permission/saveRolePermission',
|
||||
saveDeptForUserRole = '/sys/permission/saveDeptForUserRole',
|
||||
queryUserDeptsList = '/sys/sysDepart/queryAffiliatedDepts',
|
||||
queryDataRule = '/sys/role/datarule',
|
||||
getParentDesignList = '/act/process/extActDesignFlowData/getDesFormFlows',
|
||||
getRoleDegisnList = '/joa/designform/designFormCommuse/getRoleDegisnList',
|
||||
saveRoleDesign = '/joa/designform/designFormCommuse/sysRoleDesignAdd',
|
||||
userList = '/sys/user/userRoleList',
|
||||
userListWithDepts = '/sys/user/userRoleListWithDepts',
|
||||
saveDeptForRoleUser = '/sys/role/saveDeptsForUser',
|
||||
deleteUserRole = '/sys/user/deleteUserRole',
|
||||
batchDeleteUserRole = '/sys/user/deleteUserRoleBatch',
|
||||
addUserRole = '/sys/user/addSysUserRole',
|
||||
saveRoleIndex = '/sys/sysRoleIndex/add',
|
||||
editRoleIndex = '/sys/sysRoleIndex/edit',
|
||||
queryIndexByCode = '/sys/sysRoleIndex/queryByCode',
|
||||
}
|
||||
/**
|
||||
* 导出api
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 系统角色列表
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
/**
|
||||
* 租户角色列表
|
||||
* @param params
|
||||
*/
|
||||
export const listByTenant = (params) => defHttp.get({ url: Api.listByTenant, params });
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
*/
|
||||
export const deleteRole = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteRole, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除角色
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteRole = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新角色
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateRole = (params, isUpdate) => {
|
||||
let url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 编码校验
|
||||
* @param params
|
||||
*/
|
||||
// update-begin--author:liaozhiyang---date:20231215---for:【QQYUN-7415】表单调用接口进行校验的添加防抖
|
||||
let timer;
|
||||
export const isRoleExist = (params) => {
|
||||
return new Promise((resolve, rejected) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
defHttp
|
||||
.get({ url: Api.isRoleExist, params }, { isTransformResponse: false })
|
||||
.then((res) => {
|
||||
resolve(res);
|
||||
})
|
||||
.catch((error) => {
|
||||
rejected(error);
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
// update-end--author:liaozhiyang---date:20231215---for:【QQYUN-7415】表单调用接口进行校验的添加防抖
|
||||
/**
|
||||
* 根据角色查询树信息
|
||||
*/
|
||||
export const queryTreeListForRole = () => defHttp.get({ url: Api.queryTreeListForRole });
|
||||
/**
|
||||
* 查询角色权限
|
||||
*/
|
||||
export const queryRolePermission = (params) => defHttp.get({ url: Api.queryRolePermission, params });
|
||||
/**
|
||||
* 查询特定权限下的子孙权限
|
||||
*/
|
||||
export const queryScopeRolePermission = (params) => defHttp.get({ url: Api.queryScopeRolePermission, params });
|
||||
|
||||
/**
|
||||
* 保存角色权限
|
||||
*/
|
||||
export const saveRolePermission = (params) => defHttp.post({ url: Api.saveRolePermission, params });
|
||||
/**
|
||||
* 查询角色数据规则
|
||||
*/
|
||||
export const queryDataRule = (params) =>
|
||||
defHttp.get({ url: `${Api.queryDataRule}/${params.functionId}/${params.roleId}` }, { isTransformResponse: false });
|
||||
/**
|
||||
* 保存角色数据规则
|
||||
*/
|
||||
export const saveDataRule = (params) => defHttp.post({ url: Api.queryDataRule, params });
|
||||
/**
|
||||
* 获取表单数据
|
||||
* @return List<Map>
|
||||
*/
|
||||
export const getParentDesignList = () => defHttp.get({ url: Api.getParentDesignList });
|
||||
/**
|
||||
* 获取角色表单数据
|
||||
* @return List<Map>
|
||||
*/
|
||||
export const getRoleDegisnList = (params) => defHttp.get({ url: Api.getRoleDegisnList, params });
|
||||
/**
|
||||
* 提交角色工单信息
|
||||
*/
|
||||
export const saveRoleDesign = (params) => defHttp.post({ url: Api.saveRoleDesign, params });
|
||||
/**
|
||||
* 角色列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const userList = (params) => defHttp.get({ url: Api.userList, params });
|
||||
/**
|
||||
* 角色列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const userListWithDepts = (params) => defHttp.get({ url: Api.userListWithDepts, params });
|
||||
/**
|
||||
* 保存用户角色的应用部门
|
||||
*/
|
||||
export const saveUserDeptsApi = (params) => defHttp.post({ url: Api.saveDeptForRoleUser, params });
|
||||
/**
|
||||
* 获取用户所在全部部门接口
|
||||
* @param params
|
||||
*/
|
||||
export const userDepts = (params) => defHttp.get({ url: Api.queryUserDeptsList, params });
|
||||
/**
|
||||
* 删除角色用户
|
||||
*/
|
||||
export const deleteUserRole = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteUserRole, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除角色用户
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteUserRole = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.batchDeleteUserRole, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 添加已有用户
|
||||
*/
|
||||
export const addUserRole = (params, handleSuccess) => {
|
||||
return defHttp.post({ url: Api.addUserRole, params }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新
|
||||
* @param params
|
||||
* @param isUpdate 是否是更新数据
|
||||
*/
|
||||
export const saveOrUpdateRoleIndex = (params, isUpdate) => {
|
||||
let url = isUpdate ? Api.editRoleIndex : Api.saveRoleIndex;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 为用户的角色指定应用部门
|
||||
* @param params
|
||||
*/
|
||||
export const addDepts = (params) => {
|
||||
return defHttp.post({ url: Api.saveDeptForUserRole, params });
|
||||
};
|
||||
/**
|
||||
* 根据code查询首页配置
|
||||
* @param params
|
||||
*/
|
||||
export const queryIndexByCode = (params) => defHttp.get({ url: Api.queryIndexByCode, params }, { isTransformResponse: false });
|
||||
@@ -0,0 +1,227 @@
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { isRoleExist } from './role.api';
|
||||
import { JDictSelectTag, JSelectDept } from '@/components/Form';
|
||||
import { h } from 'vue';
|
||||
export const columns = [
|
||||
{
|
||||
title: '角色名称',
|
||||
dataIndex: 'roleName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '角色编码',
|
||||
dataIndex: 'roleCode',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
/**
|
||||
* 角色用户Columns
|
||||
*/
|
||||
export const userColumns = [
|
||||
{
|
||||
title: '用户id',
|
||||
dataIndex: 'userId',
|
||||
},
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
dataIndex: 'realname',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status_dictText',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '所属部门',
|
||||
dataIndex: 'deptId', // 确保这是你后端返回的逗号分隔字符串,如 "1,2,3"
|
||||
width: 250,
|
||||
ellipsis: true,
|
||||
customRender: ({ text }) => {
|
||||
// 如果没有数据,直接返回空
|
||||
if (!text) return '';
|
||||
|
||||
// 使用 h 函数渲染部门选择组件
|
||||
return h(JSelectDept, {
|
||||
value: text, // 传入 "1,2,3"
|
||||
disabled: true, // 必须禁用,否则用户在表格里能点开选
|
||||
rowKey: 'id', // 对应数据库主键
|
||||
checkStrictly: true, // 父子不互相关联
|
||||
multi: true, // 开启多选支持,处理逗号分隔
|
||||
// 如果你希望显示成简单的文本而不是输入框样式,可以根据组件支持情况调整
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const userColumnsWithoutDept = [
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
dataIndex: 'realname',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status_dictText',
|
||||
width: 80,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '角色名称',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
field: 'roleCode',
|
||||
label: '角色编码',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
/**
|
||||
* 角色用户搜索form
|
||||
*/
|
||||
export const searchUserFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'username',
|
||||
label: '用户账号',
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
labelWidth: 74,
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'id',
|
||||
label: '',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '角色名称',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
field: 'roleCode',
|
||||
label: '角色编码',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
dynamicRules: ({ values, model }) => {
|
||||
console.log('values:', values);
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请输入角色编码');
|
||||
}
|
||||
if (values) {
|
||||
return new Promise((resolve, reject) => {
|
||||
isRoleExist({ id: model.id, roleCode: value })
|
||||
.then((res) => {
|
||||
res.success ? resolve() : reject(res.message || '校验失败');
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err.message || '验证失败');
|
||||
});
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'description',
|
||||
component: 'InputTextArea',
|
||||
},
|
||||
];
|
||||
|
||||
export const formDescSchema = [
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '角色名称',
|
||||
},
|
||||
{
|
||||
field: 'roleCode',
|
||||
label: '角色编码',
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
field: 'description',
|
||||
},
|
||||
];
|
||||
|
||||
export const roleIndexFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'id',
|
||||
label: '',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '角色编码',
|
||||
field: 'roleCode',
|
||||
component: 'Input',
|
||||
dynamicDisabled: true,
|
||||
},
|
||||
{
|
||||
label: '首页路由',
|
||||
field: 'url',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
helpMessage: '首页路由的访问地址',
|
||||
},
|
||||
{
|
||||
label: '组件地址',
|
||||
field: 'component',
|
||||
component: 'Input',
|
||||
helpMessage: '首页路由的组件地址',
|
||||
componentProps: {
|
||||
placeholder: '请输入前端组件',
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
field: 'route',
|
||||
label: '是否路由菜单',
|
||||
helpMessage: '非路由菜单设置成首页,需开启',
|
||||
component: 'Switch',
|
||||
defaultValue: true,
|
||||
},
|
||||
{
|
||||
label: '优先级',
|
||||
field: 'priority',
|
||||
component: 'InputNumber',
|
||||
},
|
||||
{
|
||||
label: '是否开启',
|
||||
field: 'status',
|
||||
component: 'JSwitch',
|
||||
componentProps: {
|
||||
options: ['1', '0'],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<BasicDrawer title="数据规则/按钮权限配置" :width="365" @close="onClose" @register="registerDrawer">
|
||||
<a-spin :spinning="loading">
|
||||
<a-tabs defaultActiveKey="1">
|
||||
<a-tab-pane tab="数据规则" key="1">
|
||||
<a-checkbox-group v-model:value="dataRuleChecked" v-if="dataRuleList.length > 0">
|
||||
<a-row>
|
||||
<a-col :span="24" v-for="(item, index) in dataRuleList" :key="'dr' + index">
|
||||
<a-checkbox :value="item.id">{{ item.ruleName }}</a-checkbox>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<div style="width: 100%; margin-top: 15px">
|
||||
<a-button type="primary" :loading="loading" :size="'small'" preIcon="ant-design:save-filled" @click="saveDataRuleForRole">
|
||||
<span>点击保存</span>
|
||||
</a-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-checkbox-group>
|
||||
<a-empty v-else description="无配置信息" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
|
||||
import { queryDepartDataRule, saveDepartDataRule } from '../depart.api';
|
||||
|
||||
defineEmits(['register']);
|
||||
const loading = ref<boolean>(false);
|
||||
const departId = ref('');
|
||||
const functionId = ref('');
|
||||
const dataRuleList = ref<Array<any>>([]);
|
||||
const dataRuleChecked = ref<Array<any>>([]);
|
||||
|
||||
// 注册抽屉组件
|
||||
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
|
||||
departId.value = unref(data.departId);
|
||||
functionId.value = unref(data.functionId);
|
||||
loadData();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { datarule, drChecked } = await queryDepartDataRule(functionId, departId);
|
||||
dataRuleList.value = datarule;
|
||||
if (drChecked) {
|
||||
dataRuleChecked.value = drChecked.split(',');
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function saveDataRuleForRole() {
|
||||
let params = {
|
||||
departId: departId.value,
|
||||
permissionId: functionId.value,
|
||||
dataRuleIds: dataRuleChecked.value.join(','),
|
||||
};
|
||||
saveDepartDataRule(params);
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
doReset();
|
||||
}
|
||||
|
||||
function doReset() {
|
||||
functionId.value = '';
|
||||
dataRuleList.value = [];
|
||||
dataRuleChecked.value = [];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<BasicModal :title="title" :width="800" v-bind="$attrs" @ok="handleOk" @register="registerModal">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
|
||||
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
|
||||
import { saveOrUpdateDepart } from '../depart.api';
|
||||
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const props = defineProps({
|
||||
rootTreeData: { type: Array, default: () => [] },
|
||||
});
|
||||
const prefixCls = inject('prefixCls');
|
||||
// 当前是否是更新模式
|
||||
const isUpdate = ref<boolean>(false);
|
||||
// 当前的弹窗数据
|
||||
const model = ref<object>({});
|
||||
const title = computed(() => (isUpdate.value ? '编辑' : '新增'));
|
||||
|
||||
//注册表单
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: useBasicFormSchema().basicFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
// 注册弹窗
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
isUpdate.value = unref(data?.isUpdate);
|
||||
// 当前是否为添加子级
|
||||
let isChild = unref(data?.isChild);
|
||||
let categoryOptions = isChild ? orgCategoryOptions.child : orgCategoryOptions.root;
|
||||
// 隐藏不需要展示的字段
|
||||
updateSchema([
|
||||
{
|
||||
field: 'parentId',
|
||||
show: isChild,
|
||||
componentProps: {
|
||||
// 如果是添加子部门,就禁用该字段
|
||||
disabled: isChild,
|
||||
treeData: props.rootTreeData,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgCode',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'orgCategory',
|
||||
componentProps: { options: categoryOptions },
|
||||
},
|
||||
]);
|
||||
|
||||
let record = unref(data?.record);
|
||||
if (typeof record !== 'object') {
|
||||
record = {};
|
||||
}
|
||||
// 赋默认值
|
||||
record = Object.assign(
|
||||
{
|
||||
departOrder: 0,
|
||||
orgCategory: categoryOptions[0].value,
|
||||
},
|
||||
record
|
||||
);
|
||||
model.value = record;
|
||||
await setFieldsValue({ ...record });
|
||||
});
|
||||
|
||||
// 提交事件
|
||||
async function handleOk() {
|
||||
try {
|
||||
setModalProps({ confirmLoading: true });
|
||||
let values = await validate();
|
||||
//提交表单
|
||||
await saveOrUpdateDepart(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
<BasicForm @register="registerForm" />
|
||||
<div class="j-box-bottom-button offset-20" style="margin-top: 30px">
|
||||
<div class="j-box-bottom-button-float" :class="[`${prefixCls}`]">
|
||||
<a-button preIcon="ant-design:sync-outlined" @click="onReset">重置</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:save-filled" @click="onSubmit">保存</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { saveOrUpdateDepart } from '../depart.api';
|
||||
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
const { prefixCls } = useDesign('j-depart-form-content');
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
rootTreeData: { type: Array, default: () => [] },
|
||||
});
|
||||
const loading = ref<boolean>(false);
|
||||
// 当前是否是更新模式
|
||||
const isUpdate = ref<boolean>(true);
|
||||
// 当前的弹窗数据
|
||||
const model = ref<object>({});
|
||||
|
||||
//注册表单
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: useBasicFormSchema().basicFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
|
||||
const categoryOptions = computed(() => {
|
||||
if (!!props?.data?.parentId) {
|
||||
return orgCategoryOptions.child;
|
||||
} else {
|
||||
return orgCategoryOptions.root;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// 禁用字段
|
||||
updateSchema([
|
||||
{ field: 'parentId', componentProps: { disabled: true } },
|
||||
{ field: 'orgCode', componentProps: { disabled: true } },
|
||||
]);
|
||||
// data 变化,重填表单
|
||||
watch(
|
||||
() => props.data,
|
||||
async () => {
|
||||
let record = unref(props.data);
|
||||
if (typeof record !== 'object') {
|
||||
record = {};
|
||||
}
|
||||
model.value = record;
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...record });
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
// 更新 父部门 选项
|
||||
watch(
|
||||
() => props.rootTreeData,
|
||||
async () => {
|
||||
updateSchema([
|
||||
{
|
||||
field: 'parentId',
|
||||
componentProps: { treeData: props.rootTreeData },
|
||||
},
|
||||
]);
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
// 监听并更改 orgCategory options
|
||||
watch(
|
||||
categoryOptions,
|
||||
async () => {
|
||||
updateSchema([
|
||||
{
|
||||
field: 'orgCategory',
|
||||
componentProps: { options: categoryOptions.value },
|
||||
},
|
||||
]);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
});
|
||||
|
||||
// 重置表单
|
||||
async function onReset() {
|
||||
await resetFields();
|
||||
await setFieldsValue({ ...model.value });
|
||||
}
|
||||
|
||||
// 提交事件
|
||||
async function onSubmit() {
|
||||
try {
|
||||
loading.value = true;
|
||||
let values = await validate();
|
||||
values = Object.assign({}, model.value, values);
|
||||
//提交表单
|
||||
await saveOrUpdateDepart(values, isUpdate.value);
|
||||
//刷新列表
|
||||
emit('success');
|
||||
Object.assign(model.value, values);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
// update-begin-author:liusq date:20230625 for: [issues/563]暗色主题部分失效
|
||||
|
||||
@prefix-cls: ~'@{namespace}-j-depart-form-content';
|
||||
/*begin 兼容暗夜模式*/
|
||||
.@{prefix-cls} {
|
||||
background: @component-background;
|
||||
border-top: 1px solid @border-color-base;
|
||||
}
|
||||
/*end 兼容暗夜模式*/
|
||||
// update-end-author:liusq date:20230625 for: [issues/563]暗色主题部分失效
|
||||
</style>
|
||||
@@ -0,0 +1,338 @@
|
||||
<template>
|
||||
<a-card :bordered="false" style="height: 100%">
|
||||
<div class="j-table-operator" style="width: 100%">
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="onAddDepart">新增</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="onAddChildDepart()">添加下级</a-button>
|
||||
<a-upload name="file" :showUploadList="false" :customRequest="onImportXls">
|
||||
<a-button type="primary" preIcon="ant-design:import-outlined">导入</a-button>
|
||||
</a-upload>
|
||||
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
|
||||
<!-- <a-button type="primary" preIcon="ant-design:sync-outlined">同步企微?</a-button>-->
|
||||
<!-- <a-button type="primary" preIcon="ant-design:sync-outlined">同步钉钉?</a-button>-->
|
||||
<template v-if="checkedKeys.length > 0">
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="onDeleteBatch">
|
||||
<icon icon="ant-design:delete-outlined" />
|
||||
<span>删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>
|
||||
<span>批量操作 </span>
|
||||
<icon icon="akar-icons:chevron-down" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</div>
|
||||
<a-alert type="info" show-icon class="alert" style="margin-bottom: 8px">
|
||||
<template #message>
|
||||
<template v-if="checkedKeys.length > 0">
|
||||
<span>已选中 {{ checkedKeys.length }} 条记录</span>
|
||||
<a-divider type="vertical" />
|
||||
<a @click="checkedKeys = []">清空</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>未选中任何数据</span>
|
||||
</template>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-spin :spinning="loading">
|
||||
<a-input-search placeholder="按部门名称搜索…" style="margin-bottom: 10px" @search="onSearch" />
|
||||
<!--组织机构树-->
|
||||
<template v-if="treeData.length > 0">
|
||||
<a-tree
|
||||
v-if="!treeReloading"
|
||||
checkable
|
||||
:clickRowToExpand="false"
|
||||
:treeData="treeData"
|
||||
:selectedKeys="selectedKeys"
|
||||
:checkStrictly="checkStrictly"
|
||||
:load-data="loadChildrenTreeData"
|
||||
:checkedKeys="checkedKeys"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
@check="onCheck"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #title="{ key: treeKey, title, dataRef }">
|
||||
<a-dropdown :trigger="['contextmenu']">
|
||||
<Popconfirm
|
||||
:open="visibleTreeKey === treeKey"
|
||||
title="确定要删除吗?"
|
||||
ok-text="确定"
|
||||
cancel-text="取消"
|
||||
placement="rightTop"
|
||||
@confirm="onDelete(dataRef)"
|
||||
@openChange="onVisibleChange"
|
||||
>
|
||||
<span>{{ title }}</span>
|
||||
</Popconfirm>
|
||||
|
||||
<template #overlay>
|
||||
<a-menu @click="">
|
||||
<a-menu-item key="1" @click="onAddChildDepart(dataRef)">添加子级</a-menu-item>
|
||||
<a-menu-item key="2" @click="visibleTreeKey = treeKey">
|
||||
<span style="color: red">删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-tree>
|
||||
</template>
|
||||
<a-empty v-else description="暂无数据" />
|
||||
</a-spin>
|
||||
<DepartFormModal :rootTreeData="treeData" @register="registerModal" @success="loadRootTreeData" />
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, nextTick, ref, unref } from 'vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useMethods } from '/@/hooks/system/useMethods';
|
||||
import { Api, deleteBatchDepart, queryDepartTreeSync } from '../depart.api';
|
||||
import { searchByKeywords } from '/@/views/system/departUser/depart.user.api';
|
||||
import DepartFormModal from './DepartFormModal.vue';
|
||||
import { Popconfirm } from 'ant-design-vue';
|
||||
|
||||
const prefixCls = inject('prefixCls');
|
||||
const emit = defineEmits(['select', 'rootTreeData']);
|
||||
const { createMessage } = useMessage();
|
||||
const { handleImportXls, handleExportXls } = useMethods();
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
// 部门树列表数据
|
||||
const treeData = ref<any[]>([]);
|
||||
// 当前选中的项
|
||||
const checkedKeys = ref<any[]>([]);
|
||||
// 当前展开的项
|
||||
const expandedKeys = ref<any[]>([]);
|
||||
// 当前选中的项
|
||||
const selectedKeys = ref<any[]>([]);
|
||||
// 树组件重新加载
|
||||
const treeReloading = ref<boolean>(false);
|
||||
// 树父子是否关联
|
||||
const checkStrictly = ref<boolean>(true);
|
||||
// 当前选中的部门
|
||||
const currentDepart = ref<any>(null);
|
||||
// 控制确认删除提示框是否显示
|
||||
const visibleTreeKey = ref<any>(null);
|
||||
// 搜索关键字
|
||||
const searchKeyword = ref('');
|
||||
|
||||
// 注册 modal
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
// 加载顶级部门信息
|
||||
async function loadRootTreeData() {
|
||||
try {
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
const result = await queryDepartTreeSync();
|
||||
if (Array.isArray(result)) {
|
||||
treeData.value = result;
|
||||
}
|
||||
if (expandedKeys.value.length === 0) {
|
||||
autoExpandParentNode();
|
||||
} else {
|
||||
if (selectedKeys.value.length === 0) {
|
||||
let item = treeData.value[0];
|
||||
if (item) {
|
||||
// 默认选中第一个
|
||||
setSelectedKey(item.id, item);
|
||||
}
|
||||
} else {
|
||||
emit('select', currentDepart.value);
|
||||
}
|
||||
}
|
||||
emit('rootTreeData', treeData.value);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
loadRootTreeData();
|
||||
|
||||
// 加载子级部门信息
|
||||
async function loadChildrenTreeData(treeNode) {
|
||||
try {
|
||||
const result = await queryDepartTreeSync({
|
||||
pid: treeNode.dataRef.id,
|
||||
});
|
||||
if (result.length == 0) {
|
||||
treeNode.dataRef.isLeaf = true;
|
||||
} else {
|
||||
treeNode.dataRef.children = result;
|
||||
if (expandedKeys.value.length > 0) {
|
||||
// 判断获取的子级是否有当前展开的项
|
||||
let subKeys: any[] = [];
|
||||
for (let key of expandedKeys.value) {
|
||||
if (result.findIndex((item) => item.id === key) !== -1) {
|
||||
subKeys.push(key);
|
||||
}
|
||||
}
|
||||
if (subKeys.length > 0) {
|
||||
expandedKeys.value = [...expandedKeys.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
treeData.value = [...treeData.value];
|
||||
emit('rootTreeData', treeData.value);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// 自动展开父节点,只展开一级
|
||||
function autoExpandParentNode() {
|
||||
let item = treeData.value[0];
|
||||
if (item) {
|
||||
if (!item.isLeaf) {
|
||||
expandedKeys.value = [item.key];
|
||||
}
|
||||
// 默认选中第一个
|
||||
setSelectedKey(item.id, item);
|
||||
reloadTree();
|
||||
} else {
|
||||
emit('select', null);
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载树组件,防止无法默认展开数据
|
||||
async function reloadTree() {
|
||||
await nextTick();
|
||||
treeReloading.value = true;
|
||||
await nextTick();
|
||||
treeReloading.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选中的行
|
||||
*/
|
||||
function setSelectedKey(key: string, data?: object) {
|
||||
selectedKeys.value = [key];
|
||||
if (data) {
|
||||
currentDepart.value = data;
|
||||
emit('select', data);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加一级部门
|
||||
function onAddDepart() {
|
||||
openModal(true, { isUpdate: false, isChild: false });
|
||||
}
|
||||
|
||||
// 添加子级部门
|
||||
function onAddChildDepart(data = currentDepart.value) {
|
||||
if (data == null) {
|
||||
createMessage.warning('请先选择一个部门');
|
||||
return;
|
||||
}
|
||||
const record = { parentId: data.id };
|
||||
openModal(true, { isUpdate: false, isChild: true, record });
|
||||
}
|
||||
|
||||
// 搜索事件
|
||||
async function onSearch(value: string) {
|
||||
if (value) {
|
||||
try {
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
let result = await searchByKeywords({ keyWord: value });
|
||||
if (Array.isArray(result)) {
|
||||
treeData.value = result;
|
||||
}
|
||||
autoExpandParentNode();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
} else {
|
||||
loadRootTreeData();
|
||||
}
|
||||
searchKeyword.value = value;
|
||||
}
|
||||
|
||||
// 树复选框选择事件
|
||||
function onCheck(e) {
|
||||
if (Array.isArray(e)) {
|
||||
checkedKeys.value = e;
|
||||
} else {
|
||||
checkedKeys.value = e.checked;
|
||||
}
|
||||
}
|
||||
|
||||
// 树选择事件
|
||||
function onSelect(selKeys, event) {
|
||||
console.log('select: ', selKeys, event);
|
||||
if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
|
||||
setSelectedKey(selKeys[0], event.selectedNodes[0]);
|
||||
} else {
|
||||
// 这样可以防止用户取消选择
|
||||
setSelectedKey(selectedKeys.value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ids 删除部门
|
||||
* @param idListRef array
|
||||
* @param confirm 是否显示确认提示框
|
||||
*/
|
||||
async function doDeleteDepart(idListRef, confirm = true) {
|
||||
const idList = unref(idListRef);
|
||||
if (idList.length > 0) {
|
||||
try {
|
||||
loading.value = true;
|
||||
await deleteBatchDepart({ ids: idList.join(',') }, confirm);
|
||||
await loadRootTreeData();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除单个部门
|
||||
async function onDelete(data) {
|
||||
if (data) {
|
||||
onVisibleChange(false);
|
||||
doDeleteDepart([data.id], false);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量删除部门
|
||||
async function onDeleteBatch() {
|
||||
try {
|
||||
await doDeleteDepart(checkedKeys);
|
||||
checkedKeys.value = [];
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
function onVisibleChange(visible) {
|
||||
if (!visible) {
|
||||
visibleTreeKey.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function onImportXls(d) {
|
||||
handleImportXls(d, Api.importExcelUrl, () => {
|
||||
loadRootTreeData();
|
||||
});
|
||||
}
|
||||
|
||||
function onExportXls() {
|
||||
//update-begin---author:wangshuai---date:2024-07-05---for:【TV360X-1671】部门管理不支持选中的记录导出---
|
||||
let params = {}
|
||||
if(checkedKeys.value && checkedKeys.value.length > 0) {
|
||||
params['selections'] = checkedKeys.value.join(',')
|
||||
}
|
||||
handleExportXls('部门信息', Api.exportXlsUrl,params);
|
||||
//update-end---author:wangshuai---date:2024-07-05---for:【TV360X-1671】部门管理不支持选中的记录导出---
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
loadRootTreeData,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
<template v-if="treeData.length > 0">
|
||||
<BasicTree
|
||||
ref="basicTree"
|
||||
class="depart-rule-tree"
|
||||
checkable
|
||||
:treeData="treeData"
|
||||
:checkedKeys="checkedKeys"
|
||||
:selectedKeys="selectedKeys"
|
||||
:expandedKeys="expandedKeys"
|
||||
:checkStrictly="true"
|
||||
style="height: 500px; overflow: auto"
|
||||
@check="onCheck"
|
||||
@expand="onExpand"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #title="{ slotTitle, ruleFlag }">
|
||||
<span>{{ slotTitle }}</span>
|
||||
<Icon v-if="ruleFlag" icon="ant-design:align-left-outlined" style="margin-left: 5px; color: red" />
|
||||
</template>
|
||||
</BasicTree>
|
||||
</template>
|
||||
<a-empty v-else description="无可配置部门权限" />
|
||||
|
||||
<div class="j-box-bottom-button offset-20" style="margin-top: 30px">
|
||||
<div class="j-box-bottom-button-float" :class="[`${prefixCls}`]">
|
||||
<a-dropdown :trigger="['click']" placement="top">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="3" @click="toggleCheckALL(true)">{{ t('component.tree.selectAll') }}</a-menu-item>
|
||||
<a-menu-item key="4" @click="toggleCheckALL(false)">{{ t('component.tree.unSelectAll') }}</a-menu-item>
|
||||
<a-menu-item key="5" @click="toggleExpandAll(true)">{{ t('component.tree.expandAll') }}</a-menu-item>
|
||||
<a-menu-item key="6" @click="toggleExpandAll(false)">{{ t('component.tree.unExpandAll') }}</a-menu-item>
|
||||
<a-menu-item key="7" @click="toggleRelationAll(false)">{{ t('component.tree.checkStrictly') }}</a-menu-item>
|
||||
<a-menu-item key="8" @click="toggleRelationAll(true)">{{ t('component.tree.checkUnStrictly') }}</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button style="float: left">
|
||||
树操作
|
||||
<Icon icon="ant-design:up-outlined" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
<a-button type="primary" preIcon="ant-design:save-filled" @click="onSubmit">保存</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
<DepartDataRuleDrawer @register="registerDataRuleDrawer" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, inject, ref, nextTick } from 'vue';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { BasicTree } from '/@/components/Tree/index';
|
||||
import DepartDataRuleDrawer from './DepartDataRuleDrawer.vue';
|
||||
import { queryRoleTreeList, queryDepartPermission, saveDepartPermission } from '../depart.api';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { translateTitle } from '/@/utils/common/compUtils';
|
||||
import { DEPART_MANGE_AUTH_CONFIG_KEY } from '/@/enums/cacheEnum';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
const { prefixCls } = useDesign('j-depart-form-content');
|
||||
const props = defineProps({
|
||||
data: { type: Object, default: () => ({}) },
|
||||
});
|
||||
// 当前选中的部门ID,可能会为空,代表未选择部门
|
||||
const departId = computed(() => props.data?.id);
|
||||
|
||||
const basicTree = ref();
|
||||
const loading = ref<boolean>(false);
|
||||
//树的全部节点信息
|
||||
const allTreeKeys = ref([]);
|
||||
const treeData = ref<any[]>([]);
|
||||
const expandedKeys = ref<Array<any>>([]);
|
||||
const selectedKeys = ref<Array<any>>([]);
|
||||
const checkedKeys = ref<Array<any>>([]);
|
||||
const lastCheckedKeys = ref<Array<any>>([]);
|
||||
const checkStrictly = ref(false);
|
||||
const { t } = useI18n();
|
||||
|
||||
// 注册数据规则授权弹窗抽屉
|
||||
const [registerDataRuleDrawer, dataRuleDrawer] = useDrawer();
|
||||
|
||||
// onCreated
|
||||
loadData({
|
||||
success: (ids) => {
|
||||
// update-begin--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
const localData = localStorage.getItem(DEPART_MANGE_AUTH_CONFIG_KEY);
|
||||
if (localData) {
|
||||
const obj = JSON.parse(localData);
|
||||
obj.level && toggleRelationAll(obj.level == 'relation' ? false : true);
|
||||
obj.expand && toggleExpandAll(obj.expand == 'openAll' ? true :false);
|
||||
} else {
|
||||
// expandedKeys.value = ids;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
}
|
||||
});
|
||||
watch(departId, () => loadDepartPermission(), { immediate: true });
|
||||
|
||||
async function loadData(options: any = {}) {
|
||||
try {
|
||||
loading.value = true;
|
||||
let { treeList, ids } = await queryRoleTreeList();
|
||||
//update-begin---author:wangshuai---date:2024-04-08---for:【issues/1169】部门管理功能中的【部门权限】中未翻译 t('') 多语言---
|
||||
treeData.value = translateTitle(treeList);
|
||||
//update-end---author:wangshuai---date:2024-04-08---for:【issues/1169】部门管理功能中的【部门权限】中未翻译 t('') 多语言---
|
||||
// update-begin--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
allTreeKeys.value = ids;
|
||||
options.success?.(ids);
|
||||
// update-end--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDepartPermission() {
|
||||
if (departId.value) {
|
||||
try {
|
||||
loading.value = true;
|
||||
let keys = await queryDepartPermission({ departId: departId.value });
|
||||
checkedKeys.value = keys;
|
||||
lastCheckedKeys.value = [...keys];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
loading.value = true;
|
||||
await saveDepartPermission({
|
||||
departId: departId.value,
|
||||
permissionIds: checkedKeys.value.join(','),
|
||||
lastpermissionIds: lastCheckedKeys.value.join(','),
|
||||
});
|
||||
await loadData();
|
||||
await loadDepartPermission();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击选中
|
||||
* 2024-07-04
|
||||
* liaozhiyang
|
||||
*/
|
||||
function onCheck(o, e) {
|
||||
// checkStrictly: true=>层级独立,false=>层级关联.
|
||||
if (checkStrictly.value) {
|
||||
checkedKeys.value = o.checked ? o.checked : o;
|
||||
} else {
|
||||
const keys = getNodeAllKey(e.node, 'children', 'key');
|
||||
if (e.checked) {
|
||||
// 反复操作下可能会有重复的keys,得用new Set去重下
|
||||
checkedKeys.value = [...new Set([...checkedKeys.value, ...keys])];
|
||||
} else {
|
||||
const result = removeMatchingItems(checkedKeys.value, keys);
|
||||
checkedKeys.value = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 2024-07-04
|
||||
* liaozhiyang
|
||||
* 删除相匹配数组的项
|
||||
*/
|
||||
function removeMatchingItems(arr1, arr2) {
|
||||
// 使用哈希表记录 arr2 中的元素
|
||||
const hashTable = {};
|
||||
for (const item of arr2) {
|
||||
hashTable[item] = true;
|
||||
}
|
||||
// 使用 filter 方法遍历第一个数组,过滤出不在哈希表中存在的项
|
||||
return arr1.filter((item) => !hashTable[item]);
|
||||
}
|
||||
/**
|
||||
* 2024-07-04
|
||||
* liaozhiyang
|
||||
* 获取当前节点及以下所有子孙级的key
|
||||
*/
|
||||
function getNodeAllKey(node: any, children: any, key: string) {
|
||||
const result: any = [];
|
||||
result.push(node[key]);
|
||||
const recursion = (data) => {
|
||||
data.forEach((item: any) => {
|
||||
result.push(item[key]);
|
||||
if (item[children]?.length) {
|
||||
recursion(item[children]);
|
||||
}
|
||||
});
|
||||
};
|
||||
node[children]?.length && recursion(node[children]);
|
||||
return result;
|
||||
}
|
||||
|
||||
// tree展开事件
|
||||
function onExpand($expandedKeys) {
|
||||
expandedKeys.value = $expandedKeys;
|
||||
}
|
||||
|
||||
// tree选中事件
|
||||
function onSelect($selectedKeys, { selectedNodes }) {
|
||||
if (selectedNodes[0]?.ruleFlag) {
|
||||
let functionId = $selectedKeys[0];
|
||||
dataRuleDrawer.openDrawer(true, { departId, functionId });
|
||||
}
|
||||
selectedKeys.value = [];
|
||||
}
|
||||
|
||||
// 切换父子关联
|
||||
async function toggleCheckStrictly(flag) {
|
||||
checkStrictly.value = flag;
|
||||
await nextTick();
|
||||
checkedKeys.value = basicTree.value.getCheckedKeys();
|
||||
}
|
||||
|
||||
// 切换展开收起
|
||||
async function toggleExpandAll(flag) {
|
||||
// update-begin--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
if (flag) {
|
||||
expandedKeys.value = allTreeKeys.value;
|
||||
saveLocalOperation('expand', 'openAll');
|
||||
} else {
|
||||
expandedKeys.value = [];
|
||||
saveLocalOperation('expand', 'closeAll');
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
}
|
||||
|
||||
// 切换全选
|
||||
async function toggleCheckALL(flag) {
|
||||
// update-begin--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
if (flag) {
|
||||
checkedKeys.value = allTreeKeys.value;
|
||||
} else {
|
||||
checkedKeys.value = [];
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
}
|
||||
|
||||
// 切换层级关联(独立)
|
||||
const toggleRelationAll = (flag) => {
|
||||
// update-begin--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
checkStrictly.value = flag;
|
||||
if (flag) {
|
||||
saveLocalOperation('level', 'standAlone');
|
||||
} else {
|
||||
saveLocalOperation('level', 'relation');
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240704---for:【TV360X-1689】同步系统角色改法加上缓存层级关联等功能
|
||||
};
|
||||
/**
|
||||
* 2024-07-04
|
||||
* liaozhiyang
|
||||
* 缓存
|
||||
* */
|
||||
const saveLocalOperation = (key, value) => {
|
||||
const localData = localStorage.getItem(DEPART_MANGE_AUTH_CONFIG_KEY);
|
||||
const obj = localData ? JSON.parse(localData) : {};
|
||||
obj[key] = value;
|
||||
localStorage.setItem(DEPART_MANGE_AUTH_CONFIG_KEY, JSON.stringify(obj))
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
// 【VUEN-188】解决滚动条不灵敏的问题
|
||||
.depart-rule-tree :deep(.scrollbar__bar) {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,122 @@
|
||||
import { unref } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
export enum Api {
|
||||
queryDepartTreeSync = '/sys/sysDepart/queryDepartTreeSync',
|
||||
save = '/sys/sysDepart/add',
|
||||
edit = '/sys/sysDepart/edit',
|
||||
delete = '/sys/sysDepart/delete',
|
||||
deleteBatch = '/sys/sysDepart/deleteBatch',
|
||||
exportXlsUrl = '/sys/sysDepart/exportXls',
|
||||
importExcelUrl = '/sys/sysDepart/importExcel',
|
||||
|
||||
roleQueryTreeList = '/sys/role/queryTreeList',
|
||||
queryDepartPermission = '/sys/permission/queryDepartPermission',
|
||||
saveDepartPermission = '/sys/permission/saveDepartPermission',
|
||||
|
||||
dataRule = '/sys/sysDepartPermission/datarule',
|
||||
|
||||
getCurrentUserDeparts = '/sys/user/getCurrentUserDeparts',
|
||||
selectDepart = '/sys/selectDepart',
|
||||
getUpdateDepartInfo = '/sys/user/getUpdateDepartInfo',
|
||||
doUpdateDepartInfo = '/sys/user/doUpdateDepartInfo',
|
||||
changeDepartChargePerson = '/sys/user/changeDepartChargePerson',
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门树列表
|
||||
*/
|
||||
export const queryDepartTreeSync = (params?) => defHttp.get({ url: Api.queryDepartTreeSync, params });
|
||||
|
||||
/**
|
||||
* 保存或者更新部门角色
|
||||
*/
|
||||
export const saveOrUpdateDepart = (params, isUpdate) => {
|
||||
if (isUpdate) {
|
||||
return defHttp.put({ url: Api.edit, params });
|
||||
} else {
|
||||
return defHttp.post({ url: Api.save, params });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量删除部门角色
|
||||
*/
|
||||
export const deleteBatchDepart = (params, confirm = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doDelete = () => {
|
||||
resolve(defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }));
|
||||
};
|
||||
if (confirm) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要删除吗?',
|
||||
onOk: () => doDelete(),
|
||||
onCancel: () => reject(),
|
||||
});
|
||||
} else {
|
||||
doDelete();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取权限树列表
|
||||
*/
|
||||
export const queryRoleTreeList = (params?) => defHttp.get({ url: Api.roleQueryTreeList, params });
|
||||
/**
|
||||
* 查询部门权限
|
||||
*/
|
||||
export const queryDepartPermission = (params?) => defHttp.get({ url: Api.queryDepartPermission, params });
|
||||
/**
|
||||
* 保存部门权限
|
||||
*/
|
||||
export const saveDepartPermission = (params) => defHttp.post({ url: Api.saveDepartPermission, params });
|
||||
|
||||
/**
|
||||
* 查询部门数据权限列表
|
||||
*/
|
||||
export const queryDepartDataRule = (functionId, departId, params?) => {
|
||||
let url = `${Api.dataRule}/${unref(functionId)}/${unref(departId)}`;
|
||||
return defHttp.get({ url, params });
|
||||
};
|
||||
/**
|
||||
* 保存部门数据权限
|
||||
*/
|
||||
export const saveDepartDataRule = (params) => defHttp.post({ url: Api.dataRule, params });
|
||||
/**
|
||||
* 获取登录用户部门信息
|
||||
*/
|
||||
export const getUserDeparts = (params?) => defHttp.get({ url: Api.getCurrentUserDeparts, params });
|
||||
/**
|
||||
* 切换选择部门
|
||||
*/
|
||||
export const selectDepart = (params?) => defHttp.put({ url: Api.selectDepart, params });
|
||||
|
||||
/**
|
||||
* 编辑部门前获取部门相关信息
|
||||
* @param id
|
||||
*/
|
||||
export const getUpdateDepartInfo = (id) => defHttp.get({ url: Api.getUpdateDepartInfo, params: {id} });
|
||||
|
||||
/**
|
||||
* 编辑部门
|
||||
* @param params
|
||||
*/
|
||||
export const doUpdateDepartInfo = (params) => defHttp.put({ url: Api.doUpdateDepartInfo, params });
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
* @param id
|
||||
*/
|
||||
export const deleteDepart = (id) => defHttp.delete({ url: Api.delete, params:{ id } }, { joinParamsToUrl: true });
|
||||
|
||||
/**
|
||||
* 设置负责人 取消负责人
|
||||
* @param params
|
||||
*/
|
||||
export const changeDepartChargePerson = (params) => defHttp.put({ url: Api.changeDepartChargePerson, params });
|
||||
@@ -0,0 +1,105 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
// 部门基础表单
|
||||
export function useBasicFormSchema() {
|
||||
const basicFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'departName',
|
||||
label: '机构名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入机构/部门名称',
|
||||
},
|
||||
rules: [{ required: true, message: '机构名称不能为空' }],
|
||||
},
|
||||
{
|
||||
field: 'parentId',
|
||||
label: '上级部门',
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
treeData: [],
|
||||
placeholder: '无',
|
||||
dropdownStyle: { maxHeight: '200px', overflow: 'auto' },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgCode',
|
||||
label: '机构编码',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入机构编码',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'orgCategory',
|
||||
label: '机构类型',
|
||||
component: 'RadioButtonGroup',
|
||||
componentProps: { options: [] },
|
||||
},
|
||||
{
|
||||
field: 'departOrder',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
componentProps: {},
|
||||
},
|
||||
// {
|
||||
// field: 'mobile',
|
||||
// label: '电话',
|
||||
// component: 'Input',
|
||||
// componentProps: {
|
||||
// placeholder: '请输入电话',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// field: 'fax',
|
||||
// label: '传真',
|
||||
// component: 'Input',
|
||||
// componentProps: {
|
||||
// placeholder: '请输入传真',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// field: 'address',
|
||||
// label: '地址',
|
||||
// component: 'Input',
|
||||
// componentProps: {
|
||||
// placeholder: '请输入地址',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// field: 'memo',
|
||||
// label: '备注',
|
||||
// component: 'InputTextArea',
|
||||
// componentProps: {
|
||||
// placeholder: '请输入备注',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
field: 'id',
|
||||
label: 'ID',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '部门类别',
|
||||
field: 'departType',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'depart_type',
|
||||
placeholder: '请选择部门类别',
|
||||
},
|
||||
},
|
||||
];
|
||||
return { basicFormSchema };
|
||||
}
|
||||
|
||||
// 机构类型选项
|
||||
export const orgCategoryOptions = {
|
||||
// 一级部门
|
||||
root: [{ value: '1', label: '公司' }],
|
||||
// 子级部门
|
||||
child: [
|
||||
{ value: '2', label: '部门' },
|
||||
{ value: '3', label: '岗位' },
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
//noinspection LessUnresolvedVariable
|
||||
@prefix-cls: ~'@{namespace}-depart-manage';
|
||||
|
||||
.@{prefix-cls} {
|
||||
// update-begin-author:liusq date:20230625 for: [issues/563]暗色主题部分失效
|
||||
background: @component-background;
|
||||
// update-end-author:liusq date:20230625 for: [issues/563]暗色主题部分失效
|
||||
|
||||
&--box {
|
||||
.ant-tabs-nav {
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<a-row :class="['p-4', `${prefixCls}--box`]" type="flex" :gutter="10">
|
||||
<a-col :xl="12" :lg="24" :md="24" style="margin-bottom: 10px">
|
||||
<DepartLeftTree ref="leftTree" @select="onTreeSelect" @rootTreeData="onRootTreeData" />
|
||||
</a-col>
|
||||
<a-col :xl="12" :lg="24" :md="24" style="margin-bottom: 10px">
|
||||
<div style="height: 100%;" :class="[`${prefixCls}`]">
|
||||
<a-tabs v-show="departData != null" defaultActiveKey="base-info">
|
||||
<a-tab-pane tab="基本信息" key="base-info" forceRender style="position: relative">
|
||||
<div style="padding: 20px">
|
||||
<DepartFormTab :data="departData" :rootTreeData="rootTreeData" @success="onSuccess" />
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<!-- <a-tab-pane tab="部门权限" key="role-info">-->
|
||||
<!-- <div style="padding: 0 20px 20px">-->
|
||||
<!-- <DepartRuleTab :data="departData" />-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-tab-pane>-->
|
||||
</a-tabs>
|
||||
<div v-show="departData == null" style="padding-top: 40px">
|
||||
<a-empty description="尚未选择部门" />
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="system-depart">
|
||||
import { provide, ref } from 'vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import DepartLeftTree from './components/DepartLeftTree.vue';
|
||||
import DepartFormTab from './components/DepartFormTab.vue';
|
||||
import DepartRuleTab from './components/DepartRuleTab.vue';
|
||||
|
||||
const { prefixCls } = useDesign('depart-manage');
|
||||
provide('prefixCls', prefixCls);
|
||||
|
||||
// 给子组件定义一个ref变量
|
||||
const leftTree = ref();
|
||||
|
||||
// 当前选中的部门信息
|
||||
const departData = ref({});
|
||||
const rootTreeData = ref<any[]>([]);
|
||||
|
||||
// 左侧树选择后触发
|
||||
function onTreeSelect(data) {
|
||||
console.log('onTreeSelect: ', data);
|
||||
departData.value = data;
|
||||
}
|
||||
|
||||
// 左侧树rootTreeData触发
|
||||
function onRootTreeData(data) {
|
||||
rootTreeData.value = data;
|
||||
}
|
||||
|
||||
function onSuccess() {
|
||||
leftTree.value.loadRootTreeData();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import './index.less';
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="修改密码" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" name="PassWordModal" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formPasswordSchema } from './user.data';
|
||||
import { changePassword } from './user.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: formPasswordSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
//表单赋值
|
||||
await setFieldsValue({ ...data });
|
||||
});
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await changePassword(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="用户代理" @ok="handleSubmit" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formAgentSchema } from './user.data';
|
||||
import { getUserAgent, saveOrUpdateAgent } from './user.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: formAgentSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
//查询获取表单数据
|
||||
const res = await getUserAgent({ userName: data.userName });
|
||||
data = res.result ? res.result : data;
|
||||
//表单赋值
|
||||
await setFieldsValue({ ...data });
|
||||
});
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateAgent(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
:title="getTitle"
|
||||
:width="adaptiveWidth"
|
||||
@ok="handleSubmit"
|
||||
:showFooter="showFooter"
|
||||
destroyOnClose
|
||||
>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, ref, computed, unref, useAttrs } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './user.data';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { saveOrUpdateUser, getUserRoles, getUserDepartList, getAllRolesListNoByTenant, getAllRolesList } from './user.api';
|
||||
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
|
||||
import { getTenantId } from "/@/utils/auth";
|
||||
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const attrs = useAttrs();
|
||||
const isUpdate = ref(true);
|
||||
const rowId = ref('');
|
||||
const departOptions = ref([]);
|
||||
let isFormDepartUser = false;
|
||||
//表单配置
|
||||
const [registerForm, { setProps, resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
labelWidth: 90,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// TODO [VUEN-527] https://www.teambition.com/task/6239beb894b358003fe93626
|
||||
const showFooter = ref(true);
|
||||
//表单赋值
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
await resetFields();
|
||||
showFooter.value = data?.showFooter ?? true;
|
||||
setDrawerProps({ confirmLoading: false, showFooter: showFooter.value });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.record.id;
|
||||
//租户信息定义成数组
|
||||
/* if (data.record.relTenantIds && !Array.isArray(data.record.relTenantIds)) {
|
||||
data.record.relTenantIds = data.record.relTenantIds.split(',');
|
||||
} else {
|
||||
//【issues/I56C5I】用户管理中连续点两次编辑租户配置就丢失了
|
||||
//data.record.relTenantIds = [];
|
||||
}*/
|
||||
|
||||
//查角色/赋值/try catch 处理,不然编辑有问题
|
||||
try {
|
||||
const userRoles = await getUserRoles({ userid: data.record.id });
|
||||
if (userRoles && userRoles.length > 0) {
|
||||
data.record.selectedroles = userRoles;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
//查所属部门/赋值
|
||||
const userDepart = await getUserDepartList({ userId: data.record.id });
|
||||
if (userDepart && userDepart.length > 0) {
|
||||
data.record.selecteddeparts = userDepart;
|
||||
let selectDepartKeys = Array.from(userDepart, ({ key }) => key);
|
||||
data.record.selecteddeparts = selectDepartKeys.join(',');
|
||||
departOptions.value = userDepart.map((item) => {
|
||||
return { label: item.title, value: item.key };
|
||||
});
|
||||
}
|
||||
//负责部门/赋值
|
||||
data.record.departIds && !Array.isArray(data.record.departIds) && (data.record.departIds = data.record.departIds.split(','));
|
||||
//update-begin---author:zyf Date:20211210 for:避免空值显示异常------------
|
||||
//update-begin---author:liusq Date:20231008 for:[issues/772]避免空值显示异常------------
|
||||
data.record.departIds = (!data.record.departIds || data.record.departIds == '') ? [] : data.record.departIds;
|
||||
//update-end-----author:liusq Date:20231008 for:[issues/772]避免空值显示异常------------
|
||||
//update-begin---author:zyf Date:20211210 for:避免空值显示异常------------
|
||||
}
|
||||
//处理角色用户列表情况(和角色列表有关系)
|
||||
data.selectedroles && (await setFieldsValue({ selectedroles: data.selectedroles }));
|
||||
// -update-begin--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
isFormDepartUser = data?.departDisabled === true ? true : false;
|
||||
// -update-end--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
//编辑时隐藏密码/角色列表隐藏角色信息/我的部门时隐藏所属部门
|
||||
updateSchema([
|
||||
{
|
||||
field: 'password',
|
||||
// 【QQYUN-8324】
|
||||
ifShow: !unref(isUpdate),
|
||||
},
|
||||
{
|
||||
field: 'confirmPassword',
|
||||
ifShow: !unref(isUpdate),
|
||||
},
|
||||
{
|
||||
field: 'selectedroles',
|
||||
show: !data.isRole,
|
||||
},
|
||||
{
|
||||
field: 'departIds',
|
||||
componentProps: { options: departOptions },
|
||||
},
|
||||
{
|
||||
field: 'selecteddeparts',
|
||||
show: !data?.departDisabled,
|
||||
},
|
||||
{
|
||||
field: 'selectedroles',
|
||||
show: !data?.departDisabled,
|
||||
//update-begin---author:wangshuai ---date:20230424 for:【issues/4844】多租户模式下,新增或编辑用户,选择角色一栏,角色选项没有做租户隔离------------
|
||||
//判断是否为多租户模式
|
||||
componentProps:{
|
||||
api: data.tenantSaas?getAllRolesList:getAllRolesListNoByTenant
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20230424 for:【issues/4844】多租户模式下,新增或编辑用户,选择角色一栏,角色选项没有做租户隔离------------
|
||||
},
|
||||
//update-begin---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
{
|
||||
field: 'relTenantIds',
|
||||
componentProps:{
|
||||
disabled: !!data.tenantSaas,
|
||||
},
|
||||
},
|
||||
//update-end---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
]);
|
||||
//update-begin---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
if(!unref(isUpdate) && data.tenantSaas){
|
||||
await setFieldsValue({ relTenantIds: getTenantId().toString() })
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
// 无论新增还是编辑,都可以设置表单值
|
||||
if (typeof data.record === 'object') {
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
// 隐藏底部时禁用整个表单
|
||||
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1117【issue】0523周开源问题
|
||||
setProps({ disabled: !showFooter.value });
|
||||
//update-end-author:taoyan date:2022-5-24 for: VUEN-1117【issue】0523周开源问题
|
||||
});
|
||||
//获取标题
|
||||
const getTitle = computed(() => {
|
||||
// update-begin--author:liaozhiyang---date:20240306---for:【QQYUN-8389】系统用户详情抽屉title更改
|
||||
if (!unref(isUpdate)) {
|
||||
return '新增用户';
|
||||
} else {
|
||||
return unref(showFooter) ? '编辑用户' : '用户详情';
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240306---for:【QQYUN-8389】系统用户详情抽屉title更改
|
||||
});
|
||||
const { adaptiveWidth } = useDrawerAdaptiveWidth();
|
||||
|
||||
//提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
let values = await validate();
|
||||
setDrawerProps({ confirmLoading: true });
|
||||
values.userIdentity === 1 && (values.departIds = '');
|
||||
let isUpdateVal = unref(isUpdate);
|
||||
// -update-begin--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
let params = values;
|
||||
if (isFormDepartUser) {
|
||||
params = { ...params, updateFromPage: 'deptUsers' };
|
||||
}
|
||||
// -update-end--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
//提交表单
|
||||
await saveOrUpdateUser(params, isUpdateVal);
|
||||
//关闭弹窗
|
||||
closeDrawer();
|
||||
//刷新列表
|
||||
emit('success',{isUpdateVal ,values});
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="离职交接" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup name="user-quit-agent-modal">
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formQuitAgentSchema } from './user.data';
|
||||
import { getUserAgent, userQuitAgent } from './user.api';
|
||||
import dayjs from 'dayjs';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
|
||||
schemas: formQuitAgentSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: true });
|
||||
|
||||
let userId = data.userId;
|
||||
//查询获取表单数据
|
||||
const res = await getUserAgent({ userName: data.userName });
|
||||
data = res.result ? res.result : data;
|
||||
let date = new Date();
|
||||
if (!data.startTime) {
|
||||
data.startTime = dayjs(date).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
if (!data.endTime) {
|
||||
data.endTime = getYear(date);
|
||||
}
|
||||
|
||||
//update-begin---author:wangshuai ---date:20230703 for:【QQYUN-5685】5、离职人员可以选自己------------
|
||||
await updateSchema(
|
||||
[{
|
||||
field:'agentUserName',
|
||||
componentProps:{
|
||||
excludeUserIdList:[userId]
|
||||
}
|
||||
}]
|
||||
)
|
||||
//update-end---author:wangshuai ---date:20230703 for:【QQYUN-5685】5、离职人员可以选自己------------
|
||||
//表单赋值
|
||||
await setFieldsValue({ ...data });
|
||||
setModalProps({ confirmLoading: false });
|
||||
});
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await userQuitAgent(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success',values.userName);
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后30年
|
||||
*/
|
||||
function getYear(date) {
|
||||
//update-begin---author:wangshuai ---date:20221207 for:[QQYUN-3285]交接人设置 结束时间有问题------------
|
||||
//这是一个数值
|
||||
let y = date.getFullYear() + 30;
|
||||
let m = dayjs(date).format('MM');
|
||||
let d = dayjs(date).format('DD');
|
||||
let hour = dayjs(date).format('HH:mm:ss');
|
||||
console.log('年月日', y + '-' + m + '-' + d);
|
||||
return dayjs(y + '-' + m + '-' + d + ' ' + hour).format('YYYY-MM-DD HH:mm:ss');
|
||||
//update-end---author:wangshuai ---date:20221207 for:[QQYUN-3285]交接人设置 结束时间有问题--------------
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="离职人员信息" :showOkBtn="false" width="1000px" destroyOnClose>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleRevert">
|
||||
<Icon icon="ant-design:redo-outlined"></Icon>
|
||||
批量取消
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="user-quit-modal">
|
||||
import { ref, toRaw, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { recycleColumns } from './user.data';
|
||||
import { getQuitList, putCancelQuit, deleteRecycleBin } from './user.api';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const [registerModal] = useModalInner(() => {
|
||||
checkedKeys.value = [];
|
||||
});
|
||||
//注册table数据
|
||||
const { prefixCls, tableContext } = useListPage({
|
||||
tableProps: {
|
||||
api: getQuitList,
|
||||
columns: recycleColumns,
|
||||
rowKey: 'id',
|
||||
canResize: false,
|
||||
useSearchForm: false,
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
},
|
||||
},
|
||||
});
|
||||
//注册table数据
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
|
||||
|
||||
/**
|
||||
* 取消离职事件
|
||||
* @param record
|
||||
*/
|
||||
async function handleCancelQuit(record) {
|
||||
await putCancelQuit({ userIds: record.id, usernames: record.username }, reload);
|
||||
emit('success');
|
||||
}
|
||||
/**
|
||||
* 批量取消离职事件
|
||||
*/
|
||||
function batchHandleRevert() {
|
||||
Modal.confirm({
|
||||
title: '取消离职',
|
||||
content: '取消离职交接人也会清空',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
let rowValue = selectedRows.value;
|
||||
let rowData: any = [];
|
||||
for (const value of rowValue) {
|
||||
rowData.push(value.username);
|
||||
}
|
||||
handleCancelQuit({ id: toRaw(unref(selectedRowKeys)).join(','), username: rowData.join(',') });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//获取操作栏事件
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '取消离职',
|
||||
icon: 'ant-design:redo-outlined',
|
||||
popConfirm: {
|
||||
title: '是否取消离职,取消离职交接人也会清空',
|
||||
confirm: handleCancelQuit.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.ant-popover-inner-content){
|
||||
width: 185px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="用户回收站" :showOkBtn="false" width="1000px" destroyOnClose @fullScreen="handleFullScreen">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection" :scroll="scroll">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-dropdown v-if="checkedKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
批量删除
|
||||
</a-menu-item>
|
||||
<a-menu-item key="1" @click="batchHandleRevert">
|
||||
<Icon icon="ant-design:redo-outlined"></Icon>
|
||||
批量还原
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRaw, unref, watch } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { recycleColumns } from './user.data';
|
||||
import { getRecycleBinList, putRecycleBin, deleteRecycleBin } from './user.api';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const [registerModal] = useModalInner(() => {
|
||||
checkedKeys.value = [];
|
||||
});
|
||||
const scroll = ref({ y: 0 });
|
||||
//注册table数据
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: getRecycleBinList,
|
||||
columns: recycleColumns,
|
||||
rowKey: 'id',
|
||||
striped: true,
|
||||
useSearchForm: false,
|
||||
showTableSetting: false,
|
||||
clickToRowSelect: false,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
pagination: true,
|
||||
tableSetting: { fullScreen: true },
|
||||
canResize: false,
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
// slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
// update-begin--author:liaozhiyang---date:20240704---for:【TV360X-1657】系统用户回收站弹窗分页展示在可视区内
|
||||
const handleFullScreen = (maximize) => {
|
||||
setTableHeight(maximize);
|
||||
};
|
||||
const setTableHeight = (maximize) => {
|
||||
const clientHeight = document.documentElement.clientHeight;
|
||||
scroll.value = {
|
||||
y: clientHeight - (maximize ? 300 : 500),
|
||||
};
|
||||
};
|
||||
setTableHeight(false);
|
||||
watch(
|
||||
checkedKeys,
|
||||
(newValue, oldValue) => {
|
||||
if (checkedKeys.value.length && oldValue.length == 0) {
|
||||
scroll.value = {
|
||||
y: scroll.value.y - 50,
|
||||
};
|
||||
} else if (checkedKeys.value.length == 0 && oldValue.length) {
|
||||
scroll.value = {
|
||||
y: scroll.value.y + 50,
|
||||
};
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
// update-end--author:liaozhiyang---date:20240704---for:【TV360X-1657】系统用户回收站弹窗分页展示在可视区内
|
||||
/**
|
||||
* 选择列配置
|
||||
*/
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
columnWidth: 50,
|
||||
selectedRowKeys: checkedKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
/**
|
||||
* 选择事件
|
||||
*/
|
||||
function onSelectChange(selectedRowKeys: (string | number)[]) {
|
||||
checkedKeys.value = selectedRowKeys;
|
||||
}
|
||||
/**
|
||||
* 还原事件
|
||||
*/
|
||||
async function handleRevert(record) {
|
||||
await putRecycleBin({ userIds: record.id }, reload);
|
||||
emit('success');
|
||||
}
|
||||
/**
|
||||
* 批量还原事件
|
||||
*/
|
||||
function batchHandleRevert() {
|
||||
handleRevert({ id: toRaw(unref(checkedKeys)).join(',') });
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteRecycleBin({ userIds: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
function batchHandleDelete() {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要永久删除吗?删除后将不可恢复!',
|
||||
onOk: () => handleDelete({ id: toRaw(unref(checkedKeys)).join(',') }),
|
||||
onCancel() {},
|
||||
});
|
||||
}
|
||||
//获取操作栏事件
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '取回',
|
||||
icon: 'ant-design:redo-outlined',
|
||||
popConfirm: {
|
||||
title: '是否确认还原',
|
||||
confirm: handleRevert.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '彻底删除',
|
||||
icon: 'ant-design:scissor-outlined',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,299 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls" :disabled="isDisabledAuth('system:user:export')"> 导出</a-button>
|
||||
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||
<a-button type="primary" @click="openModal(true, {})" preIcon="ant-design:hdd-outlined"> 回收站</a-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
删除
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" @click="batchFrozen(2)">
|
||||
<Icon icon="ant-design:lock-outlined"></Icon>
|
||||
冻结
|
||||
</a-menu-item>
|
||||
<a-menu-item key="3" @click="batchFrozen(1)">
|
||||
<Icon icon="ant-design:unlock-outlined"></Icon>
|
||||
解冻
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="mdi:chevron-down"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--用户抽屉-->
|
||||
<UserDrawer @register="registerDrawer" @success="handleSuccess" />
|
||||
<!--修改密码-->
|
||||
<PasswordModal @register="registerPasswordModal" @success="reload" />
|
||||
<!--用户代理-->
|
||||
<UserAgentModal @register="registerAgentModal" @success="reload" />
|
||||
<!--回收站-->
|
||||
<UserRecycleBinModal @register="registerModal" @success="reload" />
|
||||
<!-- 离职受理人弹窗 -->
|
||||
<UserQuitAgentModal @register="registerQuitAgentModal" @success="reload" />
|
||||
<!-- 离职人员列弹窗 -->
|
||||
<UserQuitModal @register="registerQuitModal" @success="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="system-user" setup>
|
||||
//ts语法
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicTable, TableAction, ActionItem } from '/@/components/Table';
|
||||
import UserDrawer from './UserDrawer.vue';
|
||||
import UserRecycleBinModal from './UserRecycleBinModal.vue';
|
||||
import PasswordModal from './PasswordModal.vue';
|
||||
import UserAgentModal from './UserAgentModal.vue';
|
||||
import JThirdAppButton from '/@/components/jeecg/thirdApp/JThirdAppButton.vue';
|
||||
import UserQuitAgentModal from './UserQuitAgentModal.vue';
|
||||
import UserQuitModal from './UserQuitModal.vue';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { columns, searchFormSchema } from './user.data';
|
||||
import { listNoCareTenant, deleteUser, batchDeleteUser, getImportUrl, getExportUrl, frozenBatch } from './user.api';
|
||||
import {usePermission} from "/@/hooks/web/usePermission";
|
||||
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const { isDisabledAuth } = usePermission();
|
||||
//注册drawer
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
//回收站model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
//密码model
|
||||
const [registerPasswordModal, { openModal: openPasswordModal }] = useModal();
|
||||
//代理人model
|
||||
const [registerAgentModal, { openModal: openAgentModal }] = useModal();
|
||||
//离职代理人model
|
||||
const [registerQuitAgentModal, { openModal: openQuitAgentModal }] = useModal();
|
||||
//离职用户列表model
|
||||
const [registerQuitModal, { openModal: openQuitModal }] = useModal();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||
designScope: 'user-list',
|
||||
tableProps: {
|
||||
title: '用户列表',
|
||||
api: listNoCareTenant,
|
||||
columns: columns,
|
||||
size: 'small',
|
||||
formConfig: {
|
||||
// labelWidth: 200,
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return Object.assign({ column: 'createTime', order: 'desc' }, params);
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: '用户列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
});
|
||||
|
||||
//注册table数据
|
||||
const [registerTable, { reload, updateTableDataRecord }, { rowSelection, selectedRows, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleCreate() {
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
showFooter: true,
|
||||
tenantSaas: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
async function handleEdit(record: Recordable) {
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
showFooter: true,
|
||||
tenantSaas: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
async function handleDetail(record: Recordable) {
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
tenantSaas: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
if ('admin' == record.username) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
await deleteUser({ id: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
let hasAdmin = unref(selectedRows).filter((item) => item.username == 'admin');
|
||||
if (unref(hasAdmin).length > 0) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
await batchDeleteUser({ ids: selectedRowKeys.value }, () => {
|
||||
selectedRowKeys.value = [];
|
||||
reload();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开修改密码弹窗
|
||||
*/
|
||||
function handleChangePassword(username) {
|
||||
openPasswordModal(true, { username });
|
||||
}
|
||||
/**
|
||||
* 打开代理人弹窗
|
||||
*/
|
||||
function handleAgentSettings(userName) {
|
||||
openAgentModal(true, { userName });
|
||||
}
|
||||
/**
|
||||
* 冻结解冻
|
||||
*/
|
||||
async function handleFrozen(record, status) {
|
||||
if ('admin' == record.username) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
await frozenBatch({ ids: record.id, status: status }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量冻结解冻
|
||||
*/
|
||||
function batchFrozen(status) {
|
||||
let hasAdmin = selectedRows.value.filter((item) => item.username == 'admin');
|
||||
if (unref(hasAdmin).length > 0) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认操作',
|
||||
content: '是否' + (status == 1 ? '解冻' : '冻结') + '选中账号?',
|
||||
onOk: async () => {
|
||||
await frozenBatch({ ids: unref(selectedRowKeys).join(','), status: status }, reload);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*同步钉钉和微信回调
|
||||
*/
|
||||
function onSyncFinally({ isToLocal }) {
|
||||
// 同步到本地时刷新下数据
|
||||
if (isToLocal) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
// ifShow: () => hasPermission('system:user:edit'),
|
||||
},
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
//auth: 'user:changepwd',
|
||||
onClick: handleChangePassword.bind(null, record.username),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '冻结',
|
||||
ifShow: record.status == 1,
|
||||
popConfirm: {
|
||||
title: '确定冻结吗?',
|
||||
confirm: handleFrozen.bind(null, record, 2),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '解冻',
|
||||
ifShow: record.status == 2,
|
||||
popConfirm: {
|
||||
title: '确定解冻吗?',
|
||||
confirm: handleFrozen.bind(null, record, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '代理人',
|
||||
onClick: handleAgentSettings.bind(null, record.username),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 离职
|
||||
* @param userName
|
||||
*/
|
||||
function handleQuit(userName) {
|
||||
//打开离职代理人弹窗
|
||||
openQuitAgentModal(true, { userName });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,251 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { isObject } from '/@/utils/is';
|
||||
enum Api {
|
||||
listNoCareTenant = '/sys/user/listAllExcludeAdmin',
|
||||
list = '/sys/user/list',
|
||||
save = '/sys/user/add',
|
||||
edit = '/sys/user/edit',
|
||||
agentSave = '/sys/sysUserAgent/add',
|
||||
agentEdit = '/sys/sysUserAgent/edit',
|
||||
getUserRole = '/sys/user/queryUserRole',
|
||||
duplicateCheck = '/sys/duplicate/check',
|
||||
deleteUser = '/sys/user/delete',
|
||||
deleteBatch = '/sys/user/deleteBatch',
|
||||
importExcel = '/sys/user/importExcel',
|
||||
exportXls = '/sys/user/exportXls',
|
||||
recycleBinList = '/sys/user/recycleBin',
|
||||
putRecycleBin = '/sys/user/putRecycleBin',
|
||||
deleteRecycleBin = '/sys/user/deleteRecycleBin',
|
||||
allRolesList = '/sys/role/queryall',
|
||||
allRolesListNoByTenant = '/sys/role/queryallNoByTenant',
|
||||
allTenantList = '/sys/tenant/queryList',
|
||||
allPostList = '/sys/position/list',
|
||||
userDepartList = '/sys/user/userDepartList',
|
||||
changePassword = '/sys/user/changePassword',
|
||||
frozenBatch = '/sys/user/frozenBatch',
|
||||
getUserAgent = '/sys/sysUserAgent/queryByUserName',
|
||||
userQuitAgent = '/sys/user/userQuitAgent',
|
||||
getQuitList = '/sys/user/getQuitList',
|
||||
putCancelQuit = '/sys/user/putCancelQuit',
|
||||
updateUserTenantStatus='/sys/tenant/updateUserTenantStatus',
|
||||
getUserTenantPageList='/sys/tenant/getUserTenantPageList',
|
||||
}
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 列表接口(查询用户,通过租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 列表接口(查询全部用户,不通过租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const listNoCareTenant = (params) => defHttp.get({ url: Api.listNoCareTenant, params });
|
||||
|
||||
/**
|
||||
* 用户角色接口
|
||||
* @param params
|
||||
*/
|
||||
export const getUserRoles = (params) => defHttp.get({ url: Api.getUserRole, params }, { errorMessageMode: 'none' });
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
export const deleteUser = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteUser, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除用户
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteUser = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新用户
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateUser = (params, isUpdate) => {
|
||||
let url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 唯一校验
|
||||
* @param params
|
||||
*/
|
||||
export const duplicateCheck = (params) => defHttp.get({ url: Api.duplicateCheck, params }, { isTransformResponse: false });
|
||||
|
||||
/**
|
||||
* 20231215
|
||||
* liaozhiyang
|
||||
* 唯一校验( 延迟【防抖】)
|
||||
* @param params
|
||||
*/
|
||||
const timer = {};
|
||||
export const duplicateCheckDelay = (params) => {
|
||||
return new Promise((resove, rejected) => {
|
||||
// -update-begin--author:liaozhiyang---date:20240619---for:【TV360X-1380】表单中使用多个duplicateCheckDelay,validate方法调用时会导致promise被挂起保存不了
|
||||
let key;
|
||||
if (isObject(params)) {
|
||||
key = `${params.tableName}_${params.fieldName}`;
|
||||
} else {
|
||||
key = params;
|
||||
}
|
||||
clearTimeout(timer[key]);
|
||||
// -update-end--author:liaozhiyang---date:20240619---for:【TV360X-1380】表单中使用多个duplicateCheckDelay,validate方法调用时会导致promise被挂起保存不了
|
||||
timer[key] = setTimeout(() => {
|
||||
defHttp
|
||||
.get({ url: Api.duplicateCheck, params }, { isTransformResponse: false })
|
||||
.then((res: any) => {
|
||||
resove(res as any);
|
||||
})
|
||||
.catch((error) => {
|
||||
rejected(error);
|
||||
});
|
||||
delete timer[key];
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取全部角色(租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const getAllRolesList = (params) => defHttp.get({ url: Api.allRolesList, params });
|
||||
/**
|
||||
* 获取全部角色(不租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const getAllRolesListNoByTenant = (params) => defHttp.get({ url: Api.allRolesListNoByTenant, params });
|
||||
/**
|
||||
* 获取全部租户
|
||||
*/
|
||||
export const getAllTenantList = (params) => defHttp.get({ url: Api.allTenantList, params });
|
||||
/**
|
||||
* 获取指定用户负责部门
|
||||
*/
|
||||
export const getUserDepartList = (params) => defHttp.get({ url: Api.userDepartList, params }, { successMessageMode: 'none' });
|
||||
/**
|
||||
* 获取全部职务
|
||||
*/
|
||||
export const getAllPostList = (params) => {
|
||||
return new Promise((resolve) => {
|
||||
defHttp.get({ url: Api.allPostList, params }).then((res) => {
|
||||
resolve(res.records);
|
||||
});
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 回收站列表
|
||||
* @param params
|
||||
*/
|
||||
export const getRecycleBinList = (params) => defHttp.get({ url: Api.recycleBinList, params });
|
||||
/**
|
||||
* 回收站还原
|
||||
* @param params
|
||||
*/
|
||||
export const putRecycleBin = (params, handleSuccess) => {
|
||||
return defHttp.put({ url: Api.putRecycleBin, params }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 回收站删除
|
||||
* @param params
|
||||
*/
|
||||
export const deleteRecycleBin = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteRecycleBin, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 修改密码
|
||||
* @param params
|
||||
*/
|
||||
export const changePassword = (params) => {
|
||||
return defHttp.put({ url: Api.changePassword, params });
|
||||
};
|
||||
/**
|
||||
* 冻结解冻
|
||||
* @param params
|
||||
*/
|
||||
export const frozenBatch = (params, handleSuccess) => {
|
||||
return defHttp.put({ url: Api.frozenBatch, params }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取用户代理
|
||||
* @param params
|
||||
*/
|
||||
export const getUserAgent = (params) => defHttp.get({ url: Api.getUserAgent, params }, { isTransformResponse: false });
|
||||
/**
|
||||
* 保存或者更新用户代理
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateAgent = (params) => {
|
||||
let url = params.id ? Api.agentEdit : Api.agentSave;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户离职(新增代理人和用户状态变更操作)
|
||||
* @param params
|
||||
*/
|
||||
export const userQuitAgent = (params) => {
|
||||
return defHttp.put({ url: Api.userQuitAgent, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户离职列表
|
||||
* @param params
|
||||
*/
|
||||
export const getQuitList = (params) => {
|
||||
return defHttp.get({ url: Api.getQuitList, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消离职
|
||||
* @param params
|
||||
*/
|
||||
export const putCancelQuit = (params, handleSuccess) => {
|
||||
return defHttp.put({ url: Api.putCancelQuit, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 待审批获取列表数据
|
||||
*/
|
||||
export const getUserTenantPageList = (params) => {
|
||||
return defHttp.get({ url: Api.getUserTenantPageList, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新租户状态
|
||||
* @param params
|
||||
*/
|
||||
export const updateUserTenantStatus = (params) => {
|
||||
return defHttp.put({ url: Api.updateUserTenantStatus, params }, { joinParamsToUrl: true, isTransformResponse: false });
|
||||
};
|
||||
@@ -0,0 +1,618 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
import { getAllRolesListNoByTenant, getAllTenantList } from './user.api';
|
||||
import { rules } from '/@/utils/helper/validator';
|
||||
import { render } from '/@/utils/common/renderUtils';
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
dataIndex: 'realname',
|
||||
width: 100,
|
||||
},
|
||||
// {
|
||||
// title: '头像',
|
||||
// dataIndex: 'avatar',
|
||||
// width: 120,
|
||||
// customRender: render.renderAvatar,
|
||||
// },
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sex',
|
||||
width: 80,
|
||||
sorter: true,
|
||||
customRender: ({ text }) => {
|
||||
return render.renderDict(text, 'sex');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '生日',
|
||||
dataIndex: 'birthday',
|
||||
width: 100,
|
||||
},
|
||||
// {
|
||||
// title: '手机号',
|
||||
// dataIndex: 'phone',
|
||||
// width: 100,
|
||||
// },
|
||||
{
|
||||
title: '部门',
|
||||
width: 150,
|
||||
dataIndex: 'orgCodeTxt',
|
||||
},
|
||||
{
|
||||
title: '负责部门',
|
||||
width: 150,
|
||||
dataIndex: 'departIds_dictText',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status_dictText',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '密级',
|
||||
width: 150,
|
||||
dataIndex: 'userSecurityLevel_dictText',
|
||||
},
|
||||
{
|
||||
title: '是否专项组成员',
|
||||
width: 150,
|
||||
dataIndex: 'isSpecial_dictText',
|
||||
},
|
||||
];
|
||||
|
||||
export const recycleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
dataIndex: 'realname',
|
||||
width: 100,
|
||||
},
|
||||
// {
|
||||
// title: '头像',
|
||||
// dataIndex: 'avatar',
|
||||
// width: 80,
|
||||
// customRender: render.renderAvatar,
|
||||
// },
|
||||
{
|
||||
title: '性别',
|
||||
dataIndex: 'sex',
|
||||
width: 80,
|
||||
sorter: true,
|
||||
customRender: ({ text }) => {
|
||||
return render.renderDict(text, 'sex');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '账号',
|
||||
field: 'username',
|
||||
component: 'JInput',
|
||||
//colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '名字',
|
||||
field: 'realname',
|
||||
component: 'JInput',
|
||||
//colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '性别',
|
||||
field: 'sex',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'sex',
|
||||
placeholder: '请选择性别',
|
||||
stringToNumber: true,
|
||||
},
|
||||
//colProps: { span: 6 },
|
||||
},
|
||||
// {
|
||||
// label: '手机号码',
|
||||
// field: 'phone',
|
||||
// component: 'Input',
|
||||
// //colProps: { span: 6 },
|
||||
// },
|
||||
{
|
||||
label: '用户状态',
|
||||
field: 'status',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'user_status',
|
||||
placeholder: '请选择状态',
|
||||
stringToNumber: true,
|
||||
},
|
||||
},
|
||||
//colProps: { span: 6 },
|
||||
{
|
||||
label: '密级',
|
||||
field: 'phone',
|
||||
component: 'Input',
|
||||
//colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '手机号码',
|
||||
field: 'phone',
|
||||
component: 'Input',
|
||||
//colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '用户账号',
|
||||
field: 'username',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
dynamicRules: ({ model, schema }) => rules.duplicateCheckRule('sys_user', 'username', model, schema, true),
|
||||
},
|
||||
{
|
||||
label: '登录密码',
|
||||
field: 'password',
|
||||
component: 'StrengthMeter',
|
||||
componentProps: {
|
||||
autocomplete: 'new-password',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入登录密码',
|
||||
},
|
||||
{
|
||||
pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/,
|
||||
message: '密码由8位数字、大小写字母和特殊符号组成!',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '确认密码',
|
||||
field: 'confirmPassword',
|
||||
component: 'InputPassword',
|
||||
dynamicRules: ({ values }) => rules.confirmPassword(values, true),
|
||||
},
|
||||
{
|
||||
label: '用户姓名',
|
||||
field: 'realname',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '工号',
|
||||
field: 'workNo',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
dynamicRules: ({ model, schema }) => rules.duplicateCheckRule('sys_user', 'work_no', model, schema, true),
|
||||
},
|
||||
{
|
||||
label: '职务',
|
||||
field: 'post',
|
||||
required: false,
|
||||
component: 'JSelectPosition',
|
||||
componentProps: {
|
||||
labelKey: 'name',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '角色',
|
||||
field: 'selectedroles',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
mode: 'multiple',
|
||||
api: getAllRolesListNoByTenant,
|
||||
labelField: 'roleName',
|
||||
valueField: 'id',
|
||||
immediate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '所属部门',
|
||||
field: 'selecteddeparts',
|
||||
component: 'JSelectDept',
|
||||
componentProps: ({ formActionType, formModel }) => {
|
||||
return {
|
||||
sync: false,
|
||||
checkStrictly: true,
|
||||
defaultExpandLevel: 2,
|
||||
|
||||
onSelect: (options, values) => {
|
||||
const { updateSchema } = formActionType;
|
||||
//所属部门修改后更新负责部门下拉框数据
|
||||
updateSchema([
|
||||
{
|
||||
field: 'departIds',
|
||||
componentProps: { options },
|
||||
},
|
||||
]);
|
||||
//update-begin---author:wangshuai---date:2024-05-11---for:【issues/1222】用户编辑界面“所属部门”与“负责部门”联动出错整---
|
||||
if (!values) {
|
||||
formModel.departIds = [];
|
||||
return;
|
||||
}
|
||||
//update-end---author:wangshuai---date:2024-05-11---for:【issues/1222】用户编辑界面“所属部门”与“负责部门”联动出错整---
|
||||
//所属部门修改后更新负责部门数据
|
||||
formModel.departIds && (formModel.departIds = formModel.departIds.filter((item) => values.value.indexOf(item) > -1));
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '租户',
|
||||
field: 'relTenantIds',
|
||||
component: 'JSearchSelect',
|
||||
componentProps: {
|
||||
dict: 'sys_tenant,name,id',
|
||||
async: true,
|
||||
multiple: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '身份',
|
||||
field: 'userIdentity',
|
||||
component: 'RadioGroup',
|
||||
defaultValue: 1,
|
||||
componentProps: ({ formModel }) => {
|
||||
return {
|
||||
options: [
|
||||
{ label: '普通用户', value: 1, key: '1' },
|
||||
{ label: '上级', value: 2, key: '2' },
|
||||
],
|
||||
onChange: () => {
|
||||
formModel.userIdentity == 1 && (formModel.departIds = []);
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '负责部门',
|
||||
field: 'departIds',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
mode: 'multiple',
|
||||
},
|
||||
ifShow: ({ values }) => values.userIdentity == 2,
|
||||
},
|
||||
// {
|
||||
// label: '头像',
|
||||
// field: 'avatar',
|
||||
// component: 'JImageUpload',
|
||||
// componentProps: {
|
||||
// fileMax: 1,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
label: '生日',
|
||||
field: 'birthday',
|
||||
component: 'DatePicker',
|
||||
},
|
||||
{
|
||||
label: '性别',
|
||||
field: 'sex',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'sex',
|
||||
placeholder: '请选择性别',
|
||||
stringToNumber: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '邮箱',
|
||||
// field: 'email',
|
||||
// component: 'Input',
|
||||
// required: true,
|
||||
// dynamicRules: ({ model, schema }) => {
|
||||
// return [
|
||||
// { ...rules.duplicateCheckRule('sys_user', 'email', model, schema, true)[0], trigger: 'blur' },
|
||||
// { ...rules.rule('email', false)[0], trigger: 'blur' },
|
||||
// ];
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// label: '手机号码',
|
||||
// field: 'phone',
|
||||
// component: 'Input',
|
||||
// required: true,
|
||||
// dynamicRules: ({ model, schema }) => {
|
||||
// return [
|
||||
// { ...rules.duplicateCheckRule('sys_user', 'phone', model, schema, true)[0], trigger: 'blur' },
|
||||
// { pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误', trigger: 'blur' },
|
||||
// ];
|
||||
// },
|
||||
// },
|
||||
{
|
||||
label: '座机',
|
||||
field: 'telephone',
|
||||
component: 'Input',
|
||||
rules: [{ pattern: /^0\d{2,3}-[1-9]\d{6,7}$/, message: '请输入正确的座机号码' }],
|
||||
},
|
||||
{
|
||||
label: '工作流引擎',
|
||||
field: 'activitiSync',
|
||||
defaultValue: 1,
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'activiti_sync',
|
||||
type: 'radio',
|
||||
stringToNumber: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '密级',
|
||||
field: 'user_security_level',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'user_security_level',
|
||||
placeholder: '请选择密级',
|
||||
stringToNumber: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '专项组成员',
|
||||
field: 'is_special',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'is_special',
|
||||
placeholder: '请选择是否为专项组成员',
|
||||
stringToNumber: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formPasswordSchema: FormSchema[] = [
|
||||
{
|
||||
label: '用户账号',
|
||||
field: 'username',
|
||||
component: 'Input',
|
||||
componentProps: { readOnly: true },
|
||||
},
|
||||
{
|
||||
label: '登录密码',
|
||||
field: 'password',
|
||||
component: 'StrengthMeter',
|
||||
componentProps: {
|
||||
placeholder: '请输入登录密码',
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入登录密码',
|
||||
},
|
||||
{
|
||||
pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/,
|
||||
message: '密码由8位数字、大小写字母和特殊符号组成!',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '确认密码',
|
||||
field: 'confirmPassword',
|
||||
component: 'InputPassword',
|
||||
dynamicRules: ({ values }) => rules.confirmPassword(values, true),
|
||||
},
|
||||
];
|
||||
|
||||
export const formAgentSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'userName',
|
||||
label: '用户名',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
readOnly: true,
|
||||
allowClear: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'agentUserName',
|
||||
label: '代理人用户名',
|
||||
required: true,
|
||||
component: 'JSelectUser',
|
||||
componentProps: {
|
||||
rowKey: 'username',
|
||||
labelKey: 'realname',
|
||||
maxSelectCount: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'startTime',
|
||||
label: '代理开始时间',
|
||||
component: 'DatePicker',
|
||||
required: true,
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择代理开始时间',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
label: '代理结束时间',
|
||||
component: 'DatePicker',
|
||||
required: true,
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择代理结束时间',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'JDictSelectTag',
|
||||
defaultValue: '1',
|
||||
componentProps: {
|
||||
dictCode: 'valid_status',
|
||||
type: 'radioButton',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const formQuitAgentSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
field: 'userName',
|
||||
label: '用户名',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
readOnly: true,
|
||||
allowClear: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'agentUserName',
|
||||
label: '交接人员',
|
||||
//required: true,
|
||||
component: 'JSelectUser',
|
||||
componentProps: {
|
||||
rowKey: 'username',
|
||||
labelKey: 'realname',
|
||||
maxSelectCount: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'startTime',
|
||||
label: '交接开始时间',
|
||||
component: 'DatePicker',
|
||||
//required: true,
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择交接开始时间',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
label: '交接结束时间',
|
||||
component: 'DatePicker',
|
||||
//required: true,
|
||||
componentProps: {
|
||||
showTime: true,
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
placeholder: '请选择交接结束时间',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '状态',
|
||||
component: 'JDictSelectTag',
|
||||
defaultValue: '1',
|
||||
componentProps: {
|
||||
dictCode: 'valid_status',
|
||||
type: 'radioButton',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
//租户用户列表
|
||||
export const userTenantColumns: BasicColumn[] = [
|
||||
{
|
||||
title: '用户账号',
|
||||
dataIndex: 'username',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
dataIndex: 'realname',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
dataIndex: 'avatar',
|
||||
width: 120,
|
||||
customRender: render.renderAvatar,
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
dataIndex: 'phone',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '部门',
|
||||
width: 150,
|
||||
dataIndex: 'orgCodeTxt',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 80,
|
||||
customRender: ({ text }) => {
|
||||
if (text === '1') {
|
||||
return '正常';
|
||||
} else if (text === '3') {
|
||||
return '审批中';
|
||||
} else {
|
||||
return '已拒绝';
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '密级',
|
||||
width: 150,
|
||||
dataIndex: 'userSecurityLevel_dictText',
|
||||
},
|
||||
{
|
||||
title: '是否专项组成员',
|
||||
width: 150,
|
||||
dataIndex: 'isSpecial_dictText',
|
||||
},
|
||||
];
|
||||
|
||||
//用户租户搜索表单
|
||||
export const userTenantFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '账号',
|
||||
field: 'username',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '名字',
|
||||
field: 'realname',
|
||||
component: 'Input',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '性别',
|
||||
field: 'sex',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: {
|
||||
dictCode: 'sex',
|
||||
placeholder: '请选择性别',
|
||||
stringToNumber: true,
|
||||
},
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<Description @register="register" class="mt-4" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { Description, DescItem, useDescription } from '/@/components/Description/index';
|
||||
const mockData = {
|
||||
username: 'test',
|
||||
nickName: 'VB',
|
||||
age: '123',
|
||||
phone: '15695909xxx',
|
||||
email: '190848757@qq.com',
|
||||
addr: '厦门市思明区',
|
||||
sex: '男',
|
||||
certy: '3504256199xxxxxxxxx',
|
||||
tag: 'orange',
|
||||
};
|
||||
const schema: DescItem[] = [
|
||||
{
|
||||
field: 'username',
|
||||
label: '用户名',
|
||||
},
|
||||
{
|
||||
field: 'nickName',
|
||||
label: '昵称',
|
||||
render: (curVal, data) => {
|
||||
return `${data.username}-${curVal}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
label: '联系电话',
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: '邮箱',
|
||||
},
|
||||
{
|
||||
field: 'addr',
|
||||
label: '地址',
|
||||
},
|
||||
];
|
||||
export default defineComponent({
|
||||
components: { Description },
|
||||
setup() {
|
||||
const [register] = useDescription({
|
||||
title: 'useDescription',
|
||||
data: mockData,
|
||||
schema: schema,
|
||||
});
|
||||
return { mockData, schema, register };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="修改密码" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" name="PassWordModal" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formPasswordSchema } from './user.data';
|
||||
import { changePassword } from './user.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: formPasswordSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
//表单赋值
|
||||
await setFieldsValue({ ...data });
|
||||
});
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await changePassword(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="用户代理" @ok="handleSubmit" destroyOnClose>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formAgentSchema } from './user.data';
|
||||
import { getUserAgent, saveOrUpdateAgent } from './user.api';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
schemas: formAgentSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
//查询获取表单数据
|
||||
const res = await getUserAgent({ userName: data.userName });
|
||||
data = res.result ? res.result : data;
|
||||
//表单赋值
|
||||
await setFieldsValue({ ...data });
|
||||
});
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateAgent(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success');
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
:title="getTitle"
|
||||
:width="adaptiveWidth"
|
||||
@ok="handleSubmit"
|
||||
:showFooter="showFooter"
|
||||
destroyOnClose
|
||||
>
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, ref, computed, unref, useAttrs } from 'vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formSchema } from './user.data';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { saveOrUpdateUser, getUserRoles, getUserDepartList, getAllRolesListNoByTenant, getAllRolesList } from './user.api';
|
||||
import { useDrawerAdaptiveWidth } from '/@/hooks/jeecg/useAdaptiveWidth';
|
||||
import { getTenantId } from "/@/utils/auth";
|
||||
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const attrs = useAttrs();
|
||||
const isUpdate = ref(true);
|
||||
const rowId = ref('');
|
||||
const departOptions = ref([]);
|
||||
let isFormDepartUser = false;
|
||||
//表单配置
|
||||
const [registerForm, { setProps, resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
labelWidth: 90,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
// TODO [VUEN-527] https://www.teambition.com/task/6239beb894b358003fe93626
|
||||
const showFooter = ref(true);
|
||||
//表单赋值
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
await resetFields();
|
||||
showFooter.value = data?.showFooter ?? true;
|
||||
setDrawerProps({ confirmLoading: false, showFooter: showFooter.value });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.record.id;
|
||||
//租户信息定义成数组
|
||||
/* if (data.record.relTenantIds && !Array.isArray(data.record.relTenantIds)) {
|
||||
data.record.relTenantIds = data.record.relTenantIds.split(',');
|
||||
} else {
|
||||
//【issues/I56C5I】用户管理中连续点两次编辑租户配置就丢失了
|
||||
//data.record.relTenantIds = [];
|
||||
}*/
|
||||
|
||||
//查角色/赋值/try catch 处理,不然编辑有问题
|
||||
try {
|
||||
const userRoles = await getUserRoles({ userid: data.record.id });
|
||||
if (userRoles && userRoles.length > 0) {
|
||||
data.record.selectedroles = userRoles;
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
//查所属部门/赋值
|
||||
const userDepart = await getUserDepartList({ userId: data.record.id });
|
||||
if (userDepart && userDepart.length > 0) {
|
||||
data.record.selecteddeparts = userDepart;
|
||||
let selectDepartKeys = Array.from(userDepart, ({ key }) => key);
|
||||
data.record.selecteddeparts = selectDepartKeys.join(',');
|
||||
departOptions.value = userDepart.map((item) => {
|
||||
return { label: item.title, value: item.key };
|
||||
});
|
||||
}
|
||||
//负责部门/赋值
|
||||
data.record.departIds && !Array.isArray(data.record.departIds) && (data.record.departIds = data.record.departIds.split(','));
|
||||
//update-begin---author:zyf Date:20211210 for:避免空值显示异常------------
|
||||
//update-begin---author:liusq Date:20231008 for:[issues/772]避免空值显示异常------------
|
||||
data.record.departIds = (!data.record.departIds || data.record.departIds == '') ? [] : data.record.departIds;
|
||||
//update-end-----author:liusq Date:20231008 for:[issues/772]避免空值显示异常------------
|
||||
//update-begin---author:zyf Date:20211210 for:避免空值显示异常------------
|
||||
}
|
||||
//处理角色用户列表情况(和角色列表有关系)
|
||||
data.selectedroles && (await setFieldsValue({ selectedroles: data.selectedroles }));
|
||||
// -update-begin--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
isFormDepartUser = data?.departDisabled === true ? true : false;
|
||||
// -update-end--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
//编辑时隐藏密码/角色列表隐藏角色信息/我的部门时隐藏所属部门
|
||||
updateSchema([
|
||||
{
|
||||
field: 'password',
|
||||
// 【QQYUN-8324】
|
||||
ifShow: !unref(isUpdate),
|
||||
},
|
||||
{
|
||||
field: 'confirmPassword',
|
||||
ifShow: !unref(isUpdate),
|
||||
},
|
||||
{
|
||||
field: 'selectedroles',
|
||||
show: !data.isRole,
|
||||
},
|
||||
{
|
||||
field: 'departIds',
|
||||
componentProps: { options: departOptions },
|
||||
},
|
||||
{
|
||||
field: 'selecteddeparts',
|
||||
show: !data?.departDisabled,
|
||||
},
|
||||
{
|
||||
field: 'selectedroles',
|
||||
show: !data?.departDisabled,
|
||||
//update-begin---author:wangshuai ---date:20230424 for:【issues/4844】多租户模式下,新增或编辑用户,选择角色一栏,角色选项没有做租户隔离------------
|
||||
//判断是否为多租户模式
|
||||
componentProps:{
|
||||
api: data.tenantSaas?getAllRolesList:getAllRolesListNoByTenant
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20230424 for:【issues/4844】多租户模式下,新增或编辑用户,选择角色一栏,角色选项没有做租户隔离------------
|
||||
},
|
||||
//update-begin---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
{
|
||||
field: 'relTenantIds',
|
||||
componentProps:{
|
||||
disabled: !!data.tenantSaas,
|
||||
},
|
||||
},
|
||||
//update-end---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
]);
|
||||
//update-begin---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
if(!unref(isUpdate) && data.tenantSaas){
|
||||
await setFieldsValue({ relTenantIds: getTenantId().toString() })
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20230522 for:【issues/4935】租户用户编辑界面中租户下拉框未过滤,显示当前系统所有的租户------------
|
||||
// 无论新增还是编辑,都可以设置表单值
|
||||
if (typeof data.record === 'object') {
|
||||
setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
// 隐藏底部时禁用整个表单
|
||||
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1117【issue】0523周开源问题
|
||||
setProps({ disabled: !showFooter.value });
|
||||
//update-end-author:taoyan date:2022-5-24 for: VUEN-1117【issue】0523周开源问题
|
||||
});
|
||||
//获取标题
|
||||
const getTitle = computed(() => {
|
||||
// update-begin--author:liaozhiyang---date:20240306---for:【QQYUN-8389】系统用户详情抽屉title更改
|
||||
if (!unref(isUpdate)) {
|
||||
return '新增用户';
|
||||
} else {
|
||||
return unref(showFooter) ? '编辑用户' : '用户详情';
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240306---for:【QQYUN-8389】系统用户详情抽屉title更改
|
||||
});
|
||||
const { adaptiveWidth } = useDrawerAdaptiveWidth();
|
||||
|
||||
//提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
let values = await validate();
|
||||
setDrawerProps({ confirmLoading: true });
|
||||
values.userIdentity === 1 && (values.departIds = '');
|
||||
let isUpdateVal = unref(isUpdate);
|
||||
// -update-begin--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
let params = values;
|
||||
if (isFormDepartUser) {
|
||||
params = { ...params, updateFromPage: 'deptUsers' };
|
||||
}
|
||||
// -update-end--author:liaozhiyang---date:20240702---for:【TV360X-1737】部门用户编辑接口,增加参数updateFromPage:"deptUsers"
|
||||
//提交表单
|
||||
await saveOrUpdateUser(params, isUpdateVal);
|
||||
//关闭弹窗
|
||||
closeDrawer();
|
||||
//刷新列表
|
||||
emit('success',{isUpdateVal ,values});
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="离职交接" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup name="user-quit-agent-modal">
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { formQuitAgentSchema } from './user.data';
|
||||
import { getUserAgent, userQuitAgent } from './user.api';
|
||||
import dayjs from 'dayjs';
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
|
||||
schemas: formQuitAgentSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: true });
|
||||
|
||||
let userId = data.userId;
|
||||
//查询获取表单数据
|
||||
const res = await getUserAgent({ userName: data.userName });
|
||||
data = res.result ? res.result : data;
|
||||
let date = new Date();
|
||||
if (!data.startTime) {
|
||||
data.startTime = dayjs(date).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
if (!data.endTime) {
|
||||
data.endTime = getYear(date);
|
||||
}
|
||||
|
||||
//update-begin---author:wangshuai ---date:20230703 for:【QQYUN-5685】5、离职人员可以选自己------------
|
||||
await updateSchema(
|
||||
[{
|
||||
field:'agentUserName',
|
||||
componentProps:{
|
||||
excludeUserIdList:[userId]
|
||||
}
|
||||
}]
|
||||
)
|
||||
//update-end---author:wangshuai ---date:20230703 for:【QQYUN-5685】5、离职人员可以选自己------------
|
||||
//表单赋值
|
||||
await setFieldsValue({ ...data });
|
||||
setModalProps({ confirmLoading: false });
|
||||
});
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await userQuitAgent(values);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//刷新列表
|
||||
emit('success',values.userName);
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后30年
|
||||
*/
|
||||
function getYear(date) {
|
||||
//update-begin---author:wangshuai ---date:20221207 for:[QQYUN-3285]交接人设置 结束时间有问题------------
|
||||
//这是一个数值
|
||||
let y = date.getFullYear() + 30;
|
||||
let m = dayjs(date).format('MM');
|
||||
let d = dayjs(date).format('DD');
|
||||
let hour = dayjs(date).format('HH:mm:ss');
|
||||
console.log('年月日', y + '-' + m + '-' + d);
|
||||
return dayjs(y + '-' + m + '-' + d + ' ' + hour).format('YYYY-MM-DD HH:mm:ss');
|
||||
//update-end---author:wangshuai ---date:20221207 for:[QQYUN-3285]交接人设置 结束时间有问题--------------
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="离职人员信息" :showOkBtn="false" width="1000px" destroyOnClose>
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleRevert">
|
||||
<Icon icon="ant-design:redo-outlined"></Icon>
|
||||
批量取消
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="user-quit-modal">
|
||||
import { ref, toRaw, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { recycleColumns } from './user.data';
|
||||
import { getQuitList, putCancelQuit, deleteRecycleBin } from './user.api';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const [registerModal] = useModalInner(() => {
|
||||
checkedKeys.value = [];
|
||||
});
|
||||
//注册table数据
|
||||
const { prefixCls, tableContext } = useListPage({
|
||||
tableProps: {
|
||||
api: getQuitList,
|
||||
columns: recycleColumns,
|
||||
rowKey: 'id',
|
||||
canResize: false,
|
||||
useSearchForm: false,
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
},
|
||||
},
|
||||
});
|
||||
//注册table数据
|
||||
const [registerTable, { reload }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
|
||||
|
||||
/**
|
||||
* 取消离职事件
|
||||
* @param record
|
||||
*/
|
||||
async function handleCancelQuit(record) {
|
||||
await putCancelQuit({ userIds: record.id, usernames: record.username }, reload);
|
||||
emit('success');
|
||||
}
|
||||
/**
|
||||
* 批量取消离职事件
|
||||
*/
|
||||
function batchHandleRevert() {
|
||||
Modal.confirm({
|
||||
title: '取消离职',
|
||||
content: '取消离职交接人也会清空',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
let rowValue = selectedRows.value;
|
||||
let rowData: any = [];
|
||||
for (const value of rowValue) {
|
||||
rowData.push(value.username);
|
||||
}
|
||||
handleCancelQuit({ id: toRaw(unref(selectedRowKeys)).join(','), username: rowData.join(',') });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//获取操作栏事件
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '取消离职',
|
||||
icon: 'ant-design:redo-outlined',
|
||||
popConfirm: {
|
||||
title: '是否取消离职,取消离职交接人也会清空',
|
||||
confirm: handleCancelQuit.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.ant-popover-inner-content){
|
||||
width: 185px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="用户回收站" :showOkBtn="false" width="1000px" destroyOnClose @fullScreen="handleFullScreen">
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection" :scroll="scroll">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-dropdown v-if="checkedKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
批量删除
|
||||
</a-menu-item>
|
||||
<a-menu-item key="1" @click="batchHandleRevert">
|
||||
<Icon icon="ant-design:redo-outlined"></Icon>
|
||||
批量还原
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRaw, unref, watch } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { recycleColumns } from './user.data';
|
||||
import { getRecycleBinList, putRecycleBin, deleteRecycleBin } from './user.api';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
// 声明Emits
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const checkedKeys = ref<Array<string | number>>([]);
|
||||
const [registerModal] = useModalInner(() => {
|
||||
checkedKeys.value = [];
|
||||
});
|
||||
const scroll = ref({ y: 0 });
|
||||
//注册table数据
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: getRecycleBinList,
|
||||
columns: recycleColumns,
|
||||
rowKey: 'id',
|
||||
striped: true,
|
||||
useSearchForm: false,
|
||||
showTableSetting: false,
|
||||
clickToRowSelect: false,
|
||||
bordered: true,
|
||||
showIndexColumn: false,
|
||||
pagination: true,
|
||||
tableSetting: { fullScreen: true },
|
||||
canResize: false,
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
// slots: { customRender: 'action' },
|
||||
fixed: undefined,
|
||||
},
|
||||
});
|
||||
// update-begin--author:liaozhiyang---date:20240704---for:【TV360X-1657】系统用户回收站弹窗分页展示在可视区内
|
||||
const handleFullScreen = (maximize) => {
|
||||
setTableHeight(maximize);
|
||||
};
|
||||
const setTableHeight = (maximize) => {
|
||||
const clientHeight = document.documentElement.clientHeight;
|
||||
scroll.value = {
|
||||
y: clientHeight - (maximize ? 300 : 500),
|
||||
};
|
||||
};
|
||||
setTableHeight(false);
|
||||
watch(
|
||||
checkedKeys,
|
||||
(newValue, oldValue) => {
|
||||
if (checkedKeys.value.length && oldValue.length == 0) {
|
||||
scroll.value = {
|
||||
y: scroll.value.y - 50,
|
||||
};
|
||||
} else if (checkedKeys.value.length == 0 && oldValue.length) {
|
||||
scroll.value = {
|
||||
y: scroll.value.y + 50,
|
||||
};
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
// update-end--author:liaozhiyang---date:20240704---for:【TV360X-1657】系统用户回收站弹窗分页展示在可视区内
|
||||
/**
|
||||
* 选择列配置
|
||||
*/
|
||||
const rowSelection = {
|
||||
type: 'checkbox',
|
||||
columnWidth: 50,
|
||||
selectedRowKeys: checkedKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
/**
|
||||
* 选择事件
|
||||
*/
|
||||
function onSelectChange(selectedRowKeys: (string | number)[]) {
|
||||
checkedKeys.value = selectedRowKeys;
|
||||
}
|
||||
/**
|
||||
* 还原事件
|
||||
*/
|
||||
async function handleRevert(record) {
|
||||
await putRecycleBin({ userIds: record.id }, reload);
|
||||
emit('success');
|
||||
}
|
||||
/**
|
||||
* 批量还原事件
|
||||
*/
|
||||
function batchHandleRevert() {
|
||||
handleRevert({ id: toRaw(unref(checkedKeys)).join(',') });
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteRecycleBin({ userIds: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
function batchHandleDelete() {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '删除',
|
||||
content: '确定要永久删除吗?删除后将不可恢复!',
|
||||
onOk: () => handleDelete({ id: toRaw(unref(checkedKeys)).join(',') }),
|
||||
onCancel() {},
|
||||
});
|
||||
}
|
||||
//获取操作栏事件
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '取回',
|
||||
icon: 'ant-design:redo-outlined',
|
||||
popConfirm: {
|
||||
title: '是否确认还原',
|
||||
confirm: handleRevert.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '彻底删除',
|
||||
icon: 'ant-design:scissor-outlined',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,299 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls" :disabled="isDisabledAuth('system:user:export')"> 导出</a-button>
|
||||
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||
<a-button type="primary" @click="openModal(true, {})" preIcon="ant-design:hdd-outlined"> 回收站</a-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
删除
|
||||
</a-menu-item>
|
||||
<a-menu-item key="2" @click="batchFrozen(2)">
|
||||
<Icon icon="ant-design:lock-outlined"></Icon>
|
||||
冻结
|
||||
</a-menu-item>
|
||||
<a-menu-item key="3" @click="batchFrozen(1)">
|
||||
<Icon icon="ant-design:unlock-outlined"></Icon>
|
||||
解冻
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="mdi:chevron-down"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--用户抽屉-->
|
||||
<UserDrawer @register="registerDrawer" @success="handleSuccess" />
|
||||
<!--修改密码-->
|
||||
<PasswordModal @register="registerPasswordModal" @success="reload" />
|
||||
<!--用户代理-->
|
||||
<UserAgentModal @register="registerAgentModal" @success="reload" />
|
||||
<!--回收站-->
|
||||
<UserRecycleBinModal @register="registerModal" @success="reload" />
|
||||
<!-- 离职受理人弹窗 -->
|
||||
<UserQuitAgentModal @register="registerQuitAgentModal" @success="reload" />
|
||||
<!-- 离职人员列弹窗 -->
|
||||
<UserQuitModal @register="registerQuitModal" @success="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="system-user" setup>
|
||||
//ts语法
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicTable, TableAction, ActionItem } from '/@/components/Table';
|
||||
import UserDrawer from './UserDrawer.vue';
|
||||
import UserRecycleBinModal from './UserRecycleBinModal.vue';
|
||||
import PasswordModal from './PasswordModal.vue';
|
||||
import UserAgentModal from './UserAgentModal.vue';
|
||||
import JThirdAppButton from '/@/components/jeecg/thirdApp/JThirdAppButton.vue';
|
||||
import UserQuitAgentModal from './UserQuitAgentModal.vue';
|
||||
import UserQuitModal from './UserQuitModal.vue';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { columns, searchFormSchema } from './user.data';
|
||||
import { listNoCareTenant, deleteUser, batchDeleteUser, getImportUrl, getExportUrl, frozenBatch } from './user.api';
|
||||
import {usePermission} from "/@/hooks/web/usePermission";
|
||||
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const { isDisabledAuth } = usePermission();
|
||||
//注册drawer
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
//回收站model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
//密码model
|
||||
const [registerPasswordModal, { openModal: openPasswordModal }] = useModal();
|
||||
//代理人model
|
||||
const [registerAgentModal, { openModal: openAgentModal }] = useModal();
|
||||
//离职代理人model
|
||||
const [registerQuitAgentModal, { openModal: openQuitAgentModal }] = useModal();
|
||||
//离职用户列表model
|
||||
const [registerQuitModal, { openModal: openQuitModal }] = useModal();
|
||||
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||
designScope: 'user-list',
|
||||
tableProps: {
|
||||
title: '用户列表',
|
||||
api: listNoCareTenant,
|
||||
columns: columns,
|
||||
size: 'small',
|
||||
formConfig: {
|
||||
// labelWidth: 200,
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
return Object.assign({ column: 'createTime', order: 'desc' }, params);
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: '用户列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
});
|
||||
|
||||
//注册table数据
|
||||
const [registerTable, { reload, updateTableDataRecord }, { rowSelection, selectedRows, selectedRowKeys }] = tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleCreate() {
|
||||
openDrawer(true, {
|
||||
isUpdate: false,
|
||||
showFooter: true,
|
||||
tenantSaas: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
async function handleEdit(record: Recordable) {
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
showFooter: true,
|
||||
tenantSaas: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
async function handleDetail(record: Recordable) {
|
||||
openDrawer(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
showFooter: false,
|
||||
tenantSaas: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
if ('admin' == record.username) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
await deleteUser({ id: record.id }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
let hasAdmin = unref(selectedRows).filter((item) => item.username == 'admin');
|
||||
if (unref(hasAdmin).length > 0) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
await batchDeleteUser({ ids: selectedRowKeys.value }, () => {
|
||||
selectedRowKeys.value = [];
|
||||
reload();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开修改密码弹窗
|
||||
*/
|
||||
function handleChangePassword(username) {
|
||||
openPasswordModal(true, { username });
|
||||
}
|
||||
/**
|
||||
* 打开代理人弹窗
|
||||
*/
|
||||
function handleAgentSettings(userName) {
|
||||
openAgentModal(true, { userName });
|
||||
}
|
||||
/**
|
||||
* 冻结解冻
|
||||
*/
|
||||
async function handleFrozen(record, status) {
|
||||
if ('admin' == record.username) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
await frozenBatch({ ids: record.id, status: status }, reload);
|
||||
}
|
||||
/**
|
||||
* 批量冻结解冻
|
||||
*/
|
||||
function batchFrozen(status) {
|
||||
let hasAdmin = selectedRows.value.filter((item) => item.username == 'admin');
|
||||
if (unref(hasAdmin).length > 0) {
|
||||
createMessage.warning('管理员账号不允许此操作!');
|
||||
return;
|
||||
}
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认操作',
|
||||
content: '是否' + (status == 1 ? '解冻' : '冻结') + '选中账号?',
|
||||
onOk: async () => {
|
||||
await frozenBatch({ ids: unref(selectedRowKeys).join(','), status: status }, reload);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*同步钉钉和微信回调
|
||||
*/
|
||||
function onSyncFinally({ isToLocal }) {
|
||||
// 同步到本地时刷新下数据
|
||||
if (isToLocal) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
// ifShow: () => hasPermission('system:user:edit'),
|
||||
},
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '密码',
|
||||
//auth: 'user:changepwd',
|
||||
onClick: handleChangePassword.bind(null, record.username),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '冻结',
|
||||
ifShow: record.status == 1,
|
||||
popConfirm: {
|
||||
title: '确定冻结吗?',
|
||||
confirm: handleFrozen.bind(null, record, 2),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '解冻',
|
||||
ifShow: record.status == 2,
|
||||
popConfirm: {
|
||||
title: '确定解冻吗?',
|
||||
confirm: handleFrozen.bind(null, record, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '代理人',
|
||||
onClick: handleAgentSettings.bind(null, record.username),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 离职
|
||||
* @param userName
|
||||
*/
|
||||
function handleQuit(userName) {
|
||||
//打开离职代理人弹窗
|
||||
openQuitAgentModal(true, { userName });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,251 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { isObject } from '/@/utils/is';
|
||||
enum Api {
|
||||
listNoCareTenant = '/sys/user/listAll',
|
||||
list = '/sys/user/list',
|
||||
save = '/sys/user/add',
|
||||
edit = '/sys/user/edit',
|
||||
agentSave = '/sys/sysUserAgent/add',
|
||||
agentEdit = '/sys/sysUserAgent/edit',
|
||||
getUserRole = '/sys/user/queryUserRole',
|
||||
duplicateCheck = '/sys/duplicate/check',
|
||||
deleteUser = '/sys/user/delete',
|
||||
deleteBatch = '/sys/user/deleteBatch',
|
||||
importExcel = '/sys/user/importExcel',
|
||||
exportXls = '/sys/user/exportXls',
|
||||
recycleBinList = '/sys/user/recycleBin',
|
||||
putRecycleBin = '/sys/user/putRecycleBin',
|
||||
deleteRecycleBin = '/sys/user/deleteRecycleBin',
|
||||
allRolesList = '/sys/role/queryall',
|
||||
allRolesListNoByTenant = '/sys/role/queryallNoByTenant',
|
||||
allTenantList = '/sys/tenant/queryList',
|
||||
allPostList = '/sys/position/list',
|
||||
userDepartList = '/sys/user/userDepartList',
|
||||
changePassword = '/sys/user/changePassword',
|
||||
frozenBatch = '/sys/user/frozenBatch',
|
||||
getUserAgent = '/sys/sysUserAgent/queryByUserName',
|
||||
userQuitAgent = '/sys/user/userQuitAgent',
|
||||
getQuitList = '/sys/user/getQuitList',
|
||||
putCancelQuit = '/sys/user/putCancelQuit',
|
||||
updateUserTenantStatus='/sys/tenant/updateUserTenantStatus',
|
||||
getUserTenantPageList='/sys/tenant/getUserTenantPageList',
|
||||
}
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 列表接口(查询用户,通过租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 列表接口(查询全部用户,不通过租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const listNoCareTenant = (params) => defHttp.get({ url: Api.listNoCareTenant, params });
|
||||
|
||||
/**
|
||||
* 用户角色接口
|
||||
* @param params
|
||||
*/
|
||||
export const getUserRoles = (params) => defHttp.get({ url: Api.getUserRole, params }, { errorMessageMode: 'none' });
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
export const deleteUser = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteUser, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除用户
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteUser = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新用户
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateUser = (params, isUpdate) => {
|
||||
let url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 唯一校验
|
||||
* @param params
|
||||
*/
|
||||
export const duplicateCheck = (params) => defHttp.get({ url: Api.duplicateCheck, params }, { isTransformResponse: false });
|
||||
|
||||
/**
|
||||
* 20231215
|
||||
* liaozhiyang
|
||||
* 唯一校验( 延迟【防抖】)
|
||||
* @param params
|
||||
*/
|
||||
const timer = {};
|
||||
export const duplicateCheckDelay = (params) => {
|
||||
return new Promise((resove, rejected) => {
|
||||
// -update-begin--author:liaozhiyang---date:20240619---for:【TV360X-1380】表单中使用多个duplicateCheckDelay,validate方法调用时会导致promise被挂起保存不了
|
||||
let key;
|
||||
if (isObject(params)) {
|
||||
key = `${params.tableName}_${params.fieldName}`;
|
||||
} else {
|
||||
key = params;
|
||||
}
|
||||
clearTimeout(timer[key]);
|
||||
// -update-end--author:liaozhiyang---date:20240619---for:【TV360X-1380】表单中使用多个duplicateCheckDelay,validate方法调用时会导致promise被挂起保存不了
|
||||
timer[key] = setTimeout(() => {
|
||||
defHttp
|
||||
.get({ url: Api.duplicateCheck, params }, { isTransformResponse: false })
|
||||
.then((res: any) => {
|
||||
resove(res as any);
|
||||
})
|
||||
.catch((error) => {
|
||||
rejected(error);
|
||||
});
|
||||
delete timer[key];
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取全部角色(租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const getAllRolesList = (params) => defHttp.get({ url: Api.allRolesList, params });
|
||||
/**
|
||||
* 获取全部角色(不租户隔离)
|
||||
* @param params
|
||||
*/
|
||||
export const getAllRolesListNoByTenant = (params) => defHttp.get({ url: Api.allRolesListNoByTenant, params });
|
||||
/**
|
||||
* 获取全部租户
|
||||
*/
|
||||
export const getAllTenantList = (params) => defHttp.get({ url: Api.allTenantList, params });
|
||||
/**
|
||||
* 获取指定用户负责部门
|
||||
*/
|
||||
export const getUserDepartList = (params) => defHttp.get({ url: Api.userDepartList, params }, { successMessageMode: 'none' });
|
||||
/**
|
||||
* 获取全部职务
|
||||
*/
|
||||
export const getAllPostList = (params) => {
|
||||
return new Promise((resolve) => {
|
||||
defHttp.get({ url: Api.allPostList, params }).then((res) => {
|
||||
resolve(res.records);
|
||||
});
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 回收站列表
|
||||
* @param params
|
||||
*/
|
||||
export const getRecycleBinList = (params) => defHttp.get({ url: Api.recycleBinList, params });
|
||||
/**
|
||||
* 回收站还原
|
||||
* @param params
|
||||
*/
|
||||
export const putRecycleBin = (params, handleSuccess) => {
|
||||
return defHttp.put({ url: Api.putRecycleBin, params }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 回收站删除
|
||||
* @param params
|
||||
*/
|
||||
export const deleteRecycleBin = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteRecycleBin, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 修改密码
|
||||
* @param params
|
||||
*/
|
||||
export const changePassword = (params) => {
|
||||
return defHttp.put({ url: Api.changePassword, params });
|
||||
};
|
||||
/**
|
||||
* 冻结解冻
|
||||
* @param params
|
||||
*/
|
||||
export const frozenBatch = (params, handleSuccess) => {
|
||||
return defHttp.put({ url: Api.frozenBatch, params }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 获取用户代理
|
||||
* @param params
|
||||
*/
|
||||
export const getUserAgent = (params) => defHttp.get({ url: Api.getUserAgent, params }, { isTransformResponse: false });
|
||||
/**
|
||||
* 保存或者更新用户代理
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateAgent = (params) => {
|
||||
let url = params.id ? Api.agentEdit : Api.agentSave;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户离职(新增代理人和用户状态变更操作)
|
||||
* @param params
|
||||
*/
|
||||
export const userQuitAgent = (params) => {
|
||||
return defHttp.put({ url: Api.userQuitAgent, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户离职列表
|
||||
* @param params
|
||||
*/
|
||||
export const getQuitList = (params) => {
|
||||
return defHttp.get({ url: Api.getQuitList, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消离职
|
||||
* @param params
|
||||
*/
|
||||
export const putCancelQuit = (params, handleSuccess) => {
|
||||
return defHttp.put({ url: Api.putCancelQuit, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 待审批获取列表数据
|
||||
*/
|
||||
export const getUserTenantPageList = (params) => {
|
||||
return defHttp.get({ url: Api.getUserTenantPageList, params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新租户状态
|
||||
* @param params
|
||||
*/
|
||||
export const updateUserTenantStatus = (params) => {
|
||||
return defHttp.put({ url: Api.updateUserTenantStatus, params }, { joinParamsToUrl: true, isTransformResponse: false });
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<Description @register="register" class="mt-4" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { Description, DescItem, useDescription } from '/@/components/Description/index';
|
||||
const mockData = {
|
||||
username: 'test',
|
||||
nickName: 'VB',
|
||||
age: '123',
|
||||
phone: '15695909xxx',
|
||||
email: '190848757@qq.com',
|
||||
addr: '厦门市思明区',
|
||||
sex: '男',
|
||||
certy: '3504256199xxxxxxxxx',
|
||||
tag: 'orange',
|
||||
};
|
||||
const schema: DescItem[] = [
|
||||
{
|
||||
field: 'username',
|
||||
label: '用户名',
|
||||
},
|
||||
{
|
||||
field: 'nickName',
|
||||
label: '昵称',
|
||||
render: (curVal, data) => {
|
||||
return `${data.username}-${curVal}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
label: '联系电话',
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
label: '邮箱',
|
||||
},
|
||||
{
|
||||
field: 'addr',
|
||||
label: '地址',
|
||||
},
|
||||
];
|
||||
export default defineComponent({
|
||||
components: { Description },
|
||||
setup() {
|
||||
const [register] = useDescription({
|
||||
title: 'useDescription',
|
||||
data: mockData,
|
||||
schema: schema,
|
||||
});
|
||||
return { mockData, schema, register };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user