初始版本提交
This commit is contained in:
55
src/views/system/user/components/DeptTree.vue
Normal file
55
src/views/system/user/components/DeptTree.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="overflow-hidden bg-white h-full" :style="{ 'border-right': '1px solid #e5e7eb' }">
|
||||
<BasicTree
|
||||
:title="t('组织列表')"
|
||||
toolbar
|
||||
search
|
||||
:clickRowToExpand="true"
|
||||
:treeData="treeData"
|
||||
:selectedKeys="selectedKeys"
|
||||
expandOnSearch
|
||||
:fieldNames="{ key: 'id', title: 'name' }"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<template #title="{ name, departmentType }">
|
||||
<a-tag color="processing" v-if="departmentType === 1">公司</a-tag>
|
||||
<a-tag color="warning" v-else-if="departmentType === 0">部门</a-tag>
|
||||
{{ name }}
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
import { getDepartmentTree } from '/@/api/system/department';
|
||||
|
||||
import { BasicTree, TreeItem } from '/@/components/Tree';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
export default defineComponent({
|
||||
name: 'DeptTree',
|
||||
components: { BasicTree },
|
||||
|
||||
emits: ['select'],
|
||||
setup(_, { emit }) {
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
|
||||
async function fetch() {
|
||||
treeData.value = (await getDepartmentTree()) as unknown as TreeItem[];
|
||||
let id = treeData.value[0].id;
|
||||
selectedKeys.value.push(id);
|
||||
emit('select', id);
|
||||
}
|
||||
|
||||
function handleSelect(keys: string) {
|
||||
emit('select', keys[0]);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetch();
|
||||
});
|
||||
return { treeData, handleSelect, selectedKeys, t };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
177
src/views/system/user/components/OrganizationModal.vue
Normal file
177
src/views/system/user/components/OrganizationModal.vue
Normal file
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:width="800"
|
||||
title="新增组织"
|
||||
:fixedHeight="true"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey" @change="handleTabChange">
|
||||
<a-tab-pane key="1" tab="全部">
|
||||
<BasicTable @register="registerTableAll" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="已选" force-render>
|
||||
<BasicTable @register="registerTableSelected" />
|
||||
</a-tab-pane>
|
||||
<template #rightExtra>
|
||||
<div class="left-extra">
|
||||
<a-input
|
||||
v-model:value="searchText"
|
||||
placeholder="请输入查询关键字"
|
||||
@keypress.enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined style="color: #ccc" />
|
||||
</template>
|
||||
</a-input>
|
||||
<span class="left-text">已选中{{ num }}条</span>
|
||||
</div>
|
||||
<a-button type="primary" danger :icon="h(DeleteOutlined)" @click="handleClear">
|
||||
清空
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, h } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
|
||||
import { getDepartmentEnabledTree } from '/@/api/system/department';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { DeleteOutlined, SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
const { t } = useI18n();
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: t('组织名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('组织编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
const activeKey = ref('1');
|
||||
const selectedList = ref<any[]>([]);
|
||||
const num = ref(0);
|
||||
const searchText = ref('');
|
||||
|
||||
const emits = defineEmits(['success']);
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({ confirmLoading: false });
|
||||
setSelectedRowKeys1(data.orgs?.map((x) => x.id) || []);
|
||||
reload();
|
||||
});
|
||||
|
||||
const [
|
||||
registerTableAll,
|
||||
{
|
||||
getSelectRows,
|
||||
setSelectedRowKeys: setSelectedRowKeys1,
|
||||
getSelectRowKeys: getSelectRowKeys1,
|
||||
reload,
|
||||
},
|
||||
] = useTable({
|
||||
api: getDepartmentEnabledTree,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, name: searchText.value };
|
||||
},
|
||||
showIndexColumn: false,
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
onChange: (val) => {
|
||||
num.value = val.length;
|
||||
},
|
||||
},
|
||||
striped: false,
|
||||
});
|
||||
|
||||
const [
|
||||
registerTableSelected,
|
||||
{ setTableData, setSelectedRowKeys: setSelectedRowKeys2, getSelectRowKeys: getSelectRowKeys2 },
|
||||
] = useTable({
|
||||
dataSource: selectedList.value,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
onChange: (val) => {
|
||||
num.value = val.length;
|
||||
},
|
||||
},
|
||||
striped: false,
|
||||
});
|
||||
|
||||
const handleTabChange = (key) => {
|
||||
if (key === '2') {
|
||||
setSelectedRowKeys2(cloneDeep(getSelectRowKeys1()));
|
||||
selectedList.value = cloneDeep(getSelectRows());
|
||||
selectedList.value.map((x) => {
|
||||
delete x.children;
|
||||
});
|
||||
setTableData(selectedList.value);
|
||||
} else {
|
||||
setSelectedRowKeys1(getSelectRowKeys2());
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
searchText.value;
|
||||
reload();
|
||||
};
|
||||
const handleClear = () => {
|
||||
setSelectedRowKeys1([]);
|
||||
};
|
||||
const handleSubmit = () => {
|
||||
getSelectRows().map((x) => {
|
||||
delete x.children;
|
||||
});
|
||||
emits('success', getSelectRows());
|
||||
closeModal();
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.vben-basic-table .ant-table-wrapper) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-nav-wrap) {
|
||||
flex: none !important;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-extra-content) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 80%;
|
||||
margin-left: 60px;
|
||||
|
||||
.left-extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.left-text {
|
||||
width: 90px;
|
||||
margin-left: 10px;
|
||||
color: #5e95ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
border-radius: 10px;
|
||||
background: #f8f8f8;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
</style>
|
||||
195
src/views/system/user/components/PostModal.vue
Normal file
195
src/views/system/user/components/PostModal.vue
Normal file
@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:width="1000"
|
||||
title="新增岗位"
|
||||
:fixedHeight="true"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="w-2/5 xl:w-1/3">
|
||||
<DeptTree @select="handleSelect" />
|
||||
</div>
|
||||
<div class="w-3/5 xl:w-2/3">
|
||||
<a-tabs v-model:activeKey="activeKey" @change="handleTabChange">
|
||||
<a-tab-pane key="1" tab="全部">
|
||||
<BasicTable @register="registerTableAll" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="已选" force-render>
|
||||
<BasicTable @register="registerTableSelected" />
|
||||
</a-tab-pane>
|
||||
<template #rightExtra>
|
||||
<div class="left-extra">
|
||||
<a-input
|
||||
v-model:value="searchText"
|
||||
placeholder="请输入查询关键字"
|
||||
@keypress.enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined style="color: #ccc" />
|
||||
</template>
|
||||
</a-input>
|
||||
<span class="left-text">已选中{{ num }}条</span>
|
||||
</div>
|
||||
<a-button type="primary" danger :icon="h(DeleteOutlined)" @click="handleClear">
|
||||
清空
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, h } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
|
||||
import { getTreePostList } from '/@/api/system/post';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { DeleteOutlined, SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import DeptTree from './DeptTree.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: t('岗位名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('岗位编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
const activeKey = ref('1');
|
||||
const selectedList = ref<any[]>([]);
|
||||
const num = ref(0);
|
||||
const searchText = ref('');
|
||||
const selectDeptId = ref('');
|
||||
|
||||
const emits = defineEmits(['success']);
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({ confirmLoading: false });
|
||||
setSelectedRowKeys1(data.posts?.map((x) => x.id) || []);
|
||||
reload();
|
||||
});
|
||||
|
||||
const [
|
||||
registerTableAll,
|
||||
{
|
||||
getSelectRows,
|
||||
setSelectedRowKeys: setSelectedRowKeys1,
|
||||
getSelectRowKeys: getSelectRowKeys1,
|
||||
reload,
|
||||
},
|
||||
] = useTable({
|
||||
api: getTreePostList,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
showIndexColumn: false,
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, deptId: selectDeptId.value, name: searchText.value };
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
onChange: (val) => {
|
||||
num.value = val.length;
|
||||
},
|
||||
},
|
||||
striped: false,
|
||||
});
|
||||
|
||||
const [
|
||||
registerTableSelected,
|
||||
{ setTableData, setSelectedRowKeys: setSelectedRowKeys2, getSelectRowKeys: getSelectRowKeys2 },
|
||||
] = useTable({
|
||||
dataSource: selectedList.value,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
onChange: (val) => {
|
||||
num.value = val.length;
|
||||
},
|
||||
},
|
||||
striped: false,
|
||||
});
|
||||
|
||||
const handleTabChange = (key) => {
|
||||
if (key === '2') {
|
||||
setSelectedRowKeys2(cloneDeep(getSelectRowKeys1()));
|
||||
selectedList.value = cloneDeep(getSelectRows());
|
||||
selectedList.value.map((x) => {
|
||||
delete x.children;
|
||||
});
|
||||
setTableData(selectedList.value);
|
||||
} else {
|
||||
setSelectedRowKeys1(getSelectRowKeys2());
|
||||
}
|
||||
};
|
||||
const handleSelect = (deptId = '') => {
|
||||
selectDeptId.value = deptId;
|
||||
reload({ searchInfo: { deptId, name: searchText.value } });
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
searchText.value;
|
||||
reload();
|
||||
};
|
||||
const handleClear = () => {
|
||||
setSelectedRowKeys1([]);
|
||||
};
|
||||
const handleSubmit = () => {
|
||||
getSelectRows().map((x) => {
|
||||
delete x.children;
|
||||
});
|
||||
emits('success', getSelectRows());
|
||||
closeModal();
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.vben-basic-table .ant-table-wrapper) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-nav) {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-nav-wrap) {
|
||||
flex: none !important;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-extra-content) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 80%;
|
||||
margin-left: 15px;
|
||||
|
||||
.left-extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.left-text {
|
||||
width: 90px;
|
||||
margin-left: 10px;
|
||||
color: #5e95ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
border-radius: 10px;
|
||||
background: #f8f8f8;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
</style>
|
||||
172
src/views/system/user/components/RoleModal.vue
Normal file
172
src/views/system/user/components/RoleModal.vue
Normal file
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:width="800"
|
||||
title="新增角色"
|
||||
:fixedHeight="true"
|
||||
@ok="handleSubmit"
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey" @change="handleTabChange">
|
||||
<a-tab-pane key="1" tab="全部">
|
||||
<BasicTable @register="registerTableAll" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="已选" force-render>
|
||||
<BasicTable @register="registerTableSelected" />
|
||||
</a-tab-pane>
|
||||
<template #rightExtra>
|
||||
<div class="left-extra">
|
||||
<a-input
|
||||
v-model:value="searchText"
|
||||
placeholder="请输入查询关键字"
|
||||
@keypress.enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<SearchOutlined style="color: #ccc" />
|
||||
</template>
|
||||
</a-input>
|
||||
<span class="left-text">已选中{{ num }}条</span>
|
||||
</div>
|
||||
<a-button type="primary" danger :icon="h(DeleteOutlined)" @click="handleClear">
|
||||
清空
|
||||
</a-button>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, h } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
|
||||
import { getRolePageList } from '/@/api/system/role';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { DeleteOutlined, SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
const { t } = useI18n();
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: t('角色名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('角色编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
const activeKey = ref('1');
|
||||
const selectedList = ref<any[]>([]);
|
||||
const num = ref(0);
|
||||
const searchText = ref('');
|
||||
|
||||
const emits = defineEmits(['success']);
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
setModalProps({ confirmLoading: false });
|
||||
setSelectedRowKeys1(data.roles?.map((x) => x.id) || []);
|
||||
selectedList.value = cloneDeep(data.roles);
|
||||
setTableData(selectedList.value);
|
||||
reload();
|
||||
});
|
||||
|
||||
const [
|
||||
registerTableAll,
|
||||
{
|
||||
getSelectRows,
|
||||
setSelectedRowKeys: setSelectedRowKeys1,
|
||||
getSelectRowKeys: getSelectRowKeys1,
|
||||
reload,
|
||||
},
|
||||
] = useTable({
|
||||
api: getRolePageList,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
beforeFetch: (params) => {
|
||||
return { ...params, name: searchText.value };
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
onChange: (val) => {
|
||||
num.value = val.length;
|
||||
},
|
||||
},
|
||||
striped: false,
|
||||
});
|
||||
|
||||
const [
|
||||
registerTableSelected,
|
||||
{ setTableData, setSelectedRowKeys: setSelectedRowKeys2, getSelectRowKeys: getSelectRowKeys2 },
|
||||
] = useTable({
|
||||
dataSource: selectedList.value,
|
||||
rowKey: 'id',
|
||||
columns,
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
onChange: (val) => {
|
||||
num.value = val.length;
|
||||
},
|
||||
},
|
||||
striped: false,
|
||||
});
|
||||
|
||||
const handleTabChange = (key) => {
|
||||
if (key === '2') {
|
||||
setSelectedRowKeys2(cloneDeep(getSelectRowKeys1()));
|
||||
selectedList.value = cloneDeep(getSelectRows());
|
||||
setTableData(selectedList.value);
|
||||
} else {
|
||||
setSelectedRowKeys1(getSelectRowKeys2());
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
searchText.value;
|
||||
reload();
|
||||
};
|
||||
const handleClear = () => {
|
||||
setSelectedRowKeys1([]);
|
||||
};
|
||||
const handleSubmit = () => {
|
||||
emits('success', getSelectRows());
|
||||
closeModal();
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.vben-basic-table .ant-table-wrapper) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-nav-wrap) {
|
||||
flex: none !important;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-extra-content) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 80%;
|
||||
margin-left: 60px;
|
||||
|
||||
.left-extra {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.left-text {
|
||||
width: 90px;
|
||||
margin-left: 10px;
|
||||
color: #5e95ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
border-radius: 10px;
|
||||
background: #f8f8f8;
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
</style>
|
||||
691
src/views/system/user/components/UserModal.vue
Normal file
691
src/views/system/user/components/UserModal.vue
Normal file
@ -0,0 +1,691 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
||||
<a-tabs
|
||||
v-model:activeKey="activeKey"
|
||||
:class="{ 'h-full': ['3', '4', '5'].includes(activeKey) }"
|
||||
>
|
||||
<a-tab-pane key="1" tab="基础信息">
|
||||
<BasicForm @register="registerForm1" :style="{ 'margin-right': '10px' }">
|
||||
<template #password="{ model, field }">
|
||||
<a-input
|
||||
v-model:value="model[field]"
|
||||
:type="isShowPwd ? 'text' : 'password'"
|
||||
placeholder="请输入密码"
|
||||
@change="changePwd"
|
||||
allowClear
|
||||
>
|
||||
<template #suffix>
|
||||
<div class="input-state" v-if="model[field]">
|
||||
<a-progress
|
||||
:percent="pwdPercent"
|
||||
:steps="3"
|
||||
:stroke-color="pwdColor"
|
||||
:showInfo="false"
|
||||
/>
|
||||
<span>{{ pwdText }}</span>
|
||||
</div>
|
||||
<Icon
|
||||
:icon="
|
||||
isShowPwd ? 'ant-design:eye-outlined' : 'ant-design:eye-invisible-outlined'
|
||||
"
|
||||
color="#00000073"
|
||||
@click="changeState"
|
||||
style="cursor: pointer"
|
||||
/>
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
</BasicForm>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="身份信息" force-render>
|
||||
<BasicForm @register="registerForm2" :style="{ 'margin-right': '10px' }" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" tab="角色信息" force-render>
|
||||
<BasicTable @register="registerTable1">
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.flag === 'INDEX'">
|
||||
<plus-outlined :style="{ color: '#5E95FF' }" @click="handleRoleCreate" />
|
||||
</template>
|
||||
<template v-else>{{ column.customTitle }}</template>
|
||||
</template>
|
||||
<template #action="{ index }">
|
||||
<a-button type="link" danger @click="handleDelete(index, 1)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="4" tab="岗位信息" force-render>
|
||||
<BasicTable @register="registerTable2">
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.flag === 'INDEX'">
|
||||
<plus-outlined :style="{ color: '#5E95FF' }" @click="handlePostCreate" />
|
||||
</template>
|
||||
<template v-else>{{ column.customTitle }}</template>
|
||||
</template>
|
||||
<template #action="{ index }">
|
||||
<a-button type="link" danger @click="handleDelete(index, 2)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="5" tab="负责部门" force-render>
|
||||
<BasicTable @register="registerTable3">
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.flag === 'INDEX'">
|
||||
<plus-outlined :style="{ color: '#5E95FF' }" @click="handleOrgCreate" />
|
||||
</template>
|
||||
<template v-else>{{ column.customTitle }}</template>
|
||||
</template>
|
||||
<template #action="{ index }">
|
||||
<a-button type="link" danger @click="handleDelete(index, 3)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<RoleModal @register="registerRoleModal" @success="handleRoleSuccess" />
|
||||
<PostModal @register="registerPostModal" @success="handlePostSuccess" />
|
||||
<OrganizationModal @register="registerOrgModal" @success="handleOrgSuccess" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner, useModal } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { BasicTable, useTable, FormSchema, BasicColumn } from '/@/components/Table';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { getDepartmentTree } from '/@/api/system/department';
|
||||
import { addUser, getUser, updateUser } from '/@/api/system/user';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import { testPwdState } from '/@/utils/event/design';
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import RoleModal from './RoleModal.vue';
|
||||
import PostModal from './PostModal.vue';
|
||||
import OrganizationModal from './OrganizationModal.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const accountFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'userName',
|
||||
label: '账号',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入账号',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: '用户姓名',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入用户姓名',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'code',
|
||||
label: t('编号'),
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入编号',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'departmentIds',
|
||||
label: t('所属机构'),
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
placeholder: '请选择所属机构',
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
key: 'id',
|
||||
value: 'id',
|
||||
},
|
||||
multiple: true,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'password',
|
||||
label: '登录密码',
|
||||
component: 'Input',
|
||||
ifShow: false,
|
||||
slot: 'password',
|
||||
colProps: { span: 12 },
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
if (!value) {
|
||||
return Promise.reject('请输入登录密码');
|
||||
}
|
||||
if (pwdText.value === '弱') {
|
||||
return Promise.reject('密码强度设置过低,请至少保证中等强度。');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'birthDate',
|
||||
label: '生日',
|
||||
component: 'DatePicker',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择生日',
|
||||
format: 'YYYY-MM-DD',
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('邮箱'),
|
||||
field: 'email',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入邮箱',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('手机号码'),
|
||||
field: 'mobile',
|
||||
component: 'Input',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入手机号码',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '电话号码',
|
||||
field: 'phoneNumber',
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入电话号码',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'qqNumber',
|
||||
label: 'QQ号码',
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入QQ号码',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'wechatNumber',
|
||||
label: '微信',
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入微信',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'bindIp',
|
||||
label: '绑定IP',
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入绑定IP',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'address',
|
||||
label: '联系地址',
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入联系地址',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: '排序',
|
||||
component: 'InputNumber',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入排序',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('备注'),
|
||||
field: 'remark',
|
||||
component: 'InputTextArea',
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: '请输入备注',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const identityFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'identityCardNumber',
|
||||
label: '身份证号',
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请输入身份证号',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'politicsStatus',
|
||||
label: '政治面貌',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择政治面貌',
|
||||
itemId: '1737728866478612482',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'administrativePost',
|
||||
label: '行政职务',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择行政职务',
|
||||
itemId: '1737732631323631617',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'administrativeRank',
|
||||
label: '行政职级',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择行政职级',
|
||||
itemId: '1737738124947509249',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'secretLevel',
|
||||
label: '用户密级',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择用户密级',
|
||||
itemId: '1737748310911242241',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'professionalTitleGrade',
|
||||
label: '职称等级',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择职称等级',
|
||||
itemId: '1737755116874170369',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'technicalPosition',
|
||||
label: '技术职务',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择技术职务',
|
||||
itemId: '1737758467082878978',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'managerialPosition',
|
||||
label: '管理职务',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择管理职务',
|
||||
itemId: '1737758998798991362',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'vocationalSkill',
|
||||
label: '职业技能',
|
||||
component: 'DicSelect',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: '请选择职业技能',
|
||||
itemId: '1738008457994719233',
|
||||
isShowAdd: false,
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'gender',
|
||||
label: t('性别'),
|
||||
component: 'RadioGroup',
|
||||
required: true,
|
||||
colProps: { span: 12 },
|
||||
defaultValue: 1,
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '男', value: 1 },
|
||||
{ label: '女', value: 0 },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
const roleColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('角色编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('角色名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
const postColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('岗位编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('岗位名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
const orgColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('组织编码'),
|
||||
dataIndex: 'code',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: t('组织名称'),
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
const { notification } = useMessage();
|
||||
const isUpdate = ref(true);
|
||||
const rowId = ref('');
|
||||
const isShowPwd = ref(false);
|
||||
const pwdPercent = ref(0);
|
||||
const pwdColor = ref('#e74242');
|
||||
const pwdText = ref('弱');
|
||||
const activeKey = ref('1');
|
||||
const roleDatasource = ref<any[]>([]);
|
||||
const postDatasource = ref<any[]>([]);
|
||||
const orgDatasource = ref<any[]>([]);
|
||||
|
||||
const [
|
||||
registerForm1,
|
||||
{
|
||||
setFieldsValue: setFieldsValue1,
|
||||
updateSchema: updateSchema1,
|
||||
resetFields: resetFields1,
|
||||
validate: validate1,
|
||||
},
|
||||
] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: accountFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
actionColOptions: {
|
||||
span: 23,
|
||||
},
|
||||
});
|
||||
|
||||
const [
|
||||
registerForm2,
|
||||
{ setFieldsValue: setFieldsValue2, resetFields: resetFields2, validate: validate2 },
|
||||
] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: identityFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
actionColOptions: {
|
||||
span: 23,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable1, { setTableData: setTableData1 }] = useTable({
|
||||
rowKey: 'id',
|
||||
dataSource: roleDatasource.value,
|
||||
columns: roleColumns,
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable2, { setTableData: setTableData2 }] = useTable({
|
||||
rowKey: 'id',
|
||||
dataSource: postDatasource.value,
|
||||
columns: postColumns,
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable3, { setTableData: setTableData3 }] = useTable({
|
||||
rowKey: 'id',
|
||||
dataSource: orgDatasource.value,
|
||||
columns: orgColumns,
|
||||
striped: false,
|
||||
actionColumn: {
|
||||
width: 80,
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
slots: { customRender: 'action' },
|
||||
},
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
resetFields1();
|
||||
resetFields2();
|
||||
setModalProps({ confirmLoading: false, width: 800, fixedHeight: true, destroyOnClose: true });
|
||||
activeKey.value = '1';
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
roleDatasource.value = [];
|
||||
if (unref(isUpdate)) {
|
||||
rowId.value = data.id;
|
||||
const record = await getUser(data.id);
|
||||
const departmentIds = record.departmentIds.split(',');
|
||||
setFieldsValue1({
|
||||
...record,
|
||||
departmentIds,
|
||||
});
|
||||
setFieldsValue2({
|
||||
...record,
|
||||
});
|
||||
roleDatasource.value = record.roles || [];
|
||||
postDatasource.value = record.posts || [];
|
||||
orgDatasource.value = record.chargeDepartments || [];
|
||||
|
||||
setTableData1(roleDatasource.value);
|
||||
setTableData2(postDatasource.value);
|
||||
setTableData3(orgDatasource.value);
|
||||
}
|
||||
const treeData = await getDepartmentTree();
|
||||
|
||||
updateSchema1([
|
||||
{
|
||||
field: 'password',
|
||||
ifShow: !unref(isUpdate),
|
||||
},
|
||||
{
|
||||
field: 'departmentIds',
|
||||
componentProps: { treeData },
|
||||
},
|
||||
]);
|
||||
});
|
||||
const [registerRoleModal, { openModal: openRoleModal }] = useModal();
|
||||
const [registerPostModal, { openModal: openPostModal }] = useModal();
|
||||
const [registerOrgModal, { openModal: openOrgModal }] = useModal();
|
||||
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? t('新增用户') : t('编辑用户')));
|
||||
|
||||
const changeState = () => {
|
||||
isShowPwd.value = !isShowPwd.value;
|
||||
};
|
||||
|
||||
const changePwd = (e) => {
|
||||
const pwdScore = testPwdState(e.target.value);
|
||||
if (pwdScore < 50) {
|
||||
pwdPercent.value = 30;
|
||||
pwdColor.value = '#e74242';
|
||||
pwdText.value = '弱';
|
||||
} else if (pwdScore < 75) {
|
||||
pwdPercent.value = 60;
|
||||
pwdColor.value = '#efbd47';
|
||||
pwdText.value = '中';
|
||||
} else {
|
||||
pwdPercent.value = 100;
|
||||
pwdColor.value = '#55d187';
|
||||
pwdText.value = '强';
|
||||
}
|
||||
};
|
||||
|
||||
const handleRoleCreate = () => {
|
||||
openRoleModal(true, {
|
||||
roles: roleDatasource.value,
|
||||
});
|
||||
};
|
||||
const handlePostCreate = () => {
|
||||
openPostModal(true, {
|
||||
posts: postDatasource.value,
|
||||
});
|
||||
};
|
||||
const handleOrgCreate = () => {
|
||||
openOrgModal(true, {
|
||||
orgs: orgDatasource.value,
|
||||
});
|
||||
};
|
||||
const handleDelete = (index, type) => {
|
||||
switch (type) {
|
||||
case 1:
|
||||
roleDatasource.value.splice(index, 1);
|
||||
break;
|
||||
case 2:
|
||||
postDatasource.value.splice(index, 1);
|
||||
break;
|
||||
case 3:
|
||||
orgDatasource.value.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRoleSuccess = (data) => {
|
||||
roleDatasource.value = data;
|
||||
setTableData1(roleDatasource.value);
|
||||
};
|
||||
const handlePostSuccess = (data) => {
|
||||
postDatasource.value = data;
|
||||
setTableData2(postDatasource.value);
|
||||
};
|
||||
const handleOrgSuccess = (data) => {
|
||||
orgDatasource.value = data;
|
||||
setTableData3(orgDatasource.value);
|
||||
};
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await validate1();
|
||||
const values2 = await validate2();
|
||||
const roleIds = roleDatasource.value?.map((x) => x.id);
|
||||
const postIds = postDatasource.value?.map((x) => x.id);
|
||||
const chargeDepartmentIds = orgDatasource.value?.map((x) => x.id);
|
||||
const data = {
|
||||
...values,
|
||||
...values2,
|
||||
departmentIds: values.departmentIds.toString(),
|
||||
roleIds,
|
||||
postIds,
|
||||
chargeDepartmentIds,
|
||||
};
|
||||
setModalProps({ confirmLoading: true });
|
||||
|
||||
// TODO custom api
|
||||
if (!unref(isUpdate)) {
|
||||
//false 新增
|
||||
await addUser(data);
|
||||
notification.success({
|
||||
message: t('新增用户'),
|
||||
description: t('成功'),
|
||||
}); //提示消息
|
||||
} else {
|
||||
data.id = rowId.value;
|
||||
await updateUser(data);
|
||||
notification.success({
|
||||
message: t('编辑用户'),
|
||||
description: t('成功'),
|
||||
}); //提示消息
|
||||
}
|
||||
|
||||
closeModal();
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.pwd-progress {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
right: 14%;
|
||||
top: 17%;
|
||||
width: 90px;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.input-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #b1b1b1;
|
||||
margin-left: 5px;
|
||||
|
||||
span {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-progress-steps-item) {
|
||||
height: 4px !important;
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-content),
|
||||
:deep(.vben-basic-table .ant-table-container) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.vben-basic-table .ant-table) {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user