mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-11-03 22:57:14 +08:00 
			
		
		
		
	新增:新增系统管理/部门管理/新增功能
This commit is contained in:
		@@ -0,0 +1,65 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package top.charles7c.cnadmin.common.util;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.lang.tree.Tree;
 | 
			
		||||
import cn.hutool.core.lang.tree.TreeNodeConfig;
 | 
			
		||||
import cn.hutool.core.lang.tree.TreeUtil;
 | 
			
		||||
import cn.hutool.core.lang.tree.parser.NodeParser;
 | 
			
		||||
import cn.hutool.core.util.ReflectUtil;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 树工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author Charles7c
 | 
			
		||||
 * @since 2023/1/22 22:11
 | 
			
		||||
 */
 | 
			
		||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
			
		||||
public class TreeUtils {
 | 
			
		||||
 | 
			
		||||
    /** 默认属性配置对象(根据前端树结构灵活调整名称) */
 | 
			
		||||
    private static final TreeNodeConfig DEFAULT_CONFIG =
 | 
			
		||||
        TreeNodeConfig.DEFAULT_CONFIG.setNameKey("title").setIdKey("key").setWeightKey("sort");
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 树构建
 | 
			
		||||
     *
 | 
			
		||||
     * @param <T>
 | 
			
		||||
     *            转换的实体 为数据源里的对象类型
 | 
			
		||||
     * @param <E>
 | 
			
		||||
     *            ID类型
 | 
			
		||||
     * @param list
 | 
			
		||||
     *            源数据集合
 | 
			
		||||
     * @param nodeParser
 | 
			
		||||
     *            转换器
 | 
			
		||||
     * @return List
 | 
			
		||||
     */
 | 
			
		||||
    public static <T, E> List<Tree<E>> build(List<T> list, NodeParser<T, E> nodeParser) {
 | 
			
		||||
        if (CollUtil.isEmpty(list)) {
 | 
			
		||||
            return new ArrayList<>();
 | 
			
		||||
        }
 | 
			
		||||
        E parentId = (E)ReflectUtil.getFieldValue(list.get(0), DEFAULT_CONFIG.getParentIdKey());
 | 
			
		||||
        return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package top.charles7c.cnadmin.system.model.request;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
import javax.validation.constraints.NotBlank;
 | 
			
		||||
import javax.validation.constraints.Size;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 创建部门信息
 | 
			
		||||
 *
 | 
			
		||||
 * @author Charles7c
 | 
			
		||||
 * @since 2023/1/24 00:21
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Schema(description = "创建部门信息")
 | 
			
		||||
public class CreateDeptRequest implements Serializable {
 | 
			
		||||
 | 
			
		||||
    private static final long serialVersionUID = 1L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 上级部门 ID
 | 
			
		||||
     */
 | 
			
		||||
    @Schema(description = "上级部门 ID", defaultValue = "0")
 | 
			
		||||
    private Long parentId = 0L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门名称
 | 
			
		||||
     */
 | 
			
		||||
    @Schema(description = "部门名称")
 | 
			
		||||
    @NotBlank(message = "部门名称不能为空")
 | 
			
		||||
    private String deptName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 部门排序
 | 
			
		||||
     */
 | 
			
		||||
    @Schema(description = "部门排序", defaultValue = "999")
 | 
			
		||||
    private Integer deptSort = 999;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 描述
 | 
			
		||||
     */
 | 
			
		||||
    @Schema(description = "描述")
 | 
			
		||||
    @Size(max = 200, message = "描述长度不能超过 200 个字符")
 | 
			
		||||
    private String description;
 | 
			
		||||
}
 | 
			
		||||
@@ -18,7 +18,10 @@ package top.charles7c.cnadmin.system.service;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.lang.tree.Tree;
 | 
			
		||||
 | 
			
		||||
import top.charles7c.cnadmin.system.model.query.DeptQuery;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.request.CreateDeptRequest;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.vo.DeptVO;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -37,4 +40,44 @@ public interface DeptService {
 | 
			
		||||
     * @return 列表数据
 | 
			
		||||
     */
 | 
			
		||||
    List<DeptVO> list(DeptQuery query);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构建树
 | 
			
		||||
     *
 | 
			
		||||
     * @param list
 | 
			
		||||
     *            原始列表数据
 | 
			
		||||
     * @return 树列表
 | 
			
		||||
     */
 | 
			
		||||
    List<DeptVO> buildListTree(List<DeptVO> list);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构建树
 | 
			
		||||
     *
 | 
			
		||||
     * @param list
 | 
			
		||||
     *            原始列表数据
 | 
			
		||||
     * @return 树列表
 | 
			
		||||
     */
 | 
			
		||||
    List<Tree<Long>> buildTree(List<DeptVO> list);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 新增
 | 
			
		||||
     *
 | 
			
		||||
     * @param request
 | 
			
		||||
     *            创建信息
 | 
			
		||||
     * @return 新增记录 ID
 | 
			
		||||
     */
 | 
			
		||||
    Long create(CreateDeptRequest request);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查部门名称是否存在
 | 
			
		||||
     *
 | 
			
		||||
     * @param deptName
 | 
			
		||||
     *            部门名称
 | 
			
		||||
     * @param parentId
 | 
			
		||||
     *            上级部门 ID
 | 
			
		||||
     * @param deptId
 | 
			
		||||
     *            部门 ID
 | 
			
		||||
     * @return 是否存在
 | 
			
		||||
     */
 | 
			
		||||
    boolean checkDeptNameExist(String deptName, Long parentId, Long deptId);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,17 +24,23 @@ import java.util.stream.Collectors;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.bean.BeanUtil;
 | 
			
		||||
import cn.hutool.core.collection.CollUtil;
 | 
			
		||||
import cn.hutool.core.lang.tree.Tree;
 | 
			
		||||
 | 
			
		||||
import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum;
 | 
			
		||||
import top.charles7c.cnadmin.common.util.ExceptionUtils;
 | 
			
		||||
import top.charles7c.cnadmin.common.util.TreeUtils;
 | 
			
		||||
import top.charles7c.cnadmin.common.util.helper.QueryHelper;
 | 
			
		||||
import top.charles7c.cnadmin.system.mapper.DeptMapper;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.entity.SysDept;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.query.DeptQuery;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.request.CreateDeptRequest;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.vo.DeptVO;
 | 
			
		||||
import top.charles7c.cnadmin.system.service.DeptService;
 | 
			
		||||
import top.charles7c.cnadmin.system.service.UserService;
 | 
			
		||||
@@ -60,31 +66,11 @@ public class DeptServiceImpl implements DeptService {
 | 
			
		||||
        List<SysDept> list = deptMapper.selectList(queryWrapper);
 | 
			
		||||
        List<DeptVO> voList = BeanUtil.copyToList(list, DeptVO.class);
 | 
			
		||||
        voList.forEach(this::fill);
 | 
			
		||||
        return buildTree(voList);
 | 
			
		||||
        return voList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 填充数据
 | 
			
		||||
     *
 | 
			
		||||
     * @param vo
 | 
			
		||||
     *            VO
 | 
			
		||||
     */
 | 
			
		||||
    private void fill(DeptVO vo) {
 | 
			
		||||
        Long updateUser = vo.getUpdateUser();
 | 
			
		||||
        if (updateUser == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        vo.setUpdateUserString(ExceptionUtils.exToNull(() -> userService.getById(vo.getUpdateUser())).getNickname());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 构建树
 | 
			
		||||
     *
 | 
			
		||||
     * @param list
 | 
			
		||||
     *            原始列表数据
 | 
			
		||||
     * @return 树列表
 | 
			
		||||
     */
 | 
			
		||||
    private List<DeptVO> buildTree(List<DeptVO> list) {
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<DeptVO> buildListTree(List<DeptVO> list) {
 | 
			
		||||
        if (CollUtil.isEmpty(list)) {
 | 
			
		||||
            return new ArrayList<>();
 | 
			
		||||
        }
 | 
			
		||||
@@ -134,4 +120,43 @@ public class DeptServiceImpl implements DeptService {
 | 
			
		||||
        return list.stream().filter(d -> Objects.equals(d.getParentId(), dept.getDeptId()))
 | 
			
		||||
            .map(d -> d.setChildren(this.getChildren(d, list))).collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<Tree<Long>> buildTree(List<DeptVO> list) {
 | 
			
		||||
        return TreeUtils.build(list, (dept, tree) -> {
 | 
			
		||||
            tree.setId(dept.getDeptId());
 | 
			
		||||
            tree.setName(dept.getDeptName());
 | 
			
		||||
            tree.setParentId(dept.getParentId());
 | 
			
		||||
            tree.setWeight(dept.getDeptSort());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Transactional(rollbackFor = Exception.class)
 | 
			
		||||
    public Long create(CreateDeptRequest request) {
 | 
			
		||||
        SysDept sysDept = BeanUtil.copyProperties(request, SysDept.class);
 | 
			
		||||
        sysDept.setStatus(DisEnableStatusEnum.ENABLE);
 | 
			
		||||
        deptMapper.insert(sysDept);
 | 
			
		||||
        return sysDept.getDeptId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean checkDeptNameExist(String deptName, Long parentId, Long deptId) {
 | 
			
		||||
        return deptMapper.exists(Wrappers.<SysDept>lambdaQuery().eq(SysDept::getDeptName, deptName)
 | 
			
		||||
            .eq(SysDept::getParentId, parentId).ne(deptId != null, SysDept::getDeptId, deptId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 填充数据
 | 
			
		||||
     *
 | 
			
		||||
     * @param vo
 | 
			
		||||
     *            VO
 | 
			
		||||
     */
 | 
			
		||||
    private void fill(DeptVO vo) {
 | 
			
		||||
        Long updateUser = vo.getUpdateUser();
 | 
			
		||||
        if (updateUser == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        vo.setUpdateUserString(ExceptionUtils.exToNull(() -> userService.getById(vo.getUpdateUser())).getNickname());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								continew-admin-ui/src/api/common/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								continew-admin-ui/src/api/common/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
import qs from 'query-string';
 | 
			
		||||
import { DeptParams } from '@/api/system/dept';
 | 
			
		||||
import { TreeNodeData } from '@arco-design/web-vue';
 | 
			
		||||
 | 
			
		||||
export default function getDeptTree(params: DeptParams) {
 | 
			
		||||
  return axios.get<TreeNodeData[]>('/common/tree/dept', {
 | 
			
		||||
    params,
 | 
			
		||||
    paramsSerializer: (obj) => {
 | 
			
		||||
      return qs.stringify(obj);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import axios from 'axios';
 | 
			
		||||
 | 
			
		||||
import qs from 'query-string';
 | 
			
		||||
 | 
			
		||||
export interface DeptRecord {
 | 
			
		||||
  deptId: string;
 | 
			
		||||
  deptName: string;
 | 
			
		||||
  parentId: string;
 | 
			
		||||
  parentId: number;
 | 
			
		||||
  deptSort: number;
 | 
			
		||||
  description: string;
 | 
			
		||||
  status: number;
 | 
			
		||||
@@ -14,10 +13,9 @@ export interface DeptRecord {
 | 
			
		||||
  children: Array<DeptRecord>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface DeptParams extends Partial<DeptRecord> {
 | 
			
		||||
  page: number;
 | 
			
		||||
  size: number;
 | 
			
		||||
  sort: Array<string>;
 | 
			
		||||
export interface DeptParams {
 | 
			
		||||
  deptName?: string;
 | 
			
		||||
  status?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getDeptList(params: DeptParams) {
 | 
			
		||||
@@ -28,3 +26,13 @@ export function getDeptList(params: DeptParams) {
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CreateDeptReq {
 | 
			
		||||
  parentId: number;
 | 
			
		||||
  deptName: string;
 | 
			
		||||
  deptSort: number;
 | 
			
		||||
  description: string;
 | 
			
		||||
}
 | 
			
		||||
export function createDept(req: CreateDeptReq) {
 | 
			
		||||
  return axios.post('/system/dept', req);
 | 
			
		||||
}
 | 
			
		||||
@@ -19,6 +19,9 @@ export default {
 | 
			
		||||
  'searchTable.form.reset': 'Reset',
 | 
			
		||||
  'searchTable.form.selectDefault': 'All',
 | 
			
		||||
  'searchTable.operation.create': 'Create',
 | 
			
		||||
  'searchTable.operation.update': 'Update',
 | 
			
		||||
  'searchTable.operation.delete': 'Delete',
 | 
			
		||||
  'searchTable.operation.export': 'Export',
 | 
			
		||||
  'searchTable.operation.import': 'Import',
 | 
			
		||||
  'searchTable.operation.download': 'Download',
 | 
			
		||||
  // columns
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,10 @@ export default {
 | 
			
		||||
  'searchTable.form.search': '查询',
 | 
			
		||||
  'searchTable.form.reset': '重置',
 | 
			
		||||
  'searchTable.form.selectDefault': '全部',
 | 
			
		||||
  'searchTable.operation.create': '新建',
 | 
			
		||||
  'searchTable.operation.create': '新增',
 | 
			
		||||
  'searchTable.operation.update': '修改',
 | 
			
		||||
  'searchTable.operation.delete': '删除',
 | 
			
		||||
  'searchTable.operation.export': '导出',
 | 
			
		||||
  'searchTable.operation.import': '批量导入',
 | 
			
		||||
  'searchTable.operation.download': '下载',
 | 
			
		||||
  // columns
 | 
			
		||||
 
 | 
			
		||||
@@ -12,17 +12,17 @@
 | 
			
		||||
                  v-model="queryFormData.deptName"
 | 
			
		||||
                  placeholder="输入部门名称搜索"
 | 
			
		||||
                  allow-clear
 | 
			
		||||
                  style="width: 150px;"
 | 
			
		||||
                  style="width: 150px"
 | 
			
		||||
                  @press-enter="toQuery"
 | 
			
		||||
                />
 | 
			
		||||
              </a-form-item>
 | 
			
		||||
              <a-form-item field="status" hide-label>
 | 
			
		||||
                <a-select
 | 
			
		||||
                  v-model="queryFormData.status"
 | 
			
		||||
                  :options="statusOptions"
 | 
			
		||||
                  :options="treeData"
 | 
			
		||||
                  placeholder="状态搜索"
 | 
			
		||||
                  allow-clear
 | 
			
		||||
                  style="width: 150px;"
 | 
			
		||||
                  style="width: 150px"
 | 
			
		||||
                />
 | 
			
		||||
              </a-form-item>
 | 
			
		||||
              <a-button type="primary" @click="toQuery">
 | 
			
		||||
@@ -40,24 +40,230 @@
 | 
			
		||||
            </a-form>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <!-- 工具栏 -->
 | 
			
		||||
          <a-row style="margin-bottom: 16px">
 | 
			
		||||
            <a-col :span="12">
 | 
			
		||||
              <a-space>
 | 
			
		||||
                <a-button type="primary" @click="toCreate">
 | 
			
		||||
                  <template #icon>
 | 
			
		||||
                    <icon-plus />
 | 
			
		||||
                  </template>
 | 
			
		||||
                  {{ $t('searchTable.operation.create') }}
 | 
			
		||||
                </a-button>
 | 
			
		||||
                <a-button
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  status="success"
 | 
			
		||||
                  disabled
 | 
			
		||||
                  title="尚未开发"
 | 
			
		||||
                >
 | 
			
		||||
                  <template #icon>
 | 
			
		||||
                    <icon-edit />
 | 
			
		||||
                  </template>
 | 
			
		||||
                  {{ $t('searchTable.operation.update') }}
 | 
			
		||||
                </a-button>
 | 
			
		||||
                <a-button
 | 
			
		||||
                  type="primary"
 | 
			
		||||
                  status="danger"
 | 
			
		||||
                  disabled
 | 
			
		||||
                  title="尚未开发"
 | 
			
		||||
                >
 | 
			
		||||
                  <template #icon>
 | 
			
		||||
                    <icon-delete />
 | 
			
		||||
                  </template>
 | 
			
		||||
                  {{ $t('searchTable.operation.delete') }}
 | 
			
		||||
                </a-button>
 | 
			
		||||
              </a-space>
 | 
			
		||||
            </a-col>
 | 
			
		||||
            <a-col
 | 
			
		||||
              :span="12"
 | 
			
		||||
              style="display: flex; align-items: center; justify-content: end"
 | 
			
		||||
            >
 | 
			
		||||
              <a-button
 | 
			
		||||
                type="primary"
 | 
			
		||||
                status="warning"
 | 
			
		||||
                disabled
 | 
			
		||||
                title="尚未开发"
 | 
			
		||||
              >
 | 
			
		||||
                <template #icon>
 | 
			
		||||
                  <icon-download />
 | 
			
		||||
                </template>
 | 
			
		||||
                {{ $t('searchTable.operation.export') }}
 | 
			
		||||
              </a-button>
 | 
			
		||||
              <a-tooltip :content="$t('searchTable.actions.refresh')">
 | 
			
		||||
                <div class="action-icon" @click="toQuery">
 | 
			
		||||
                  <icon-refresh size="18" />
 | 
			
		||||
                </div>
 | 
			
		||||
              </a-tooltip>
 | 
			
		||||
              <a-dropdown @select="handleSelectDensity">
 | 
			
		||||
                <a-tooltip :content="$t('searchTable.actions.density')">
 | 
			
		||||
                  <div class="action-icon"><icon-line-height size="18" /></div>
 | 
			
		||||
                </a-tooltip>
 | 
			
		||||
                <template #content>
 | 
			
		||||
                  <a-doption
 | 
			
		||||
                    v-for="item in densityList"
 | 
			
		||||
                    :key="item.value"
 | 
			
		||||
                    :value="item.value"
 | 
			
		||||
                    :class="{ active: item.value === size }"
 | 
			
		||||
                  >
 | 
			
		||||
                    <span>{{ item.name }}</span>
 | 
			
		||||
                  </a-doption>
 | 
			
		||||
                </template>
 | 
			
		||||
              </a-dropdown>
 | 
			
		||||
              <a-tooltip :content="$t('searchTable.actions.columnSetting')">
 | 
			
		||||
                <a-popover
 | 
			
		||||
                  trigger="click"
 | 
			
		||||
                  position="bl"
 | 
			
		||||
                  @popup-visible-change="popupVisibleChange"
 | 
			
		||||
                >
 | 
			
		||||
                  <div class="action-icon"><icon-settings size="18" /></div>
 | 
			
		||||
                  <template #content>
 | 
			
		||||
                    <div id="tableSetting">
 | 
			
		||||
                      <div
 | 
			
		||||
                        v-for="(item, index) in showColumns"
 | 
			
		||||
                        :key="item.dataIndex"
 | 
			
		||||
                        class="setting"
 | 
			
		||||
                      >
 | 
			
		||||
                        <div style="margin-right: 4px; cursor: move">
 | 
			
		||||
                          <icon-drag-arrow />
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div>
 | 
			
		||||
                          <a-checkbox
 | 
			
		||||
                            v-model="item.checked"
 | 
			
		||||
                            @change="handleChange($event, item as TableColumnData, index)"
 | 
			
		||||
                          >
 | 
			
		||||
                          </a-checkbox>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="title">
 | 
			
		||||
                          {{ item.title === '#' ? '序列号' : item.title }}
 | 
			
		||||
                        </div>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </template>
 | 
			
		||||
                </a-popover>
 | 
			
		||||
              </a-tooltip>
 | 
			
		||||
            </a-col>
 | 
			
		||||
          </a-row>
 | 
			
		||||
 | 
			
		||||
          <!-- 表格渲染 -->
 | 
			
		||||
          <a-table
 | 
			
		||||
            :columns="columns"
 | 
			
		||||
            ref="tableRef"
 | 
			
		||||
            :columns="cloneColumns as TableColumnData[]"
 | 
			
		||||
            :data="renderData"
 | 
			
		||||
            :pagination="false"
 | 
			
		||||
            :default-expand-all-rows="true"
 | 
			
		||||
            :hide-expand-button-on-empty="true"
 | 
			
		||||
            ref="tableRef"
 | 
			
		||||
            row-key="deptId"
 | 
			
		||||
            :bordered="false"
 | 
			
		||||
            :stripe="true"
 | 
			
		||||
            :loading="loading"
 | 
			
		||||
            size="large"
 | 
			
		||||
            :size="size"
 | 
			
		||||
          >
 | 
			
		||||
            <template #status="{ record }">
 | 
			
		||||
              <a-switch v-model="record.status" :checked-value="1" :unchecked-value="2" />
 | 
			
		||||
              <a-switch
 | 
			
		||||
                v-model="record.status"
 | 
			
		||||
                :checked-value="1"
 | 
			
		||||
                :unchecked-value="2"
 | 
			
		||||
              />
 | 
			
		||||
            </template>
 | 
			
		||||
            <template #operations>
 | 
			
		||||
              <a-button
 | 
			
		||||
                v-permission="['admin']"
 | 
			
		||||
                type="text"
 | 
			
		||||
                size="small"
 | 
			
		||||
                disabled
 | 
			
		||||
                title="尚未开发"
 | 
			
		||||
              >
 | 
			
		||||
                <template #icon>
 | 
			
		||||
                  <icon-edit />
 | 
			
		||||
                </template>
 | 
			
		||||
                修改
 | 
			
		||||
              </a-button>
 | 
			
		||||
              <a-button
 | 
			
		||||
                v-permission="['admin']"
 | 
			
		||||
                type="text"
 | 
			
		||||
                size="small"
 | 
			
		||||
                disabled
 | 
			
		||||
                title="尚未开发"
 | 
			
		||||
              >
 | 
			
		||||
                <template #icon>
 | 
			
		||||
                  <icon-delete />
 | 
			
		||||
                </template>
 | 
			
		||||
                删除
 | 
			
		||||
              </a-button>
 | 
			
		||||
            </template>
 | 
			
		||||
          </a-table>
 | 
			
		||||
 | 
			
		||||
          <!-- 窗口 -->
 | 
			
		||||
          <a-modal
 | 
			
		||||
            title="新增部门"
 | 
			
		||||
            :width="570"
 | 
			
		||||
            :visible="visible"
 | 
			
		||||
            :mask-closable="false"
 | 
			
		||||
            unmount-on-close
 | 
			
		||||
            @ok="handleOk"
 | 
			
		||||
            @cancel="handleCancel"
 | 
			
		||||
          >
 | 
			
		||||
            <a-form ref="formRef" :model="formData" :rules="rules">
 | 
			
		||||
              <a-form-item
 | 
			
		||||
                field="parentId"
 | 
			
		||||
                :validate-trigger="['change', 'blur']"
 | 
			
		||||
                label="上级部门"
 | 
			
		||||
              >
 | 
			
		||||
                <a-tree-select
 | 
			
		||||
                  v-model="formData.parentId"
 | 
			
		||||
                  :data="treeData"
 | 
			
		||||
                  :allow-search="true"
 | 
			
		||||
                  :allow-clear="true"
 | 
			
		||||
                  :filter-tree-node="filterDept"
 | 
			
		||||
                  placeholder="请选择上级部门"
 | 
			
		||||
                />
 | 
			
		||||
              </a-form-item>
 | 
			
		||||
              <a-form-item
 | 
			
		||||
                field="deptName"
 | 
			
		||||
                :validate-trigger="['change', 'blur']"
 | 
			
		||||
                label="部门名称"
 | 
			
		||||
              >
 | 
			
		||||
                <a-input
 | 
			
		||||
                  v-model="formData.deptName"
 | 
			
		||||
                  placeholder="请输入部门名称"
 | 
			
		||||
                  size="large"
 | 
			
		||||
                  allow-clear
 | 
			
		||||
                >
 | 
			
		||||
                </a-input>
 | 
			
		||||
              </a-form-item>
 | 
			
		||||
              <a-form-item
 | 
			
		||||
                field="deptSort"
 | 
			
		||||
                :validate-trigger="['change', 'blur']"
 | 
			
		||||
                label="部门排序"
 | 
			
		||||
              >
 | 
			
		||||
                <a-input-number
 | 
			
		||||
                  v-model="formData.deptSort"
 | 
			
		||||
                  :min="1"
 | 
			
		||||
                  placeholder="请输入部门排序"
 | 
			
		||||
                  mode="button"
 | 
			
		||||
                  size="large"
 | 
			
		||||
                >
 | 
			
		||||
                </a-input-number>
 | 
			
		||||
              </a-form-item>
 | 
			
		||||
              <a-form-item
 | 
			
		||||
                field="description"
 | 
			
		||||
                :validate-trigger="['change', 'blur']"
 | 
			
		||||
                label="描述"
 | 
			
		||||
              >
 | 
			
		||||
                <a-textarea
 | 
			
		||||
                  v-model="formData.description"
 | 
			
		||||
                  placeholder="请输入描述"
 | 
			
		||||
                  size="large"
 | 
			
		||||
                  :max-length="200"
 | 
			
		||||
                  show-word-limit
 | 
			
		||||
                  :auto-size="{
 | 
			
		||||
                    minRows:3,
 | 
			
		||||
                  }"
 | 
			
		||||
                >
 | 
			
		||||
                </a-textarea >
 | 
			
		||||
              </a-form-item>
 | 
			
		||||
            </a-form>
 | 
			
		||||
          </a-modal>
 | 
			
		||||
        </a-col>
 | 
			
		||||
      </a-row>
 | 
			
		||||
    </a-card>
 | 
			
		||||
@@ -65,19 +271,47 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
  import { computed, ref } from 'vue';
 | 
			
		||||
  import { computed, nextTick, reactive, ref, watch } from 'vue';
 | 
			
		||||
  import useLoading from '@/hooks/loading';
 | 
			
		||||
  import { Message, TableInstance } from '@arco-design/web-vue';
 | 
			
		||||
  import { getDeptList, DeptRecord, DeptParams } from '@/api/system/dept';
 | 
			
		||||
  import { FieldRule, Message, TableInstance, TreeNodeData } from '@arco-design/web-vue';
 | 
			
		||||
  import { getDeptList, DeptRecord, DeptParams, createDept } from '@/api/system/dept';
 | 
			
		||||
  import getDeptTree from '@/api/common';
 | 
			
		||||
  import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
 | 
			
		||||
  import { FormInstance } from '@arco-design/web-vue/es/form';
 | 
			
		||||
  import { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
 | 
			
		||||
  import cloneDeep from 'lodash/cloneDeep';
 | 
			
		||||
  import Sortable from 'sortablejs';
 | 
			
		||||
  import { useI18n } from 'vue-i18n';
 | 
			
		||||
 | 
			
		||||
  type SizeProps = 'mini' | 'small' | 'medium' | 'large';
 | 
			
		||||
  type Column = TableColumnData & { checked?: true };
 | 
			
		||||
  const cloneColumns = ref<Column[]>([]);
 | 
			
		||||
  const showColumns = ref<Column[]>([]);
 | 
			
		||||
  const size = ref<SizeProps>('large');
 | 
			
		||||
  const { t } = useI18n();
 | 
			
		||||
  const densityList = computed(() => [
 | 
			
		||||
    {
 | 
			
		||||
      name: t('searchTable.size.mini'),
 | 
			
		||||
      value: 'mini',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: t('searchTable.size.small'),
 | 
			
		||||
      value: 'small',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: t('searchTable.size.medium'),
 | 
			
		||||
      value: 'medium',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: t('searchTable.size.large'),
 | 
			
		||||
      value: 'large',
 | 
			
		||||
    },
 | 
			
		||||
  ]);
 | 
			
		||||
  const { loading, setLoading } = useLoading(true);
 | 
			
		||||
  const tableRef = ref<TableInstance>();
 | 
			
		||||
  const queryFormRef = ref<FormInstance>();
 | 
			
		||||
  const queryFormData = ref({
 | 
			
		||||
    deptName: '',
 | 
			
		||||
    deptName: undefined,
 | 
			
		||||
    status: undefined,
 | 
			
		||||
  });
 | 
			
		||||
  const statusOptions = computed<SelectOptionData[]>(() => [
 | 
			
		||||
@@ -133,11 +367,18 @@
 | 
			
		||||
      title: '修改时间',
 | 
			
		||||
      dataIndex: 'updateTime',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      title: '操作',
 | 
			
		||||
      slotName: 'operations',
 | 
			
		||||
      align: 'center',
 | 
			
		||||
    },
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  // 分页查询列表
 | 
			
		||||
  const fetchData = async (
 | 
			
		||||
    params: DeptParams = { page: 1, size: 10, sort: ['parentId,asc', 'deptSort,asc', 'createTime,desc'] }
 | 
			
		||||
    params: DeptParams = {
 | 
			
		||||
      ...queryFormData.value
 | 
			
		||||
    }
 | 
			
		||||
  ) => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    try {
 | 
			
		||||
@@ -146,11 +387,128 @@
 | 
			
		||||
    } finally {
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
    }
 | 
			
		||||
    setTimeout(function() {
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      tableRef.value?.expandAll();
 | 
			
		||||
    }, 0);
 | 
			
		||||
  };
 | 
			
		||||
  fetchData();
 | 
			
		||||
 | 
			
		||||
  const handleSelectDensity = (
 | 
			
		||||
    val: string | number | Record<string, any> | undefined,
 | 
			
		||||
    e: Event
 | 
			
		||||
  ) => {
 | 
			
		||||
    size.value = val as SizeProps;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleChange = (
 | 
			
		||||
    checked: boolean | (string | boolean | number)[],
 | 
			
		||||
    column: Column,
 | 
			
		||||
    index: number
 | 
			
		||||
  ) => {
 | 
			
		||||
    if (!checked) {
 | 
			
		||||
      cloneColumns.value = showColumns.value.filter(
 | 
			
		||||
        (item) => item.dataIndex !== column.dataIndex
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      cloneColumns.value.splice(index, 0, column);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const exchangeArray = <T extends Array<any>>(
 | 
			
		||||
    array: T,
 | 
			
		||||
    beforeIdx: number,
 | 
			
		||||
    newIdx: number,
 | 
			
		||||
    isDeep = false
 | 
			
		||||
  ): T => {
 | 
			
		||||
    const newArray = isDeep ? cloneDeep(array) : array;
 | 
			
		||||
    if (beforeIdx > -1 && newIdx > -1) {
 | 
			
		||||
      // 先替换后面的,然后拿到替换的结果替换前面的
 | 
			
		||||
      newArray.splice(
 | 
			
		||||
        beforeIdx,
 | 
			
		||||
        1,
 | 
			
		||||
        newArray.splice(newIdx, 1, newArray[beforeIdx]).pop()
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return newArray;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const popupVisibleChange = (val: boolean) => {
 | 
			
		||||
    if (val) {
 | 
			
		||||
      nextTick(() => {
 | 
			
		||||
        const el = document.getElementById('tableSetting') as HTMLElement;
 | 
			
		||||
        const sortable = new Sortable(el, {
 | 
			
		||||
          onEnd(e: any) {
 | 
			
		||||
            const { oldIndex, newIndex } = e;
 | 
			
		||||
            exchangeArray(cloneColumns.value, oldIndex, newIndex);
 | 
			
		||||
            exchangeArray(showColumns.value, oldIndex, newIndex);
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  watch(
 | 
			
		||||
    () => columns.value,
 | 
			
		||||
    (val) => {
 | 
			
		||||
      cloneColumns.value = cloneDeep(val);
 | 
			
		||||
      cloneColumns.value.forEach((item, index) => {
 | 
			
		||||
        item.checked = true;
 | 
			
		||||
      });
 | 
			
		||||
      showColumns.value = cloneDeep(cloneColumns.value);
 | 
			
		||||
    },
 | 
			
		||||
    { deep: true, immediate: true }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const visible = ref(false);
 | 
			
		||||
  const treeData = ref<TreeNodeData[]>();
 | 
			
		||||
  const formRef = ref<FormInstance>();
 | 
			
		||||
  const formData = reactive({
 | 
			
		||||
    parentId: undefined,
 | 
			
		||||
    deptName: '',
 | 
			
		||||
    deptSort: 999,
 | 
			
		||||
    description: '',
 | 
			
		||||
  });
 | 
			
		||||
  const rules = computed((): Record<string, FieldRule[]> => {
 | 
			
		||||
    return {
 | 
			
		||||
      deptName: [
 | 
			
		||||
        { required: true, message: '请输入部门名称' }
 | 
			
		||||
      ],
 | 
			
		||||
      deptSort: [
 | 
			
		||||
        { required: true, message: '请输入部门排序' }
 | 
			
		||||
      ],
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
  // 创建
 | 
			
		||||
  const toCreate = async () => {
 | 
			
		||||
    visible.value = true;
 | 
			
		||||
    const { data } = await getDeptTree({});
 | 
			
		||||
    treeData.value = data;
 | 
			
		||||
  };
 | 
			
		||||
  const filterDept = (searchValue: string, nodeData: TreeNodeData) => {
 | 
			
		||||
    if (nodeData.title) {
 | 
			
		||||
      return nodeData.title.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  };
 | 
			
		||||
  const handleOk = async () => {
 | 
			
		||||
    const errors = await formRef.value?.validate();
 | 
			
		||||
    if (errors) return false;
 | 
			
		||||
    const res = await createDept({
 | 
			
		||||
      parentId: formData.parentId || 0,
 | 
			
		||||
      deptName: formData.deptName,
 | 
			
		||||
      deptSort: formData.deptSort,
 | 
			
		||||
      description: formData.description,
 | 
			
		||||
    });
 | 
			
		||||
    if (!res.success) return false;
 | 
			
		||||
    Message.success(res.msg);
 | 
			
		||||
    handleCancel();
 | 
			
		||||
    fetchData();
 | 
			
		||||
    return true;
 | 
			
		||||
  };
 | 
			
		||||
  const handleCancel = () => {
 | 
			
		||||
    visible.value = false;
 | 
			
		||||
    formRef.value?.resetFields()
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
@@ -179,4 +537,22 @@
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .action-icon {
 | 
			
		||||
    margin-left: 12px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
  }
 | 
			
		||||
  .active {
 | 
			
		||||
    color: #0960bd;
 | 
			
		||||
    background-color: #e3f4fc;
 | 
			
		||||
  }
 | 
			
		||||
  .setting {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    width: 200px;
 | 
			
		||||
    .title {
 | 
			
		||||
      margin-left: 12px;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package top.charles7c.cnadmin.webapi.controller.common;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.Operation;
 | 
			
		||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.lang.tree.Tree;
 | 
			
		||||
 | 
			
		||||
import top.charles7c.cnadmin.common.model.vo.R;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.query.DeptQuery;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.vo.DeptVO;
 | 
			
		||||
import top.charles7c.cnadmin.system.service.DeptService;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 公共 API
 | 
			
		||||
 *
 | 
			
		||||
 * @author Charles7c
 | 
			
		||||
 * @since 2023/1/22 21:48
 | 
			
		||||
 */
 | 
			
		||||
@Tag(name = "公共 API")
 | 
			
		||||
@Validated
 | 
			
		||||
@RestController
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@RequestMapping(value = "/common", produces = MediaType.APPLICATION_JSON_VALUE)
 | 
			
		||||
public class CommonController {
 | 
			
		||||
 | 
			
		||||
    private final DeptService deptService;
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "查询部门树")
 | 
			
		||||
    @GetMapping("/tree/dept")
 | 
			
		||||
    public R<List<Tree<Long>>> deptTree(@Validated DeptQuery query) {
 | 
			
		||||
        List<DeptVO> list = deptService.list(query);
 | 
			
		||||
        List<Tree<Long>> deptTree = deptService.buildTree(list);
 | 
			
		||||
        return R.ok(deptTree);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,12 +25,11 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 | 
			
		||||
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import top.charles7c.cnadmin.common.model.vo.R;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.query.DeptQuery;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.request.CreateDeptRequest;
 | 
			
		||||
import top.charles7c.cnadmin.system.model.vo.DeptVO;
 | 
			
		||||
import top.charles7c.cnadmin.system.service.DeptService;
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +52,19 @@ public class DeptController {
 | 
			
		||||
    @GetMapping
 | 
			
		||||
    public R<List<DeptVO>> list(@Validated DeptQuery query) {
 | 
			
		||||
        List<DeptVO> list = deptService.list(query);
 | 
			
		||||
        return R.ok(list);
 | 
			
		||||
        return R.ok(deptService.buildListTree(list));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Operation(summary = "新增部门")
 | 
			
		||||
    @PostMapping
 | 
			
		||||
    public R<Long> create(@Validated @RequestBody CreateDeptRequest request) {
 | 
			
		||||
        // 校验
 | 
			
		||||
        String deptName = request.getDeptName();
 | 
			
		||||
        boolean isExist = deptService.checkDeptNameExist(deptName, request.getParentId(), null);
 | 
			
		||||
        if (isExist) {
 | 
			
		||||
            return R.fail(String.format("新增失败,'%s'已存在", deptName));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return R.ok("新增成功", deptService.create(request));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user