diff --git a/jshERP-boot/docs/jsh_erp.sql b/jshERP-boot/docs/jsh_erp.sql
index 5acae587..4991924f 100644
--- a/jshERP-boot/docs/jsh_erp.sql
+++ b/jshERP-boot/docs/jsh_erp.sql
@@ -338,6 +338,7 @@ INSERT INTO `jsh_function` VALUES ('243', '000108', '机构管理', '0001', '/sy
 INSERT INTO `jsh_function` VALUES ('244', '030112', '库存预警', '0301', '/report/stock_warning_report', '/report/StockWarningReport', '\0', '0670', '', '电脑版', '', 'profile', '0');
 INSERT INTO `jsh_function` VALUES ('245', '000107', '插件管理', '0001', '/system/plugin', '/system/PluginList', '\0', '0170', '', '电脑版', '1', 'profile', '0');
 INSERT INTO `jsh_function` VALUES ('246', '030113', '商品库存', '0301', '/report/material_stock', '/report/MaterialStock', '\0', '0605', '', '电脑版', '', 'profile', '0');
+INSERT INTO `jsh_function` VALUES ('247', '010105', '多属性设置', '0101', '/material/material_attribute', '/material/MaterialAttributeList', '\0', '0243', '', '电脑版', '1', 'profile', '0');
 
 -- ----------------------------
 -- Table structure for jsh_in_out_item
@@ -423,6 +424,29 @@ INSERT INTO `jsh_material` VALUES ('586', '17', '序列号商品测试', '', nul
 INSERT INTO `jsh_material` VALUES ('587', '17', '商品test1', '南通中远', null, '', 'test1', '', '个', '', null, null, '', '', '', '', '0', '63', '0');
 INSERT INTO `jsh_material` VALUES ('588', '21', '商品200', 'fafda', '112.000000', 'weqwe', '300ml', '红色', '个', 'aaaabbbbb', null, null, '', '', '', '', '0', '63', '0');
 
+-- ----------------------------
+-- Table structure for jsh_material_attribute
+-- ----------------------------
+DROP TABLE IF EXISTS `jsh_material_attribute`;
+CREATE TABLE `jsh_material_attribute` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `attribute_field` varchar(50) DEFAULT NULL COMMENT '属性字段',
+  `attribute_name` varchar(50) DEFAULT NULL COMMENT '属性名',
+  `attribute_value` varchar(500) DEFAULT NULL COMMENT '属性值',
+  `tenant_id` bigint(20) DEFAULT NULL COMMENT '租户id',
+  `delete_flag` varchar(1) DEFAULT '0' COMMENT '删除标记，0未删除，1删除',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='产品属性';
+
+-- ----------------------------
+-- Records of jsh_material_attribute
+-- ----------------------------
+INSERT INTO `jsh_material_attribute` VALUES ('1', 'color', '颜色', '红色|橙色|黄色|绿色|蓝色|紫色', '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('2', 'size', '尺寸', 'S|M|L|XL|XXL|XXXL', '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('3', 'brand', '品牌', '品牌1|品牌2', '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('4', 'other1', '自定义1', null, '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('5', 'other2', '自定义2', null, '63', '0');
+
 -- ----------------------------
 -- Table structure for jsh_material_category
 -- ----------------------------
diff --git a/jshERP-boot/docs/数据库更新记录-首次安装请勿使用.txt b/jshERP-boot/docs/数据库更新记录-首次安装请勿使用.txt
index 7e3876bd..c0f68448 100644
--- a/jshERP-boot/docs/数据库更新记录-首次安装请勿使用.txt
+++ b/jshERP-boot/docs/数据库更新记录-首次安装请勿使用.txt
@@ -1125,4 +1125,33 @@ alter table jsh_material add img_name varchar(500) DEFAULT NULL COMMENT '图片
 -- by jishenghua
 -- 给租户表增加字段enabled
 -- --------------------------------------------------------
-alter table jsh_tenant add enabled bit(1) DEFAULT 1 COMMENT '启用 0-禁用  1-启用' after bills_num_limit;
\ No newline at end of file
+alter table jsh_tenant add enabled bit(1) DEFAULT 1 COMMENT '启用 0-禁用  1-启用' after bills_num_limit;
+
+-- --------------------------------------------------------
+-- 时间 2021年7月21日
+-- by jishenghua
+-- 增加商品属性表
+-- --------------------------------------------------------
+DROP TABLE IF EXISTS `jsh_material_attribute`;
+CREATE TABLE `jsh_material_attribute` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `attribute_field` varchar(50) DEFAULT NULL COMMENT '属性字段',
+  `attribute_name` varchar(50) DEFAULT NULL COMMENT '属性名',
+  `attribute_value` varchar(500) DEFAULT NULL COMMENT '属性值',
+  `tenant_id` bigint(20) DEFAULT NULL COMMENT '租户id',
+  `delete_flag` varchar(1) DEFAULT '0' COMMENT '删除标记，0未删除，1删除',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='产品属性';
+
+INSERT INTO `jsh_material_attribute` VALUES ('1', 'color', '颜色', '红色|橙色|黄色|绿色|蓝色|紫色', '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('2', 'size', '尺寸', 'S|M|L|XL|XXL|XXXL', '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('3', 'brand', '品牌', '品牌1|品牌2', '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('4', 'other1', '自定义1', null, '63', '0');
+INSERT INTO `jsh_material_attribute` VALUES ('5', 'other2', '自定义2', null, '63', '0');
+
+-- --------------------------------------------------------
+-- 时间 2021年7月22日
+-- by jishenghua
+-- 增加多属性设置菜单
+-- --------------------------------------------------------
+INSERT INTO `jsh_erp`.`jsh_function` (`id`, `number`, `name`, `parent_number`, `url`, `component`, `state`, `sort`, `enabled`, `type`, `push_btn`, `icon`, `delete_flag`) VALUES ('247', '010105', '多属性设置', '0101', '/material/material_attribute', '/material/MaterialAttributeList', '\0', '0247', '', '电脑版', '1', 'profile', '0');
diff --git a/jshERP-boot/src/main/java/com/jsh/erp/datasource/entities/MaterialAttribute.java b/jshERP-boot/src/main/java/com/jsh/erp/datasource/entities/MaterialAttribute.java
new file mode 100644
index 00000000..1304428b
--- /dev/null
+++ b/jshERP-boot/src/main/java/com/jsh/erp/datasource/entities/MaterialAttribute.java
@@ -0,0 +1,63 @@
+package com.jsh.erp.datasource.entities;
+
+public class MaterialAttribute {
+    private Long id;
+
+    private String attributeField;
+
+    private String attributeName;
+
+    private String attributeValue;
+
+    private Long tenantId;
+
+    private String deleteFlag;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getAttributeField() {
+        return attributeField;
+    }
+
+    public void setAttributeField(String attributeField) {
+        this.attributeField = attributeField == null ? null : attributeField.trim();
+    }
+
+    public String getAttributeName() {
+        return attributeName;
+    }
+
+    public void setAttributeName(String attributeName) {
+        this.attributeName = attributeName == null ? null : attributeName.trim();
+    }
+
+    public String getAttributeValue() {
+        return attributeValue;
+    }
+
+    public void setAttributeValue(String attributeValue) {
+        this.attributeValue = attributeValue == null ? null : attributeValue.trim();
+    }
+
+    public Long getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(Long tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public String getDeleteFlag() {
+        return deleteFlag;
+    }
+
+    public void setDeleteFlag(String deleteFlag) {
+        this.deleteFlag = deleteFlag == null ? null : deleteFlag.trim();
+    }
+}
\ No newline at end of file
diff --git a/jshERP-boot/src/main/java/com/jsh/erp/datasource/entities/MaterialAttributeExample.java b/jshERP-boot/src/main/java/com/jsh/erp/datasource/entities/MaterialAttributeExample.java
new file mode 100644
index 00000000..5bbd8d46
--- /dev/null
+++ b/jshERP-boot/src/main/java/com/jsh/erp/datasource/entities/MaterialAttributeExample.java
@@ -0,0 +1,599 @@
+package com.jsh.erp.datasource.entities;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MaterialAttributeExample {
+    protected String orderByClause;
+
+    protected boolean distinct;
+
+    protected List<Criteria> oredCriteria;
+
+    public MaterialAttributeExample() {
+        oredCriteria = new ArrayList<>();
+    }
+
+    public void setOrderByClause(String orderByClause) {
+        this.orderByClause = orderByClause;
+    }
+
+    public String getOrderByClause() {
+        return orderByClause;
+    }
+
+    public void setDistinct(boolean distinct) {
+        this.distinct = distinct;
+    }
+
+    public boolean isDistinct() {
+        return distinct;
+    }
+
+    public List<Criteria> getOredCriteria() {
+        return oredCriteria;
+    }
+
+    public void or(Criteria criteria) {
+        oredCriteria.add(criteria);
+    }
+
+    public Criteria or() {
+        Criteria criteria = createCriteriaInternal();
+        oredCriteria.add(criteria);
+        return criteria;
+    }
+
+    public Criteria createCriteria() {
+        Criteria criteria = createCriteriaInternal();
+        if (oredCriteria.size() == 0) {
+            oredCriteria.add(criteria);
+        }
+        return criteria;
+    }
+
+    protected Criteria createCriteriaInternal() {
+        Criteria criteria = new Criteria();
+        return criteria;
+    }
+
+    public void clear() {
+        oredCriteria.clear();
+        orderByClause = null;
+        distinct = false;
+    }
+
+    protected abstract static class GeneratedCriteria {
+        protected List<Criterion> criteria;
+
+        protected GeneratedCriteria() {
+            super();
+            criteria = new ArrayList<>();
+        }
+
+        public boolean isValid() {
+            return criteria.size() > 0;
+        }
+
+        public List<Criterion> getAllCriteria() {
+            return criteria;
+        }
+
+        public List<Criterion> getCriteria() {
+            return criteria;
+        }
+
+        protected void addCriterion(String condition) {
+            if (condition == null) {
+                throw new RuntimeException("Value for condition cannot be null");
+            }
+            criteria.add(new Criterion(condition));
+        }
+
+        protected void addCriterion(String condition, Object value, String property) {
+            if (value == null) {
+                throw new RuntimeException("Value for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value));
+        }
+
+        protected void addCriterion(String condition, Object value1, Object value2, String property) {
+            if (value1 == null || value2 == null) {
+                throw new RuntimeException("Between values for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value1, value2));
+        }
+
+        public Criteria andIdIsNull() {
+            addCriterion("id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIsNotNull() {
+            addCriterion("id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdEqualTo(Long value) {
+            addCriterion("id =", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotEqualTo(Long value) {
+            addCriterion("id <>", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThan(Long value) {
+            addCriterion("id >", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThanOrEqualTo(Long value) {
+            addCriterion("id >=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThan(Long value) {
+            addCriterion("id <", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThanOrEqualTo(Long value) {
+            addCriterion("id <=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIn(List<Long> values) {
+            addCriterion("id in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotIn(List<Long> values) {
+            addCriterion("id not in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdBetween(Long value1, Long value2) {
+            addCriterion("id between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotBetween(Long value1, Long value2) {
+            addCriterion("id not between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldIsNull() {
+            addCriterion("attribute_field is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldIsNotNull() {
+            addCriterion("attribute_field is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldEqualTo(String value) {
+            addCriterion("attribute_field =", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldNotEqualTo(String value) {
+            addCriterion("attribute_field <>", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldGreaterThan(String value) {
+            addCriterion("attribute_field >", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldGreaterThanOrEqualTo(String value) {
+            addCriterion("attribute_field >=", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldLessThan(String value) {
+            addCriterion("attribute_field <", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldLessThanOrEqualTo(String value) {
+            addCriterion("attribute_field <=", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldLike(String value) {
+            addCriterion("attribute_field like", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldNotLike(String value) {
+            addCriterion("attribute_field not like", value, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldIn(List<String> values) {
+            addCriterion("attribute_field in", values, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldNotIn(List<String> values) {
+            addCriterion("attribute_field not in", values, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldBetween(String value1, String value2) {
+            addCriterion("attribute_field between", value1, value2, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeFieldNotBetween(String value1, String value2) {
+            addCriterion("attribute_field not between", value1, value2, "attributeField");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameIsNull() {
+            addCriterion("attribute_name is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameIsNotNull() {
+            addCriterion("attribute_name is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameEqualTo(String value) {
+            addCriterion("attribute_name =", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameNotEqualTo(String value) {
+            addCriterion("attribute_name <>", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameGreaterThan(String value) {
+            addCriterion("attribute_name >", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameGreaterThanOrEqualTo(String value) {
+            addCriterion("attribute_name >=", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameLessThan(String value) {
+            addCriterion("attribute_name <", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameLessThanOrEqualTo(String value) {
+            addCriterion("attribute_name <=", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameLike(String value) {
+            addCriterion("attribute_name like", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameNotLike(String value) {
+            addCriterion("attribute_name not like", value, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameIn(List<String> values) {
+            addCriterion("attribute_name in", values, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameNotIn(List<String> values) {
+            addCriterion("attribute_name not in", values, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameBetween(String value1, String value2) {
+            addCriterion("attribute_name between", value1, value2, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeNameNotBetween(String value1, String value2) {
+            addCriterion("attribute_name not between", value1, value2, "attributeName");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueIsNull() {
+            addCriterion("attribute_value is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueIsNotNull() {
+            addCriterion("attribute_value is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueEqualTo(String value) {
+            addCriterion("attribute_value =", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueNotEqualTo(String value) {
+            addCriterion("attribute_value <>", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueGreaterThan(String value) {
+            addCriterion("attribute_value >", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueGreaterThanOrEqualTo(String value) {
+            addCriterion("attribute_value >=", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueLessThan(String value) {
+            addCriterion("attribute_value <", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueLessThanOrEqualTo(String value) {
+            addCriterion("attribute_value <=", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueLike(String value) {
+            addCriterion("attribute_value like", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueNotLike(String value) {
+            addCriterion("attribute_value not like", value, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueIn(List<String> values) {
+            addCriterion("attribute_value in", values, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueNotIn(List<String> values) {
+            addCriterion("attribute_value not in", values, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueBetween(String value1, String value2) {
+            addCriterion("attribute_value between", value1, value2, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andAttributeValueNotBetween(String value1, String value2) {
+            addCriterion("attribute_value not between", value1, value2, "attributeValue");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdIsNull() {
+            addCriterion("tenant_id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdIsNotNull() {
+            addCriterion("tenant_id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdEqualTo(Long value) {
+            addCriterion("tenant_id =", value, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdNotEqualTo(Long value) {
+            addCriterion("tenant_id <>", value, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdGreaterThan(Long value) {
+            addCriterion("tenant_id >", value, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdGreaterThanOrEqualTo(Long value) {
+            addCriterion("tenant_id >=", value, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdLessThan(Long value) {
+            addCriterion("tenant_id <", value, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdLessThanOrEqualTo(Long value) {
+            addCriterion("tenant_id <=", value, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdIn(List<Long> values) {
+            addCriterion("tenant_id in", values, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdNotIn(List<Long> values) {
+            addCriterion("tenant_id not in", values, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdBetween(Long value1, Long value2) {
+            addCriterion("tenant_id between", value1, value2, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andTenantIdNotBetween(Long value1, Long value2) {
+            addCriterion("tenant_id not between", value1, value2, "tenantId");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagIsNull() {
+            addCriterion("delete_flag is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagIsNotNull() {
+            addCriterion("delete_flag is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagEqualTo(String value) {
+            addCriterion("delete_flag =", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagNotEqualTo(String value) {
+            addCriterion("delete_flag <>", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagGreaterThan(String value) {
+            addCriterion("delete_flag >", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagGreaterThanOrEqualTo(String value) {
+            addCriterion("delete_flag >=", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagLessThan(String value) {
+            addCriterion("delete_flag <", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagLessThanOrEqualTo(String value) {
+            addCriterion("delete_flag <=", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagLike(String value) {
+            addCriterion("delete_flag like", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagNotLike(String value) {
+            addCriterion("delete_flag not like", value, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagIn(List<String> values) {
+            addCriterion("delete_flag in", values, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagNotIn(List<String> values) {
+            addCriterion("delete_flag not in", values, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagBetween(String value1, String value2) {
+            addCriterion("delete_flag between", value1, value2, "deleteFlag");
+            return (Criteria) this;
+        }
+
+        public Criteria andDeleteFlagNotBetween(String value1, String value2) {
+            addCriterion("delete_flag not between", value1, value2, "deleteFlag");
+            return (Criteria) this;
+        }
+    }
+
+    public static class Criteria extends GeneratedCriteria {
+        protected Criteria() {
+            super();
+        }
+    }
+
+    public static class Criterion {
+        private String condition;
+
+        private Object value;
+
+        private Object secondValue;
+
+        private boolean noValue;
+
+        private boolean singleValue;
+
+        private boolean betweenValue;
+
+        private boolean listValue;
+
+        private String typeHandler;
+
+        public String getCondition() {
+            return condition;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public Object getSecondValue() {
+            return secondValue;
+        }
+
+        public boolean isNoValue() {
+            return noValue;
+        }
+
+        public boolean isSingleValue() {
+            return singleValue;
+        }
+
+        public boolean isBetweenValue() {
+            return betweenValue;
+        }
+
+        public boolean isListValue() {
+            return listValue;
+        }
+
+        public String getTypeHandler() {
+            return typeHandler;
+        }
+
+        protected Criterion(String condition) {
+            super();
+            this.condition = condition;
+            this.typeHandler = null;
+            this.noValue = true;
+        }
+
+        protected Criterion(String condition, Object value, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.typeHandler = typeHandler;
+            if (value instanceof List<?>) {
+                this.listValue = true;
+            } else {
+                this.singleValue = true;
+            }
+        }
+
+        protected Criterion(String condition, Object value) {
+            this(condition, value, null);
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.secondValue = secondValue;
+            this.typeHandler = typeHandler;
+            this.betweenValue = true;
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue) {
+            this(condition, value, secondValue, null);
+        }
+    }
+}
\ No newline at end of file
diff --git a/jshERP-boot/src/main/java/com/jsh/erp/datasource/mappers/MaterialAttributeMapper.java b/jshERP-boot/src/main/java/com/jsh/erp/datasource/mappers/MaterialAttributeMapper.java
new file mode 100644
index 00000000..b33f29bd
--- /dev/null
+++ b/jshERP-boot/src/main/java/com/jsh/erp/datasource/mappers/MaterialAttributeMapper.java
@@ -0,0 +1,30 @@
+package com.jsh.erp.datasource.mappers;
+
+import com.jsh.erp.datasource.entities.MaterialAttribute;
+import com.jsh.erp.datasource.entities.MaterialAttributeExample;
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+
+public interface MaterialAttributeMapper {
+    long countByExample(MaterialAttributeExample example);
+
+    int deleteByExample(MaterialAttributeExample example);
+
+    int deleteByPrimaryKey(Long id);
+
+    int insert(MaterialAttribute record);
+
+    int insertSelective(MaterialAttribute record);
+
+    List<MaterialAttribute> selectByExample(MaterialAttributeExample example);
+
+    MaterialAttribute selectByPrimaryKey(Long id);
+
+    int updateByExampleSelective(@Param("record") MaterialAttribute record, @Param("example") MaterialAttributeExample example);
+
+    int updateByExample(@Param("record") MaterialAttribute record, @Param("example") MaterialAttributeExample example);
+
+    int updateByPrimaryKeySelective(MaterialAttribute record);
+
+    int updateByPrimaryKey(MaterialAttribute record);
+}
\ No newline at end of file
diff --git a/jshERP-boot/src/main/java/com/jsh/erp/datasource/mappers/MaterialAttributeMapperEx.java b/jshERP-boot/src/main/java/com/jsh/erp/datasource/mappers/MaterialAttributeMapperEx.java
new file mode 100644
index 00000000..12c974aa
--- /dev/null
+++ b/jshERP-boot/src/main/java/com/jsh/erp/datasource/mappers/MaterialAttributeMapperEx.java
@@ -0,0 +1,20 @@
+package com.jsh.erp.datasource.mappers;
+
+import com.jsh.erp.datasource.entities.MaterialAttribute;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface MaterialAttributeMapperEx {
+
+    List<MaterialAttribute> selectByConditionMaterialAttribute(
+            @Param("attributeField") String attributeField,
+            @Param("offset") Integer offset,
+            @Param("rows") Integer rows);
+
+    Long countsByMaterialAttribute(
+            @Param("attributeField") String attributeField);
+
+    int batchDeleteMaterialAttributeByIds(
+            @Param("ids") String ids[]);
+}
\ No newline at end of file
diff --git a/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeComponent.java b/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeComponent.java
new file mode 100644
index 00000000..5052c246
--- /dev/null
+++ b/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeComponent.java
@@ -0,0 +1,70 @@
+package com.jsh.erp.service.materialAttribute;
+
+import com.alibaba.fastjson.JSONObject;
+import com.jsh.erp.service.ICommonQuery;
+import com.jsh.erp.utils.Constants;
+import com.jsh.erp.utils.QueryUtils;
+import com.jsh.erp.utils.StringUtil;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+
+@Service(value = "materialAttribute_component")
+@MaterialAttributeResource
+public class MaterialAttributeComponent implements ICommonQuery {
+
+    @Resource
+    private MaterialAttributeService materialAttributeService;
+
+    @Override
+    public Object selectOne(Long id) throws Exception {
+        return materialAttributeService.getMaterialAttribute(id);
+    }
+
+    @Override
+    public List<?> select(Map<String, String> map)throws Exception {
+        return getMaterialList(map);
+    }
+
+    private List<?> getMaterialList(Map<String, String> map) throws Exception{
+        String search = map.get(Constants.SEARCH);
+        String attributeField = StringUtil.getInfo(search, "attributeField");
+        return materialAttributeService.select(attributeField, QueryUtils.offset(map), QueryUtils.rows(map));
+    }
+
+    @Override
+    public Long counts(Map<String, String> map)throws Exception {
+        String search = map.get(Constants.SEARCH);
+        String attributeField = StringUtil.getInfo(search, "attributeField");
+        return materialAttributeService.countMaterialAttribute(attributeField);
+    }
+
+    @Override
+    public int insert(JSONObject obj, HttpServletRequest request) throws Exception{
+        return materialAttributeService.insertMaterialAttribute(obj, request);
+    }
+
+    @Override
+    public int update(JSONObject obj, HttpServletRequest request)throws Exception {
+        return materialAttributeService.updateMaterialAttribute(obj, request);
+    }
+
+    @Override
+    public int delete(Long id, HttpServletRequest request)throws Exception {
+        return materialAttributeService.deleteMaterialAttribute(id, request);
+    }
+
+    @Override
+    public int deleteBatch(String ids, HttpServletRequest request)throws Exception {
+        return materialAttributeService.batchDeleteMaterialAttribute(ids, request);
+    }
+
+    @Override
+    public int checkIsNameExist(Long id, String name)throws Exception {
+        return materialAttributeService.checkIsNameExist(id, name);
+    }
+
+}
diff --git a/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeResource.java b/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeResource.java
new file mode 100644
index 00000000..77ea1f30
--- /dev/null
+++ b/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeResource.java
@@ -0,0 +1,15 @@
+package com.jsh.erp.service.materialAttribute;
+
+import com.jsh.erp.service.ResourceInfo;
+
+import java.lang.annotation.*;
+
+/**
+ * @author jishenghua qq752718920  2021-07-21 22:26:27
+ */
+@ResourceInfo(value = "materialAttribute")
+@Inherited
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MaterialAttributeResource {
+}
diff --git a/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeService.java b/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeService.java
new file mode 100644
index 00000000..971d0990
--- /dev/null
+++ b/jshERP-boot/src/main/java/com/jsh/erp/service/materialAttribute/MaterialAttributeService.java
@@ -0,0 +1,142 @@
+package com.jsh.erp.service.materialAttribute;
+
+import com.alibaba.fastjson.JSONObject;
+import com.jsh.erp.constants.BusinessConstants;
+import com.jsh.erp.datasource.entities.MaterialAttribute;
+import com.jsh.erp.datasource.entities.MaterialAttributeExample;
+import com.jsh.erp.datasource.mappers.MaterialAttributeMapper;
+import com.jsh.erp.datasource.mappers.MaterialAttributeMapperEx;
+import com.jsh.erp.exception.BusinessRunTimeException;
+import com.jsh.erp.exception.JshException;
+import com.jsh.erp.service.log.LogService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class MaterialAttributeService {
+    private Logger logger = LoggerFactory.getLogger(MaterialAttributeService.class);
+
+    @Resource
+    private LogService logService;
+
+    @Resource
+    private MaterialAttributeMapper materialAttributeMapper;
+
+    @Resource
+    private MaterialAttributeMapperEx materialAttributeMapperEx;
+
+    public MaterialAttribute getMaterialAttribute(long id)throws Exception {
+        MaterialAttribute result=null;
+        try{
+            result=materialAttributeMapper.selectByPrimaryKey(id);
+        }catch(Exception e){
+            JshException.readFail(logger, e);
+        }
+        return result;
+    }
+
+    public List<MaterialAttribute> getMaterialAttribute() throws Exception{
+        MaterialAttributeExample example = new MaterialAttributeExample();
+        example.createCriteria().andDeleteFlagNotEqualTo(BusinessConstants.DELETE_FLAG_DELETED);
+        List<MaterialAttribute> list=null;
+        try{
+            list=materialAttributeMapper.selectByExample(example);
+        }catch(Exception e){
+            JshException.readFail(logger, e);
+        }
+        return list;
+    }
+
+    public List<MaterialAttribute> select(String attributeField, int offset, int rows)
+            throws Exception{
+        List<MaterialAttribute> list = new ArrayList<>();
+        try{
+            list= materialAttributeMapperEx.selectByConditionMaterialAttribute(attributeField, offset, rows);
+        }catch(Exception e){
+            JshException.readFail(logger, e);
+        }
+        return list;
+    }
+
+    public Long countMaterialAttribute(String attributeField)throws Exception {
+        Long result =null;
+        try{
+            result= materialAttributeMapperEx.countsByMaterialAttribute(attributeField);
+        }catch(Exception e){
+            JshException.readFail(logger, e);
+        }
+        return result;
+    }
+
+    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
+    public int insertMaterialAttribute(JSONObject obj, HttpServletRequest request)throws Exception {
+        MaterialAttribute m = JSONObject.parseObject(obj.toJSONString(), MaterialAttribute.class);
+        try{
+            materialAttributeMapper.insertSelective(m);
+            logService.insertLog("商品属性",
+                    new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_ADD).append(m.getAttributeName()).toString(), request);
+            return 1;
+        }
+        catch (BusinessRunTimeException ex) {
+            throw new BusinessRunTimeException(ex.getCode(), ex.getMessage());
+        }
+        catch(Exception e){
+            JshException.writeFail(logger, e);
+            return 0;
+        }
+    }
+
+    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
+    public int updateMaterialAttribute(JSONObject obj, HttpServletRequest request) throws Exception{
+        MaterialAttribute materialAttribute = JSONObject.parseObject(obj.toJSONString(), MaterialAttribute.class);
+        try{
+            materialAttributeMapper.updateByPrimaryKeySelective(materialAttribute);
+            logService.insertLog("商品属性",
+                    new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_EDIT).append(materialAttribute.getAttributeName()).toString(), request);
+            return 1;
+        }catch(Exception e){
+            JshException.writeFail(logger, e);
+            return 0;
+        }
+    }
+
+    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
+    public int deleteMaterialAttribute(Long id, HttpServletRequest request)throws Exception {
+        return batchDeleteMaterialAttributeByIds(id.toString());
+    }
+
+    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
+    public int batchDeleteMaterialAttribute(String ids, HttpServletRequest request)throws Exception {
+        return batchDeleteMaterialAttributeByIds(ids);
+    }
+
+    @Transactional(value = "transactionManager", rollbackFor = Exception.class)
+    public int batchDeleteMaterialAttributeByIds(String ids) throws Exception{
+        String [] idArray=ids.split(",");
+        try{
+            return materialAttributeMapperEx.batchDeleteMaterialAttributeByIds(idArray);
+        }catch(Exception e){
+            JshException.writeFail(logger, e);
+            return 0;
+        }
+    }
+
+    public int checkIsNameExist(Long id, String name)throws Exception {
+        MaterialAttributeExample example = new MaterialAttributeExample();
+        example.createCriteria().andIdNotEqualTo(id).andAttributeNameEqualTo(name).andDeleteFlagNotEqualTo(BusinessConstants.DELETE_FLAG_DELETED);
+        List<MaterialAttribute> list =null;
+        try{
+            list = materialAttributeMapper.selectByExample(example);
+        }catch(Exception e){
+            JshException.readFail(logger, e);
+        }
+        return list==null?0:list.size();
+    }
+}
diff --git a/jshERP-boot/src/main/resources/mapper_xml/MaterialAttributeMapper.xml b/jshERP-boot/src/main/resources/mapper_xml/MaterialAttributeMapper.xml
new file mode 100644
index 00000000..4623e9bf
--- /dev/null
+++ b/jshERP-boot/src/main/resources/mapper_xml/MaterialAttributeMapper.xml
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jsh.erp.datasource.mappers.MaterialAttributeMapper">
+  <resultMap id="BaseResultMap" type="com.jsh.erp.datasource.entities.MaterialAttribute">
+    <id column="id" jdbcType="BIGINT" property="id" />
+    <result column="attribute_field" jdbcType="VARCHAR" property="attributeField" />
+    <result column="attribute_name" jdbcType="VARCHAR" property="attributeName" />
+    <result column="attribute_value" jdbcType="VARCHAR" property="attributeValue" />
+    <result column="tenant_id" jdbcType="BIGINT" property="tenantId" />
+    <result column="delete_flag" jdbcType="VARCHAR" property="deleteFlag" />
+  </resultMap>
+  <sql id="Example_Where_Clause">
+    <where>
+      <foreach collection="oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Update_By_Example_Where_Clause">
+    <where>
+      <foreach collection="example.oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Base_Column_List">
+    id, attribute_field, attribute_name, attribute_value, tenant_id, delete_flag
+  </sql>
+  <select id="selectByExample" parameterType="com.jsh.erp.datasource.entities.MaterialAttributeExample" resultMap="BaseResultMap">
+    select
+    <if test="distinct">
+      distinct
+    </if>
+    <include refid="Base_Column_List" />
+    from jsh_material_attribute
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+    <if test="orderByClause != null">
+      order by ${orderByClause}
+    </if>
+  </select>
+  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
+    select 
+    <include refid="Base_Column_List" />
+    from jsh_material_attribute
+    where id = #{id,jdbcType=BIGINT}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
+    delete from jsh_material_attribute
+    where id = #{id,jdbcType=BIGINT}
+  </delete>
+  <delete id="deleteByExample" parameterType="com.jsh.erp.datasource.entities.MaterialAttributeExample">
+    delete from jsh_material_attribute
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </delete>
+  <insert id="insert" parameterType="com.jsh.erp.datasource.entities.MaterialAttribute">
+    insert into jsh_material_attribute (id, attribute_field, attribute_name, 
+      attribute_value, tenant_id, delete_flag
+      )
+    values (#{id,jdbcType=BIGINT}, #{attributeField,jdbcType=VARCHAR}, #{attributeName,jdbcType=VARCHAR}, 
+      #{attributeValue,jdbcType=VARCHAR}, #{tenantId,jdbcType=BIGINT}, #{deleteFlag,jdbcType=VARCHAR}
+      )
+  </insert>
+  <insert id="insertSelective" parameterType="com.jsh.erp.datasource.entities.MaterialAttribute">
+    insert into jsh_material_attribute
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="attributeField != null">
+        attribute_field,
+      </if>
+      <if test="attributeName != null">
+        attribute_name,
+      </if>
+      <if test="attributeValue != null">
+        attribute_value,
+      </if>
+      <if test="tenantId != null">
+        tenant_id,
+      </if>
+      <if test="deleteFlag != null">
+        delete_flag,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=BIGINT},
+      </if>
+      <if test="attributeField != null">
+        #{attributeField,jdbcType=VARCHAR},
+      </if>
+      <if test="attributeName != null">
+        #{attributeName,jdbcType=VARCHAR},
+      </if>
+      <if test="attributeValue != null">
+        #{attributeValue,jdbcType=VARCHAR},
+      </if>
+      <if test="tenantId != null">
+        #{tenantId,jdbcType=BIGINT},
+      </if>
+      <if test="deleteFlag != null">
+        #{deleteFlag,jdbcType=VARCHAR},
+      </if>
+    </trim>
+  </insert>
+  <select id="countByExample" parameterType="com.jsh.erp.datasource.entities.MaterialAttributeExample" resultType="java.lang.Long">
+    select count(*) from jsh_material_attribute
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </select>
+  <update id="updateByExampleSelective" parameterType="map">
+    update jsh_material_attribute
+    <set>
+      <if test="record.id != null">
+        id = #{record.id,jdbcType=BIGINT},
+      </if>
+      <if test="record.attributeField != null">
+        attribute_field = #{record.attributeField,jdbcType=VARCHAR},
+      </if>
+      <if test="record.attributeName != null">
+        attribute_name = #{record.attributeName,jdbcType=VARCHAR},
+      </if>
+      <if test="record.attributeValue != null">
+        attribute_value = #{record.attributeValue,jdbcType=VARCHAR},
+      </if>
+      <if test="record.tenantId != null">
+        tenant_id = #{record.tenantId,jdbcType=BIGINT},
+      </if>
+      <if test="record.deleteFlag != null">
+        delete_flag = #{record.deleteFlag,jdbcType=VARCHAR},
+      </if>
+    </set>
+    <if test="_parameter != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByExample" parameterType="map">
+    update jsh_material_attribute
+    set id = #{record.id,jdbcType=BIGINT},
+      attribute_field = #{record.attributeField,jdbcType=VARCHAR},
+      attribute_name = #{record.attributeName,jdbcType=VARCHAR},
+      attribute_value = #{record.attributeValue,jdbcType=VARCHAR},
+      tenant_id = #{record.tenantId,jdbcType=BIGINT},
+      delete_flag = #{record.deleteFlag,jdbcType=VARCHAR}
+    <if test="_parameter != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByPrimaryKeySelective" parameterType="com.jsh.erp.datasource.entities.MaterialAttribute">
+    update jsh_material_attribute
+    <set>
+      <if test="attributeField != null">
+        attribute_field = #{attributeField,jdbcType=VARCHAR},
+      </if>
+      <if test="attributeName != null">
+        attribute_name = #{attributeName,jdbcType=VARCHAR},
+      </if>
+      <if test="attributeValue != null">
+        attribute_value = #{attributeValue,jdbcType=VARCHAR},
+      </if>
+      <if test="tenantId != null">
+        tenant_id = #{tenantId,jdbcType=BIGINT},
+      </if>
+      <if test="deleteFlag != null">
+        delete_flag = #{deleteFlag,jdbcType=VARCHAR},
+      </if>
+    </set>
+    where id = #{id,jdbcType=BIGINT}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.jsh.erp.datasource.entities.MaterialAttribute">
+    update jsh_material_attribute
+    set attribute_field = #{attributeField,jdbcType=VARCHAR},
+      attribute_name = #{attributeName,jdbcType=VARCHAR},
+      attribute_value = #{attributeValue,jdbcType=VARCHAR},
+      tenant_id = #{tenantId,jdbcType=BIGINT},
+      delete_flag = #{deleteFlag,jdbcType=VARCHAR}
+    where id = #{id,jdbcType=BIGINT}
+  </update>
+</mapper>
\ No newline at end of file
diff --git a/jshERP-boot/src/main/resources/mapper_xml/MaterialAttributeMapperEx.xml b/jshERP-boot/src/main/resources/mapper_xml/MaterialAttributeMapperEx.xml
new file mode 100644
index 00000000..03e7cfa2
--- /dev/null
+++ b/jshERP-boot/src/main/resources/mapper_xml/MaterialAttributeMapperEx.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jsh.erp.datasource.mappers.MaterialAttributeMapperEx">
+
+    <select id="selectByConditionMaterialAttribute" resultType="com.jsh.erp.datasource.entities.MaterialAttribute">
+        select * from jsh_material_attribute ma
+        where 1=1
+        <if test="attributeField != null">
+            and ma.attribute_field = #{attributeField}
+        </if>
+        and ifnull(ma.delete_flag,'0') !='1'
+        order by ma.id desc
+        <if test="offset != null and rows != null">
+            limit #{offset},#{rows}
+        </if>
+    </select>
+
+    <select id="countsByMaterialAttribute" resultType="java.lang.Long">
+        SELECT count(ma.id) from jsh_material_attribute ma
+        where 1=1
+        <if test="attributeField != null">
+            and ma.attribute_field = #{attributeField}
+        </if>
+        and ifnull(ma.delete_flag,'0') !='1'
+    </select>
+
+    <update id="batchDeleteMaterialAttributeByIds">
+        update jsh_material_attribute
+        set delete_flag='1'
+        where 1=1
+        and id in (
+        <foreach collection="ids" item="id" separator=",">
+            #{id}
+        </foreach>
+        )
+    </update>
+</mapper>
\ No newline at end of file
diff --git a/jshERP-boot/src/test/resources/generatorConfig.xml b/jshERP-boot/src/test/resources/generatorConfig.xml
index 7c9ee128..fd0f5e3d 100644
--- a/jshERP-boot/src/test/resources/generatorConfig.xml
+++ b/jshERP-boot/src/test/resources/generatorConfig.xml
@@ -51,6 +51,7 @@
         <table tableName="jsh_inoutitem" domainObjectName="InOutItem"></table>
         <table tableName="jsh_log" domainObjectName="Log"></table>
         <table tableName="jsh_material" domainObjectName="Material"></table>
+        <table tableName="jsh_material_attribute" domainObjectName="MaterialAttribute"></table>
         <table tableName="jsh_material_extend" domainObjectName="MaterialExtend"></table>
         <table tableName="jsh_material_current_stock" domainObjectName="MaterialCurrentStock"></table>
         <table tableName="jsh_material_initial_stock" domainObjectName="MaterialInitialStock"></table>
