| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- <template>
- <van-popup :close-on-click-overlay="false" v-model:show="deptCascadeVisible" position="bottom">
- <div class="content-container">
- <div class="action-buttons-container">
- <div style="color:#dc3545;" @click="close">取消</div>
- <div>部门选择</div>
- <div style="color: #007bff" @click="switchPeople">下一步</div>
- </div>
- <div class="cascade-container">
- <van-cascader
- v-model="cascadeValue"
- title="请选择组织"
- :options="options"
- :swipeable="true"
- :columns-num="3"
- @change="SelectedDeptChange"
- :field-names="{
- text: 'text',
- value: 'value',
- children: 'children'
- }"
- />
- </div>
- </div>
- </van-popup>
- <van-popup :close-on-click-overlay="false" v-model:show="dialogVisible" position="bottom">
-
- <div class="action-buttons-container">
- <div style="color: #ffc107" @click="switchDept">返回</div>
- <div>人员选择</div>
- <div v-if="multiple" style="color: #28a745" @click="handleSelect">确认</div>
- </div>
-
- <div class="content-container">
- <div class="search-container">
- <div class="search-label">人员查询</div>
- <div class="search-row">
- <van-field v-model="userName" class="search-field" placeholder="请输入姓名"></van-field>
- <div class="action-buttons">
- <van-button icon="Search" type="primary" @click="getAllTableData">查询</van-button>
- </div>
- </div>
- </div>
-
- <div class="table-container">
- <div class="table-label">人员选择</div>
- <div class="table">
- <div class="table-header">
- <div class="table-column">
- <div class="th" style="flex: 0 0 40px;"></div>
- <div class="th">工号</div>
- <div class="th">姓名</div>
- <div v-if="!multiple" class="th">操作</div>
- </div>
- </div>
- <div class="table-body">
- <!-- 多选模式 -->
- <van-checkbox-group v-if="multiple" v-model="checked" @change="onChange">
- <van-checkbox v-for="item in tableData" shape="square" :name="item.user">
- <div class="employee-row">
- <div class="employee-id">{{ item.user.userCode }}</div>
- <div class="employee-name">{{ item.user.userDesc }}</div>
- </div>
- </van-checkbox>
- </van-checkbox-group>
- <!-- 单选模式 -->
- <div v-else class="employee-list">
- <div
- v-for="item in tableData"
- :key="item.user.id"
- class="employee-item"
- >
- <div class="employee-row">
- <div class="employee-id">{{ item.user.userCode }}</div>
- <div class="employee-name">{{ item.user.userDesc }}</div>
- <div class="employee-name">
- <van-button style="margin: 10px"
- icon="Select"
- type="success"
- size="small"
- plain
- @click="handleSelectSingle(item)"
- >
- 选择
- </van-button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </van-popup>
- </template>
-
-
- <script setup>
- import { ref, getCurrentInstance, nextTick } from 'vue';
- import { showFailToast } from 'vant';
-
- const {
- proxy
- } = getCurrentInstance();
-
- const props = defineProps({
- multiple: {
- type: Boolean,
- default: false
- },
- mainKey: {
- type: String,
- default: ''
- }
- });
-
- const getUserCodeList = (mainKey) => {
- if (!mainKey) return [];
- const json = sessionStorage.getItem(mainKey);
- try {
- return json ? JSON.parse(json) : [];
- } catch (error) {
- return [];
- }
- };
-
- /* 组织树 */
- const deptCascadeVisible = ref(false)
- const switchPeople = () => {
- // 多选模式:初始化缓存数据
- if (props.multiple && props.mainKey) {
- selectedUsers.value = getUserCodeList(props.mainKey);
- }
- deptCascadeVisible.value = false
- dialogVisible.value = true
- }
-
- /* 人员列表 */
- const dialogVisible = ref(false);
- const switchDept = () => {
- if (props.multiple && props.mainKey) {
- // 多选模式:删除上次已选数据
- for (let item of lastCheckedUsers.value) {
- selectedUsers.value = selectedUsers.value.filter(user => user.userCode !== item.userCode);
- }
-
- // 切换其他部门前将数据加入缓存
- for (let item of checkedUsers.value) {
- selectedUsers.value.push(item);
- }
- sessionStorage.setItem(props.mainKey, JSON.stringify(selectedUsers.value));
- }
- dialogVisible.value = false
- deptCascadeVisible.value = true
- }
-
- /* 读取用户所在部门信息 */
- const deptJson = localStorage.getItem('dept');
- const deptInformation = ref([]);
- try {
- deptInformation.value = deptJson ? JSON.parse(deptJson) : [];
- } catch (error) {
- deptInformation.value = [];
- }
- const deptCode = deptInformation.value[0]?.deptCode || '';
- const rawSuperDeptCode = deptCode.length > 2 ? deptCode.slice(0, -2) : '';
-
- /* 读取用户角色信息 */
- const roleJson = localStorage.getItem('role');
- const roleInformation = ref([]);
- try {
- roleInformation.value = roleJson ? JSON.parse(roleJson) : [];
- } catch (error) {
- roleInformation.value = [];
- }
- const roleCodes = roleInformation.value
- .filter(item => item.roleCode && item.roleCode.includes('SX0'))
- .map(item => item.roleCode);
-
- const hasRole = (codes) => {
- if (!roleCodes.length) return false;
- return roleCodes.some(code => codes.some(target => code.includes(target)));
- };
-
- const normalizeSuperDept = (code) => {
- if (!code) return '';
- if (code === 'D11011301') return 'D110113';
- if (code === 'D1101') return 'D11';
- return code;
- };
-
- /* 打开和关闭弹出框 */
- const currentDeptCode = ref('');
- const open = async () => {
- // 多选模式:初始化缓存数据
- if (props.multiple && props.mainKey) {
- selectedUsers.value = getUserCodeList(props.mainKey);
- }
-
- // 始终使用 'D' 查询所有组织,确保显示完整组织树
- // currentDeptCode 仅用于默认选择,不影响查询
- const userDeptCode = deptInformation.value[0]?.deptCode || 'D';
- currentDeptCode.value = userDeptCode;
-
- // 先显示弹窗,再加载数据
- deptCascadeVisible.value = true;
-
- // 重置并加载数据
- options.value = [];
- cascadeValue.value = '';
-
- // getSafeDataTree 内部会强制使用 'D' 查询所有组织
- await getSafeDataTree('D');
- };
- const close = () => {
- deptCascadeVisible.value = false
- dialogVisible.value = false;
- };
- defineExpose({
- open,
- close
- });
-
- /* 条件查询该部门下的所有人员信息 */
- const currentPage = ref(1);
- const pageSize = ref(10);
- const userName = ref('');
- const tableData = ref([]);
-
- /* 多选模式相关 */
- const selectedUsers = ref([]);
- const lastCheckedUsers = ref([]);
- const checkedUsers = ref([]);
- const checked = ref([]);
-
- const currentDeptId = ref('');
- /* 获取表格信息 */
- const getTableData = async () => {
- var url = 'framework/SysUserDept/queryByDeptUserNamePage';
- var param = {
- page: currentPage.value,
- rows: pageSize.value,
- deptId: currentDeptId.value,
- userName: userName.value
- };
- await proxy.$axios.get(url, param).then(response => {
- if (response.data.code === 0) {
- tableData.value = response.data.data.records;
- pageSize.value = response.data.data.total;
-
- // 多选模式:处理缓存数据
- if (props.multiple && props.mainKey) {
- selectedUsers.value = getUserCodeList(props.mainKey);
- lastCheckedUsers.value = [];
- checked.value = [];
- for (let item of tableData.value) {
- if (selectedUsers.value.find(content => content.userCode === item.user.userCode)) {
- checked.value.push(item.user);
- lastCheckedUsers.value.push(item.user);
- }
- }
- }
- } else {
- showFailToast('操作失败!' + response.data.msg);
- }
- });
- };
-
- const getAllTableData = async () => {
- await getTableData();
- await getTableData();
- };
-
- const SelectedDeptChange = (selectInfo) => {
- currentDeptInfo.value = findNodeWithBacktrack(dataTree.value[0], selectInfo.value);
- currentDeptId.value = currentDeptInfo.value.id;
- getAllTableData();
- };
-
- /* 选中数据返回主页面 */
- const emit = defineEmits(['receiveFromChild']);
-
- // 单选模式:选择单个人员
- const handleSelectSingle = (item) => {
- emit('receiveFromChild', item);
- close();
- };
-
- // 多选模式:确认选择
- const handleSelect = () => {
- if (!props.multiple) return;
-
- // 确保 selectedUsers 已初始化
- if (props.mainKey && selectedUsers.value.length === 0) {
- selectedUsers.value = getUserCodeList(props.mainKey);
- }
-
- // 删除上次已选数据
- for (let item of lastCheckedUsers.value) {
- selectedUsers.value = selectedUsers.value.filter(user => user.userCode !== item.userCode);
- }
-
- // 将当前选中的数据加入缓存
- for (let item of checkedUsers.value) {
- // 避免重复添加
- if (!selectedUsers.value.find(user => user.userCode === item.userCode)) {
- selectedUsers.value.push(item);
- }
- }
-
- if (props.mainKey) {
- sessionStorage.setItem(props.mainKey, JSON.stringify(selectedUsers.value));
- }
-
- emit('receiveFromChild', selectedUsers.value);
- close();
- };
-
- const onChange = (value) => {
- if (props.multiple) {
- checkedUsers.value = value;
- }
- };
-
- // 转换部门数据为 cascade 格式
- function convertToCascadeFormat(data) {
- if (!data || !Array.isArray(data)) return [];
- return data.map(item => ({
- text: item.deptName,
- value: item.deptCode,
- children: item.children && item.children.length > 0 ? convertToCascadeFormat(item.children) : []
- }));
- }
-
- // 叶子节点去掉children
- const processTreeNodes = (node) => {
- // 如果节点没有子节点,删除children属性并返回
- if (!node.children || node.children.length === 0) {
- if (node.children) delete node.children;
- return;
- }
-
- // 递归处理所有子节点
- node.children.forEach(child => processTreeNodes(child));
-
- // 检查当前节点是否应该保留children属性
- const hasChildrenWithChildren = node.children.some(child =>
- child.children && child.children.length > 0
- );
-
- // 如果所有子节点都没有子节点(都是叶子节点),删除它们的children属性
- if (!hasChildrenWithChildren) {
- node.children.forEach(child => {
- if (child.children) delete child.children;
- });
- }
- }
-
- const dataTree = ref([]);
- const options = ref([]);
- const cascadeValue = ref();
- const currentDeptInfo = ref({});
- const getSafeDataTree = async (deptCode) => {
- // 强制使用 'D' 查询所有组织,确保显示完整组织树
- const finalDeptCode = 'D';
-
- let url = 'sgsafe/SafeDepartment/queryDeptTreeWithLeafByDeptCode';
- let param = {
- deptCode: finalDeptCode
- };
-
- await proxy.$axios.get(url, param).then(res => {
- if (!res || !res.data || !res.data.data) {
- showFailToast('获取组织架构数据失败');
- return;
- }
-
- dataTree.value = res.data.data;
-
- // 不过滤部门树,直接使用原始数据,显示完整组织架构
- options.value = convertToCascadeFormat(dataTree.value);
-
- options.value.forEach(child => {
- processTreeNodes(child)
- })
-
- /* 组件默认选择用户当前部门,如果查询的是所有组织('D'),则使用用户实际部门代码 */
- const userDeptCode = deptInformation.value[0]?.deptCode || 'D';
- const defaultDeptCode = finalDeptCode === 'D' ? userDeptCode : currentDeptCode.value;
- cascadeValue.value = defaultDeptCode;
-
- /* 默认返回当前部门所有信息 */
- if (dataTree.value && dataTree.value.length > 0) {
- currentDeptInfo.value = findNodeWithBacktrack(dataTree.value[0], defaultDeptCode);
- if (currentDeptInfo.value && currentDeptInfo.value.id) {
- currentDeptId.value = currentDeptInfo.value.id;
- getAllTableData();
- } else {
- // 如果找不到默认部门,则选择根节点
- if (dataTree.value[0]) {
- currentDeptInfo.value = dataTree.value[0];
- currentDeptId.value = dataTree.value[0].id;
- cascadeValue.value = dataTree.value[0].deptCode;
- getAllTableData();
- }
- }
- }
- }).catch(error => {
- showFailToast('获取组织架构数据出错');
- });
- };
-
- /* 根据deptCode查出部门信息 */
- function findNodeWithBacktrack(node, targetDeptCode) {
- if (node.deptCode === targetDeptCode) {
- return node;
- }
- if (node.children && node.children.length > 0) {
- for (const child of node.children) {
- const found = findNodeWithBacktrack(child, targetDeptCode);
- if (found && found.deptCode) {
- return found;
- }
- }
- }
- return {};
- }
-
- </script>
-
- <style scoped>
-
- :deep(.van-checkbox__label) {
- flex: 1;
- }
-
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
- }
-
- body {
- background: linear-gradient(135deg, #f5f7fa 0%, #e4edf9 100%);
- min-height: 100vh;
- padding: 20px;
- display: flex;
- justify-content: center;
- align-items: flex-start;
- }
-
- .content-container {
- height: 100%;
- display: flex;
- flex-direction: column;
- background: #f8fafd;
- }
-
- .search-container {
- background: #f8fafd;
- border-radius: 12px;
- padding: 15px;
- margin-bottom: 20px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
- }
-
- .search-label {
- font-size: 14px;
- color: #5a7eb5;
- font-weight: 500;
- margin-bottom: 8px;
- display: flex;
- align-items: center;
- }
-
- .search-label::before {
- content: "";
- display: inline-block;
- width: 4px;
- height: 16px;
- background: #4a90e2;
- border-radius: 2px;
- margin-right: 8px;
- }
-
- .search-row {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 12px;
- padding: 10px;
- background: white;
- border-radius: 8px;
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
- }
-
- .search-field {
- flex: 1;
- min-width: 250px;
- }
-
- .action-buttons {
- display: flex;
- gap: 12px;
- }
-
- .table-container {
- background: #f8fafd;
- border-radius: 12px;
- padding: 15px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
- }
-
- .table-label {
- font-size: 14px;
- color: #5a7eb5;
- font-weight: 500;
- margin-bottom: 8px;
- display: flex;
- align-items: center;
- }
-
- .table-label::before {
- content: "";
- display: inline-block;
- width: 4px;
- height: 16px;
- background: #4a90e2;
- border-radius: 2px;
- margin-right: 8px;
- }
-
- .table {
- width: 100%;
- border-collapse: separate;
- border-spacing: 0;
- background: white;
- border-radius: 8px;
- overflow: hidden;
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
- }
-
- .table-header {
- background: linear-gradient(120deg, #f0f7ff, #e6f1ff);
- }
-
- .table-column {
- display: flex;
- width: 100%;
- border-bottom: 1px solid #e0eeff;
- }
-
- .th {
- padding: 16px 15px;
- font-weight: 600;
- color: #3a6ab7;
- flex: 1;
- }
-
- .table-body .table-column {
- transition: background-color 0.2s;
- border-bottom: 1px solid #f0f7ff;
- }
-
- .table-body .tr:last-child {
- border-bottom: none;
- }
-
- .table-body .tr:hover {
- background-color: #f8fbff;
- }
-
- .employee-row {
- display: flex;
- width: 100%;
- }
-
- .employee-id, .employee-name {
- flex: 1;
- padding: 0 15px;
- display: flex;
- align-items: center;
- }
-
- .employee-row {
- transition: all 0.3s ease;
- }
-
- .van-checkbox {
- width: 100%;
- padding: 12px 15px;
- }
-
- .van-checkbox__label {
- width: 100%;
- }
-
- .action-buttons-container {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15px 20px;
- background: white;
- border-bottom: 1px solid #e8e8e8;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
- z-index: 10;
- }
-
- .cascade-container {
- flex: 1;
- overflow: hidden;
- background: rgb(255, 255, 255);
- height: calc(100vh - 200px);
- min-height: 400px;
- }
-
- .cascade-container :deep(.van-cascader) {
- height: 100%;
- }
-
- .cascade-container :deep(.van-cascader__content) {
- height: calc(100% - 44px);
- }
-
- </style>
|