Commit dec88768 by zhangtw

物料分类管理新增页面

parent f3255ab9
......@@ -42,3 +42,11 @@ export function delMaterials_category(id) {
method: 'delete'
})
}
// 查询部门下拉树结构
export function categoryTreeSelect() {
return request({
url: '/inventory/materials_category/categoryTree',
method: 'get'
})
}
\ No newline at end of file
<template>
<div class="app-container">
<div class="app-container" style="overflow: hidden;">
<splitpanes class="default-theme">
<!-- 树结构:左侧面板 -->
<pane size="16" style="overflow: auto;">
<TreeComponent
ref="treeComponent"
:tree-data="categoryTreeData"
:tree-props="treeProps"
:node-key="nodeKey"
:show-search="true"
search-placeholder="请输入分类名称"
:default-expand-all="true"
:highlight-current="true"
:loading="loadingTree"
@node-click="handleTreeClick"
>
<!-- 自定义节点内容插槽 -->
<template #node-content="{ node, data }">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
</span>
</template>
</TreeComponent>
</pane>
<!-- 物料管理:右侧面板 -->
<pane size="84" style="overflow: auto;">
<!-- 右侧内容保持不变 -->
<div style="padding: 10px; display: flex; flex-direction: column;">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="88px">
<el-form-item label="物料编码" prop="materialCode">
<el-input
......@@ -33,9 +61,9 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="物料分类" prop="categoryCode">
<el-form-item label="物料分类" prop="categoryNameInput">
<el-input
v-model="queryParams.categoryCode"
v-model="queryParams.categoryNameInput"
placeholder="请输入物料分类"
clearable
@keyup.enter.native="handleQuery"
......@@ -108,19 +136,11 @@
v-hasPermi="['inventory:materials:export']"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-s-grid"
size="mini"
@click="openMaterialsCategory"
v-hasPermi="['inventory:materials:category']"
>物料分类管理</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="materialsList" @selection-change="handleSelectionChange" :scroll-x="true">
<!-- 表格列保持不变 -->
<el-table-column type="selection" width="55" align="center" />
<el-table-column type="index" label="序号" align="center"/>
<el-table-column label="物料编码" align="center" prop="materialCode" />
......@@ -129,7 +149,7 @@
<el-table-column label="TS Code" align="center" prop="tsCode" />
<el-table-column label="物料分类" align="center" prop="categoryCode" >
<template slot-scope="scope">
{{ categoryMap[scope.row.categoryCode] || scope.row.categoryCode }}
{{ scope.row.displayCategory || categoryMap[scope.row.categoryCode] || scope.row.categoryCode }}
</template>
</el-table-column>
<el-table-column label="危险类别ID" align="center" prop="hazardId" />
......@@ -198,6 +218,9 @@
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</pane>
</splitpanes>
<!-- 添加或修改物料对话框 -->
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
......@@ -211,8 +234,13 @@
<el-input v-model="form.sapNo" placeholder="请输入SAP物料号" />
</el-form-item>
<el-form-item label="物料分类" prop="categoryCode">
<el-select v-model="form.categoryCode" placeholder="请选择物料分类" clearable @click="getCategoryList">
<el-option v-for="item in categoryOptions" :key="item.value" :label="item.label" :value="item.value">
<el-select v-model="form.categoryCode" placeholder="请选择物料分类" clearable>
<el-option
v-for="item in categoryTreeOptions"
:key="item.categoryCode"
:label="item.categoryName"
:value="item.categoryCode"
>
</el-option>
</el-select>
</el-form-item>
......@@ -243,9 +271,6 @@
<el-form-item label="风险等级" prop="riskLevel">
<el-input v-model="form.riskLevel" placeholder="请输入风险等级" />
</el-form-item>
<!-- <el-form-item label="创建人编码" prop="createUserCode">
<el-input v-model="form.createUserCode" placeholder="请输入创建人编码" />
</el-form-item> -->
</el-col>
<el-col :span="12">
<el-form-item label="物料名称" prop="materialName">
......@@ -287,9 +312,6 @@
<el-form-item label="排序" prop="sortNo">
<el-input v-model="form.sortNo" placeholder="请输入排序" />
</el-form-item>
<!-- <el-form-item label="更新人编码" prop="updateUserCode">
<el-input v-model="form.updateUserCode" placeholder="请输入更新人编码" />
</el-form-item> -->
</el-col>
</el-row>
</el-form>
......@@ -299,21 +321,6 @@
</div>
</el-dialog>
<!-- 物料分类管理抽屉 -->
<el-drawer
title="物料分类管理"
:visible.sync="parentDialogVisible"
direction="rtl"
size="80%"
append-to-body
>
<MaterialsCategory
ref="materialsCategoryRef"
:init-query="{ categoryCode: queryParams.categoryCode }"
@dialog-close="parentDialogVisible = false"
@form-submit="handleCategorySubmit"
/>
</el-drawer>
<import-excel
ref="import"
title="导入"
......@@ -328,37 +335,61 @@
<script>
import { listMaterials, getMaterials, delMaterials, addMaterials, updateMaterials } from "@/api/inventory/materials"
import { listMaterials_category } from "@/api/inventory/materials_category"
import MaterialsCategory from "@/views/inventory/materials_category/index.vue"
import TreeComponent from '@/views/inventory/materials_category/treeComponent.vue'
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
import Treeselect from "@riophae/vue-treeselect"
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import ImportExcel from "@/components/ImportExcel/index"
export default {
components: { MaterialsCategory, ImportExcel},
name: "Materials",
components: {
TreeComponent,
Splitpanes,
Pane,
Treeselect,
ImportExcel
},
data() {
return {
// 树相关数据
categoryTreeData: [],
treeProps: {
children: 'children',
label: 'label',
value: 'sid'
},
nodeKey: 'sid',
loadingTree: false,
// 分类映射和选项
categoryMap: {},
categoryForm: '',
categoryOptions: [],
// 遮罩层
loading: true,
categoryNameToCodeMap: {},
categoryTreeOptions: [],
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 物料表格数据
materialsList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 物料分类抽屉显示状态
parentDialogVisible: false,
// 当前选中的树节点ID
currentNodeId: null,
// 查询参数
queryParams: {
pageNum: 1,
......@@ -368,61 +399,183 @@ export default {
sapNo: null,
tsCode: null,
categoryCode: null,
categoryNameInput: null,
specification: null,
},
// 表单参数
form: {},
form: {
id: null,
materialCode: null,
materialName: null,
sapNo: null,
tsCode: null,
categoryCode: null,
hazardId: null,
specification: null,
materialUnit: null,
unitWeight: null,
packageWeight: null,
totalWeight: null,
volume: null,
shelfLifeDays: null,
storageTemperature: null,
specialRequirements: null,
isBatchManaged: 0,
isSerialManaged: 0,
minStockLevel: null,
maxStockLevel: null,
isUsed: 1,
isActive: 1,
riskLevel: null,
sortNo: null,
createUserCode: null,
updateUserCode: null
},
// 表单校验
rules: {
materialCode: [{ required: true, message: '请输入物料编码', trigger: 'blur' }],
materialName: [{ required: true, message: '请输入物料名称', trigger: 'blur' }],
sapNo: [{ required: true, message: '请输入SAP物料号', trigger: 'blur' }],
tsCode: [{ required: true, message: '请输入TS Code', trigger: 'blur' }]
}
},
loading: true
}
},
created() {
this.getList(),
this.getCategoryList();
async created() {
await Promise.all([
this.getCategoryTreeData(),
this.getCategoryList()
]);
this.getList()
},
methods: {
getCategoryList() {
listMaterials_category().then(response => {
this.categoryOptions = response.rows.map(item => ({
label: item.categoryName,
value: item.categoryCode
}));
this.categoryMap = response.rows.reduce((map,item) => {
map[item.categoryCode] = item.categoryName;
return map;
}, {});
}).catch(error => {
console.error('加载分类失败',error);
async getCategoryList() {
try {
const response = await listMaterials_category({
pageNum: 1,
pageSize: 1000
});
if (response.rows && response.rows.length > 0) {
// 正向映射:code → name
this.categoryMap = {};
// 反向映射:name → code(支持模糊匹配时用数组存储)
this.categoryNameToCodeMap = {};
response.rows.forEach(item => {
if (item.isUsed !== 0 && item.isUsed !== '0') {
this.categoryMap[item.categoryCode] = item.categoryName;
// 反向映射:名称作为key,编码作为value(若名称重复可存数组)
if (!this.categoryNameToCodeMap[item.categoryName]) {
this.categoryNameToCodeMap[item.categoryName] = item.categoryCode;
} else {
// 处理名称重复的情况(可选)
if (Array.isArray(this.categoryNameToCodeMap[item.categoryName])) {
this.categoryNameToCodeMap[item.categoryName].push(item.categoryCode);
} else {
this.categoryNameToCodeMap[item.categoryName] = [this.categoryNameToCodeMap[item.categoryName], item.categoryCode];
}
}
}
});
this.categoryTreeOptions = response.rows
.filter(item => item.isUsed !== 0 && item.isUsed !== '0')
.map(item => ({
categoryCode: item.categoryCode,
categoryName: item.categoryName
}));
}
} catch (error) {
console.error('获取分类列表失败:', error);
}
},
/** 打开物料分类管理 */
openMaterialsCategory() {
this.parentDialogVisible = true
if (this.$refs.materialsCategoryRef) {
this.$refs.materialsCategoryRef.resetForm()
this.$refs.materialsCategoryRef.getList()
/** 获取分类树数据 */
async getCategoryTreeData() {
this.loadingTree = true
try {
// 调用物料分类列表API获取所有分类
const response = await listMaterials_category({
pageNum: 1,
pageSize: 1000 // 获取足够多的数据
})
if (response.rows && response.rows.length > 0) {
// 过滤掉已禁用的分类
const activeCategories = response.rows.filter(item =>
item.isUsed !== 0 && item.isUsed !== '0'
)
// 构建树形结构
this.categoryTreeData = this.buildTreeData(activeCategories)
console.log('分类树数据:', this.categoryTreeData)
}
} catch (error) {
console.error('获取分类树数据失败:', error)
} finally {
this.loadingTree = false
}
},
/** 构建树形结构 */
buildTreeData(list, parentId = null) {
const result = list
.filter(item => {
if (parentId === null) {
// 根节点:parentId为null、0或空
return !item.parentId || item.parentId === 0 || item.parentId === '0'
} else {
// 子节点:parentId匹配
return item.parentId == parentId
}
})
.map(item => {
const children = this.buildTreeData(list, item.id)
return {
...item,
sid: String(item.id),
label: item.categoryName,
children: children.length > 0 ? children : undefined
}
})
return result
},
/** 物料分类提交回调 */
handleCategorySubmit(res) {
this.parentDialogVisible = false
// 刷新物料列表
/** 处理树节点点击 */
handleTreeClick(data) {
console.log('点击树节点:', data)
this.currentNodeId = data.sid
// 更新查询参数,按选中的分类筛选物料
this.queryParams.categoryCode = data.categoryCode
this.queryParams.categoryNameInput = null // 清空名称输入
this.queryParams.pageNum = 1
this.getList()
},
/** 查询物料列表 */
getList() {
this.loading = true
listMaterials(this.queryParams).then(response => {
this.materialsList = response.rows.filter(item => item.isUsed !== 0 && item.isUsed !== '0');
this.total = response.total
this.loading = false
// 对物料列表中的分类字段做映射处理
this.materialsList = response.rows
.filter(item => item.isUsed !== 0 && item.isUsed !== '0')
.map(item => ({
...item,
// 兜底:如果映射表中没有,显示原始code并标注
displayCategory: this.categoryMap[item.categoryCode] || `${item.categoryCode}(未匹配分类)`
}));
this.total = response.total;
}).catch(() => {
this.loading = false;
}).finally(() => {
this.loading = false;
})
},
......@@ -451,29 +604,51 @@ export default {
shelfLifeDays: null,
storageTemperature: null,
specialRequirements: null,
isBatchManaged: '0',
isSerialManaged: '0',
isBatchManaged: 0,
isSerialManaged: 0,
minStockLevel: null,
maxStockLevel: null,
isUsed: '1',
isActive: '1',
isUsed: 1,
isActive: 1,
riskLevel: null,
sortNo: null,
createUserCode: null,
updateUserCode: null
}
this.resetForm("form")
this.$refs.form && this.$refs.form.resetFields()
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
// 清空原有分类编码
this.queryParams.categoryCode = null;
// 获取用户输入的分类名称
const inputName = this.queryParams.categoryNameInput;
if (inputName) {
// 1. 精确匹配名称 → 编码
const matchedCode = this.categoryNameToCodeMap[inputName];
if (matchedCode) {
// 若有多个编码(名称重复),取第一个
this.queryParams.categoryCode = Array.isArray(matchedCode) ? matchedCode[0] : matchedCode;
} else {
// 2. 模糊匹配名称 → 编码(可选)
const matchedCodes = Object.entries(this.categoryMap)
.filter(([code, name]) => name.includes(inputName))
.map(([code]) => code);
if (matchedCodes.length > 0) {
this.queryParams.categoryCode = matchedCodes[0]; // 或传多个(需后端支持)
}
}
}
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.resetForm("queryForm");
this.queryParams = {
pageNum: 1,
pageSize: 10,
......@@ -482,9 +657,20 @@ export default {
sapNo: null,
tsCode: null,
categoryCode: null,
categoryNameInput: null,
specification: null,
};
this.currentNodeId = null;
// 修复树形高亮重置:直接操作 TreeComponent 内部的 el-tree
if (this.$refs.treeComponent && this.$refs.treeComponent.$refs.tree) {
this.$refs.treeComponent.$refs.tree.setCurrentKey(null); // 清空选中
} else if (this.$refs.treeComponent) {
// 如果 TreeComponent 有自定义重置方法
this.$refs.treeComponent.resetTree();
}
this.getList()
this.getList();
},
// 多选框选中数据
......@@ -514,7 +700,7 @@ export default {
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
this.$refs.form.validate(valid => {
if (valid) {
const promise = this.form.id
? updateMaterials(this.form)
......@@ -524,6 +710,8 @@ export default {
this.$modal.msgSuccess(this.form.id ? "修改成功" : "新增成功")
this.open = false
this.getList()
}).catch(error => {
console.error('提交失败:', error)
})
}
})
......@@ -550,7 +738,28 @@ export default {
this.download('inventory/materials/export', {
...this.queryParams
}, `materials_${new Date().getTime()}.xlsx`)
},
/** 表单重置方法 */
resetForm(formName) {
if (this.$refs[formName]) {
this.$refs[formName].resetFields()
}
}
}
}
</script>
<style scoped>
.app-container {
height: 100vh;
}
.custom-tree-node {
font-size: 14px;
}
.mb8 {
margin-bottom: 8px;
}
</style>
\ No newline at end of file
<template>
<div class="app-container">
<div class="app-container" style="overflow: hidden;">
<splitpanes class="default-theme" style="height: 100%;">
<!-- 树结构:左侧面板 -->
<pane size="16" style="height: 100%; overflow: auto;">
<TreeComponent
:tree-data="categoryOptions"
:tree-props="defaultProps"
:node-key="nodeKey"
:show-search="true"
search-placeholder="请输入分类名称"
:default-expand-all="true"
:highlight-current="true"
:loading="loadingTree"
@node-click="handleTreeClick"
style="height: 100%;"
/>
</pane>
<!-- 物料分类管理:右侧面板 -->
<pane size="84" style="height: 100%; overflow: auto;">
<div style="padding: 10px; height: 100%; display: flex; flex-direction: column;">
<!-- 搜索表单 -->
<el-form
:model="queryParams"
......@@ -7,6 +27,7 @@
:inline="true"
v-show="showSearch"
label-width="128px"
style="margin-bottom: 10px;"
>
<el-form-item label="物料分类编码" prop="categoryCode">
<el-input
......@@ -24,32 +45,6 @@
@keyup.enter="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="排序" prop="sortNo">
<el-input
v-model="queryParams.sortNo"
placeholder="请输入排序"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
clearable
v-model="queryParams.createTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择创建时间">
</el-date-picker>
</el-form-item>
<el-form-item label="修改时间" prop="updateTime">
<el-date-picker
clearable
v-model="queryParams.updateTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择修改时间">
</el-date-picker>
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
......@@ -57,7 +52,7 @@
</el-form>
<!-- 操作按钮 -->
<el-row :gutter="10" class="mb8">
<el-row :gutter="10" class="mb8" style="margin-bottom: 10px;">
<el-col :span="1.5">
<el-button
type="primary"
......@@ -89,6 +84,14 @@
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-sort"
@click="toggleExpandAll"
>展开/折叠</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
......@@ -99,33 +102,52 @@
<right-toolbar v-model="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据表格 -->
<!-- 树形数据表格(移除分页) -->
<div style="flex: 1; overflow: auto;">
<el-table
ref="treeTable"
v-if="refreshTable"
v-loading="loading"
:data="materials_categoryList"
:data="currentTreeNodeData"
@selection-change="handleSelectionChange"
style="width: 100%;"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="物料分类编码" align="center" prop="categoryCode" />
<el-table-column label="物料分类名称" align="center" prop="categoryName" />
<el-table-column label="排序" align="center" prop="sortNo" />
<el-table-column label="创建时间" align="center" prop="createTime" width="120">
<el-table-column
label="物料分类名称"
align="center"
prop="categoryName"
min-width="200"
/>
<el-table-column label="物料分类编码" align="center" prop="categoryCode" width="200" />
<el-table-column label="排序" align="center" prop="sortNo" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="200">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="修改时间" align="center" prop="updateTime" width="120">
<el-table-column label="修改时间" align="center" prop="updateTime" width="200">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="250">
<template slot-scope="scope">
<el-button
size="mini"
link
type="text"
icon="el-icon-plus"
@click="handleAdd(scope.row.id)"
v-hasPermi="['inventory:materials_category:add']"
>新增下级</el-button>
<el-button
size="mini"
link
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['inventory:materials_category:edit']"
......@@ -141,17 +163,12 @@
</template>
</el-table-column>
</el-table>
</div>
</div>
</pane>
</splitpanes>
<!-- 分页组件 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加/修改对话框(通过props控制显示) -->
<!-- 添加/修改对话框 -->
<el-dialog
:title="dialogTitle"
:visible.sync="dialogOpen"
......@@ -165,6 +182,15 @@
:rules="rules"
label-width="120px"
>
<el-form-item label="上级分类" prop="parentId" v-if="form.id && form.parentId">
<treeselect
v-model="form.parentId"
:options="categoryOptions"
:normalizer="normalizer"
placeholder="选择上级分类"
:disabled="!!form.id"
/>
</el-form-item>
<el-form-item label="物料分类编码" prop="categoryCode">
<el-input v-model="form.categoryCode" placeholder="请输入物料编码" />
</el-form-item>
......@@ -174,11 +200,14 @@
<el-form-item label="排序" prop="sortNo">
<el-input v-model="form.sortNo" placeholder="请输入排序" />
</el-form-item>
<!-- <el-form-item label="创建人编码" prop="createUserCode">
<el-input v-model="form.createUserCode" placeholder="请输入创建人编码" />
</el-form-item>
<el-form-item label="更新人编码" prop="updateUserCode">
<el-input v-model="form.updateUserCode" placeholder="请输入更新人编码" />
<!-- <el-form-item label="状态" prop="isUsed">
<el-radio-group v-model="form.isUsed">
<el-radio
v-for="dict in dict.type.sys_normal_disable"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item> -->
</el-form>
<div slot="footer" class="dialog-footer">
......@@ -186,6 +215,7 @@
<el-button @click="handleDialogClose"> </el-button>
</div>
</el-dialog>
</div>
</template>
......@@ -195,28 +225,32 @@ import {
getMaterials_category,
delMaterials_category,
addMaterials_category,
updateMaterials_category
updateMaterials_category,
categoryTreeSelect
} from "@/api/inventory/materials_category"
import { Splitpanes, Pane } from 'splitpanes'
import "@riophae/vue-treeselect/dist/vue-treeselect.css"
import 'splitpanes/dist/splitpanes.css'
import Treeselect from "@riophae/vue-treeselect"
import TreeComponent from "./treeComponent.vue"
export default {
name: "MaterialsCategory",
components: { Treeselect, Splitpanes, Pane, TreeComponent },
dicts: ['sys_normal_disable'],
props: {
// 外部传入的初始查询参数(可选)
initQuery: {
type: Object,
default: () => ({})
},
// 外部控制对话框显示(可选)
dialogVisible: {
type: Boolean,
default: false
},
// 外部传入的表单数据(编辑用)
dialogFormData: {
type: Object,
default: () => ({})
},
// 外部传入的对话框标题
dialogTitle: {
type: String,
default: "新增物料分类"
......@@ -225,50 +259,64 @@ export default {
data() {
return {
materials_categoryList: [],
currentTreeNodeData: [],
loading: true,
loadingTree: false,
showSearch: true,
ids: [],
single: true,
multiple: true,
total: 0,
// 内部对话框状态(避免直接修改props)
dialogOpen: false,
categoryOptions: [],
defaultProps: {
children: 'children',
label: 'label',
value: 'sid'
},
nodeKey: 'sid',
currentNodeId: null,
isExpandAll: true,
refreshTable: true,
form: {
id: null,
categoryCode: null,
categoryName: null,
sortNo: null,
createUserCode: null,
updateUserCode: null
updateUserCode: null,
parentId: null,
isUsed: 1
},
queryParams: {
pageNum: 1,
pageSize: 10,
pageSize: 1000, // 设置为较大的值,一次性获取所有数据
categoryCode: null,
categoryName: null,
sortNo: null,
createTime: null,
updateTime: null,
parentId: null,
isUsed: 1
},
rules: {}
rules: {
categoryCode: [{required: true, message:'请输入物料分类编码',trigger:'blur'}],
categoryName: [{required: true, message:'请输入物料分类名称', trigger:'blur'}]
}
}
},
watch: {
// 监听外部传入的对话框显示状态
dialogVisible: {
immediate: true,
handler(val) {
this.dialogOpen = val
}
},
// 监听外部传入的表单数据
dialogFormData: {
immediate: true,
handler(val) {
this.form = { ...val }
}
},
// 监听初始查询参数
initQuery: {
immediate: true,
handler(val) {
......@@ -279,26 +327,318 @@ export default {
},
created() {
this.getList()
this.getCategoryOptions()
},
methods: {
/** 查询列表(内部方法) */
/** 查询分类树结构 */
getCategoryOptions() {
this.loadingTree = true
categoryTreeSelect().then(response => {
this.categoryOptions = response.data
this.loadingTree = false
}).catch(() => {
this.loadingTree = false
})
},
/** 处理树节点点击 */
/** 处理树节点点击 */
handleTreeClick(data) {
console.log('点击树节点:', data)
// 确保数据存在且有sid属性
if (!data || !data.sid) {
console.warn('无效的节点数据,缺少sid属性');
return;
}
// 确保使用字符串类型的id进行操作,避免类型转换问题
this.currentNodeId = String(data.sid)
console.log(this.categoryOptions)
// 1. 找到当前节点的根节点
const rootNode = this.findRootNode(this.currentNodeId, this.categoryOptions);
if (rootNode) {
// 2. 筛选根节点的完整分支数据
const rootNodeIdStr = String(rootNode.sid);
this.filterRootBranchData(this.materials_categoryList, rootNodeIdStr);
// 3. 自动展开到当前节点
this.$nextTick(() => {
// 尝试展开节点,即使数据可能尚未完全准备好
// expandToNode方法内部已经有适当的错误处理
this.expandToNode(this.currentNodeId);
});
}
},
/** 查找表格数据中的节点ID */
findTableNodeId(treeNode) {
if (!treeNode) return null
// 首先在materials_categoryList中查找对应的节点
const tableNode = this.materials_categoryList.find(item =>
item.categoryCode === treeNode.categoryCode ||
item.categoryName === treeNode.label ||
item.id === treeNode.id
)
return tableNode ? tableNode.id : null
},
/** 筛选根节点的完整分支数据 */
filterRootBranchData(allData, rootNodeId) {
// 确保rootNodeId为字符串类型
const rootNodeIdStr = String(rootNodeId);
// 获取根节点分支的所有节点ID(包含根节点和所有子节点)
const branchNodeIds = this.getAllChildNodeIds(allData, rootNodeIdStr);
// 筛选出根分支的所有数据
// 使用字符串比较避免类型转换问题
const filteredData = allData.filter(item =>
branchNodeIds.some(branchId => String(branchId) === String(item.id))
);
// 构建树形结构并赋值给表格
this.currentTreeNodeData = this.handleTree(filteredData, "id");
},
/** 展开到指定节点 */
expandToNode(nodeId) {
const treeTable = this.$refs.treeTable;
// 增加对treeTable.store.nodesMap的检查
if (!treeTable || !treeTable.store || !treeTable.store.nodesMap) {
console.log(treeTable)
console.warn('树表格组件未完全初始化或nodesMap不存在');
return;
}
// 确保nodeId为字符串类型
const nodeIdStr = String(nodeId);
// 找到节点路径
const nodePath = this.findNodePath(this.currentTreeNodeData, nodeIdStr);
// 逐级展开
if (nodePath && nodePath.length > 0) {
nodePath.forEach(id => {
const idStr = String(id);
try {
// 安全地查找节点,避免访问不存在的属性
const node = treeTable.store.nodesMap[idStr] ||
treeTable.store.nodesMap[id];
if (node && typeof node === 'object' && node.expanded === false && node.expandable) {
node.expanded = true;
}
} catch (error) {
console.warn(`展开节点时出错: ${error.message}, 节点ID: ${idStr}`);
}
});
}
},
/** 查找节点路径 */
findNodePath(treeData, targetId, path = []) {
// 确保targetId为字符串类型
const targetIdStr = String(targetId);
for (const item of treeData) {
if (String(item.id) === targetIdStr) {
return [...path, item.id];
}
if (item.children && item.children.length) {
const found = this.findNodePath(item.children, targetIdStr, [...path, item.id]);
if (found) return found;
}
}
return null;
},
/** 转换部门数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children
}
return {
id: node.sid,
label: node.label,
children: node.children
}
},
/** 查询物料分类列表 */
/** 查询物料分类列表 */
getList() {
this.loading = true
listMaterials_category(this.queryParams).then(response => {
this.materials_categoryList = response.rows
this.total = response.total
this.materials_categoryList = response.rows.filter(item => item.isUsed !== 0 && item.isUsed !== '0');
// 如果没有选中树节点,显示所有数据
if (!this.currentNodeId) {
this.currentTreeNodeData = this.handleTree(this.materials_categoryList, "id");
} else {
// 如果有选中的树节点,保持当前显示
// 直接使用 currentNodeId(sid)作为表格数据的 id
const rootNode = this.findRootNode(this.currentNodeId, this.categoryOptions);
if (rootNode) {
// rootNode.sid 就是表格中的 id
this.filterRootBranchData(this.materials_categoryList, rootNode.sid);
}
}
this.loading = false
// 通知父组件列表加载完成
this.$emit("list-loaded", {
list: response.rows,
total: response.total
total: response.rows.length
})
})
},
/** 选择指定的树节点 */
selectTreeNode(nodeId) {
if (!nodeId) return;
// 确保nodeId为字符串类型
const nodeIdStr = String(nodeId);
// 查找对应的树节点
const findTreeNode = (nodes) => {
for (const node of nodes) {
if (String(node.sid) === nodeIdStr) {
return node;
}
if (node.children && node.children.length > 0) {
const found = findTreeNode(node.children);
if (found) return found;
}
}
return null;
};
// 在categoryOptions中查找节点
const treeNode = findTreeNode(this.categoryOptions);
if (treeNode) {
// 如果找到节点,触发点击事件
this.handleTreeClick(treeNode);
} else {
console.warn(`未找到ID为${nodeIdStr}的树节点`);
// 如果未找到节点,可能是根节点已变更,尝试找到新的根节点
const rootNode = this.findRootNode(nodeIdStr, this.materials_categoryList);
if (rootNode) {
this.filterRootBranchData(this.materials_categoryList, rootNode.id);
}
}
},
/** 获取节点及其所有子节点的ID(基于表格数据) */
getAllChildNodeIds(tableData, nodeId) {
const result = [];
// 确保nodeId为字符串类型
const nodeIdStr = String(nodeId);
// 递归查找所有子节点
const findChildren = (parentId) => {
const parentIdStr = String(parentId);
// 使用字符串比较确保兼容性
const children = tableData.filter(item =>
item.parentId && String(item.parentId) === parentIdStr
);
children.forEach(child => {
result.push(child.id);
findChildren(child.id);
});
};
// 添加当前节点
// 使用字符串比较查找当前节点
const currentNode = tableData.find(item => String(item.id) === nodeIdStr);
if (currentNode) {
result.push(currentNode.id);
findChildren(currentNode.id);
}
return result;
},
/** 查找当前节点的根节点 */
findRootNode(nodeId, treeData) {
// 确保nodeId为字符串类型
const nodeIdStr = String(nodeId);
// 首先找到当前节点
const findNodeById = (nodes, id) => {
// 检查nodes是否有效
if (!nodes || !Array.isArray(nodes)) return null;
for (const node of nodes) {
// 确保比较时使用字符串类型,处理表格数据和树数据的不同字段名
if (String(node.sid || node.id) === id) {
return node;
}
if (node.children && node.children.length > 0) {
const found = findNodeById(node.children, id);
if (found) return found;
}
}
return null;
};
const currentNode = findNodeById(treeData, nodeIdStr);
if (!currentNode) return null;
// 查找根节点(parentId为null或空或0)
const findRoot = (node) => {
if (!node) return null;
// 检查多种可能的根节点条件
const parentId = String(node.parentId || '');
if (!parentId || parentId === '0' || parentId === 'null' || parentId === 'undefined') {
return node;
}
const parentNode = findNodeById(treeData, parentId);
// 如果找不到父节点,当前节点就是根节点
console.log(parentNode)
return parentNode ? findRoot(parentNode) : node;
};
return findRoot(currentNode);
},
/** 处理树形数据 */
handleTree(data, id, parentId, children) {
id = id || 'id'
parentId = parentId || 'parentId'
children = children || 'children'
// 确保根节点筛选逻辑正确,处理各种可能的parentId值
const rootNodes = data.filter(item => {
const pId = item[parentId];
return !pId || pId === null || pId === 0 || String(pId) === '0' || String(pId) === '';
})
const addChildren = (parent) => {
// 使用字符串比较查找子节点,确保类型一致性
const childNodes = data.filter(item => {
return String(item[parentId]) === String(parent[id]);
})
if (childNodes.length > 0) {
parent[children] = childNodes
childNodes.forEach(child => addChildren(child))
}
}
rootNodes.forEach(root => addChildren(root))
return rootNodes
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.currentNodeId = null // 搜索时清除当前选中的节点
this.getList()
},
......@@ -307,35 +647,62 @@ export default {
this.$refs["queryRef"].resetFields()
this.queryParams = {
pageNum: 1,
pageSize: 10,
pageSize: 1000,
categoryCode: null,
categoryName: null,
sortNo: null,
createTime: null,
updateTime: null,
...this.initQuery // 保留初始查询参数
parentId: null,
...this.initQuery
}
// 重置时清除当前选中的节点
this.currentNodeId = null
// 重置左侧树的选择状态
if (this.$refs.treeComponent && this.$refs.treeComponent.resetTree) {
this.$refs.treeComponent.resetTree();
}
this.getList()
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false
this.isExpandAll = !this.isExpandAll
this.$nextTick(() => {
this.refreshTable = true
// 手动设置展开/折叠
const treeTable = this.$refs.treeTable;
if (treeTable && treeTable.store) {
Object.keys(treeTable.store.nodesMap).forEach(key => {
const node = treeTable.store.nodesMap[key];
if (node && node.expandable) {
node.expanded = this.isExpandAll;
}
});
}
})
},
/** 多选框选中事件 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
// 通知父组件选中数据变化
this.$emit("selection-change", selection)
},
/** 新增按钮操作 */
handleAdd() {
handleAdd(parentId) {
this.form = {
id: null,
categoryCode: null,
categoryName: null,
sortNo: null,
createUserCode: null,
updateUserCode: null
updateUserCode: null,
parentId: parentId || null,
isUsed: 1
}
this.dialogOpen = true
this.$emit("dialog-open", { type: "add", form: this.form })
......@@ -363,8 +730,38 @@ export default {
const type = this.form.id ? "update" : "add"
this.$modal.msgSuccess(`${type === "add" ? "新增" : "修改"}成功`)
this.dialogOpen = false
this.getList()
// 通知父组件提交成功
// 保存当前节点ID,用于数据重新加载后恢复选择
const currentNodeIdBeforeReload = this.currentNodeId;
const isAddChild = type === "add" && this.form.parentId;
// 重新加载数据
Promise.all([
// 先重新加载树数据
new Promise(resolve => {
this.getCategoryOptions();
// 延迟一下确保树数据加载完成
setTimeout(() => resolve(), 100);
}),
// 再重新加载列表数据
new Promise(resolve => {
this.getList();
// 延迟一下确保列表数据加载完成
setTimeout(() => resolve(), 100);
})
]).then(() => {
// 数据加载完成后,恢复或更新选择
this.$nextTick(() => {
if (isAddChild) {
// 新增下级后,选择父节点
this.selectTreeNode(this.form.parentId);
} else if (currentNodeIdBeforeReload) {
// 修改后,保持当前选择
this.selectTreeNode(currentNodeIdBeforeReload);
}
});
});
this.$emit("form-submit", {
type,
data: this.form,
......@@ -381,9 +778,10 @@ export default {
this.$modal.confirm(`是否确认删除物料分类编号为"${_ids}"的数据项?`).then(() => {
return delMaterials_category(_ids)
}).then(() => {
// 重新加载数据
this.getList()
this.getCategoryOptions()
this.$modal.msgSuccess("删除成功")
// 通知父组件删除成功
this.$emit("item-delete", { ids: _ids })
}).catch(() => {})
},
......@@ -393,7 +791,6 @@ export default {
this.download('inventory/materials_category/export', {
...this.queryParams
}, `materials_category_${new Date().getTime()}.xlsx`)
// 通知父组件导出操作
this.$emit("export-data", this.queryParams)
},
......@@ -403,7 +800,7 @@ export default {
this.$emit("dialog-close")
},
/** 表单重置(供外部调用) */
/** 表单重置 */
resetForm() {
this.form = {
id: null,
......@@ -411,10 +808,28 @@ export default {
categoryName: null,
sortNo: null,
createUserCode: null,
updateUserCode: null
updateUserCode: null,
parentId: null,
isUsed: 1
}
this.$refs["materials_categoryRef"]?.resetFields()
}
}
}
</script>
<style scoped>
.app-container {
height: 100%;
}
.mb8 {
margin-bottom: 8px;
}
/* 添加滚动条样式 */
.el-table {
max-height: 100%;
overflow-y: auto;
}
</style>
\ No newline at end of file
<template>
<div class="tree-container" :style="containerStyle">
<!-- 搜索框 -->
<div class="tree-header" v-if="showSearch">
<el-input
v-model="searchText"
:placeholder="searchPlaceholder"
clearable
size="small"
prefix-icon="el-icon-search"
@input="handleSearch"
style="width: 100%; margin-bottom: 10px;"
/>
</div>
<!-- 树组件 -->
<div class="tree-body" :style="treeBodyStyle">
<el-tree
v-if="treeData && treeData.length > 0"
ref="treeRef"
:data="filteredTreeData"
:props="treeProps"
:node-key="nodeKey"
:default-expand-all="defaultExpandAll"
:expand-on-click-node="expandOnClickNode"
:highlight-current="highlightCurrent"
:filter-node-method="filterNodeMethod"
:empty-text="emptyText"
:style="treeStyle"
@node-click="handleNodeClick"
@node-expand="handleNodeExpand"
@node-collapse="handleNodeCollapse"
@current-change="handleCurrentChange"
>
<!-- 自定义节点内容插槽 -->
<template #default="{ node, data }">
<slot name="node-content" :node="node" :data="data">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
</span>
</slot>
</template>
</el-tree>
<!-- 空状态 -->
<div v-else class="tree-empty">
<slot name="empty">
<div style="padding: 20px; text-align: center; color: #999;">
{{ loading ? '加载中...' : '暂无数据' }}
</div>
</slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TreeComponent',
props: {
// 树数据
treeData: {
type: Array,
default: () => []
},
// 树配置
treeProps: {
type: Object,
default: () => ({
children: 'children',
label: 'label',
value: 'sid'
})
},
// 节点key
nodeKey: {
type: String,
default: 'sid'
},
// 是否显示搜索框
showSearch: {
type: Boolean,
default: true
},
// 搜索框占位符
searchPlaceholder: {
type: String,
default: '请输入搜索内容'
},
// 是否默认展开所有节点
defaultExpandAll: {
type: Boolean,
default: true
},
// 是否点击节点时展开
expandOnClickNode: {
type: Boolean,
default: false
},
// 是否高亮当前选中节点
highlightCurrent: {
type: Boolean,
default: true
},
// 容器样式
containerStyle: {
type: Object,
default: () => ({
height: '100%',
padding: '10px'
})
},
// 树容器样式
treeBodyStyle: {
type: Object,
default: () => ({
height: 'calc(100% - 50px)',
overflow: 'auto'
})
},
// 树样式
treeStyle: {
type: Object,
default: () => ({})
},
// 空状态文本
emptyText: {
type: String,
default: '暂无数据'
},
// 加载状态
loading: {
type: Boolean,
default: false
},
// 初始选中的节点key
defaultSelectedKey: {
type: [String, Number],
default: null
}
},
data() {
return {
searchText: '',
filteredTreeData: [],
selectedNode: null
}
},
watch: {
treeData: {
immediate: true,
handler(newData) {
this.filteredTreeData = newData
this.$nextTick(() => {
if (this.defaultSelectedKey && this.$refs.treeRef) {
this.$refs.treeRef.setCurrentKey(this.defaultSelectedKey)
}
})
}
},
defaultSelectedKey: {
immediate: true,
handler(newKey) {
if (newKey && this.$refs.treeRef) {
this.$refs.treeRef.setCurrentKey(newKey)
}
}
}
},
methods: {
/**
* 过滤节点方法
*/
filterNodeMethod(value, data, node) {
if (!value) return true
const label = data[this.treeProps.label] || ''
return label.toLowerCase().includes(value.toLowerCase())
},
/**
* 处理搜索
*/
handleSearch(value) {
this.$refs.treeRef.filter(value)
},
/**
* 节点点击事件
*/
handleNodeClick(data, node, component) {
this.selectedNode = { data, node, component }
this.$emit('node-click', data, node, component)
},
/**
* 节点展开事件
*/
handleNodeExpand(data, node, component) {
this.$emit('node-expand', data, node, component)
},
/**
* 节点折叠事件
*/
handleNodeCollapse(data, node, component) {
this.$emit('node-collapse', data, node, component)
},
/**
* 当前节点变化事件
*/
handleCurrentChange(data, node) {
this.$emit('current-change', data, node)
},
/**
* 重置树结构
*/
resetTree() {
// 修复:使用正确的 ref 名称 treeRef
if (this.$refs.treeRef) {
this.$refs.treeRef.setCurrentKey(null);
this.searchText = '';
this.$refs.treeRef.filter(''); // 清空搜索
}
},
/**
* 设置当前选中的节点
*/
setCurrentNode(node) {
this.$refs.treeRef.setCurrentNode(node)
},
/**
* 设置当前选中的节点key
*/
setCurrentKey(key) {
this.$refs.treeRef.setCurrentKey(key)
},
/**
* 获取当前选中的节点
*/
getCurrentNode() {
return this.$refs.treeRef.getCurrentNode()
},
/**
* 获取当前选中的节点key
*/
getCurrentKey() {
return this.$refs.treeRef.getCurrentKey()
},
/**
* 展开指定节点
*/
expandNode(node) {
this.$refs.treeRef.expandNode(node)
},
/**
* 折叠指定节点
*/
collapseNode(node) {
this.$refs.treeRef.collapseNode(node)
},
/**
* 展开所有节点
*/
expandAll() {
this.$refs.treeRef.expandAll()
},
/**
* 折叠所有节点
*/
collapseAll() {
this.$refs.treeRef.collapseAll()
},
/**
* 更新节点数据
*/
updateKeyChildren(key, data) {
this.$refs.treeRef.updateKeyChildren(key, data)
},
/**
* 获取节点信息
*/
getNode(key) {
return this.$refs.treeRef.getNode(key)
},
/**
* 移除节点
*/
remove(key) {
this.$refs.treeRef.remove(key)
},
/**
* 追加节点数据
*/
append(data, parentNode) {
this.$refs.treeRef.append(data, parentNode)
},
/**
* 插入节点数据
*/
insertBefore(data, refNode) {
this.$refs.treeRef.insertBefore(data, refNode)
},
/**
* 插入节点数据后
*/
insertAfter(data, refNode) {
this.$refs.treeRef.insertAfter(data, refNode)
}
}
}
</script>
<style scoped>
.tree-container {
height: 100%;
display: flex;
flex-direction: column;
}
.tree-header {
flex-shrink: 0;
}
.tree-body {
flex: 1;
overflow: auto;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.tree-empty {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #999;
}
</style>
\ No newline at end of file
......@@ -9,7 +9,16 @@
<el-input v-model="deptName" placeholder="请输入部门名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px" />
</div>
<div class="head-container">
<el-tree :data="deptOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current @node-click="handleNodeClick" />
<el-tree
:data="deptOptions"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="tree"
node-key="id"
default-expand-all
highlight-current
@node-click="handleNodeClick" />
</div>
</el-col>
</pane>
......
......@@ -3,6 +3,7 @@ package com.ruoyi.web.controller.inventory;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.utils.uuid.UUID;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -18,7 +19,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.inventory.domain.MaterialsCategory;
import com.ruoyi.common.core.domain.entity.MaterialsCategory;
import com.ruoyi.inventory.service.IMaterialsCategoryService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
......@@ -72,6 +73,16 @@ public class MaterialsCategoryController extends BaseController
}
/**
* 获取分类树结构
*/
@PreAuthorize("@ss.hasPermi('inventory:materials_category:list')")
@GetMapping("/categoryTree")
public AjaxResult categoryTree(MaterialsCategory materialsCategory)
{
return success(materialsCategoryService.selectMaterialsCategoryTreeList(materialsCategory));
}
/**
* 新增物料分类
*/
@PreAuthorize("@ss.hasPermi('inventory:materials_category:add')")
......
......@@ -63,6 +63,7 @@ public class UserConstants
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验是否唯一的返回标识 */
public final static boolean UNIQUE = true;
public final static boolean NOT_UNIQUE = false;
......
......@@ -5,6 +5,7 @@ import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.MaterialsCategory;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.utils.StringUtils;
......@@ -21,6 +22,8 @@ public class TreeSelect implements Serializable
/** 节点ID */
private Long id;
/** String节点ID */
private String sid;
/** 节点名称 */
private String label;
......@@ -43,7 +46,12 @@ public class TreeSelect implements Serializable
this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus());
this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
public TreeSelect(MaterialsCategory materialsCategory){
this.sid = materialsCategory.getId();
this.label = materialsCategory.getCategoryName();
this.disabled = StringUtils.equals(String.valueOf(0), String.valueOf(materialsCategory.getIsUsed()));
this.children = materialsCategory.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
public TreeSelect(SysMenu menu)
{
this.id = menu.getMenuId();
......@@ -90,4 +98,12 @@ public class TreeSelect implements Serializable
{
this.children = children;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
}
package com.ruoyi.inventory.domain;
package com.ruoyi.common.core.domain.entity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import java.util.ArrayList;
import java.util.List;
/**
* 物料分类对象 materials_category
*
......@@ -31,6 +34,15 @@ public class MaterialsCategory extends BaseEntity
@Excel(name = "排序")
private Long sortNo;
@Excel(name = "父级Code")
private String parentId;
@Excel(name = "使用状态")
private int isUsed;
private String parentName;
private List<MaterialsCategory> children = new ArrayList<MaterialsCategory>();
/** 创建日期 */
private String createUserCode;
......@@ -97,6 +109,38 @@ public class MaterialsCategory extends BaseEntity
return updateUserCode;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public int getIsUsed() {
return isUsed;
}
public void setIsUsed(int isUsed) {
this.isUsed = isUsed;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public List<MaterialsCategory> getChildren() {
return children;
}
public void setChildren(List<MaterialsCategory> children) {
this.children = children;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
......@@ -108,6 +152,10 @@ public class MaterialsCategory extends BaseEntity
.append("createUserCode", getCreateUserCode())
.append("updateTime", getUpdateTime())
.append("updateUserCode", getUpdateUserCode())
.append("parentId", getParentId())
.append("isUsed", getIsUsed())
.append("parentName", getParentName())
.append("children", getChildren())
.toString();
}
}
......@@ -22,5 +22,6 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.ruoyi.inventory.mapper;
import java.util.List;
import com.ruoyi.inventory.domain.MaterialsCategory;
import com.ruoyi.common.core.domain.entity.MaterialsCategory;
/**
* 物料分类Mapper接口
......@@ -18,7 +18,6 @@ public interface MaterialsCategoryMapper
* @return 物料分类
*/
public MaterialsCategory selectMaterialsCategoryById(String id);
/**
* 查询物料分类列表
*
......
......@@ -59,7 +59,12 @@ public interface MaterialsMapper
* @return 结果
*/
public int deleteMaterialsByIds(String[] ids);
/**
* 批量删除,修改物料的使用状态
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int updateMaterialsIsUsedByIds(String[] ids);
}
package com.ruoyi.inventory.service;
import java.util.List;
import com.ruoyi.inventory.domain.MaterialsCategory;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.MaterialsCategory;
/**
* 物料分类Service接口
......@@ -28,6 +30,30 @@ public interface IMaterialsCategoryService
public List<MaterialsCategory> selectMaterialsCategoryList(MaterialsCategory materialsCategory);
/**
* 查询分类树结构
*
* @param materialsCategory 物料分类
* @return 结果
*/
public List<TreeSelect> selectMaterialsCategoryTreeList(MaterialsCategory materialsCategory);
/**
* 构建前端所需要树结构
*
* @param materialsCategorys 物料分类
* @return 结果
*/
public List<MaterialsCategory> buildMaterialsCategoryTree(List<MaterialsCategory> materialsCategorys);
/**
* 构建前端所需要下拉树结构
*
* @param materialsCategorys 物料分类
* @return 结果
*/
public List<TreeSelect> buildTreeSelect(List<MaterialsCategory> materialsCategorys);
/**
* 新增物料分类
*
* @param materialsCategory 物料分类
......
package com.ruoyi.inventory.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.inventory.mapper.MaterialsCategoryMapper;
import com.ruoyi.inventory.domain.MaterialsCategory;
import com.ruoyi.common.core.domain.entity.MaterialsCategory;
import com.ruoyi.inventory.service.IMaterialsCategoryService;
/**
......@@ -43,6 +51,86 @@ public class MaterialsCategoryServiceImpl implements IMaterialsCategoryService
{
return materialsCategoryMapper.selectMaterialsCategoryList(materialsCategory);
}
/**
* 查询分类树结构
*
* @param materialsCategory 物料分类
* @return 结果
*/
@Override
public List<TreeSelect> selectMaterialsCategoryTreeList(MaterialsCategory materialsCategory)
{
List<MaterialsCategory> materialsCategorys = SpringUtils.getAopProxy(this).selectMaterialsCategoryList(materialsCategory);
return buildTreeSelect(materialsCategorys);
}
/**
* 构建前端所需要树结构
*
* @param materialsCategorys 物料分类
* @return 结果
*/
@Override
public List<MaterialsCategory> buildMaterialsCategoryTree(List<MaterialsCategory> materialsCategorys) {
List<MaterialsCategory> returnList = new ArrayList<MaterialsCategory>();
// List<String> tempList = materialsCategorys.stream().map(MaterialsCategory::getId).collect(Collectors.toList());
for (MaterialsCategory materialsCategory : materialsCategorys) {
// 如果是顶级节点, 遍历该父节点的所有子节点
if (StringUtils.isNull(materialsCategory.getParentId()) || materialsCategory.getParentId().isEmpty()){
recursionFn(materialsCategorys, materialsCategory);
returnList.add(materialsCategory);
}
}
if (returnList.isEmpty()){
returnList = materialsCategorys;
}
return returnList;
}
/**
* 递归列表
*/
private void recursionFn(List<MaterialsCategory> materialsCategoryList, MaterialsCategory materialsCategory) {
// 得到子节点列表
List<MaterialsCategory> childList = getChildList(materialsCategoryList, materialsCategory);
materialsCategory.setChildren(childList);
// 如果有子节点,递归处理
if (!childList.isEmpty()) {
for (MaterialsCategory child : childList) {
recursionFn(materialsCategoryList, child);
}
}
}
/**
* 得到子节点列表
*/
private List<MaterialsCategory> getChildList(List<MaterialsCategory> list, MaterialsCategory materialsCategory){
List<MaterialsCategory> childList = new ArrayList<>();
Iterator<MaterialsCategory> iterator = list.iterator();
while (iterator.hasNext()){
MaterialsCategory m = (MaterialsCategory) iterator.next();
if (StringUtils.equals(m.getParentId(), materialsCategory.getId())){
childList.add(m);
}
}
return childList;
}
/**
* 判断是否有子节点
*/
private boolean hasChild(List<MaterialsCategory> list, MaterialsCategory m){
return getChildList(list, m).size() > 0;
}
/**
* 构建前端所需要下拉树结构
*
* @param materialsCategorys 物料分类
* @return 结果
*/
@Override
public List<TreeSelect> buildTreeSelect(List<MaterialsCategory> materialsCategorys) {
List<MaterialsCategory> materialsCategories = buildMaterialsCategoryTree(materialsCategorys);
return materialsCategories.stream().map(TreeSelect::new).collect(Collectors.toList());
}
/**
* 新增物料分类
......
......@@ -13,20 +13,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="createUserCode" column="create_user_code" />
<result property="updateTime" column="update_time" />
<result property="updateUserCode" column="update_user_code" />
<result property="parentId" column="parent_id" />
<result property="isUsed" column="is_used" />
</resultMap>
<sql id="selectMaterialsCategoryVo">
select id, category_code, category_name, sort_no, create_time, create_user_code, update_time, update_user_code from materials_category
select id, category_code, category_name, sort_no, create_time, create_user_code, update_time, update_user_code, parent_id, is_used from materials_category
</sql>
<select id="selectMaterialsCategoryList" parameterType="MaterialsCategory" resultMap="MaterialsCategoryResult">
<include refid="selectMaterialsCategoryVo"/>
<where>
<if test="id != null and id != ''"> and id = #{id}</if>
<if test="categoryCode != null and categoryCode != ''"> and category_code like concat('%', #{categoryCode}, '%')</if>
<if test="categoryName != null and categoryName != ''"> and category_name like concat('%', #{categoryName}, '%')</if>
<if test="sortNo != null "> and sort_no = #{sortNo}</if>
<if test="createTime != null "> and create_time like concat('%', #{createTime}, '%')</if>
<if test="updateTime != null "> and update_time like concat('%', #{updateTime}, '%')</if>
<if test="parentId != null "> and parent_id = #{parentId}</if>
</where>
order by sort_no asc
</select>
......@@ -48,6 +52,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createUserCode != null">create_user_code,</if>
<if test="updateTime != null">update_time,</if>
<if test="updateUserCode != null">update_user_code,</if>
<if test="parentId != null">parent_id,</if>
<if test="isUsed != null">is_used,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
......@@ -58,6 +64,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createUserCode != null">#{createUserCode},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="updateUserCode != null">#{updateUserCode},</if>
<if test="parentId != null">#{parentId},</if>
<if test="isUsed != null">#{isUsed},</if>
</trim>
</insert>
......@@ -71,6 +79,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createUserCode != null">create_user_code = #{createUserCode},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="updateUserCode != null">update_user_code = #{updateUserCode},</if>
<if test="parentId != null">parent_id = #{parentId},</if>
<if test="isUsed != null">is_used = #{isUsed},</if>
</trim>
where id = #{id}
</update>
......
......@@ -46,7 +46,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="materialName != null and materialName != ''"> and material_name like concat('%', #{materialName}, '%')</if>
<if test="sapNo != null and sapNo != ''"> and sap_no like concat('%', #{sapNo}, '%')</if>
<if test="tsCode != null and tsCode != ''"> and ts_code like concat('%', #{tsCode}, '%')</if>
<if test="categoryCode != null and categoryCode != ''"> and category_code like concat('%', #{categoryCode}, '%')</if>
<if test="categoryCode != null and categoryCode != ''"> and category_code = #{categoryCode}</if>
<if test="hazardId != null and hazardId != ''"> and hazard_id like concat('%', #{hazardId}, '%')</if>
<if test="specification != null and specification != ''"> and specification like concat('%', #{specification}, '%')</if>
<if test="materialUnit != null and materialUnit != ''"> and material_unit like concat('%', #{materialUnit}, '%')</if>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论