init
This commit is contained in:
2025-06-27 17:42:38 +08:00
commit bd6402478b
5317 changed files with 785994 additions and 0 deletions
@@ -0,0 +1,84 @@
<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,131 @@
<template>
<BasicModal :title="title" :width="800" v-bind="$attrs" @ok="handleOk" @register="registerModal">
<BasicForm @register="registerForm">
<template #addressInfo="{ model }">
<a-input style="width: calc(100% - 100px)" v-model:value="model['address']" :disabled="true" />
<a-button style="margin-left: 10px" @click="viewMap"> 查看地图</a-button>
</template>
</BasicForm>
</BasicModal>
<Map @register="registerMap" :state="state" ref="map" @get-position="getPosition" />
</template>
<script lang="ts" setup>
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import { saveOrUpdateDepart } from '../depart.api';
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
import Map from '/@/views/consult/resource/components/Map.vue';
const [registerMap, { openModal }] = useModal();
const state = ref({});
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, getFieldsValue }] = useForm({
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
// 打开地图弹窗
function viewMap() {
openModal(true, {
record: { ...getFieldsValue(), ...state.value },
});
}
async function getPosition(val) {
let { pname, cityname, adname, address, name } = val.handleItem || '';
let nameList = [pname, cityname, adname, address, name];
let str = '';
nameList.map((item) => {
if (item !== undefined) {
str += item;
}
});
await setFieldsValue({
lat: val.lat,
lng: val.lng,
address: str,
});
state.value = {
...state.value,
latitude: val.lat,
longitude: val.lng,
address: str,
};
}
// 注册弹窗
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,188 @@
<template>
<a-spin :spinning="loading">
<BasicForm @register="registerForm">
<template #addressInfo="{ model }">
<a-input style="width: calc(100% - 100px)" v-model:value="model['address']" :disabled="true" />
<a-button style="margin-left: 10px" @click="viewMap" :disabled="!hasPer"> 查看地图</a-button>
</template>
</BasicForm>
<div class="j-box-bottom-button offset-20" style="margin-top: 30px" v-if="hasPer">
<div class="j-box-bottom-button-float">
<a-button preIcon="ant-design:sync-outlined" @click="onReset">重置</a-button>
<a-button preIcon="ant-design:save-filled" type="primary" @click="onSubmit">保存</a-button>
</div>
</div>
<Map @register="registerMap" :state="state" ref="map" @get-position="getPosition" />
</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 Map from '/@/views/consult/resource/components/Map.vue';
import { useModal } from '/@/components/Modal';
import { useUserStore } from '/@/store/modules/user';
import { orgManageApi } from '/@/utils/auth/buttonAuth/system';
import { usePermission } from '/@/hooks/web/usePermission';
const { hasPermission } = usePermission();
const [registerMap, { openModal }] = useModal();
const emit = defineEmits(['success']);
const props = defineProps({
data: { type: Object, default: () => ({}) },
rootTreeData: { type: Array, default: () => [] },
});
const { userInfo } = useUserStore();
const prefixCls = inject('prefixCls');
const loading = ref<boolean>(false);
// 当前是否是更新模式
const isUpdate = ref<boolean>(true);
// 当前的弹窗数据
const model = ref<object>({});
const state = ref({});
//注册表单
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema, getFieldsValue, clearValidate, setProps }] = useForm({
labelWidth: 130,
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
const categoryOptions = computed(() => {
if (!!props?.data?.parentId) {
return orgCategoryOptions.child;
} else {
return orgCategoryOptions.root;
}
});
onMounted(async () => {
// 禁用字段
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 });
updateState(record);
await setProps({ disabled: !hasPer.value });
},
{ 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 }
);
await clearValidate();
});
const hasPer = computed(() => {
// 修改此字段
return (
(!!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system'))) ||
(hasPermission(orgManageApi.departManageList) && props.data.manager)
);
});
function updateState(val) {
state.value = {
latitude: val.lat,
longitude: val.lng,
address: val.address,
};
}
// 打开地图弹窗
function viewMap() {
openModal(true, {
record: { ...getFieldsValue(), ...state.value },
});
}
// 关闭地图
async function getPosition(val) {
let { pname, cityname, adname, address, name } = val.handleItem || '';
let nameList = [pname, cityname, adname, address, name];
let str = '';
nameList.map((item) => {
if (item !== undefined) {
str += item;
}
});
await setFieldsValue({
lat: val.lat,
lng: val.lng,
address: str,
});
state.value = {
...state.value,
latitude: val.lat,
longitude: val.lng,
address: str,
};
}
// 重置表单
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" scoped>
/*begin 兼容暗夜模式*/
.j-box-bottom-button-float {
background-color: @component-background;
border-top: 1px solid @border-color-base;
}
/*end 兼容暗夜模式*/
</style>
@@ -0,0 +1,361 @@
<template>
<a-card :bordered="false" style="height: 100%">
<div class="j-table-operator" style="width: 100%">
<a-button preIcon="ant-design:plus-outlined" type="primary" v-auth="'system:depart:add'" @click="onAddDepart" v-if="hasPer"
>新增</a-button
>
<a-button preIcon="ant-design:plus-outlined" type="primary" v-auth="'system:depart:add'" @click="onAddChildDepart()">添加下级</a-button>
<!-- 2024年3月15日09:26:36 暂时注掉导入导出-->
<!-- <a-upload name="file" :showUploadList="false" :customRequest="onImportXls">-->
<!-- <a-button type="primary" preIcon="ant-design:import-outlined">导入</a-button>-->
<!-- </a-upload>-->
<!-- <a-button preIcon="ant-design:export-outlined" type="primary" @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" class="spin-tree">
<a-input-search placeholder="按部门名称搜索…" style="margin-bottom: 10px" @search="onSearch" />
<!--组织机构树-->
<!-- checkable-->
<template v-if="treeData.length > 0">
<a-tree
v-if="!treeReloading"
: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
:visible="visibleTreeKey === treeKey"
title="确定要删除吗?"
ok-text="确定"
cancel-text="取消"
placement="rightTop"
@confirm="onDelete(dataRef)"
@visibleChange="onVisibleChange"
>
<span>{{ title }}</span>
</Popconfirm>
<template
#overlay
v-if="
dataRef?.manager ||
(!!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system')))
"
>
<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 { defineExpose, inject, nextTick, ref, unref, computed } 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 '/@/views/system/depart/components/DepartFormModal.vue';
import { message, Popconfirm } from 'ant-design-vue';
const prefixCls = inject('prefixCls');
const emit = defineEmits(['select', 'rootTreeData']);
const { createMessage } = useMessage();
const { handleImportXls, handleExportXls } = useMethods();
import { useUserStore } from '/@/store/modules/user';
import { orgManageApi } from '/@/utils/auth/buttonAuth/system';
const { userInfo } = useUserStore();
import { usePermission } from '/@/hooks/web/usePermission';
const { hasPermission } = usePermission();
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 && 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() {
if (!hasPer.value) return message.info('无权限');
openModal(true, { isUpdate: false, isChild: false });
}
// 添加子级部门
function onAddChildDepart(data = currentDepart.value) {
if (!hasPer.value) return message.info('无权限');
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) {
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 (!hasPer.value) return message.info('无权限');
if (data) {
onVisibleChange(false);
doDeleteDepart([data.id], false);
}
}
// 批量删除部门
async function onDeleteBatch() {
try {
if (!hasPer.value) return message.info('无权限');
await doDeleteDepart(checkedKeys);
checkedKeys.value = [];
} finally {
}
}
function onVisibleChange(visible) {
if (!visible) {
visibleTreeKey.value = null;
}
}
function onImportXls(d) {
if (!hasPer.value) return message.info('无权限');
handleImportXls(d, Api.importExcelUrl, () => {
loadRootTreeData();
});
}
function onExportXls() {
if (!hasPer.value) return message.info('无权限');
handleExportXls('部门信息', Api.exportXlsUrl);
}
defineExpose({
loadRootTreeData,
});
const hasPer = computed(() => {
// 修改此字段
return (
(!!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system'))) ||
(hasPermission(orgManageApi.departManageList) && currentDepart.value?.manager)
);
});
</script>
@@ -0,0 +1,176 @@
<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="checkStrictly"
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">
<a-dropdown :trigger="['click']" placement="top">
<template #overlay>
<a-menu>
<a-menu-item key="3" @click="toggleCheckALL(true)">全部勾选</a-menu-item>
<a-menu-item key="4" @click="toggleCheckALL(false)">取消全选</a-menu-item>
<a-menu-item key="5" @click="toggleExpandAll(true)">展开所有</a-menu-item>
<a-menu-item key="6" @click="toggleExpandAll(false)">收起所有</a-menu-item>
</a-menu>
</template>
<a-button style="float: left">
树操作
<Icon icon="ant-design:up-outlined" />
</a-button>
</a-dropdown>
<a-button preIcon="ant-design:save-filled" type="primary" @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';
const props = defineProps({
data: { type: Object, default: () => ({}) },
});
// 当前选中的部门ID,可能会为空,代表未选择部门
const departId = computed(() => props.data?.id);
const prefixCls = inject('prefixCls');
const basicTree = ref();
const loading = ref<boolean>(false);
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(true);
// 注册数据规则授权弹窗抽屉
const [registerDataRuleDrawer, dataRuleDrawer] = useDrawer();
// onCreated
loadData();
watch(departId, () => loadDepartPermission(), { immediate: true });
async function loadData() {
try {
loading.value = true;
let { treeList } = await queryRoleTreeList();
treeData.value = treeList;
await nextTick();
toggleExpandAll(true);
} 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;
}
}
// tree勾选复选框事件
function onCheck(event) {
if (!Array.isArray(event)) {
checkedKeys.value = event.checked;
} else {
checkedKeys.value = event;
}
}
// 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) {
basicTree.value.expandAll(flag);
await nextTick();
expandedKeys.value = basicTree.value.getExpandedKeys();
}
// 切换全选
async function toggleCheckALL(flag) {
basicTree.value.checkAll(flag);
await nextTick();
checkedKeys.value = basicTree.value.getCheckedKeys();
}
</script>
<style lang="less" scoped>
// 【VUEN-188】解决滚动条不灵敏的问题
.depart-rule-tree :deep(.scrollbar__bar) {
pointer-events: none;
}
/*begin 兼容暗夜模式*/
.j-box-bottom-button-float {
background-color: @component-background;
border-top: 1px solid @border-color-base;
}
/*end 兼容暗夜模式*/
</style>
@@ -0,0 +1,147 @@
<template>
<BasicDrawer width="800px" :title="isUpdate ? '修改部门管理员' : '添加部门管理员'" @register="registerModal" destroyOnClose @ok="handleSubmit">
<BasicForm @register="registerForm">
<template #roles="{ model, field }">
<a-input v-model:value="model[field]" readonly style="cursor: pointer" @click="clickShowTransfer" />
</template>
</BasicForm>
<transfer-modal @register="registerTransferModal" :orgInfo="{ orgCode: props.orgCode, departId: props.departId }" @success="handleSuccess" />
</BasicDrawer>
</template>
<script setup lang="ts">
import BasicForm from '/@/components/Form/src/BasicForm.vue';
import { useForm } from '/@/components/Form';
import { ref } from 'vue';
import { formSchema } from '/@/views/system/depart/components/departManage/departManageList.data';
import BasicDrawer from '/@/components/Drawer/src/BasicDrawer.vue';
import { useDrawerInner } from '/@/components/Drawer';
import { addUserEndowDepartManager } from '/@/views/system/depart/components/departManage/departManageList.api';
import TransferModal from '/@/views/system/depart/components/departManage/components/transferModal.vue';
import { useModal } from '/@/components/Modal';
import { dealPassword } from '/@/views/system/user/user.data';
const props = defineProps({
orgCode: { type: String, default: () => '' },
departId: { type: String, default: () => '' },
});
const [registerTransferModal, { openModal }] = useModal();
const roleCodes = ref<string>('');
const listData = ref<Array<any>>([]);
const isUpdate = ref<boolean>(false);
const emit = defineEmits(['success']);
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
//labelWidth: 150,
schemas: formSchema(isUpdate.value),
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
const [registerModal, { closeDrawer, setDrawerProps }] = useDrawerInner(async (data) => {
await resetFields();
roleCodes.value = '';
listData.value = [];
setDrawerProps({ confirmLoading: false, showFooter: data.showFooter, showOkBtn: data.showFooter, showCancelBtn: data.showFooter });
// listData.value = [];
// isUpdate.value = data.isUpdate;
//
// if (unref(isUpdate)) {
// await setFieldsValue({
// ...data.record,
// });
// await clearValidate();
// }
});
function clickShowTransfer() {
openModal(true, {
list: listData.value,
});
}
function preSubmit(values) {
return {
...values,
extension: values,
};
}
async function handleSubmit() {
try {
const values = await validate();
setDrawerProps({ confirmLoading: true });
// if (!values.birthday) {
// values.birthday = values.idCard.substring(6, 10) + '-' + values.idCard.substring(10, 12) + '-' + values.idCard.substring(12, 14);
// }
// values['orgCode'] = props.orgCode;
// setDrawerProps({ confirmLoading: true });
// //提交表单
values['roleCodes'] = roleCodes.value;
values['orgCode'] = props.orgCode;
values['password'] = dealPassword(values['password']);
await addUserEndowDepartManager(values);
closeDrawer();
//刷新列表
emit('success');
} finally {
setDrawerProps({ confirmLoading: false });
}
}
function handleSuccess(list) {
listData.value = list;
let ids = '';
let names = '';
list.map((item, index) => {
item = JSON.parse(item);
if (index === 0) {
ids += item.id;
names += item.roleCode;
} else {
ids += ',' + item.id;
names += ',' + item.roleCode;
}
});
setFieldsValue({
roles: names,
});
roleCodes.value = ids;
}
</script>
<style scoped lang="less">
:deep(.ant-input-number) {
width: 100%;
}
.question-choose {
text-align: center;
position: relative;
&:before {
content: '';
width: 36%;
height: 1px;
background-color: #000000;
position: absolute;
left: 0;
top: 50%;
}
&:after {
content: '';
width: 36%;
height: 1px;
background-color: #000000;
position: absolute;
right: 0;
top: 50%;
}
}
:deep(.ant-col-sm-18) {
max-width: 100%;
}
:deep(.input-number .ant-input-number-input-wrap input) {
text-align: center;
}
</style>
@@ -0,0 +1,125 @@
<template>
<BasicDrawer width="50%" title="选择用户" @register="registerModal" destroyOnClose @close="handleClose">
<BasicTable @register="registerTable">
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<transfer-modal @register="registerTransferModal" :orgInfo="{ orgCode: props.orgCode, departId: props.departId }" @success="handleSuccess" />
</BasicDrawer>
</template>
<script setup lang="ts">
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import BasicDrawer from '/@/components/Drawer/src/BasicDrawer.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { noManagerUsers } from '/@/views/system/depart/components/departManage/departManageList.api';
import { selectColumns, searchFormSchemaSelect } from '/@/views/system/depart/components/departManage/departManageList.data';
import { useDrawerInner } from '/@/components/Drawer';
import { TableAction } from '/@/components/Table';
import TransferModal from '/@/views/system/depart/components/departManage/components/transferModal.vue';
import { useModal } from '/@/components/Modal';
const props = defineProps({
orgCode: { type: String, default: () => '' },
departId: { type: String, default: () => '' },
});
const emit = defineEmits(['reloadTable']);
const [registerTransferModal, { openModal }] = useModal();
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '员工',
api: noManagerUsers,
columns: selectColumns,
canResize: false,
clickToRowSelect: false,
beforeFetch: (params) => {
if (params['orgCode2']) {
params['orgCode'] = params['orgCode2'];
} else {
params['orgCode'] = params['orgCode1'];
}
return params;
},
formConfig: {
//labelWidth: 120,
schemas: searchFormSchemaSelect,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
labelWidth: 100,
baseColProps: {
xs: 12,
sm: 12,
md: 8,
lg: 8,
xl: 8,
xxl: 8,
},
actionColOptions: {
style: {
paddingLeft: '104px',
},
span: 24,
offset: 0,
xs: 12,
sm: 12,
md: 8,
lg: 8,
xl: 8,
xxl: 8,
},
},
actionColumn: {
width: 80,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function getTableAction(record: Recordable) {
return [
{
label: record.departId && record.departId.includes(props.departId) ? '修改' : '绑定',
onClick: handleBind.bind(null, record),
},
];
}
const [registerModal, { closeDrawer }] = useDrawerInner(async () => {
selectedRowKeys.value = [];
});
function handleBind(record: Recordable) {
openModal(true, {
record,
});
// if (!props.orgCode) {
// return message.error('请选择需要管理的部门');
// }
//
// relevanceDepartManager(props.orgCode, data).then(() => {
// closeDrawer();
// emit('reloadTable');
// });
}
function handleSuccess() {
reload();
}
function handleClose() {
emit('reloadTable');
}
</script>
<style scoped lang="less">
:deep(.ant-form-item-control-input-content) {
//display: flex;
}
</style>
@@ -0,0 +1,87 @@
<template>
<BasicModal @register="transferModal" destroyOnClose @ok="handleSubmit" zIndex="1002" width="700px" title="选择角色">
<div style="display: flex; align-items: center; justify-content: center">
<a-transfer
v-model:target-keys="targetKeys"
v-model:selected-keys="selectedKeys"
:data-source="mockData"
show-search
:list-style="{
width: '250px',
height: '500px',
}"
:filter-option="filterOption"
:titles="['无权限', '已有权限']"
:render="(item) => item.roleName"
@change="handleChange"
@select-change="handleSelectChange"
@scroll="handleScroll"
/>
</div>
</BasicModal>
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import { ref } from 'vue';
import { addManagerDepartAndEndowRole, listCustom, userRoles } from '/@/views/system/depart/components/departManage/departManageList.api';
import { useModalInner } from '/@/components/Modal';
import { propTypes } from '/@/utils/propTypes';
const props = defineProps({
orgInfo: propTypes.object.def({ orgCode: '', departId: '' }),
});
const userId = ref<string>('');
const targetKeys = ref<Array<any>>([]);
const selectedKeys = ref<Array<any>>([]);
const mockData = ref<Array<any>>([]);
const emit = defineEmits(['success']);
const [transferModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
selectedKeys.value = [];
mockData.value = [];
userId.value = data?.record?.userId;
targetKeys.value = userId.value ? [] : data.list;
const { records } = await listCustom({ pageNo: 1, pageSize: 9999, systemFlag: false });
let haveList = [];
if (userId.value) {
haveList = await userRoles({ userId: data.record.userId, pageNo: 1, pageSize: 9999 });
}
mockData.value = records.map((item: any) => {
// targetKeys.value?.push(item.roleCode);
if (haveList.includes(item.roleCode)) {
targetKeys.value?.push(item.id);
}
// selectedKeys.value?.push(item.id);
return { ...{ key: userId.value ? item.id : JSON.stringify({ id: item.id, roleCode: item.roleName }) }, ...item };
});
});
function handleChange() {}
function handleSelectChange() {}
function handleScroll() {}
async function handleSubmit() {
try {
if (userId.value) {
let params = {
...props.orgInfo,
userId: userId.value,
roleIds: targetKeys.value,
};
setModalProps({ confirmLoading: true });
await addManagerDepartAndEndowRole(params);
closeModal();
emit('success');
} else {
closeModal();
emit('success', targetKeys.value);
}
} finally {
setModalProps({ confirmLoading: false });
}
}
function filterOption(inputValue: string, option) {
return option.roleName.indexOf(inputValue) > -1;
}
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,28 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/health-system/sys/sysDepart/departManagers',
addDepartManager = '/health-system/sys/healthUserEmployeeEx/addDepartManager',
// noManagerUsers = '/health-system/sys/user/noManagerUsers',
noManagerUsers = '/health-system/sys/user/listUser',
listCustom = '/health-system/sys/role/listCustom',
userRoles = '/health-system/sys/user/userRoles',
relevanceDepartManager = '/health-system/sys/sysDepart/relevanceDepartManager',
cancelDepartManager = '/health-system/sys/sysDepart/cancelDepartManager',
addManagerDepartAndEndowRole = '/health-system/sys/role/addManagerDepartAndEndowRole',
addUserEndowDepartManager = '/health-system/sys/user/addUserEndowDepartManager',
}
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
export const noManagerUsers = (params) => defHttp.get({ url: Api.noManagerUsers, params });
export const listCustom = (params) => defHttp.get({ url: Api.listCustom, params });
export const userRoles = (params) => defHttp.get({ url: Api.userRoles, params });
export const addManagerDepartAndEndowRole = (params) => defHttp.post({ url: Api.addManagerDepartAndEndowRole, params });
export const addUserEndowDepartManager = (params) => defHttp.post({ url: Api.addUserEndowDepartManager, params });
export const addDepartManager = (params) => defHttp.post({ url: Api.addDepartManager, params });
export const relevanceDepartManager = (orgCode, params) => defHttp.post({ url: Api.relevanceDepartManager + '?orgCode=' + orgCode, params });
export const cancelDepartManager = (orgCode, params) => defHttp.post({ url: Api.cancelDepartManager + '?orgCode=' + orgCode, params });
@@ -0,0 +1,250 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { getSecondaryDepartmentList, getThirdDepartmentList } from '/@/views/system/user/user.api';
import { message } from 'ant-design-vue';
import { checkPassword } from '/@/hooks/checkPassword/checkPassword';
//列表数据
export const columns: BasicColumn[] = [
{
title: '员工账号',
align: 'center',
dataIndex: 'userName',
},
{
title: '员工姓名',
align: 'center',
dataIndex: 'realName',
},
// {
// title: '员工编号',
// align: 'center',
// dataIndex: 'workNo',
// },
{
title: '单位名称',
align: 'center',
dataIndex: 'secondDepart',
},
{
title: '部门名称',
align: 'center',
dataIndex: 'depart',
},
];
export const selectColumns: BasicColumn[] = [
{
title: '员工账号',
align: 'center',
fixed: 'left',
dataIndex: 'userName',
},
{
title: '员工姓名',
align: 'center',
fixed: 'left',
dataIndex: 'realName',
},
// {
// title: '员工编号',
// align: 'center',
// dataIndex: 'workNo',
// },
{
title: '单位名称',
align: 'center',
dataIndex: 'secondDepart',
},
{
title: '部门名称',
align: 'center',
dataIndex: 'depart',
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '员工姓名',
field: 'name',
component: 'Input',
},
];
export const searchFormSchemaSelect: FormSchema[] = [
{
label: '单位',
field: 'orgCode1',
component: 'ApiSelect',
componentProps: ({ formModel }) => {
return {
api: getSecondaryDepartmentList,
resultField: 'result',
labelField: 'departName',
valueField: 'orgCode',
immediate: true,
onChange: (_value, option) => {
formModel.secondOrgId = option?.id || '';
formModel.secondOrgCode = option?.orgCode || '';
if (formModel?.thirdOrgId) {
formModel.thirdOrgId = '';
formModel.thirdOrgCode = '';
}
},
onDeselect: () => {
formModel.thirdOrgId = '';
formModel.thirdOrgCode = '';
},
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
};
},
},
{
label: '部门',
field: 'orgCode2',
component: 'ApiSelect',
componentProps: ({ formModel }) => {
return {
api: getThirdDepartmentList,
resultField: 'list',
labelField: 'departName',
params: {
secondDepartId: formModel?.secondOrgId || 'defghjkl',
},
valueField: 'orgCode',
immediate: false,
onChange: (_value, option) => {
formModel.thirdOrgId = option?.id || '';
formModel.thirdOrgCode = option?.orgCode || '';
},
onDeselect: () => {
formModel.thirdOrgId = '';
formModel.thirdOrgCode = '';
},
onFocus: () => {
if (!formModel.secondOrgId) {
return message.warn('请先选择单位!');
}
},
showSearch: true,
filterOption: (input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
},
};
},
},
{
label: '员工姓名',
field: 'realName',
component: 'Input',
},
{
label: '员工编号',
field: 'workNo',
component: 'Input',
},
];
//表单数据
export const schema: FormSchema[] = [
{
label: '员工姓名',
field: 'name',
component: 'Input',
},
];
export const formSchema: (isUpdate: boolean) => FormSchema[] = (isUpdate) => {
return [
{
label: '用户账号',
field: 'username',
component: 'Input',
componentProps: ({ formModel }) => {
return {
onInput: () => {
formModel.username = formModel.username.replace(/[^a-zA-Z0-9_]/g, '');
},
autocomplete: 'off',
};
},
required: true,
dynamicDisabled: isUpdate,
},
{
label: '密码',
field: 'password',
component: 'StrengthMeter',
componentProps: {
autocomplete: 'new-password',
},
dynamicRules: () => {
return [
{
required: true,
validator: (_, value) => {
let { message } = checkPassword(value);
if (message === 'ok') {
return Promise.resolve();
} else {
return Promise.reject(message);
}
},
trigger: 'blur',
},
];
},
required: !isUpdate,
show: !isUpdate,
},
{
label: '姓名',
field: 'realname',
component: 'Input',
required: true,
},
{
label: '员工编号', // 原来为工号
field: 'workNo',
component: 'Input',
required: true,
ifShow: false,
},
{
label: '角色',
field: 'roles',
component: 'Input',
slot: 'roles',
required: true,
},
// {
// label: '部门',
// field: 'orgCode',
// component: 'JSelectDeptStaff',
// componentProps: ({ formModel }) => {
// return {
// sync: true,
// checkStrictly: true,
// defaultExpandLevel: 1,
// initOptions: isUpdate
// ? [
// {
// value: formModel.departId,
// label: ' ' + formModel.secondDepartName + ' - ' + formModel.departName + ' ',
// },
// ]
// : [],
// };
// },
// required: true,
// },
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
};
@@ -0,0 +1,175 @@
<template>
<div>
<!--引用表格-->
<BasicTable :rowSelection="rowSelection" @register="registerTable">
<!--插槽:table标题-->
<template #tableTitle>
<a-button preIcon="ant-design:plus-outlined" type="primary" v-auth="'system:depart-manage-list:add'" @click="handleAdd">
新增
</a-button>
<a-button type="primary" @click="selectUser">选择用户</a-button>
<a-button type="primary" @click="largeUnbind">批量解绑</a-button>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getAction(record)" />
</template>
</BasicTable>
<!-- 表单区域 -->
<depart-manage-list-drawer @register="registerModal" @success="handleSuccess" :orgCode="props.data?.orgCode" />
<select-no-manager @register="registerModal1" @reloadTable="handleSuccess" :orgCode="props.data?.orgCode" :departId="props.data?.id" />
<RestPass @register="restPassModal" />
<transfer-modal
@register="registerTransferModal"
:orgInfo="{ orgCode: props.data?.orgCode, departId: props.data?.id }"
@success="handleSuccess"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useDrawer } from '/@/components/Drawer';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './departManageList.data';
import { list, cancelDepartManager } from './departManageList.api';
import DepartManageListDrawer from '/@/views/system/depart/components/departManage/components/departManageListDrawer.vue';
import SelectNoManager from '/@/views/system/depart/components/departManage/components/selectNoManager.vue';
import { message } from 'ant-design-vue';
import RestPass from '/@/views/system/user/restPass/RestPass.vue';
import { useModal } from '/@/components/Modal';
import TransferModal from '/@/views/system/depart/components/departManage/components/transferModal.vue';
const [registerModal, { openDrawer }] = useDrawer();
const [registerModal1, { openDrawer: openDrawer1 }] = useDrawer();
const [restPassModal, { openModal: restPassModel }] = useModal();
const [registerTransferModal, { openModal: openModal1 }] = useModal();
const showFooter = ref(true);
function selectUser() {
openDrawer1(true);
}
const props = defineProps({
data: { type: Object, default: () => ({}) },
});
watch(
() => props.data?.orgCode,
() => {
reload({ page: 1 });
}
);
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '员工',
api: list,
rowKey: 'userId',
columns,
canResize: false,
clickToRowSelect: false,
beforeFetch: (params) => {
params['orgCode'] = props.data?.orgCode;
return params;
},
formConfig: {
labelWidth: 100,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
baseColProps: {
xs: 8, // <576px
sm: 8, // ≥576px
md: 8, // ≥768px
lg: 8, // ≥992px
xl: 12, // ≥1200px
xxl: 12, // ≥1600px
},
actionColOptions: {
offset: 0,
span: 8,
xs: 8, // <576px
sm: 8, // ≥576px
md: 8, // ≥768px
lg: 8, // ≥992px
xl: 12, // ≥1200px
xxl: 12, // ≥1600px
},
},
actionColumn: {
width: 170,
fixed: 'right',
},
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
showFooter.value = true;
openDrawer(true, {
isUpdate: false,
showFooter: true,
type: '新增',
});
}
function handleUnBind(d) {
let orgCode = props.data?.orgCode;
if (!orgCode) {
return message.error('请选择需要管理的部门');
}
cancelDepartManager(orgCode, d).then(() => {
reload();
});
}
function largeUnbind() {
if (selectedRowKeys.value.length === 0) return message.info('请至少选择一条数据');
handleUnBind(selectedRowKeys.value);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
// 修改密码弹窗
function restPass(record) {
restPassModel(true, {
record: record.userId,
isUpdate: true,
showFooter: false,
});
}
/**
* 下拉操作栏
*/
function getAction(record) {
return [
{
label: '修改',
onClick: handleEditBind.bind(null, record),
},
{
label: '解绑',
onClick: handleUnBind.bind(null, [record.userId]),
},
{
label: '重置密码',
onClick: restPass.bind(null, record),
},
];
}
function handleEditBind(record: Recordable) {
openModal1(true, {
record,
});
}
</script>
<style scoped></style>
@@ -0,0 +1,95 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
enum Api {
list = '/health-system/sys/sysDepart/departUsers',
save = '/sys/healthUserEmployeeEx/add',
edit = '/sys/healthUserEmployeeEx/edit',
deleteOne = '/sys/healthUserEmployeeEx/delete',
deleteBatch = '/sys/healthUserEmployeeEx/deleteBatch',
importExcel = '/sys/healthUserEmployeeEx/importExcel',
exportXls = '/sys/healthUserEmployeeEx/exportXls',
queryById = '/sys/healthUserEmployeeEx/queryById',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
export const queryByIdUrl = Api.queryById;
/**
* 请求编辑数据
*/
export const resEditData = async (params) => {
return await defHttp.get({ url: Api.queryById, params }, { joinParamsToUrl: true });
};
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
*/
export const deleteOne = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否确认删除',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp
.delete(
{
url: Api.deleteOne,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess();
});
},
});
};
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
},
});
};
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
@@ -0,0 +1,48 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { h } from 'vue';
import { Image, message } from 'ant-design-vue';
import { getDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { getSecondaryDepartmentList, getThirdDepartmentList } from '/@/views/system/user/user.api';
//列表数据
export const columns: BasicColumn[] = [
{
title: '用户账号',
align: 'center',
fixed: 'left',
dataIndex: 'username',
},
{
title: '姓名',
align: 'center',
fixed: 'left',
dataIndex: 'realname',
},
{
title: '性别',
align: 'center',
dataIndex: 'sex_dictText',
width: 64,
},
{
title: '身份证号',
align: 'center',
dataIndex: 'idCard',
width: 160,
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: '用户账号',
field: 'username',
component: 'Input',
},
{
label: '用户姓名',
field: 'realname',
component: 'Input',
},
];
//表单数据
@@ -0,0 +1,212 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable">
<!--插槽:table标题-->
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<!-- 表单区域 -->
<!-- 重置密码 -->
<RestPass @register="restPassModal" />
</div>
</template>
<script lang="ts" setup>
import { ref, unref, watch } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useDrawer } from '/@/components/Drawer';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './departUserList.data';
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list, resEditData } from './departUserList.api';
import { message } from 'ant-design-vue';
import RestPass from '/@/views/system/user/restPass/RestPass.vue';
import { useModal } from '/@/components/Modal';
import { lockUser } from '/@/views/system/user/hospitalDoctor/HospitalDoctor.api';
const [registerDrawer, { openDrawer }] = useDrawer();
const [restPassModal, { openModal: restPaddModel }] = useModal();
const showFooter = ref(true);
const props = defineProps({
data: { type: Object, default: () => ({}) },
});
watch(
() => props.data?.orgCode,
() => {
reload();
}
);
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '员工',
api: list,
columns,
canResize: false,
clickToRowSelect: false,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
showAdvancedButton: false,
fieldMapToNumber: [],
fieldMapToTime: [],
actionColOptions: {
offset: 0,
span: 8,
xs: 8, // <576px
sm: 8, // ≥576px
md: 8, // ≥768px
lg: 8, // ≥992px
xl: 8, // ≥1200px
xxl: 8, // ≥1600px
},
},
actionColumn: {
width: 80,
fixed: 'right',
},
},
exportConfig: {
name: '员工',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
success: handleSuccess,
},
});
const [registerTable, { reload }, { selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() {
showFooter.value = true;
openDrawer(true, {
isUpdate: false,
type: '新增',
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable, type) {
showFooter.value = true;
getEdit(record, type);
}
/**
* 详情
*/
function handleDetail(record: Recordable, type) {
showFooter.value = false;
getEdit(record, type);
}
/**
* 获取编辑数据
*/
function getEdit(record, type) {
resEditData({ id: record.id }).then((res) => {
openDrawer(true, {
record: {
...res.extension,
...res,
orgCode: res?.depart?.id,
departId: res?.depart?.id,
departName: res?.depart?.departName,
secondDepartName: res?.secondDepart?.departName,
},
type,
isUpdate: true,
showFooter: unref(showFooter),
});
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
if (selectedRowKeys.value.length === 0) {
message.warning('未选中任何数据');
return;
}
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [];
}
// 重置密码
function restPass(record) {
restPaddModel(true, {
record: record.id,
isUpdate: true,
showFooter: false,
});
}
// 1 锁定用户 2 激活用户
async function handleUserOpt(record, type) {
const params = {
userId: record.id,
status: type,
};
await lockUser(params, type, handleSuccess);
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
// {
// label: '编辑',
// onClick: handleEdit.bind(null, record, '编辑'),
// auth: 'system:health_user_employee_ex:edit',
// },
// {
// label: '详情',
// onClick: handleDetail.bind(null, record, '详情'),
// },
// {
// label: '删除',
// onClick: handleDelete.bind(null, record),
// auth: 'system:health_user_employee_ex:delete',
// },
{
label: '重置密码',
onClick: restPass.bind(null, record),
},
{
label: '锁定用户',
auth: 'system:user:frozen',
onClick: handleUserOpt.bind(null, record, 2),
},
{
label: '激活用户',
auth: 'system:user:frozen',
onClick: handleUserOpt.bind(null, record, 1),
},
];
}
</script>
<style scoped>
:deep(.ant-table-title) {
display: none;
}
</style>
+125
View File
@@ -0,0 +1,125 @@
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',
departTree = '/sys/core/getDepartTree',
}
/**
* 获取部门树列表
*/
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?) => {
const 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 });
export const departTree = (params) => defHttp.get({ url: Api.departTree, params });
+219
View File
@@ -0,0 +1,219 @@
import { FormSchema } from '/@/components/Form';
import { getDictCache } from '/@/utils/dict';
import { isQH } from '/@/utils/getEnv';
function checkPhone(_, value) {
const rule = /^[0-9,]+$/;
if (value && !rule.test(value)) {
return Promise.reject('只能输入数字和英文逗号!');
}
if (value?.length > 11) {
const reg = /^1[3456789]\d{9}$/;
const strs = value.split(',');
for (let i = 0; i < strs.length; i++) {
if (!reg.test(strs[i])) {
return Promise.reject('第' + (i + 1) + '个手机号码格式不正确!');
}
}
}
return Promise.resolve();
}
// 部门基础表单
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: {
disabled: true,
},
},
{
field: 'orgClass',
label: '机构分类',
component: 'JDictSelectTag',
defaultValue: '1',
componentProps: () => {
return {
type: 'radio',
dictCode: 'orgclass',
allowClear: false,
};
},
},
{
field: 'departOrder',
label: '排序',
component: 'InputNumber',
componentProps: {},
},
{
field: 'mobile',
label: '电话',
component: 'Input',
componentProps: {
placeholder: '请输入电话',
},
},
{
field: 'fax',
label: '传真',
component: 'Input',
componentProps: {
placeholder: '请输入传真',
},
},
{
label: '健康管理员姓名',
field: 'healthManageUserName',
component: 'Input',
},
{
label: '健康管理员电话',
field: 'healthManageUserMobile',
component: 'Input',
componentProps: () => {
return {
placeholder: '请输入健康管理员电话,多个电话请用英文逗号隔开',
};
},
dynamicRules: () => {
return [
{
validator: checkPhone,
trigger: 'blur',
},
];
},
},
{
label: '健康管理员地址',
field: 'healthManageUserAddress',
component: 'Input',
},
{
label: '职业体检管理员姓名',
field: 'medialManageUserName',
component: 'Input',
show: !isQH(),
},
{
label: '职业体检管理员电话',
field: 'medialManageUserMobile',
component: 'Input',
show: !isQH(),
componentProps: () => {
return {
placeholder: '请输入体检管理员电话,多个电话请用英文逗号隔开',
};
},
dynamicRules: () => {
return [
{
validator: checkPhone,
trigger: 'blur',
},
];
},
},
{
label: '职业体检管理员地址',
field: 'medialManageUserAddress',
component: 'Input',
show: !isQH(),
},
{
label: '健康监测管理员姓名',
field: 'monitorManageUserName',
component: 'Input',
show: !isQH(),
},
{
label: '健康监测管理员电话',
field: 'monitorManageUserMobile',
component: 'Input',
componentProps: () => {
return {
placeholder: '请输入健康监测管理员电话,多个电话请用英文逗号隔开',
};
},
show: !isQH(),
dynamicRules: () => {
return [
{
validator: checkPhone,
trigger: 'blur',
},
];
},
},
{
label: '详细地址',
field: 'address',
component: 'Input',
slot: 'addressInfo',
// required: true,
},
{
label: '经度',
field: 'lng',
component: 'InputNumber',
show: false,
},
{
label: '纬度',
field: 'lat',
component: 'InputNumber',
show: false,
},
{
field: 'memo',
label: '备注',
component: 'InputTextArea',
componentProps: {
placeholder: '请输入备注',
},
},
];
return { basicFormSchema };
}
// 机构类型选项
export const orgCategoryOptions = {
// 一级部门
root: [{ value: '1', label: '公司' }],
// 子级部门
child: [
{ value: '2', label: '部门' },
{ value: '3', label: '岗位' },
],
};
+10
View File
@@ -0,0 +1,10 @@
//noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-depart-manage';
.@{prefix-cls} {
&--box {
.ant-tabs-nav {
padding: 0 20px;
}
}
}
+137
View File
@@ -0,0 +1,137 @@
<template>
<div class="outer">
<div class="inner">
<a-row :class="['p-4', `${prefixCls}--box`]" type="flex" :gutter="10" style="height: 100%">
<a-col :xl="12" :lg="24" :md="24" class="col-l">
<DepartLeftTree ref="leftTree" @select="onTreeSelect" @rootTreeData="onRootTreeData" />
</a-col>
<a-col :xl="12" :lg="24" :md="24" class="col-r">
<div style="height: 100%" class="depart-manage">
<a-tabs v-show="departData != null" v-model:activeKey="activeKey" defaultActiveKey="base-info">
<a-tab-pane tab="基本信息" key="base-info" forceRender style="position: relative">
<div style="padding: 20px; overflow: hidden; background-color: #ffffff">
<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-tab-pane
tab="部门管理员"
key="depart-manage"
v-if="(hasPermission(orgManageApi.departManageList) && manager) || hasPer"
>
<div style="padding: 0 20px 20px">
<depart-manage-list :data="departData" />
</div>
</a-tab-pane>
<a-tab-pane tab="部门用户" key="depart-user" v-if="hasPermission(orgManageApi.departUserList)">
<div style="padding: 0 20px 20px">
<depart-user-list :activeKey="activeKey" :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>
</div>
</div>
</template>
<script lang="ts" setup name="system-depart">
import { provide, ref, computed } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import DepartLeftTree from './components/DepartLeftTree.vue';
import DepartFormTab from './components/DepartFormTab.vue';
import DepartUserList from './components/departUser/departUserList.vue';
import DepartManageList from './components/departManage/departManageList.vue';
import { usePermission } from '/@/hooks/web/usePermission';
import { orgManageApi } from '/@/utils/auth/buttonAuth/system';
import { useUserStore } from '/@/store/modules/user';
const { hasPermission } = usePermission();
const { prefixCls } = useDesign('depart-manage');
provide('prefixCls', prefixCls);
// 给子组件定义一个ref变量
const leftTree = ref();
const activeKey = ref('base-info');
// 当前选中的部门信息
const departData = ref({});
const rootTreeData = ref<any[]>([]);
const { userInfo } = useUserStore();
// 左侧树选择后触发
function onTreeSelect(data) {
departData.value = data;
manager.value = data?.manager;
if (data?.orgType == 1) {
activeKey.value = 'base-info';
}
}
const manager = ref();
// 左侧树rootTreeData触发
function onRootTreeData(data) {
rootTreeData.value = data;
}
function onSuccess() {
leftTree.value.loadRootTreeData();
}
const hasPer = computed(() => {
// 修改此字段
return !!userInfo?.roleCodes && (userInfo?.roleCodes.includes('admin') || userInfo?.roleCodes.includes('system'));
});
</script>
<style lang="less">
@import './index.less';
</style>
<style lang="less" scoped>
/*begin 兼容暗夜模式*/
.depart-manage {
background-color: @component-background;
}
/*end 兼容暗夜模式*/
.outer {
position: relative;
width: 100%;
height: 100%;
}
.inner {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
:deep(.spin-tree .ant-tree .ant-tree-icon-hide) {
height: 100px !important;
}
.col-l,
.col-r {
height: 100%;
overflow: auto;
}
.col-l {
background-color: #ffffff;
}
:deep(.col-l, .col-r) {
width: calc(50% - 20px);
padding: 0 !important;
}
</style>