feat(system): 新增产品线选择组件
- 重构 CsSelectOptionItemVo 类,改名为 CsSelectOptionVo - 新增 SystemComponentController 控制器,提供部门下拉选择数据源查询接口 - 新增 SystemProductLineVo 类,用于表示产品线选择框的选项 - 实现查询部门选择数据源的方法,构建产品线选择框的选项列表 - 新增前端 API 接口和组件以支持产品线选择功能 - 更新业务演示页面,添加产品线选择框
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function queryDeptSelectDataSource() {
|
||||
return request({
|
||||
url: 'api/component/queryDeptSelectDataSource',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-model="selectedValue"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择产品线"
|
||||
:disabled="disabled"
|
||||
@change="handleChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in deptOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { queryDeptSelectDataSource } from '@/api/system/component'
|
||||
import CsMessage from '@/utils/elementui/CsMessage'
|
||||
|
||||
export default {
|
||||
name: 'CuteProductLineSelect',
|
||||
props: {
|
||||
value: {
|
||||
type: [Number, String],
|
||||
default: null
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
deptOptions: [],
|
||||
selectedValue: this.value
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.selectedValue = newVal
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchDeptData()
|
||||
},
|
||||
methods: {
|
||||
fetchDeptData() {
|
||||
queryDeptSelectDataSource().then(data => {
|
||||
this.deptOptions = data
|
||||
}).catch(error => {
|
||||
console.error('获取部门数据失败:', error)
|
||||
CsMessage.Error('获取部门数据失败')
|
||||
})
|
||||
},
|
||||
handleChange(value) {
|
||||
this.selectedValue = value
|
||||
for (const item of this.deptOptions) {
|
||||
if (item.value === value) {
|
||||
this.$emit('detail', item)
|
||||
break
|
||||
}
|
||||
}
|
||||
// 绑定change事件
|
||||
this.$emit('change', value)
|
||||
// 绑定form value
|
||||
this.$emit('input', value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -12,8 +12,12 @@
|
||||
<el-form-item label="部门" prop="dept">
|
||||
<cute-dept-tree v-model="form.dept" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产品线" prop="productLine">
|
||||
<cute-product-line-select v-model="form.productLine" @detail="onProductLineDetailChange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">立即创建</el-button>
|
||||
<el-button type="primary" @click="onSubmit('form')">提 交</el-button>
|
||||
<el-button type="danger" @click="onReset('form')">重 置</el-button>
|
||||
<el-button>取 消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -30,6 +34,12 @@
|
||||
</div>
|
||||
<cute-dept-tree @node-click="onDeptTreeNodeClick" @search="onDeptTreeSearch" />
|
||||
</el-card>
|
||||
<el-card style="margin-top: 10px">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>产品线选择框</span>
|
||||
</div>
|
||||
<cute-product-line-select @change="onProductLineChange" @detail="onProductLineDetailChange" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -37,31 +47,49 @@
|
||||
|
||||
import CuteUserSelect from '@/views/components/business/CuteUserSelect.vue'
|
||||
import CuteDeptTree from '@/views/components/business/CuteDeptTree.vue'
|
||||
import CuteProductLineSelect from '@/views/components/business/CuteProductLineSelect.vue'
|
||||
|
||||
export default {
|
||||
name: 'CuteBusinessDemo',
|
||||
components: { CuteDeptTree, CuteUserSelect },
|
||||
components: { CuteProductLineSelect, CuteDeptTree, CuteUserSelect },
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
user: null,
|
||||
dept: null
|
||||
dept: null,
|
||||
productLine: null
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onUserSelectChange(data) {
|
||||
console.error('onUserSelectChange=', data)
|
||||
console.log('onUserSelectChange=', data)
|
||||
},
|
||||
onDeptTreeNodeClick(data) {
|
||||
console.error('onDeptTreeNodeClick=', data)
|
||||
console.log('onDeptTreeNodeClick=', data)
|
||||
},
|
||||
onSubmit() {
|
||||
console.error('this.form=', this.form)
|
||||
onSubmit(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
console.log('this.form=', this.form)
|
||||
} else {
|
||||
console.error('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
onReset(formName) {
|
||||
this.$refs[formName].resetFields()
|
||||
},
|
||||
onDeptTreeSearch(key) {
|
||||
console.error('onDeptTreeSearch:key', key)
|
||||
console.error('onDeptTreeSearch:form.dept', this.form.dept)
|
||||
console.log('onDeptTreeSearch:key', key)
|
||||
console.log('onDeptTreeSearch:form.dept', this.form.dept)
|
||||
},
|
||||
onProductLineChange(data) {
|
||||
console.log('onProductLineChange', data)
|
||||
},
|
||||
onProductLineDetailChange(data) {
|
||||
console.log('onProductLineDetailChange', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,9 +545,9 @@ export default {
|
||||
handleWebSocketMessage(e) {
|
||||
const that = this
|
||||
const data = e.data
|
||||
// console.error('来自服务器的数据:data', data)
|
||||
// console.log('来自服务器的数据:data', data)
|
||||
const callbackData = JSON.parse(data)
|
||||
// console.error('callbackData', callbackData)
|
||||
// console.log('callbackData', callbackData)
|
||||
try {
|
||||
that.dynamicInstance = JSON.parse(callbackData.data)
|
||||
// 判断流水线是否结束
|
||||
@@ -607,9 +607,9 @@ export default {
|
||||
id: that.dynamicTemplate.id
|
||||
}
|
||||
try {
|
||||
if (that.dynamicHook) {
|
||||
clearInterval(that.dynamicHook)
|
||||
}
|
||||
// if (that.dynamicHook) {
|
||||
// clearInterval(that.dynamicHook)
|
||||
// }
|
||||
that.dynamicStartButtonLoading = true
|
||||
// 这里的 dynamicStartupStatus状态,需要后期与上下文关联后,初始化的时候带入最近一个流水线实例
|
||||
// let result
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import java.util.Map;
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class CsSelectOptionItemVo extends CsObject {
|
||||
public class CsSelectOptionVo extends CsObject {
|
||||
private String label;
|
||||
private String value;
|
||||
private Map<String, Object> ext;
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package cn.odboy.util;
|
||||
|
||||
import cn.odboy.base.CsSelectOptionItemVo;
|
||||
import cn.odboy.base.CsSelectOptionVo;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@@ -197,22 +197,22 @@ public final class CsCollUtil extends cn.hutool.core.collection.CollUtil {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<CsSelectOptionItemVo> testData = new ArrayList<>();
|
||||
CsSelectOptionItemVo selectOption1 = CsSelectOptionItemVo.builder().label("苹果").value("apple").build();
|
||||
CsSelectOptionItemVo selectOption2 = CsSelectOptionItemVo.builder().label("苹果").value("apple").build();
|
||||
CsSelectOptionItemVo selectOption3 = CsSelectOptionItemVo.builder().label("香蕉").value("banana").build();
|
||||
CsSelectOptionItemVo selectOption4 = CsSelectOptionItemVo.builder().label("猫").value("cat").build();
|
||||
List<CsSelectOptionVo> testData = new ArrayList<>();
|
||||
CsSelectOptionVo selectOption1 = CsSelectOptionVo.builder().label("苹果").value("apple").build();
|
||||
CsSelectOptionVo selectOption2 = CsSelectOptionVo.builder().label("苹果").value("apple").build();
|
||||
CsSelectOptionVo selectOption3 = CsSelectOptionVo.builder().label("香蕉").value("banana").build();
|
||||
CsSelectOptionVo selectOption4 = CsSelectOptionVo.builder().label("猫").value("cat").build();
|
||||
testData.add(selectOption1);
|
||||
testData.add(selectOption2);
|
||||
testData.add(selectOption3);
|
||||
testData.add(selectOption4);
|
||||
List<CsSelectOptionItemVo> selectOptions1 = CsCollUtil.streamAscSort(testData, CsSelectOptionItemVo::getValue);
|
||||
List<CsSelectOptionVo> selectOptions1 = CsCollUtil.streamAscSort(testData, CsSelectOptionVo::getValue);
|
||||
System.out.println(JSON.toJSONString(selectOptions1));
|
||||
List<CsSelectOptionItemVo> selectOptions2 = CsCollUtil.streamDescSort(testData, CsSelectOptionItemVo::getValue);
|
||||
List<CsSelectOptionVo> selectOptions2 = CsCollUtil.streamDescSort(testData, CsSelectOptionVo::getValue);
|
||||
System.out.println(JSON.toJSONString(selectOptions2));
|
||||
List<CsSelectOptionItemVo> selectOptions3 = CsCollUtil.streamDistinct(testData, CsSelectOptionItemVo::getValue);
|
||||
List<CsSelectOptionVo> selectOptions3 = CsCollUtil.streamDistinct(testData, CsSelectOptionVo::getValue);
|
||||
System.out.println(JSON.toJSONString(selectOptions3));
|
||||
Map<String, String> selectMap1 = CsCollUtil.streamToMap(testData, CsSelectOptionItemVo::getValue, CsSelectOptionItemVo::getLabel);
|
||||
Map<String, String> selectMap1 = CsCollUtil.streamToMap(testData, CsSelectOptionVo::getValue, CsSelectOptionVo::getLabel);
|
||||
System.out.println(JSON.toJSONString(selectMap1));
|
||||
// 这只是一个示范
|
||||
BigDecimal total1 = CsCollUtil.streamBigDecimalAdd(testData, "value");
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package cn.odboy.system.controller;
|
||||
|
||||
import cn.odboy.system.service.SystemDeptService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@Api(tags = "系统:组件数据源")
|
||||
@RequestMapping("/api/component")
|
||||
public class SystemComponentController {
|
||||
private final SystemDeptService systemDeptService;
|
||||
|
||||
@ApiOperation("查询部门下拉选择数据源")
|
||||
@PostMapping(value = "/queryDeptSelectDataSource")
|
||||
@PreAuthorize("@el.check()")
|
||||
public ResponseEntity<?> queryDeptSelectDataSource() {
|
||||
return new ResponseEntity<>(systemDeptService.queryDeptSelectDataSource(), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -3,7 +3,7 @@ package cn.odboy.system.controller;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.odboy.base.CsPageArgs;
|
||||
import cn.odboy.base.CsResultVo;
|
||||
import cn.odboy.base.CsSelectOptionItemVo;
|
||||
import cn.odboy.base.CsSelectOptionVo;
|
||||
import cn.odboy.framework.exception.BadRequestException;
|
||||
import cn.odboy.framework.properties.AppProperties;
|
||||
import cn.odboy.system.constant.SystemCaptchaBizEnum;
|
||||
@@ -190,7 +190,7 @@ public class SystemUserController {
|
||||
@ApiOperation("查询用户基础数据")
|
||||
@PostMapping(value = "/queryUserMetadataOptions")
|
||||
@PreAuthorize("@el.check('user:list')")
|
||||
public ResponseEntity<List<CsSelectOptionItemVo>> queryUserMetadataOptions(@Validated @RequestBody CsPageArgs<SystemQueryUserArgs> args) {
|
||||
public ResponseEntity<List<CsSelectOptionVo>> queryUserMetadataOptions(@Validated @RequestBody CsPageArgs<SystemQueryUserArgs> args) {
|
||||
int maxPageSize = 50;
|
||||
SystemQueryUserArgs criteria = args.getArgs();
|
||||
LambdaQueryWrapper<SystemUserTb> wrapper = new LambdaQueryWrapper<>();
|
||||
@@ -210,7 +210,7 @@ public class SystemUserController {
|
||||
ext.put("deptId", m.getDeptId());
|
||||
ext.put("email", m.getEmail());
|
||||
ext.put("phone", m.getPhone());
|
||||
return CsSelectOptionItemVo.builder()
|
||||
return CsSelectOptionVo.builder()
|
||||
.label(m.getNickName())
|
||||
.value(m.getUsername())
|
||||
.ext(ext)
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package cn.odboy.system.dal.model;
|
||||
|
||||
import cn.odboy.base.CsObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class SystemProductLineVo extends CsObject {
|
||||
/**
|
||||
* 部门ID(select的value)
|
||||
*/
|
||||
private Long value;
|
||||
/**
|
||||
* 产品线,如"研发部/华南分部/devops组"
|
||||
*/
|
||||
private String label;
|
||||
/**
|
||||
* ID路径,如"1-2-3"
|
||||
*/
|
||||
private String idPath;
|
||||
}
|
||||
+58
@@ -9,6 +9,7 @@ import cn.odboy.framework.exception.BadRequestException;
|
||||
import cn.odboy.system.constant.SystemDataScopeEnum;
|
||||
import cn.odboy.system.dal.dataobject.SystemDeptTb;
|
||||
import cn.odboy.system.dal.model.SystemCreateDeptArgs;
|
||||
import cn.odboy.system.dal.model.SystemProductLineVo;
|
||||
import cn.odboy.system.dal.model.SystemQueryDeptArgs;
|
||||
import cn.odboy.system.dal.mysql.SystemDeptMapper;
|
||||
import cn.odboy.system.dal.mysql.SystemRoleMapper;
|
||||
@@ -17,6 +18,7 @@ import cn.odboy.system.framework.permission.core.CsSecurityHelper;
|
||||
import cn.odboy.util.ClassUtil;
|
||||
import cn.odboy.util.FileUtil;
|
||||
import cn.odboy.util.StringUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -25,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@@ -339,4 +342,59 @@ public class SystemDeptService {
|
||||
}
|
||||
return depts;
|
||||
}
|
||||
|
||||
public List<SystemProductLineVo> queryDeptSelectDataSource() {
|
||||
List<SystemDeptTb> depts = findEnabledDepts();
|
||||
return buildDeptSelectOptions(depts);
|
||||
}
|
||||
|
||||
private List<SystemProductLineVo> buildDeptSelectOptions(List<SystemDeptTb> depts) {
|
||||
// 获取所有部门并按父子关系组织
|
||||
Map<Long, SystemDeptTb> deptMap = depts.stream().collect(Collectors.toMap(SystemDeptTb::getId, Function.identity()));
|
||||
List<SystemProductLineVo> options = new ArrayList<>();
|
||||
for (SystemDeptTb dept : depts) {
|
||||
// 构建部门ID路径
|
||||
List<Long> pathIds = new ArrayList<>();
|
||||
buildDeptIdPath(dept, deptMap, pathIds);
|
||||
|
||||
// 反转路径,从顶级部门到当前部门
|
||||
Collections.reverse(pathIds);
|
||||
|
||||
SystemProductLineVo dto = new SystemProductLineVo();
|
||||
dto.setValue(dept.getId());
|
||||
dto.setIdPath(pathIds.stream().map(String::valueOf).collect(Collectors.joining("-")));
|
||||
dto.setLabel(buildNamePath(dept, deptMap)); // 保留名称路径用于显示
|
||||
|
||||
options.add(dto);
|
||||
}
|
||||
|
||||
// 按部门名称路径排序
|
||||
options.sort(Comparator.comparing(SystemProductLineVo::getLabel));
|
||||
return options;
|
||||
}
|
||||
|
||||
private void buildDeptIdPath(SystemDeptTb dept, Map<Long, SystemDeptTb> deptMap, List<Long> pathIds) {
|
||||
pathIds.add(dept.getId());
|
||||
if (dept.getPid() != null && dept.getPid() != 0 && deptMap.containsKey(dept.getPid())) {
|
||||
buildDeptIdPath(deptMap.get(dept.getPid()), deptMap, pathIds);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildNamePath(SystemDeptTb dept, Map<Long, SystemDeptTb> deptMap) {
|
||||
List<String> pathNames = new ArrayList<>();
|
||||
buildDeptNamePath(dept, deptMap, pathNames);
|
||||
Collections.reverse(pathNames);
|
||||
return String.join("/", pathNames);
|
||||
}
|
||||
|
||||
private void buildDeptNamePath(SystemDeptTb dept, Map<Long, SystemDeptTb> deptMap, List<String> pathNames) {
|
||||
pathNames.add(dept.getName());
|
||||
if (dept.getPid() != null && dept.getPid() != 0 && deptMap.containsKey(dept.getPid())) {
|
||||
buildDeptNamePath(deptMap.get(dept.getPid()), deptMap, pathNames);
|
||||
}
|
||||
}
|
||||
|
||||
private List<SystemDeptTb> findEnabledDepts() {
|
||||
return systemDeptMapper.selectList(new LambdaQueryWrapper<SystemDeptTb>().eq(SystemDeptTb::getEnabled, 1));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user