新增小程序产品规格类型相关功能模块,包括: 1. 新增Appletproductspectype实体类、Mapper、Service及Controller 2. 新增产品规格类型前端页面及接口 3. 重构产品规格相关代码,将AppletProductSpec迁移至appletProduct模块 4. 新增产品套餐相关功能 5. 更新路由配置和菜单权限 6. 优化用户登录逻辑,移除默认用户名设置master
| @ -0,0 +1,65 @@ | |||
| package org.jeecg.modules.demo.appletProduct.entity; | |||
| import java.io.Serializable; | |||
| import com.baomidou.mybatisplus.annotation.IdType; | |||
| import com.baomidou.mybatisplus.annotation.TableId; | |||
| import com.baomidou.mybatisplus.annotation.TableName; | |||
| import com.baomidou.mybatisplus.annotation.TableLogic; | |||
| import org.jeecg.common.constant.ProvinceCityArea; | |||
| import org.jeecg.common.util.SpringContextUtils; | |||
| import lombok.Data; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import org.springframework.format.annotation.DateTimeFormat; | |||
| import org.jeecgframework.poi.excel.annotation.Excel; | |||
| import java.util.Date; | |||
| import io.swagger.v3.oas.annotations.media.Schema; | |||
| import java.io.UnsupportedEncodingException; | |||
| /** | |||
| * @Description: 产品套餐 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Schema(description="产品套餐") | |||
| @Data | |||
| @TableName("applet_product_package") | |||
| public class AppletProductPackage implements Serializable { | |||
| private static final long serialVersionUID = 1L; | |||
| /**主键*/ | |||
| @TableId(type = IdType.ASSIGN_ID) | |||
| @Schema(description = "主键") | |||
| private java.lang.String id; | |||
| /**创建人*/ | |||
| @Schema(description = "创建人") | |||
| private java.lang.String createBy; | |||
| /**创建日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "创建日期") | |||
| private java.util.Date createTime; | |||
| /**更新人*/ | |||
| @Schema(description = "更新人") | |||
| private java.lang.String updateBy; | |||
| /**更新日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "更新日期") | |||
| private java.util.Date updateTime; | |||
| /**所属部门*/ | |||
| @Schema(description = "所属部门") | |||
| private java.lang.String sysOrgCode; | |||
| /**图片*/ | |||
| @Excel(name = "图片", width = 15) | |||
| @Schema(description = "图片") | |||
| private java.lang.String image; | |||
| /**标题*/ | |||
| @Excel(name = "标题", width = 15) | |||
| @Schema(description = "标题") | |||
| private java.lang.String titile; | |||
| /**产品*/ | |||
| @Schema(description = "产品") | |||
| private java.lang.String productId; | |||
| } | |||
| @ -0,0 +1,85 @@ | |||
| package org.jeecg.modules.demo.appletProduct.entity; | |||
| import java.io.Serializable; | |||
| import com.baomidou.mybatisplus.annotation.IdType; | |||
| import com.baomidou.mybatisplus.annotation.TableId; | |||
| import com.baomidou.mybatisplus.annotation.TableName; | |||
| import com.baomidou.mybatisplus.annotation.TableLogic; | |||
| import org.jeecg.common.constant.ProvinceCityArea; | |||
| import org.jeecg.common.util.SpringContextUtils; | |||
| import lombok.Data; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import org.springframework.format.annotation.DateTimeFormat; | |||
| import org.jeecgframework.poi.excel.annotation.Excel; | |||
| import java.util.Date; | |||
| import io.swagger.v3.oas.annotations.media.Schema; | |||
| import java.io.UnsupportedEncodingException; | |||
| /** | |||
| * @Description: 产品规格表 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Schema(description="产品规格表") | |||
| @Data | |||
| @TableName("applet_product_spec") | |||
| public class AppletProductSpec implements Serializable { | |||
| private static final long serialVersionUID = 1L; | |||
| /**主键*/ | |||
| @TableId(type = IdType.ASSIGN_ID) | |||
| @Schema(description = "主键") | |||
| private java.lang.String id; | |||
| /**创建人*/ | |||
| @Schema(description = "创建人") | |||
| private java.lang.String createBy; | |||
| /**创建日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "创建日期") | |||
| private java.util.Date createTime; | |||
| /**更新人*/ | |||
| @Schema(description = "更新人") | |||
| private java.lang.String updateBy; | |||
| /**更新日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "更新日期") | |||
| private java.util.Date updateTime; | |||
| /**规格名称*/ | |||
| @Excel(name = "规格名称", width = 15) | |||
| @Schema(description = "规格名称") | |||
| private java.lang.String specName; | |||
| /**详情*/ | |||
| @Excel(name = "详情", width = 15) | |||
| @Schema(description = "详情") | |||
| private java.lang.String content; | |||
| /**排序*/ | |||
| @Excel(name = "排序", width = 15) | |||
| @Schema(description = "排序") | |||
| private java.lang.Integer sortOrder; | |||
| /**产品*/ | |||
| @Schema(description = "产品") | |||
| private java.lang.String productId; | |||
| /**价格*/ | |||
| @Excel(name = "价格", width = 15) | |||
| @Schema(description = "价格") | |||
| private java.math.BigDecimal price; | |||
| /**图片*/ | |||
| @Excel(name = "图片", width = 15) | |||
| @Schema(description = "图片") | |||
| private java.lang.String image; | |||
| /**描述*/ | |||
| @Excel(name = "描述", width = 15) | |||
| @Schema(description = "描述") | |||
| private java.lang.String info; | |||
| @Excel(name = "库存", width = 15) | |||
| @Schema(description = "库存") | |||
| private Integer inventory;//库存 | |||
| @Excel(name = "上架", width = 15) | |||
| @Schema(description = "上架") | |||
| private String putaway;//上架 | |||
| } | |||
| @ -0,0 +1,31 @@ | |||
| package org.jeecg.modules.demo.appletProduct.mapper; | |||
| import java.util.List; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductPackage; | |||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
| import org.apache.ibatis.annotations.Param; | |||
| /** | |||
| * @Description: 产品套餐 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| public interface AppletProductPackageMapper extends BaseMapper<AppletProductPackage> { | |||
| /** | |||
| * 通过主表id删除子表数据 | |||
| * | |||
| * @param mainId 主表id | |||
| * @return boolean | |||
| */ | |||
| public boolean deleteByMainId(@Param("mainId") String mainId); | |||
| /** | |||
| * 通过主表id查询子表数据 | |||
| * | |||
| * @param mainId 主表id | |||
| * @return List<AppletProductPackage> | |||
| */ | |||
| public List<AppletProductPackage> selectByMainId(@Param("mainId") String mainId); | |||
| } | |||
| @ -0,0 +1,31 @@ | |||
| package org.jeecg.modules.demo.appletProduct.mapper; | |||
| import java.util.List; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec; | |||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
| import org.apache.ibatis.annotations.Param; | |||
| /** | |||
| * @Description: 产品规格表 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| public interface AppletProductSpecMapper extends BaseMapper<AppletProductSpec> { | |||
| /** | |||
| * 通过主表id删除子表数据 | |||
| * | |||
| * @param mainId 主表id | |||
| * @return boolean | |||
| */ | |||
| public boolean deleteByMainId(@Param("mainId") String mainId); | |||
| /** | |||
| * 通过主表id查询子表数据 | |||
| * | |||
| * @param mainId 主表id | |||
| * @return List<AppletProductSpec> | |||
| */ | |||
| public List<AppletProductSpec> selectByMainId(@Param("mainId") String mainId); | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| <?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="org.jeecg.modules.demo.appletProduct.mapper.AppletProductPackageMapper"> | |||
| <delete id="deleteByMainId" parameterType="java.lang.String"> | |||
| DELETE | |||
| FROM applet_product_package | |||
| WHERE | |||
| product_id = #{mainId} </delete> | |||
| <select id="selectByMainId" parameterType="java.lang.String" resultType="org.jeecg.modules.demo.appletProduct.entity.AppletProductPackage"> | |||
| SELECT * | |||
| FROM applet_product_package | |||
| WHERE | |||
| product_id = #{mainId} </select> | |||
| </mapper> | |||
| @ -0,0 +1,16 @@ | |||
| <?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="org.jeecg.modules.demo.appletProduct.mapper.AppletProductSpecMapper"> | |||
| <delete id="deleteByMainId" parameterType="java.lang.String"> | |||
| DELETE | |||
| FROM applet_product_spec | |||
| WHERE | |||
| product_id = #{mainId} </delete> | |||
| <select id="selectByMainId" parameterType="java.lang.String" resultType="org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec"> | |||
| SELECT * | |||
| FROM applet_product_spec | |||
| WHERE | |||
| product_id = #{mainId} </select> | |||
| </mapper> | |||
| @ -0,0 +1,22 @@ | |||
| package org.jeecg.modules.demo.appletProduct.service; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductPackage; | |||
| import com.baomidou.mybatisplus.extension.service.IService; | |||
| import java.util.List; | |||
| /** | |||
| * @Description: 产品套餐 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| public interface IAppletProductPackageService extends IService<AppletProductPackage> { | |||
| /** | |||
| * 通过主表id查询子表数据 | |||
| * | |||
| * @param mainId 主表id | |||
| * @return List<AppletProductPackage> | |||
| */ | |||
| public List<AppletProductPackage> selectByMainId(String mainId); | |||
| } | |||
| @ -1,14 +1,51 @@ | |||
| package org.jeecg.modules.demo.appletProduct.service; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductPackage; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProduct; | |||
| import com.baomidou.mybatisplus.extension.service.IService; | |||
| import java.io.Serializable; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| /** | |||
| * @Description: 产品表 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-07-21 | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| public interface IAppletProductService extends IService<AppletProduct> { | |||
| /** | |||
| * 添加一对多 | |||
| * | |||
| * @param appletProduct | |||
| * @param appletProductSpecList | |||
| * @param appletProductPackageList | |||
| */ | |||
| public void saveMain(AppletProduct appletProduct,List<AppletProductSpec> appletProductSpecList,List<AppletProductPackage> appletProductPackageList) ; | |||
| /** | |||
| * 修改一对多 | |||
| * | |||
| * @param appletProduct | |||
| * @param appletProductSpecList | |||
| * @param appletProductPackageList | |||
| */ | |||
| public void updateMain(AppletProduct appletProduct,List<AppletProductSpec> appletProductSpecList,List<AppletProductPackage> appletProductPackageList); | |||
| /** | |||
| * 删除一对多 | |||
| * | |||
| * @param id | |||
| */ | |||
| public void delMain (String id); | |||
| /** | |||
| * 批量删除一对多 | |||
| * | |||
| * @param idList | |||
| */ | |||
| public void delBatchMain (Collection<? extends Serializable> idList); | |||
| } | |||
| @ -0,0 +1,32 @@ | |||
| package org.jeecg.modules.demo.appletProduct.service; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec; | |||
| import com.baomidou.mybatisplus.extension.service.IService; | |||
| import java.util.List; | |||
| /** | |||
| * @Description: 产品规格表 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| public interface IAppletProductSpecService extends IService<AppletProductSpec> { | |||
| /** | |||
| * 通过主表id查询子表数据 | |||
| * | |||
| * @param mainId 主表id | |||
| * @return List<AppletProductSpec> | |||
| */ | |||
| public List<AppletProductSpec> selectByMainId(String mainId); | |||
| /** | |||
| * 检查规格名称是否已存在 | |||
| * | |||
| * @param productId 产品ID | |||
| * @param specName 规格名称 | |||
| * @param excludeId 排除的规格ID(编辑时使用) | |||
| * @return boolean true-已存在,false-不存在 | |||
| */ | |||
| public boolean checkSpecNameExists(String productId, String specName, String excludeId); | |||
| } | |||
| @ -0,0 +1,27 @@ | |||
| package org.jeecg.modules.demo.appletProduct.service.impl; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductPackage; | |||
| import org.jeecg.modules.demo.appletProduct.mapper.AppletProductPackageMapper; | |||
| import org.jeecg.modules.demo.appletProduct.service.IAppletProductPackageService; | |||
| import org.springframework.stereotype.Service; | |||
| import java.util.List; | |||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| /** | |||
| * @Description: 产品套餐 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Service | |||
| public class AppletProductPackageServiceImpl extends ServiceImpl<AppletProductPackageMapper, AppletProductPackage> implements IAppletProductPackageService { | |||
| @Autowired | |||
| private AppletProductPackageMapper appletProductPackageMapper; | |||
| @Override | |||
| public List<AppletProductPackage> selectByMainId(String mainId) { | |||
| return appletProductPackageMapper.selectByMainId(mainId); | |||
| } | |||
| } | |||
| @ -1,19 +1,122 @@ | |||
| package org.jeecg.modules.demo.appletProduct.service.impl; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProduct; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductPackage; | |||
| import org.jeecg.modules.demo.appletProduct.mapper.AppletProductSpecMapper; | |||
| import org.jeecg.modules.demo.appletProduct.mapper.AppletProductPackageMapper; | |||
| import org.jeecg.modules.demo.appletProduct.mapper.AppletProductMapper; | |||
| import org.jeecg.modules.demo.appletProduct.service.IAppletProductService; | |||
| import org.jeecg.modules.demo.appletProduct.service.IAppletProductSpecService; | |||
| import org.springframework.stereotype.Service; | |||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.transaction.annotation.Transactional; | |||
| import java.io.Serializable; | |||
| import java.util.List; | |||
| import java.util.Collection; | |||
| import java.util.Set; | |||
| import java.util.HashSet; | |||
| /** | |||
| * @Description: 产品表 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-07-21 | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Service | |||
| public class AppletProductServiceImpl extends ServiceImpl<AppletProductMapper, AppletProduct> implements IAppletProductService { | |||
| @Autowired | |||
| private AppletProductMapper appletProductMapper; | |||
| @Autowired | |||
| private AppletProductSpecMapper appletProductSpecMapper; | |||
| @Autowired | |||
| private AppletProductPackageMapper appletProductPackageMapper; | |||
| @Autowired | |||
| private IAppletProductSpecService appletProductSpecService; | |||
| @Override | |||
| @Transactional(rollbackFor = Exception.class) | |||
| public void saveMain(AppletProduct appletProduct, List<AppletProductSpec> appletProductSpecList,List<AppletProductPackage> appletProductPackageList) { | |||
| appletProductMapper.insert(appletProduct); | |||
| if(appletProductSpecList!=null && appletProductSpecList.size()>0) { | |||
| // 检查规格名称唯一性 | |||
| Set<String> specNames = new HashSet<>(); | |||
| for(AppletProductSpec entity:appletProductSpecList) { | |||
| if (entity.getSpecName() != null && !entity.getSpecName().trim().isEmpty()) { | |||
| if (specNames.contains(entity.getSpecName())) { | |||
| throw new RuntimeException("规格名称重复: " + entity.getSpecName()); | |||
| } | |||
| if (appletProductSpecService.checkSpecNameExists(appletProduct.getId(), entity.getSpecName(), null)) { | |||
| throw new RuntimeException("规格名称已存在: " + entity.getSpecName()); | |||
| } | |||
| specNames.add(entity.getSpecName()); | |||
| } | |||
| //外键设置 | |||
| entity.setProductId(appletProduct.getId()); | |||
| appletProductSpecMapper.insert(entity); | |||
| } | |||
| } | |||
| if(appletProductPackageList!=null && appletProductPackageList.size()>0) { | |||
| for(AppletProductPackage entity:appletProductPackageList) { | |||
| //外键设置 | |||
| entity.setProductId(appletProduct.getId()); | |||
| appletProductPackageMapper.insert(entity); | |||
| } | |||
| } | |||
| } | |||
| @Override | |||
| @Transactional(rollbackFor = Exception.class) | |||
| public void updateMain(AppletProduct appletProduct,List<AppletProductSpec> appletProductSpecList,List<AppletProductPackage> appletProductPackageList) { | |||
| appletProductMapper.updateById(appletProduct); | |||
| //1.先删除子表数据 | |||
| appletProductSpecMapper.deleteByMainId(appletProduct.getId()); | |||
| appletProductPackageMapper.deleteByMainId(appletProduct.getId()); | |||
| //2.子表数据重新插入 | |||
| if(appletProductSpecList!=null && appletProductSpecList.size()>0) { | |||
| // 检查规格名称唯一性 | |||
| Set<String> specNames = new HashSet<>(); | |||
| for(AppletProductSpec entity:appletProductSpecList) { | |||
| if (entity.getSpecName() != null && !entity.getSpecName().trim().isEmpty()) { | |||
| if (specNames.contains(entity.getSpecName())) { | |||
| throw new RuntimeException("规格名称重复: " + entity.getSpecName()); | |||
| } | |||
| specNames.add(entity.getSpecName()); | |||
| } | |||
| //外键设置 | |||
| entity.setProductId(appletProduct.getId()); | |||
| appletProductSpecMapper.insert(entity); | |||
| } | |||
| } | |||
| if(appletProductPackageList!=null && appletProductPackageList.size()>0) { | |||
| for(AppletProductPackage entity:appletProductPackageList) { | |||
| //外键设置 | |||
| entity.setProductId(appletProduct.getId()); | |||
| appletProductPackageMapper.insert(entity); | |||
| } | |||
| } | |||
| } | |||
| @Override | |||
| @Transactional(rollbackFor = Exception.class) | |||
| public void delMain(String id) { | |||
| appletProductSpecMapper.deleteByMainId(id); | |||
| appletProductPackageMapper.deleteByMainId(id); | |||
| appletProductMapper.deleteById(id); | |||
| } | |||
| @Override | |||
| @Transactional(rollbackFor = Exception.class) | |||
| public void delBatchMain(Collection<? extends Serializable> idList) { | |||
| for(Serializable id:idList) { | |||
| appletProductSpecMapper.deleteByMainId(id.toString()); | |||
| appletProductPackageMapper.deleteByMainId(id.toString()); | |||
| appletProductMapper.deleteById(id); | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,39 @@ | |||
| package org.jeecg.modules.demo.appletProduct.service.impl; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec; | |||
| import org.jeecg.modules.demo.appletProduct.mapper.AppletProductSpecMapper; | |||
| import org.jeecg.modules.demo.appletProduct.service.IAppletProductSpecService; | |||
| import org.springframework.stereotype.Service; | |||
| import java.util.List; | |||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| /** | |||
| * @Description: 产品规格表 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Service | |||
| public class AppletProductSpecServiceImpl extends ServiceImpl<AppletProductSpecMapper, AppletProductSpec> implements IAppletProductSpecService { | |||
| @Autowired | |||
| private AppletProductSpecMapper appletProductSpecMapper; | |||
| @Override | |||
| public List<AppletProductSpec> selectByMainId(String mainId) { | |||
| return appletProductSpecMapper.selectByMainId(mainId); | |||
| } | |||
| @Override | |||
| public boolean checkSpecNameExists(String productId, String specName, String excludeId) { | |||
| QueryWrapper<AppletProductSpec> queryWrapper = new QueryWrapper<>(); | |||
| queryWrapper.eq("product_id", productId); | |||
| queryWrapper.eq("spec_name", specName); | |||
| if (excludeId != null && !excludeId.isEmpty()) { | |||
| queryWrapper.ne("id", excludeId); | |||
| } | |||
| return this.count(queryWrapper) > 0; | |||
| } | |||
| } | |||
| @ -0,0 +1,132 @@ | |||
| package org.jeecg.modules.demo.appletProduct.vo; | |||
| import java.util.List; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProduct; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductPackage; | |||
| import lombok.Data; | |||
| import org.jeecgframework.poi.excel.annotation.Excel; | |||
| import org.jeecgframework.poi.excel.annotation.ExcelEntity; | |||
| import org.jeecgframework.poi.excel.annotation.ExcelCollection; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import org.springframework.format.annotation.DateTimeFormat; | |||
| import java.util.Date; | |||
| import org.jeecg.common.aspect.annotation.Dict; | |||
| import org.jeecg.common.constant.ProvinceCityArea; | |||
| import org.jeecg.common.util.SpringContextUtils; | |||
| import io.swagger.v3.oas.annotations.media.Schema; | |||
| /** | |||
| * @Description: 产品表 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Data | |||
| @Schema(description="产品表") | |||
| public class AppletProductPage { | |||
| /**主键*/ | |||
| @Schema(description = "主键") | |||
| private java.lang.String id; | |||
| /**创建人*/ | |||
| @Schema(description = "创建人") | |||
| private java.lang.String createBy; | |||
| /**创建日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "创建日期") | |||
| private java.util.Date createTime; | |||
| /**更新人*/ | |||
| @Schema(description = "更新人") | |||
| private java.lang.String updateBy; | |||
| /**更新日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "更新日期") | |||
| private java.util.Date updateTime; | |||
| /**产品名称*/ | |||
| @Excel(name = "产品名称", width = 15) | |||
| @Schema(description = "产品名称") | |||
| private java.lang.String name; | |||
| /**英文名称*/ | |||
| @Excel(name = "英文名称", width = 15) | |||
| @Schema(description = "英文名称") | |||
| private java.lang.String enName; | |||
| /**图片*/ | |||
| @Excel(name = "图片", width = 15) | |||
| @Schema(description = "图片") | |||
| private java.lang.String image; | |||
| /**产品描述*/ | |||
| @Excel(name = "产品描述", width = 15) | |||
| @Schema(description = "产品描述") | |||
| private java.lang.String info; | |||
| /**类型*/ | |||
| @Excel(name = "类型", width = 15, dicCode = "applet_product_type") | |||
| @Dict(dicCode = "applet_product_type") | |||
| @Schema(description = "类型") | |||
| private java.lang.String type; | |||
| /**分类*/ | |||
| @Excel(name = "分类", width = 15) | |||
| @Schema(description = "分类") | |||
| private java.lang.String classId; | |||
| /**原价*/ | |||
| @Excel(name = "原价", width = 15) | |||
| @Schema(description = "原价") | |||
| private java.math.BigDecimal originalPrice; | |||
| /**现价*/ | |||
| @Excel(name = "现价", width = 15) | |||
| @Schema(description = "现价") | |||
| private java.math.BigDecimal currentPrice; | |||
| /**单位*/ | |||
| @Excel(name = "单位", width = 15) | |||
| @Schema(description = "单位") | |||
| private java.lang.String unit; | |||
| /**详情*/ | |||
| @Excel(name = "详情", width = 15) | |||
| @Schema(description = "详情") | |||
| private java.lang.String detail; | |||
| /**已出售*/ | |||
| @Excel(name = "已出售", width = 15) | |||
| @Schema(description = "已出售") | |||
| private java.lang.Integer sold; | |||
| /**产品内容*/ | |||
| @Excel(name = "产品内容", width = 15) | |||
| @Schema(description = "产品内容") | |||
| private java.lang.String content; | |||
| /**跨境商品*/ | |||
| @Schema(description = "跨境商品") | |||
| @Excel(name = "跨境商品", width = 15,replace = {"是_Y","否_N"} ) | |||
| private java.lang.String isCrossBorder; | |||
| /**套餐*/ | |||
| @Schema(description = "套餐") | |||
| @Excel(name = "套餐", width = 15,replace = {"是_Y","否_N"} ) | |||
| private java.lang.String isMeal; | |||
| /**推荐*/ | |||
| @Schema(description = "推荐") | |||
| @Excel(name = "推荐", width = 15,replace = {"是_Y","否_N"} ) | |||
| private java.lang.String homeRecommend; | |||
| /**检测类型*/ | |||
| @Excel(name = "检测类型", width = 15, dicCode = "applett_subscribe_type") | |||
| @Dict(dicCode = "applett_subscribe_type") | |||
| @Schema(description = "检测类型") | |||
| private java.lang.String subscribeType; | |||
| /**服务*/ | |||
| @Excel(name = "服务", width = 15) | |||
| @Schema(description = "服务") | |||
| private java.lang.String service; | |||
| /**合作医院*/ | |||
| @Excel(name = "合作医院", width = 15, dictTable = "applet_hospital", dicText = "name", dicCode = "id") | |||
| @Dict(dictTable = "applet_hospital", dicText = "name", dicCode = "id") | |||
| @Schema(description = "合作医院") | |||
| private java.lang.String hospitalId; | |||
| @ExcelCollection(name="产品规格表") | |||
| @Schema(description = "产品规格表") | |||
| private List<AppletProductSpec> appletProductSpecList; | |||
| @ExcelCollection(name="产品套餐") | |||
| @Schema(description = "产品套餐") | |||
| private List<AppletProductPackage> appletProductPackageList; | |||
| } | |||
| @ -0,0 +1,26 @@ | |||
| -- 注意:该页面对应的前台目录为views/appletProduct文件夹下 | |||
| -- 如果你想更改到其他目录,请修改sql中component字段对应的值 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) | |||
| VALUES ('2025102203261870150', NULL, '产品表', '/appletProduct/appletProductList', 'appletProduct/AppletProductList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-10-22 15:26:15', NULL, NULL, 0); | |||
| -- 权限控制sql | |||
| -- 新增 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102203261870151', '2025102203261870150', '添加产品表', NULL, NULL, 0, NULL, NULL, 2, 'appletProduct:applet_product:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 15:26:15', NULL, NULL, 0, 0, '1', 0); | |||
| -- 编辑 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102203261870152', '2025102203261870150', '编辑产品表', NULL, NULL, 0, NULL, NULL, 2, 'appletProduct:applet_product:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 15:26:15', NULL, NULL, 0, 0, '1', 0); | |||
| -- 删除 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102203261870153', '2025102203261870150', '删除产品表', NULL, NULL, 0, NULL, NULL, 2, 'appletProduct:applet_product:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 15:26:15', NULL, NULL, 0, 0, '1', 0); | |||
| -- 批量删除 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102203261870154', '2025102203261870150', '批量删除产品表', NULL, NULL, 0, NULL, NULL, 2, 'appletProduct:applet_product:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 15:26:15', NULL, NULL, 0, 0, '1', 0); | |||
| -- 导出excel | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102203261870155', '2025102203261870150', '导出excel_产品表', NULL, NULL, 0, NULL, NULL, 2, 'appletProduct:applet_product:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 15:26:15', NULL, NULL, 0, 0, '1', 0); | |||
| -- 导入excel | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102203261870156', '2025102203261870150', '导入excel_产品表', NULL, NULL, 0, NULL, NULL, 2, 'appletProduct:applet_product:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 15:26:15', NULL, NULL, 0, 0, '1', 0); | |||
| @ -1,70 +1,199 @@ | |||
| <template> | |||
| <div style="min-height: 400px"> | |||
| <BasicForm @register="registerForm"></BasicForm> | |||
| <div style="width: 100%;text-align: center" v-if="!formDisabled"> | |||
| <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button> | |||
| </div> | |||
| <div> | |||
| <!-- 子表单区域 --> | |||
| <a-tabs v-model:activeKey="activeKey" animated @change="handleChangeTabs"> | |||
| <!--主表区域 --> | |||
| <a-tab-pane tab="产品表" :key="refKeys[0]" :forceRender="true" :style="tabsStyle"> | |||
| <BasicForm @register="registerForm" ref="formRef"/> | |||
| </a-tab-pane> | |||
| <!--子表单区域 --> | |||
| <a-tab-pane tab="产品规格表" key="appletProductSpec" :forceRender="true" :style="tabsStyle"> | |||
| <JVxeTable | |||
| keep-source | |||
| resizable | |||
| ref="appletProductSpec" | |||
| v-if="appletProductSpecTable.show" | |||
| :loading="appletProductSpecTable.loading" | |||
| :columns="appletProductSpecTable.columns" | |||
| :dataSource="appletProductSpecTable.dataSource" | |||
| :height="340" | |||
| :disabled="formDisabled" | |||
| :rowNumber="true" | |||
| :rowSelection="true" | |||
| :toolbar="true" | |||
| /> | |||
| </a-tab-pane> | |||
| <a-tab-pane tab="产品套餐" key="appletProductPackage" :forceRender="true" :style="tabsStyle"> | |||
| <JVxeTable | |||
| keep-source | |||
| resizable | |||
| ref="appletProductPackage" | |||
| v-if="appletProductPackageTable.show" | |||
| :loading="appletProductPackageTable.loading" | |||
| :columns="appletProductPackageTable.columns" | |||
| :dataSource="appletProductPackageTable.dataSource" | |||
| :height="340" | |||
| :disabled="formDisabled" | |||
| :rowNumber="true" | |||
| :rowSelection="true" | |||
| :toolbar="true" | |||
| /> | |||
| </a-tab-pane> | |||
| </a-tabs> | |||
| <div style="width: 100%;text-align: center;margin-top: 10px;" v-if="showFlowSubmitButton"> | |||
| <a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="handleSubmit">提 交</a-button> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script lang="ts"> | |||
| <script lang="ts" setup> | |||
| import { defHttp } from '/@/utils/http/axios'; | |||
| import {ref, computed, unref,reactive, onMounted, defineProps } from 'vue'; | |||
| import {BasicForm, useForm} from '/@/components/Form/index'; | |||
| import {computed, defineComponent} from 'vue'; | |||
| import {defHttp} from '/@/utils/http/axios'; | |||
| import { propTypes } from '/@/utils/propTypes'; | |||
| import {getBpmFormSchema} from '../AppletProduct.data'; | |||
| import {saveOrUpdate} from '../AppletProduct.api'; | |||
| import { JVxeTable } from '/@/components/jeecg/JVxeTable' | |||
| import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts' | |||
| import {formSchema,appletProductSpecColumns,appletProductPackageColumns} from '../AppletProduct.data'; | |||
| import {saveOrUpdate,appletProductSpecList,appletProductPackageList} from '../AppletProduct.api'; | |||
| import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils' | |||
| const refKeys = ref(['appletProduct','appletProductSpec', 'appletProductPackage', ]); | |||
| const activeKey = ref('appletProduct'); | |||
| const appletProductSpec = ref(); | |||
| const appletProductPackage = ref(); | |||
| const tableRefs = {appletProductSpec, appletProductPackage, }; | |||
| const appletProductSpecTable = reactive({ | |||
| loading: false, | |||
| dataSource: [], | |||
| columns:appletProductSpecColumns, | |||
| show: false | |||
| }) | |||
| const appletProductPackageTable = reactive({ | |||
| loading: false, | |||
| dataSource: [], | |||
| columns:appletProductPackageColumns, | |||
| show: false | |||
| }) | |||
| const props = defineProps({ | |||
| formData: { type: Object, default: ()=>{} }, | |||
| formBpm: { type: Boolean, default: true } | |||
| }); | |||
| const formDisabled = computed(()=>{ | |||
| if(props.formBpm === true){ | |||
| if(props.formData.disabled === false){ | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| }); | |||
| // 是否显示提交按钮 | |||
| const showFlowSubmitButton = computed(()=>{ | |||
| if(props.formBpm === true){ | |||
| if(props.formData.disabled === false){ | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| }); | |||
| export default defineComponent({ | |||
| name: "AppletProductForm", | |||
| components:{ | |||
| BasicForm | |||
| }, | |||
| props:{ | |||
| formData: propTypes.object.def({}), | |||
| formBpm: propTypes.bool.def(true), | |||
| }, | |||
| setup(props){ | |||
| const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({ | |||
| labelWidth: 150, | |||
| schemas: getBpmFormSchema(props.formData), | |||
| showActionButtonGroup: false, | |||
| baseColProps: {span: 24} | |||
| }); | |||
| //表单配置 | |||
| const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({ | |||
| labelWidth: 150, | |||
| schemas: formSchema, | |||
| showActionButtonGroup: false, | |||
| baseColProps: {span: 24} | |||
| }); | |||
| const formDisabled = computed(()=>{ | |||
| if(props.formData.disabled === false){ | |||
| return false; | |||
| } | |||
| return true; | |||
| }); | |||
| onMounted(()=>{ | |||
| initFormData(); | |||
| }); | |||
| //渲染流程表单数据 | |||
| const queryByIdUrl = '/appletProduct/appletProduct/queryById'; | |||
| async function initFormData(){ | |||
| if(props.formBpm === true){ | |||
| await reset(); | |||
| let params = {id: props.formData.dataId}; | |||
| const data = await defHttp.get({url: queryByIdUrl, params}); | |||
| //表单赋值 | |||
| await setFieldsValue({ | |||
| ...data | |||
| }); | |||
| requestSubTableData(appletProductSpecList, {id: data.id}, appletProductSpecTable, ()=>{ | |||
| appletProductSpecTable.show = true; | |||
| }) | |||
| requestSubTableData(appletProductPackageList, {id: data.id}, appletProductPackageTable, ()=>{ | |||
| appletProductPackageTable.show = true; | |||
| }) | |||
| // 隐藏底部时禁用整个表单 | |||
| setProps({ disabled: formDisabled.value }) | |||
| } | |||
| } | |||
| let formData = {}; | |||
| const queryByIdUrl = '/appletProduct/appletProduct/queryById'; | |||
| async function initFormData(){ | |||
| let params = {id: props.formData.dataId}; | |||
| const data = await defHttp.get({url: queryByIdUrl, params}); | |||
| formData = {...data} | |||
| //设置表单的值 | |||
| await setFieldsValue(formData); | |||
| //默认是禁用 | |||
| await setProps({disabled: formDisabled.value}) | |||
| } | |||
| //方法配置 | |||
| const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys); | |||
| // 弹窗tabs滚动区域的高度 | |||
| const tabsStyle = computed(() => { | |||
| let height: Nullable<string> = null | |||
| let minHeight = '100px' | |||
| // 弹窗wrapper | |||
| let overflow = 'auto'; | |||
| return {height, minHeight, overflow}; | |||
| }) | |||
| async function submitForm() { | |||
| let data = getFieldsValue(); | |||
| let params = Object.assign({}, formData, data); | |||
| console.log('表单数据', params) | |||
| await saveOrUpdate(params, true) | |||
| } | |||
| async function reset(){ | |||
| await resetFields(); | |||
| activeKey.value = 'appletProduct'; | |||
| appletProductSpecTable.dataSource = []; | |||
| appletProductPackageTable.dataSource = []; | |||
| } | |||
| function classifyIntoFormData(allValues) { | |||
| let main = Object.assign({}, allValues.formValue) | |||
| return { | |||
| ...main, // 展开 | |||
| appletProductSpecList: allValues.tablesValue[0].tableData, | |||
| appletProductPackageList: allValues.tablesValue[1].tableData, | |||
| } | |||
| } | |||
| //表单提交事件 | |||
| async function requestAddOrEdit(values) { | |||
| //提交表单 | |||
| await saveOrUpdate(values, true); | |||
| } | |||
| </script> | |||
| initFormData(); | |||
| return { | |||
| registerForm, | |||
| formDisabled, | |||
| submitForm, | |||
| } | |||
| } | |||
| }); | |||
| </script> | |||
| <style lang="less" scoped> | |||
| /** 时间和数字输入框样式 */ | |||
| :deep(.ant-input-number) { | |||
| width: 100%; | |||
| } | |||
| :deep(.ant-calendar-picker) { | |||
| width: 100%; | |||
| } | |||
| </style> | |||
| <style lang="less"> | |||
| // Online表单Tab风格专属样式 | |||
| .j-cgform-tab-modal { | |||
| .ant-modal-header { | |||
| padding-top: 8px; | |||
| padding-bottom: 8px; | |||
| border-bottom: none !important; | |||
| } | |||
| .ant-modal .ant-modal-body > .scrollbar, | |||
| .ant-tabs-nav .ant-tabs-tab { | |||
| padding-top: 0; | |||
| } | |||
| .ant-tabs-top-bar { | |||
| width: calc(100% - 55px); | |||
| position: relative; | |||
| left: -14px; | |||
| } | |||
| .ant-tabs .ant-tabs-top-content > .ant-tabs-tabpane { | |||
| overflow: hidden auto; | |||
| } | |||
| } | |||
| </style> | |||
| @ -0,0 +1,341 @@ | |||
| package org.jeecg.modules.demo.appletproductspectype.controller; | |||
| import java.util.Arrays; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.ArrayList; | |||
| import java.util.stream.Collectors; | |||
| import java.io.IOException; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URLDecoder; | |||
| import javax.servlet.http.HttpServletRequest; | |||
| import javax.servlet.http.HttpServletResponse; | |||
| import org.jeecg.common.api.vo.Result; | |||
| import org.jeecg.common.system.query.QueryGenerator; | |||
| import org.jeecg.common.system.query.QueryRuleEnum; | |||
| import org.jeecg.common.util.oConvertUtils; | |||
| import org.jeecg.modules.demo.appletproductspectype.entity.Appletproductspectype; | |||
| import org.jeecg.modules.demo.appletproductspectype.service.IAppletproductspectypeService; | |||
| import org.jeecg.modules.demo.appletProduct.entity.AppletProductSpec; | |||
| import org.jeecg.modules.demo.appletProduct.service.IAppletProductSpecService; | |||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |||
| import com.baomidou.mybatisplus.core.metadata.IPage; | |||
| import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||
| import lombok.extern.slf4j.Slf4j; | |||
| import org.jeecgframework.poi.excel.ExcelImportUtil; | |||
| import org.jeecgframework.poi.excel.def.NormalExcelConstants; | |||
| import org.jeecgframework.poi.excel.entity.ExportParams; | |||
| import org.jeecgframework.poi.excel.entity.ImportParams; | |||
| import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; | |||
| import org.jeecg.common.system.base.controller.JeecgController; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.*; | |||
| import org.springframework.web.multipart.MultipartFile; | |||
| import org.springframework.web.multipart.MultipartHttpServletRequest; | |||
| import org.springframework.web.servlet.ModelAndView; | |||
| import com.alibaba.fastjson.JSON; | |||
| import io.swagger.v3.oas.annotations.tags.Tag; | |||
| import io.swagger.v3.oas.annotations.Operation; | |||
| import org.jeecg.common.aspect.annotation.AutoLog; | |||
| import org.apache.shiro.authz.annotation.RequiresPermissions; | |||
| /** | |||
| * @Description: 小程序产品规格类型 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Tag(name="小程序产品规格类型") | |||
| @RestController | |||
| @RequestMapping("/appletproductspectype/appletproductspectype") | |||
| @Slf4j | |||
| public class AppletproductspectypeController extends JeecgController<Appletproductspectype, IAppletproductspectypeService> { | |||
| @Autowired | |||
| private IAppletproductspectypeService appletproductspectypeService; | |||
| @Autowired | |||
| private IAppletProductSpecService appletProductSpecService; | |||
| /** | |||
| * 分页列表查询 | |||
| * | |||
| * @param appletproductspectype | |||
| * @param pageNo | |||
| * @param pageSize | |||
| * @param req | |||
| * @return | |||
| */ | |||
| //@AutoLog(value = "小程序产品规格类型-分页列表查询") | |||
| @Operation(summary="小程序产品规格类型-分页列表查询") | |||
| @GetMapping(value = "/list") | |||
| public Result<IPage<Appletproductspectype>> queryPageList(Appletproductspectype appletproductspectype, | |||
| @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, | |||
| @RequestParam(name="pageSize", defaultValue="10") Integer pageSize, | |||
| HttpServletRequest req) { | |||
| QueryWrapper<Appletproductspectype> queryWrapper = QueryGenerator.initQueryWrapper(appletproductspectype, req.getParameterMap()); | |||
| Page<Appletproductspectype> page = new Page<Appletproductspectype>(pageNo, pageSize); | |||
| IPage<Appletproductspectype> pageList = appletproductspectypeService.page(page, queryWrapper); | |||
| return Result.OK(pageList); | |||
| } | |||
| /** | |||
| * 添加 | |||
| * | |||
| * @param appletproductspectype | |||
| * @return | |||
| */ | |||
| @AutoLog(value = "小程序产品规格类型-添加") | |||
| @Operation(summary="小程序产品规格类型-添加") | |||
| @RequiresPermissions("appletproductspectype:appletproductspectype:add") | |||
| @PostMapping(value = "/add") | |||
| public Result<String> add(@RequestBody Appletproductspectype appletproductspectype) { | |||
| appletproductspectypeService.save(appletproductspectype); | |||
| return Result.OK("添加成功!"); | |||
| } | |||
| /** | |||
| * 编辑 | |||
| * | |||
| * @param appletproductspectype | |||
| * @return | |||
| */ | |||
| @AutoLog(value = "小程序产品规格类型-编辑") | |||
| @Operation(summary="小程序产品规格类型-编辑") | |||
| @RequiresPermissions("appletproductspectype:appletproductspectype:edit") | |||
| @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) | |||
| public Result<String> edit(@RequestBody Appletproductspectype appletproductspectype) { | |||
| appletproductspectypeService.updateById(appletproductspectype); | |||
| return Result.OK("编辑成功!"); | |||
| } | |||
| /** | |||
| * 通过id删除 | |||
| * | |||
| * @param id | |||
| * @return | |||
| */ | |||
| @AutoLog(value = "小程序产品规格类型-通过id删除") | |||
| @Operation(summary="小程序产品规格类型-通过id删除") | |||
| @RequiresPermissions("appletproductspectype:appletproductspectype:delete") | |||
| @DeleteMapping(value = "/delete") | |||
| public Result<String> delete(@RequestParam(name="id",required=true) String id) { | |||
| appletproductspectypeService.removeById(id); | |||
| return Result.OK("删除成功!"); | |||
| } | |||
| /** | |||
| * 批量删除 | |||
| * | |||
| * @param ids | |||
| * @return | |||
| */ | |||
| @AutoLog(value = "小程序产品规格类型-批量删除") | |||
| @Operation(summary="小程序产品规格类型-批量删除") | |||
| @RequiresPermissions("appletproductspectype:appletproductspectype:deleteBatch") | |||
| @DeleteMapping(value = "/deleteBatch") | |||
| public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) { | |||
| this.appletproductspectypeService.removeByIds(Arrays.asList(ids.split(","))); | |||
| return Result.OK("批量删除成功!"); | |||
| } | |||
| /** | |||
| * 通过id查询 | |||
| * | |||
| * @param id | |||
| * @return | |||
| */ | |||
| //@AutoLog(value = "小程序产品规格类型-通过id查询") | |||
| @Operation(summary="小程序产品规格类型-通过id查询") | |||
| @GetMapping(value = "/queryById") | |||
| public Result<Appletproductspectype> queryById(@RequestParam(name="id",required=true) String id) { | |||
| Appletproductspectype appletproductspectype = appletproductspectypeService.getById(id); | |||
| if(appletproductspectype==null) { | |||
| return Result.error("未找到对应数据"); | |||
| } | |||
| return Result.OK(appletproductspectype); | |||
| } | |||
| /** | |||
| * 根据产品ID查询规格类型和规格项 | |||
| * | |||
| * @param productId | |||
| * @return | |||
| */ | |||
| @Operation(summary="根据产品ID查询规格类型和规格项") | |||
| @GetMapping(value = "/queryByProductId") | |||
| public Result<Map<String, Object>> queryByProductId(@RequestParam(name="productId",required=true) String productId) { | |||
| try { | |||
| QueryWrapper<Appletproductspectype> queryWrapper = new QueryWrapper<>(); | |||
| queryWrapper.eq("product_id", productId); | |||
| queryWrapper.orderByAsc("type", "create_time"); | |||
| List<Appletproductspectype> allSpecs = appletproductspectypeService.list(queryWrapper); | |||
| // 分离规格类型和规格项 | |||
| List<Appletproductspectype> specTypes = allSpecs.stream() | |||
| .filter(spec -> "0".equals(spec.getType())) | |||
| .collect(Collectors.toList()); | |||
| List<Appletproductspectype> specItems = allSpecs.stream() | |||
| .filter(spec -> "1".equals(spec.getType())) | |||
| .collect(Collectors.toList()); | |||
| Map<String, Object> result = new HashMap<>(); | |||
| result.put("specTypes", specTypes); | |||
| result.put("specItems", specItems); | |||
| return Result.OK(result); | |||
| } catch (Exception e) { | |||
| log.error("根据产品ID查询规格类型和规格项失败", e); | |||
| return Result.error("查询失败:" + e.getMessage()); | |||
| } | |||
| } | |||
| /** | |||
| * 生成规格实体 | |||
| * | |||
| * @param productId | |||
| * @return | |||
| */ | |||
| @Operation(summary="生成规格实体") | |||
| @PostMapping(value = "/generateSpecs") | |||
| public Result<String> generateSpecs(@RequestParam(name="productId",required=true) String productId) { | |||
| try { | |||
| // 查询该产品的所有规格类型和规格项 | |||
| QueryWrapper<Appletproductspectype> queryWrapper = new QueryWrapper<>(); | |||
| queryWrapper.eq("product_id", productId); | |||
| queryWrapper.orderByAsc("type", "create_time"); | |||
| List<Appletproductspectype> allSpecs = appletproductspectypeService.list(queryWrapper); | |||
| // 分离规格类型和规格项 | |||
| List<Appletproductspectype> specTypes = allSpecs.stream() | |||
| .filter(spec -> "0".equals(spec.getType())) | |||
| .collect(Collectors.toList()); | |||
| List<Appletproductspectype> specItems = allSpecs.stream() | |||
| .filter(spec -> "1".equals(spec.getType())) | |||
| .collect(Collectors.toList()); | |||
| if (specTypes.isEmpty() || specItems.isEmpty()) { | |||
| return Result.error("请先添加规格类型和规格项"); | |||
| } | |||
| // 按规格类型分组规格项 | |||
| Map<String, List<Appletproductspectype>> specItemsByType = specItems.stream() | |||
| .collect(Collectors.groupingBy(Appletproductspectype::getPid)); | |||
| // 生成规格组合 | |||
| List<List<Appletproductspectype>> combinations = generateCombinations(specTypes, specItemsByType); | |||
| // 创建规格实体 | |||
| int generatedCount = 0; | |||
| for (List<Appletproductspectype> combination : combinations) { | |||
| // 生成规格名称 | |||
| String specName = combination.stream() | |||
| .map(Appletproductspectype::getTitle) | |||
| .collect(Collectors.joining(",")); | |||
| // 检查规格名称是否已存在 | |||
| if (appletProductSpecService.checkSpecNameExists(productId, specName, null)) { | |||
| log.warn("规格名称已存在,跳过创建: {}", specName); | |||
| continue; | |||
| } | |||
| // 创建新的规格实体 | |||
| AppletProductSpec spec = new AppletProductSpec(); | |||
| spec.setProductId(productId); | |||
| spec.setSpecName(specName); | |||
| spec.setContent(specName); | |||
| spec.setPrice(new java.math.BigDecimal("0.00")); | |||
| spec.setInventory(0); | |||
| spec.setImage(""); | |||
| spec.setInfo(""); | |||
| spec.setPutaway("N"); | |||
| spec.setSortOrder(generatedCount + 1); | |||
| // 保存规格实体 | |||
| appletProductSpecService.save(spec); | |||
| generatedCount++; | |||
| } | |||
| return Result.OK("成功生成 " + generatedCount + " 个规格组合,请在产品规格管理中完善价格、库存等信息"); | |||
| } catch (Exception e) { | |||
| log.error("生成规格实体失败", e); | |||
| return Result.error("生成失败:" + e.getMessage()); | |||
| } | |||
| } | |||
| /** | |||
| * 生成规格组合 | |||
| */ | |||
| private List<List<Appletproductspectype>> generateCombinations( | |||
| List<Appletproductspectype> specTypes, | |||
| Map<String, List<Appletproductspectype>> specItemsByType) { | |||
| List<List<Appletproductspectype>> result = new ArrayList<>(); | |||
| List<Appletproductspectype> currentCombination = new ArrayList<>(); | |||
| generateCombinationsRecursive(specTypes, specItemsByType, 0, currentCombination, result); | |||
| return result; | |||
| } | |||
| /** | |||
| * 递归生成规格组合 | |||
| */ | |||
| private void generateCombinationsRecursive( | |||
| List<Appletproductspectype> specTypes, | |||
| Map<String, List<Appletproductspectype>> specItemsByType, | |||
| int typeIndex, | |||
| List<Appletproductspectype> currentCombination, | |||
| List<List<Appletproductspectype>> result) { | |||
| if (typeIndex >= specTypes.size()) { | |||
| result.add(new ArrayList<>(currentCombination)); | |||
| return; | |||
| } | |||
| Appletproductspectype currentType = specTypes.get(typeIndex); | |||
| List<Appletproductspectype> items = specItemsByType.get(currentType.getId()); | |||
| if (items != null && !items.isEmpty()) { | |||
| for (Appletproductspectype item : items) { | |||
| currentCombination.add(item); | |||
| generateCombinationsRecursive(specTypes, specItemsByType, typeIndex + 1, currentCombination, result); | |||
| currentCombination.remove(currentCombination.size() - 1); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * 导出excel | |||
| * | |||
| * @param request | |||
| * @param appletproductspectype | |||
| */ | |||
| @RequiresPermissions("appletproductspectype:appletproductspectype:exportXls") | |||
| @RequestMapping(value = "/exportXls") | |||
| public ModelAndView exportXls(HttpServletRequest request, Appletproductspectype appletproductspectype) { | |||
| return super.exportXls(request, appletproductspectype, Appletproductspectype.class, "小程序产品规格类型"); | |||
| } | |||
| /** | |||
| * 通过excel导入数据 | |||
| * | |||
| * @param request | |||
| * @param response | |||
| * @return | |||
| */ | |||
| @RequiresPermissions("appletproductspectype:appletproductspectype:importExcel") | |||
| @RequestMapping(value = "/importExcel", method = RequestMethod.POST) | |||
| public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) { | |||
| return super.importExcel(request, response, Appletproductspectype.class); | |||
| } | |||
| } | |||
| @ -0,0 +1,75 @@ | |||
| package org.jeecg.modules.demo.appletproductspectype.entity; | |||
| import java.io.Serializable; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.util.Date; | |||
| import java.math.BigDecimal; | |||
| import com.baomidou.mybatisplus.annotation.IdType; | |||
| import com.baomidou.mybatisplus.annotation.TableId; | |||
| import com.baomidou.mybatisplus.annotation.TableName; | |||
| import com.baomidou.mybatisplus.annotation.TableLogic; | |||
| import org.jeecg.common.constant.ProvinceCityArea; | |||
| import org.jeecg.common.util.SpringContextUtils; | |||
| import lombok.Data; | |||
| import com.fasterxml.jackson.annotation.JsonFormat; | |||
| import org.springframework.format.annotation.DateTimeFormat; | |||
| import org.jeecgframework.poi.excel.annotation.Excel; | |||
| import org.jeecg.common.aspect.annotation.Dict; | |||
| import io.swagger.v3.oas.annotations.media.Schema; | |||
| import lombok.EqualsAndHashCode; | |||
| import lombok.experimental.Accessors; | |||
| /** | |||
| * @Description: 小程序产品规格类型 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Data | |||
| @TableName("appletproductspectype") | |||
| @Accessors(chain = true) | |||
| @EqualsAndHashCode(callSuper = false) | |||
| @Schema(description="小程序产品规格类型") | |||
| public class Appletproductspectype implements Serializable { | |||
| private static final long serialVersionUID = 1L; | |||
| /**主键*/ | |||
| @TableId(type = IdType.ASSIGN_ID) | |||
| @Schema(description = "主键") | |||
| private java.lang.String id; | |||
| /**创建人*/ | |||
| @Schema(description = "创建人") | |||
| private java.lang.String createBy; | |||
| /**创建日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "创建日期") | |||
| private java.util.Date createTime; | |||
| /**更新人*/ | |||
| @Schema(description = "更新人") | |||
| private java.lang.String updateBy; | |||
| /**更新日期*/ | |||
| @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") | |||
| @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") | |||
| @Schema(description = "更新日期") | |||
| private java.util.Date updateTime; | |||
| /**所属部门*/ | |||
| @Schema(description = "所属部门") | |||
| private java.lang.String sysOrgCode; | |||
| /**名称*/ | |||
| @Excel(name = "名称", width = 15) | |||
| @Schema(description = "名称") | |||
| private java.lang.String title; | |||
| /**父ID*/ | |||
| @Excel(name = "父ID", width = 15) | |||
| @Schema(description = "父ID") | |||
| private java.lang.String pid; | |||
| /**类型(0类型,1规格项)*/ | |||
| @Excel(name = "类型(0类型,1规格项)", width = 15) | |||
| @Schema(description = "类型(0类型,1规格项)") | |||
| private java.lang.String type; | |||
| /**父ID*/ | |||
| @Excel(name = "产品", width = 15) | |||
| @Schema(description = "产品") | |||
| private java.lang.String productId; | |||
| } | |||
| @ -0,0 +1,17 @@ | |||
| package org.jeecg.modules.demo.appletproductspectype.mapper; | |||
| import java.util.List; | |||
| import org.apache.ibatis.annotations.Param; | |||
| import org.jeecg.modules.demo.appletproductspectype.entity.Appletproductspectype; | |||
| import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
| /** | |||
| * @Description: 小程序产品规格类型 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| public interface AppletproductspectypeMapper extends BaseMapper<Appletproductspectype> { | |||
| } | |||
| @ -0,0 +1,5 @@ | |||
| <?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="org.jeecg.modules.demo.appletproductspectype.mapper.AppletproductspectypeMapper"> | |||
| </mapper> | |||
| @ -0,0 +1,14 @@ | |||
| package org.jeecg.modules.demo.appletproductspectype.service; | |||
| import org.jeecg.modules.demo.appletproductspectype.entity.Appletproductspectype; | |||
| import com.baomidou.mybatisplus.extension.service.IService; | |||
| /** | |||
| * @Description: 小程序产品规格类型 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| public interface IAppletproductspectypeService extends IService<Appletproductspectype> { | |||
| } | |||
| @ -0,0 +1,19 @@ | |||
| package org.jeecg.modules.demo.appletproductspectype.service.impl; | |||
| import org.jeecg.modules.demo.appletproductspectype.entity.Appletproductspectype; | |||
| import org.jeecg.modules.demo.appletproductspectype.mapper.AppletproductspectypeMapper; | |||
| import org.jeecg.modules.demo.appletproductspectype.service.IAppletproductspectypeService; | |||
| import org.springframework.stereotype.Service; | |||
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
| /** | |||
| * @Description: 小程序产品规格类型 | |||
| * @Author: jeecg-boot | |||
| * @Date: 2025-10-22 | |||
| * @Version: V1.0 | |||
| */ | |||
| @Service | |||
| public class AppletproductspectypeServiceImpl extends ServiceImpl<AppletproductspectypeMapper, Appletproductspectype> implements IAppletproductspectypeService { | |||
| } | |||
| @ -0,0 +1,95 @@ | |||
| <template> | |||
| <view> | |||
| <!--标题和返回--> | |||
| <cu-custom :bgColor="NavBarColor" isBack :backRouterName="backRouteName"> | |||
| <block slot="backText">返回</block> | |||
| <block slot="content">小程序产品规格类型</block> | |||
| </cu-custom> | |||
| <!--表单区域--> | |||
| <view> | |||
| <form> | |||
| <view class="cu-form-group"> | |||
| <view class="flex align-center"> | |||
| <view class="title"><text space="ensp">名称:</text></view> | |||
| <input placeholder="请输入名称" v-model="model.title"/> | |||
| </view> | |||
| </view> | |||
| <view class="cu-form-group"> | |||
| <view class="flex align-center"> | |||
| <view class="title"><text space="ensp">父ID:</text></view> | |||
| <input placeholder="请输入父ID" v-model="model.pid"/> | |||
| </view> | |||
| </view> | |||
| <view class="cu-form-group"> | |||
| <view class="flex align-center"> | |||
| <view class="title"><text space="ensp">类型(0类型,1规格项):</text></view> | |||
| <input placeholder="请输入类型(0类型,1规格项)" v-model="model.type"/> | |||
| </view> | |||
| </view> | |||
| <view class="padding"> | |||
| <button class="cu-btn block bg-blue margin-tb-sm lg" @click="onSubmit"> | |||
| <text v-if="loading" class="cuIcon-loading2 cuIconfont-spin"></text>提交 | |||
| </button> | |||
| </view> | |||
| </form> | |||
| </view> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import myDate from '@/components/my-componets/my-date.vue' | |||
| export default { | |||
| name: "AppletproductspectypeForm", | |||
| components:{ myDate }, | |||
| props:{ | |||
| formData:{ | |||
| type:Object, | |||
| default:()=>{}, | |||
| required:false | |||
| } | |||
| }, | |||
| data(){ | |||
| return { | |||
| CustomBar: this.CustomBar, | |||
| NavBarColor: this.NavBarColor, | |||
| loading:false, | |||
| model: {}, | |||
| backRouteName:'index', | |||
| url: { | |||
| queryById: "/appletproductspectype/appletproductspectype/queryById", | |||
| add: "/appletproductspectype/appletproductspectype/add", | |||
| edit: "/appletproductspectype/appletproductspectype/edit", | |||
| }, | |||
| } | |||
| }, | |||
| created(){ | |||
| this.initFormData(); | |||
| }, | |||
| methods:{ | |||
| initFormData(){ | |||
| if(this.formData){ | |||
| let dataId = this.formData.dataId; | |||
| this.$http.get(this.url.queryById,{params:{id:dataId}}).then((res)=>{ | |||
| if(res.data.success){ | |||
| console.log("表单数据",res); | |||
| this.model = res.data.result; | |||
| } | |||
| }) | |||
| } | |||
| }, | |||
| onSubmit() { | |||
| let myForm = {...this.model}; | |||
| this.loading = true; | |||
| let url = myForm.id?this.url.edit:this.url.add; | |||
| this.$http.post(url,myForm).then(res=>{ | |||
| console.log("res",res) | |||
| this.loading = false | |||
| this.$Router.push({name:this.backRouteName}) | |||
| }).catch(()=>{ | |||
| this.loading = false | |||
| }); | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| @ -0,0 +1,44 @@ | |||
| <template> | |||
| <view> | |||
| <!--标题和返回--> | |||
| <cu-custom :bgColor="NavBarColor" isBack> | |||
| <block slot="backText">返回</block> | |||
| <block slot="content">小程序产品规格类型</block> | |||
| </cu-custom> | |||
| <!--滚动加载列表--> | |||
| <mescroll-body ref="mescrollRef" bottom="88" @init="mescrollInit" :up="upOption" :down="downOption" @down="downCallback" @up="upCallback"> | |||
| <view class="cu-list menu"> | |||
| <view class="cu-item" v-for="(item,index) in list" :key="index" @click="goHome"> | |||
| <view class="flex" style="width:100%"> | |||
| <text class="text-lg" style="color: #000;"> | |||
| {{ item.createBy}} | |||
| </text> | |||
| </view> | |||
| </view> | |||
| </view> | |||
| </mescroll-body> | |||
| </view> | |||
| </template> | |||
| <script> | |||
| import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js"; | |||
| import Mixin from "@/common/mixin/Mixin.js"; | |||
| export default { | |||
| name: '小程序产品规格类型', | |||
| mixins: [MescrollMixin,Mixin], | |||
| data() { | |||
| return { | |||
| CustomBar:this.CustomBar, | |||
| NavBarColor:this.NavBarColor, | |||
| url: "/appletproductspectype/appletproductspectype/list", | |||
| }; | |||
| }, | |||
| methods: { | |||
| goHome(){ | |||
| this.$Router.push({name: "index"}) | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| @ -0,0 +1,19 @@ | |||
| import { render } from '@/common/renderUtils'; | |||
| //列表数据 | |||
| export const columns = [ | |||
| { | |||
| title: '名称', | |||
| align:"center", | |||
| dataIndex: 'title' | |||
| }, | |||
| { | |||
| title: '父ID', | |||
| align:"center", | |||
| dataIndex: 'pid' | |||
| }, | |||
| { | |||
| title: '类型(0类型,1规格项)', | |||
| align:"center", | |||
| dataIndex: 'type' | |||
| }, | |||
| ]; | |||
| @ -0,0 +1,235 @@ | |||
| <route lang="json5" type="page"> | |||
| { | |||
| layout: 'default', | |||
| style: { | |||
| navigationStyle: 'custom', | |||
| navigationBarTitleText: '小程序产品规格类型', | |||
| }, | |||
| } | |||
| </route> | |||
| <template> | |||
| <PageLayout :navTitle="navTitle" :backRouteName="backRouteName"> | |||
| <scroll-view class="scrollArea" scroll-y> | |||
| <view class="form-container"> | |||
| <wd-form ref="form" :model="myFormData"> | |||
| <wd-cell-group border> | |||
| <view class="{ 'mt-14px': 0 == 0 }"> | |||
| <wd-input | |||
| label-width="100px" | |||
| v-model="myFormData['title']" | |||
| :label="get4Label('名称')" | |||
| name='title' | |||
| prop='title' | |||
| placeholder="请选择名称" | |||
| :rules="[ | |||
| ]" | |||
| clearable | |||
| /> | |||
| </view> | |||
| <view class="{ 'mt-14px': 1 == 0 }"> | |||
| <wd-input | |||
| label-width="100px" | |||
| v-model="myFormData['pid']" | |||
| :label="get4Label('父ID')" | |||
| name='pid' | |||
| prop='pid' | |||
| placeholder="请选择父ID" | |||
| :rules="[ | |||
| ]" | |||
| clearable | |||
| /> | |||
| </view> | |||
| <view class="{ 'mt-14px': 0 == 0 }"> | |||
| <wd-input | |||
| label-width="100px" | |||
| v-model="myFormData['type']" | |||
| :label="get4Label('类型(0类型,1规格项)')" | |||
| name='type' | |||
| prop='type' | |||
| placeholder="请选择类型(0类型,1规格项)" | |||
| :rules="[ | |||
| ]" | |||
| clearable | |||
| /> | |||
| </view> | |||
| </wd-cell-group> | |||
| </wd-form> | |||
| </view> | |||
| </scroll-view> | |||
| <view class="footer"> | |||
| <wd-button :disabled="loading" block :loading="loading" @click="handleSubmit">提交</wd-button> | |||
| </view> | |||
| </PageLayout> | |||
| </template> | |||
| <script lang="ts" setup> | |||
| import { onLoad } from '@dcloudio/uni-app' | |||
| import { http } from '@/utils/http' | |||
| import { useToast } from 'wot-design-uni' | |||
| import { useRouter } from '@/plugin/uni-mini-router' | |||
| import { ref, onMounted, computed,reactive } from 'vue' | |||
| import OnlineImage from '@/components/online/view/online-image.vue' | |||
| import OnlineFile from '@/components/online/view/online-file.vue' | |||
| import OnlineFileCustom from '@/components/online/view/online-file-custom.vue' | |||
| import OnlineSelect from '@/components/online/view/online-select.vue' | |||
| import OnlineTime from '@/components/online/view/online-time.vue' | |||
| import OnlineDate from '@/components/online/view/online-date.vue' | |||
| import OnlineRadio from '@/components/online/view/online-radio.vue' | |||
| import OnlineCheckbox from '@/components/online/view/online-checkbox.vue' | |||
| import OnlineMulti from '@/components/online/view/online-multi.vue' | |||
| import OnlinePopupLinkRecord from '@/components/online/view/online-popup-link-record.vue' | |||
| import OnlinePca from '@/components/online/view/online-pca.vue' | |||
| import SelectDept from '@/components/SelectDept/SelectDept.vue' | |||
| import SelectUser from '@/components/SelectUser/SelectUser.vue' | |||
| import {duplicateCheck} from "@/service/api"; | |||
| defineOptions({ | |||
| name: 'AppletproductspectypeForm', | |||
| options: { | |||
| styleIsolation: 'shared', | |||
| }, | |||
| }) | |||
| const toast = useToast() | |||
| const router = useRouter() | |||
| const form = ref(null) | |||
| // 定义响应式数据 | |||
| const myFormData = reactive({}) | |||
| const loading = ref(false) | |||
| const navTitle = ref('新增') | |||
| const dataId = ref('') | |||
| const backRouteName = ref('AppletproductspectypeList') | |||
| // 定义 initForm 方法 | |||
| const initForm = (item) => { | |||
| console.log('initForm item', item) | |||
| if(item?.dataId){ | |||
| dataId.value = item.dataId; | |||
| navTitle.value = item.dataId?'编辑':'新增'; | |||
| initData(); | |||
| } | |||
| } | |||
| // 初始化数据 | |||
| const initData = () => { | |||
| http.get("/appletproductspectype/appletproductspectype/queryById",{id:dataId.value}).then((res) => { | |||
| if (res.success) { | |||
| let obj = res.result | |||
| Object.assign(myFormData, { ...obj }) | |||
| }else{ | |||
| toast.error(res?.message || '表单加载失败!') | |||
| } | |||
| }) | |||
| } | |||
| const handleSuccess = () => { | |||
| uni.$emit('refreshList'); | |||
| router.back() | |||
| } | |||
| /** | |||
| * 校验唯一 | |||
| * @param values | |||
| * @returns {boolean} | |||
| */ | |||
| async function fieldCheck(values: any) { | |||
| const onlyField = [ | |||
| ]; | |||
| for (const field of onlyField) { | |||
| if (values[field]) { | |||
| // 仅校验有值的字段 | |||
| const res: any = await duplicateCheck({ | |||
| tableName: 'appletproductspectype', | |||
| fieldName: field, // 使用处理后的字段名 | |||
| fieldVal: values[field], | |||
| dataId: values.id, | |||
| }); | |||
| if (!res.success) { | |||
| toast.warning(res.message); | |||
| return true; // 校验失败 | |||
| } | |||
| } | |||
| } | |||
| return false; // 校验通过 | |||
| } | |||
| // 提交表单 | |||
| const handleSubmit = async () => { | |||
| // 判断字段必填和正则 | |||
| if (await fieldCheck(myFormData)) { | |||
| return | |||
| } | |||
| let url = dataId.value?'/appletproductspectype/appletproductspectype/edit':'/appletproductspectype/appletproductspectype/add'; | |||
| form.value | |||
| .validate() | |||
| .then(({ valid, errors }) => { | |||
| if (valid) { | |||
| loading.value = true; | |||
| http.post(url,myFormData).then((res) => { | |||
| loading.value = false; | |||
| if (res.success) { | |||
| toast.success('保存成功'); | |||
| handleSuccess() | |||
| }else{ | |||
| toast.error(res?.message || '表单保存失败!') | |||
| } | |||
| }) | |||
| } | |||
| }) | |||
| .catch((error) => { | |||
| console.log(error, 'error') | |||
| loading.value = false; | |||
| }) | |||
| } | |||
| // 标题 | |||
| const get4Label = computed(() => { | |||
| return (label) => { | |||
| return label && label.length > 4 ? label.substring(0, 4) : label; | |||
| } | |||
| }) | |||
| // 标题 | |||
| const getFormSchema = computed(() => { | |||
| return (dictTable,dictCode,dictText) => { | |||
| return { | |||
| dictCode, | |||
| dictTable, | |||
| dictText | |||
| }; | |||
| } | |||
| }) | |||
| /** | |||
| * 获取日期控件的扩展类型 | |||
| * @param picker | |||
| * @returns {string} | |||
| */ | |||
| const getDateExtendType = (picker: string) => { | |||
| let mapField = { | |||
| month: 'year-month', | |||
| year: 'year', | |||
| quarter: 'quarter', | |||
| week: 'week', | |||
| day: 'date', | |||
| } | |||
| return picker && mapField[picker] | |||
| ? mapField[picker] | |||
| : 'date' | |||
| } | |||
| //设置pop返回值 | |||
| const setFieldsValue = (data) => { | |||
| Object.assign(myFormData, {...data }) | |||
| } | |||
| // onLoad 生命周期钩子 | |||
| onLoad((option) => { | |||
| initForm(option) | |||
| }) | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .footer { | |||
| width: 100%; | |||
| padding: 10px 20px; | |||
| padding-bottom: calc(constant(safe-area-inset-bottom) + 10px); | |||
| padding-bottom: calc(env(safe-area-inset-bottom) + 10px); | |||
| } | |||
| :deep(.wd-cell__label) { | |||
| font-size: 14px; | |||
| color: #444; | |||
| } | |||
| :deep(.wd-cell__value) { | |||
| text-align: left; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,148 @@ | |||
| <route lang="json5" type="page"> | |||
| { | |||
| layout: 'default', | |||
| style: { | |||
| navigationBarTitleText: '小程序产品规格类型', | |||
| navigationStyle: 'custom', | |||
| }, | |||
| } | |||
| </route> | |||
| <template> | |||
| <PageLayout navTitle="小程序产品规格类型" backRouteName="index" routeMethod="pushTab"> | |||
| <view class="wrap"> | |||
| <z-paging | |||
| ref="paging" | |||
| :fixed="false" | |||
| v-model="dataList" | |||
| @query="queryList" | |||
| :default-page-size="15" | |||
| > | |||
| <template v-for="item in dataList" :key="item.id"> | |||
| <wd-swipe-action> | |||
| <view class="list" @click="handleEdit(item)"> | |||
| <template v-for="(cItem, cIndex) in columns" :key="cIndex"> | |||
| <view v-if="cIndex < 3" class="box" :style="getBoxStyle"> | |||
| <view class="field ellipsis">{{ cItem.title }}</view> | |||
| <view class="value cu-text-grey">{{ item[cItem.dataIndex] }}</view> | |||
| </view> | |||
| </template> | |||
| </view> | |||
| <template #right> | |||
| <view class="action"> | |||
| <view class="button" @click="handleAction('del', item)">删除</view> | |||
| </view> | |||
| </template> | |||
| </wd-swipe-action> | |||
| </template> | |||
| </z-paging> | |||
| <view class="add u-iconfont u-icon-add" @click="handleAdd"></view> | |||
| </view> | |||
| </PageLayout> | |||
| </template> | |||
| <script setup lang="ts"> | |||
| import { ref, onMounted, computed } from 'vue' | |||
| import { http } from '@/utils/http' | |||
| import usePageList from '@/hooks/usePageList' | |||
| import {columns} from './AppletproductspectypeData'; | |||
| defineOptions({ | |||
| name: 'AppletproductspectypeList', | |||
| options: { | |||
| styleIsolation: 'shared', | |||
| } | |||
| }) | |||
| //分页加载配置 | |||
| let { toast, router, paging, dataList, queryList } = usePageList('/appletproductspectype/appletproductspectype/list'); | |||
| //样式 | |||
| const getBoxStyle = computed(() => { | |||
| return { width: "calc(33% - 5px)" } | |||
| }) | |||
| // 其他操作 | |||
| const handleAction = (val, item) => { | |||
| if (val == 'del') { | |||
| http.delete("/appletproductspectype/appletproductspectype/delete?id="+item.id,{id:item.id}).then((res) => { | |||
| toast.success('删除成功~') | |||
| paging.value.reload() | |||
| }) | |||
| } | |||
| } | |||
| // go 新增页 | |||
| const handleAdd = () => { | |||
| router.push({ | |||
| name: 'AppletproductspectypeForm' | |||
| }) | |||
| } | |||
| //go 编辑页 | |||
| const handleEdit = (record) => { | |||
| router.push({ | |||
| name: 'AppletproductspectypeForm', | |||
| params: {dataId: record.id}, | |||
| }) | |||
| } | |||
| onMounted(() => { | |||
| // 监听刷新列表事件 | |||
| uni.$on('refreshList', () => { | |||
| queryList(1,10) | |||
| }) | |||
| }) | |||
| </script> | |||
| <style lang="scss" scoped> | |||
| .wrap { | |||
| height: 100%; | |||
| } | |||
| :deep(.wd-swipe-action) { | |||
| margin-top: 10px; | |||
| background-color: #fff; | |||
| } | |||
| .list { | |||
| padding: 10px 10px; | |||
| width: 100%; | |||
| text-align: left; | |||
| display: flex; | |||
| justify-content: space-between; | |||
| .box { | |||
| width: 33%; | |||
| .field { | |||
| margin-bottom: 10px; | |||
| line-height: 20px; | |||
| } | |||
| } | |||
| } | |||
| .action { | |||
| width: 60px; | |||
| height: 100%; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| .button { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: center; | |||
| flex: 1; | |||
| height: 100%; | |||
| color: #fff; | |||
| &:first-child { | |||
| background-color: #fa4350; | |||
| } | |||
| } | |||
| } | |||
| .add { | |||
| height: 70upx; | |||
| width: 70upx; | |||
| text-align: center; | |||
| line-height: 70upx; | |||
| background-color: #fff; | |||
| border-radius: 50%; | |||
| position: fixed; | |||
| bottom: 80upx; | |||
| right: 30upx; | |||
| box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1); | |||
| color: #666; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,67 @@ | |||
| import {BasicColumn} from '/@/components/Table'; | |||
| import {FormSchema} from '/@/components/Table'; | |||
| import { rules} from '/@/utils/helper/validator'; | |||
| import { render } from '/@/utils/common/renderUtils'; | |||
| import { getWeekMonthQuarterYear } from '/@/utils'; | |||
| //列表数据 | |||
| export const columns: BasicColumn[] = [ | |||
| { | |||
| title: '名称', | |||
| align:"center", | |||
| dataIndex: 'title' | |||
| }, | |||
| { | |||
| title: '父ID', | |||
| align:"center", | |||
| dataIndex: 'pid' | |||
| }, | |||
| { | |||
| title: '类型(0类型,1规格项)', | |||
| align:"center", | |||
| dataIndex: 'type' | |||
| }, | |||
| ]; | |||
| //查询数据 | |||
| export const searchFormSchema: FormSchema[] = [ | |||
| ]; | |||
| //表单数据 | |||
| export const formSchema: FormSchema[] = [ | |||
| { | |||
| label: '名称', | |||
| field: 'title', | |||
| component: 'Input', | |||
| }, | |||
| { | |||
| label: '父ID', | |||
| field: 'pid', | |||
| component: 'Input', | |||
| }, | |||
| { | |||
| label: '类型(0类型,1规格项)', | |||
| field: 'type', | |||
| component: 'Input', | |||
| }, | |||
| // TODO 主键隐藏字段,目前写死为ID | |||
| { | |||
| label: '', | |||
| field: 'id', | |||
| component: 'Input', | |||
| show: false | |||
| }, | |||
| ]; | |||
| // 高级查询数据 | |||
| export const superQuerySchema = { | |||
| title: {title: '名称',order: 0,view: 'text', type: 'string',}, | |||
| pid: {title: '父ID',order: 1,view: 'text', type: 'string',}, | |||
| type: {title: '类型(0类型,1规格项)',order: 2,view: 'text', type: 'string',}, | |||
| }; | |||
| /** | |||
| * 流程表单调用这个方法获取formSchema | |||
| * @param param | |||
| */ | |||
| export function getBpmFormSchema(_formData): FormSchema[]{ | |||
| // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema | |||
| return formSchema; | |||
| } | |||
| @ -0,0 +1,206 @@ | |||
| <template> | |||
| <div> | |||
| <!--引用表格--> | |||
| <BasicTable @register="registerTable" :rowSelection="rowSelection"> | |||
| <!--插槽:table标题--> | |||
| <template #tableTitle> | |||
| <a-button type="primary" v-auth="'appletproductspectype:appletproductspectype:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button> | |||
| <a-button type="primary" v-auth="'appletproductspectype:appletproductspectype:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button> | |||
| <j-upload-button type="primary" v-auth="'appletproductspectype:appletproductspectype:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button> | |||
| <a-dropdown v-if="selectedRowKeys.length > 0"> | |||
| <template #overlay> | |||
| <a-menu> | |||
| <a-menu-item key="1" @click="batchHandleDelete"> | |||
| <Icon icon="ant-design:delete-outlined"></Icon> | |||
| 删除 | |||
| </a-menu-item> | |||
| </a-menu> | |||
| </template> | |||
| <a-button v-auth="'appletproductspectype:appletproductspectype:deleteBatch'">批量操作 | |||
| <Icon icon="mdi:chevron-down"></Icon> | |||
| </a-button> | |||
| </a-dropdown> | |||
| <!-- 高级查询 --> | |||
| <super-query :config="superQueryConfig" @search="handleSuperQuery" /> | |||
| </template> | |||
| <!--操作栏--> | |||
| <template #action="{ record }"> | |||
| <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/> | |||
| </template> | |||
| <!--字段回显插槽--> | |||
| <template v-slot:bodyCell="{ column, record, index, text }"> | |||
| </template> | |||
| </BasicTable> | |||
| <!-- 表单区域 --> | |||
| <AppletproductspectypeModal @register="registerModal" @success="handleSuccess"></AppletproductspectypeModal> | |||
| </div> | |||
| </template> | |||
| <script lang="ts" name="appletproductspectype-appletproductspectype" setup> | |||
| import {ref, reactive, computed, unref} from 'vue'; | |||
| import {BasicTable, useTable, TableAction} from '/@/components/Table'; | |||
| import {useModal} from '/@/components/Modal'; | |||
| import { useListPage } from '/@/hooks/system/useListPage' | |||
| import AppletproductspectypeModal from './components/AppletproductspectypeModal.vue' | |||
| import {columns, searchFormSchema, superQuerySchema} from './Appletproductspectype.data'; | |||
| import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './Appletproductspectype.api'; | |||
| import { downloadFile } from '/@/utils/common/renderUtils'; | |||
| import { useUserStore } from '/@/store/modules/user'; | |||
| import { useMessage } from '/@/hooks/web/useMessage'; | |||
| import { getDateByPicker } from '/@/utils'; | |||
| //日期个性化选择 | |||
| const fieldPickers = reactive({ | |||
| }); | |||
| const queryParam = reactive<any>({}); | |||
| const checkedKeys = ref<Array<string | number>>([]); | |||
| const userStore = useUserStore(); | |||
| const { createMessage } = useMessage(); | |||
| //注册model | |||
| const [registerModal, {openModal}] = useModal(); | |||
| //注册table数据 | |||
| const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({ | |||
| tableProps:{ | |||
| title: '小程序产品规格类型', | |||
| api: list, | |||
| columns, | |||
| canResize:true, | |||
| formConfig: { | |||
| //labelWidth: 120, | |||
| schemas: searchFormSchema, | |||
| autoSubmitOnEnter:true, | |||
| showAdvancedButton:true, | |||
| fieldMapToNumber: [ | |||
| ], | |||
| fieldMapToTime: [ | |||
| ], | |||
| }, | |||
| actionColumn: { | |||
| width: 120, | |||
| fixed:'right' | |||
| }, | |||
| beforeFetch: (params) => { | |||
| if (params && fieldPickers) { | |||
| for (let key in fieldPickers) { | |||
| if (params[key]) { | |||
| params[key] = getDateByPicker(params[key], fieldPickers[key]); | |||
| } | |||
| } | |||
| } | |||
| return Object.assign(params, queryParam); | |||
| }, | |||
| }, | |||
| exportConfig: { | |||
| name:"小程序产品规格类型", | |||
| url: getExportUrl, | |||
| params: queryParam, | |||
| }, | |||
| importConfig: { | |||
| url: getImportUrl, | |||
| success: handleSuccess | |||
| }, | |||
| }) | |||
| const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext | |||
| // 高级查询配置 | |||
| const superQueryConfig = reactive(superQuerySchema); | |||
| /** | |||
| * 高级查询事件 | |||
| */ | |||
| function handleSuperQuery(params) { | |||
| Object.keys(params).map((k) => { | |||
| queryParam[k] = params[k]; | |||
| }); | |||
| reload(); | |||
| } | |||
| /** | |||
| * 新增事件 | |||
| */ | |||
| function handleAdd() { | |||
| openModal(true, { | |||
| isUpdate: false, | |||
| showFooter: true, | |||
| }); | |||
| } | |||
| /** | |||
| * 编辑事件 | |||
| */ | |||
| function handleEdit(record: Recordable) { | |||
| openModal(true, { | |||
| record, | |||
| isUpdate: true, | |||
| showFooter: true, | |||
| }); | |||
| } | |||
| /** | |||
| * 详情 | |||
| */ | |||
| function handleDetail(record: Recordable) { | |||
| openModal(true, { | |||
| record, | |||
| isUpdate: true, | |||
| showFooter: false, | |||
| }); | |||
| } | |||
| /** | |||
| * 删除事件 | |||
| */ | |||
| async function handleDelete(record) { | |||
| await deleteOne({id: record.id}, handleSuccess); | |||
| } | |||
| /** | |||
| * 批量删除事件 | |||
| */ | |||
| async function batchHandleDelete() { | |||
| await batchDelete({ids: selectedRowKeys.value}, handleSuccess); | |||
| } | |||
| /** | |||
| * 成功回调 | |||
| */ | |||
| function handleSuccess() { | |||
| (selectedRowKeys.value = []) && reload(); | |||
| } | |||
| /** | |||
| * 操作栏 | |||
| */ | |||
| function getTableAction(record){ | |||
| return [ | |||
| { | |||
| label: '编辑', | |||
| onClick: handleEdit.bind(null, record), | |||
| auth: 'appletproductspectype:appletproductspectype:edit' | |||
| } | |||
| ] | |||
| } | |||
| /** | |||
| * 下拉操作栏 | |||
| */ | |||
| function getDropDownAction(record){ | |||
| return [ | |||
| { | |||
| label: '详情', | |||
| onClick: handleDetail.bind(null, record), | |||
| }, { | |||
| label: '删除', | |||
| popConfirm: { | |||
| title: '是否确认删除', | |||
| confirm: handleDelete.bind(null, record), | |||
| placement: 'topLeft', | |||
| }, | |||
| auth: 'appletproductspectype:appletproductspectype:delete' | |||
| } | |||
| ] | |||
| } | |||
| </script> | |||
| <style lang="less" scoped> | |||
| :deep(.ant-picker),:deep(.ant-input-number){ | |||
| width: 100%; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,26 @@ | |||
| -- 注意:该页面对应的前台目录为views/appletproductspectype文件夹下 | |||
| -- 如果你想更改到其他目录,请修改sql中component字段对应的值 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) | |||
| VALUES ('2025102204251670080', NULL, '小程序产品规格类型', '/appletproductspectype/appletproductspectypeList', 'appletproductspectype/AppletproductspectypeList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-10-22 16:25:08', NULL, NULL, 0); | |||
| -- 权限控制sql | |||
| -- 新增 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102204251670081', '2025102204251670080', '添加小程序产品规格类型', NULL, NULL, 0, NULL, NULL, 2, 'appletproductspectype:appletproductspectype:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 16:25:08', NULL, NULL, 0, 0, '1', 0); | |||
| -- 编辑 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102204251670082', '2025102204251670080', '编辑小程序产品规格类型', NULL, NULL, 0, NULL, NULL, 2, 'appletproductspectype:appletproductspectype:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 16:25:08', NULL, NULL, 0, 0, '1', 0); | |||
| -- 删除 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102204251670083', '2025102204251670080', '删除小程序产品规格类型', NULL, NULL, 0, NULL, NULL, 2, 'appletproductspectype:appletproductspectype:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 16:25:08', NULL, NULL, 0, 0, '1', 0); | |||
| -- 批量删除 | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102204251670084', '2025102204251670080', '批量删除小程序产品规格类型', NULL, NULL, 0, NULL, NULL, 2, 'appletproductspectype:appletproductspectype:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 16:25:08', NULL, NULL, 0, 0, '1', 0); | |||
| -- 导出excel | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102204251670085', '2025102204251670080', '导出excel_小程序产品规格类型', NULL, NULL, 0, NULL, NULL, 2, 'appletproductspectype:appletproductspectype:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 16:25:08', NULL, NULL, 0, 0, '1', 0); | |||
| -- 导入excel | |||
| INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) | |||
| VALUES ('2025102204251670086', '2025102204251670080', '导入excel_小程序产品规格类型', NULL, NULL, 0, NULL, NULL, 2, 'appletproductspectype:appletproductspectype:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-10-22 16:25:08', NULL, NULL, 0, 0, '1', 0); | |||
| @ -0,0 +1,70 @@ | |||
| <template> | |||
| <div style="min-height: 400px"> | |||
| <BasicForm @register="registerForm"></BasicForm> | |||
| <div style="width: 100%;text-align: center" v-if="!formDisabled"> | |||
| <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script lang="ts"> | |||
| import {BasicForm, useForm} from '/@/components/Form/index'; | |||
| import {computed, defineComponent} from 'vue'; | |||
| import {defHttp} from '/@/utils/http/axios'; | |||
| import { propTypes } from '/@/utils/propTypes'; | |||
| import {getBpmFormSchema} from '../Appletproductspectype.data'; | |||
| import {saveOrUpdate} from '../Appletproductspectype.api'; | |||
| export default defineComponent({ | |||
| name: "AppletproductspectypeForm", | |||
| components:{ | |||
| BasicForm | |||
| }, | |||
| props:{ | |||
| formData: propTypes.object.def({}), | |||
| formBpm: propTypes.bool.def(true), | |||
| }, | |||
| setup(props){ | |||
| const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({ | |||
| labelWidth: 150, | |||
| schemas: getBpmFormSchema(props.formData), | |||
| showActionButtonGroup: false, | |||
| baseColProps: {span: 24} | |||
| }); | |||
| const formDisabled = computed(()=>{ | |||
| if(props.formData.disabled === false){ | |||
| return false; | |||
| } | |||
| return true; | |||
| }); | |||
| let formData = {}; | |||
| const queryByIdUrl = '/appletproductspectype/appletproductspectype/queryById'; | |||
| async function initFormData(){ | |||
| let params = {id: props.formData.dataId}; | |||
| const data = await defHttp.get({url: queryByIdUrl, params}); | |||
| formData = {...data} | |||
| //设置表单的值 | |||
| await setFieldsValue(formData); | |||
| //默认是禁用 | |||
| await setProps({disabled: formDisabled.value}) | |||
| } | |||
| async function submitForm() { | |||
| let data = getFieldsValue(); | |||
| let params = Object.assign({}, formData, data); | |||
| console.log('表单数据', params) | |||
| await saveOrUpdate(params, true) | |||
| } | |||
| initFormData(); | |||
| return { | |||
| registerForm, | |||
| formDisabled, | |||
| submitForm, | |||
| } | |||
| } | |||
| }); | |||
| </script> | |||
| @ -0,0 +1,99 @@ | |||
| <template> | |||
| <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit"> | |||
| <BasicForm @register="registerForm" name="AppletproductspectypeForm" /> | |||
| </BasicModal> | |||
| </template> | |||
| <script lang="ts" setup> | |||
| import {ref, computed, unref, reactive} from 'vue'; | |||
| import {BasicModal, useModalInner} from '/@/components/Modal'; | |||
| import {BasicForm, useForm} from '/@/components/Form/index'; | |||
| import {formSchema} from '../Appletproductspectype.data'; | |||
| import {saveOrUpdate} from '../Appletproductspectype.api'; | |||
| import { useMessage } from '/@/hooks/web/useMessage'; | |||
| import { getDateByPicker } from '/@/utils'; | |||
| const { createMessage } = useMessage(); | |||
| // Emits声明 | |||
| const emit = defineEmits(['register','success']); | |||
| const isUpdate = ref(true); | |||
| const isDetail = ref(false); | |||
| //表单配置 | |||
| const [registerForm, { setProps,resetFields, setFieldsValue, validate, scrollToField }] = useForm({ | |||
| labelWidth: 150, | |||
| schemas: formSchema, | |||
| showActionButtonGroup: false, | |||
| baseColProps: {span: 24} | |||
| }); | |||
| //表单赋值 | |||
| const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => { | |||
| //重置表单 | |||
| await resetFields(); | |||
| setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter}); | |||
| isUpdate.value = !!data?.isUpdate; | |||
| isDetail.value = !!data?.showFooter; | |||
| if (unref(isUpdate)) { | |||
| //表单赋值 | |||
| await setFieldsValue({ | |||
| ...data.record, | |||
| }); | |||
| } | |||
| // 隐藏底部时禁用整个表单 | |||
| setProps({ disabled: !data?.showFooter }) | |||
| }); | |||
| //日期个性化选择 | |||
| const fieldPickers = reactive({ | |||
| }); | |||
| //设置标题 | |||
| const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑')); | |||
| //表单提交事件 | |||
| async function handleSubmit(v) { | |||
| try { | |||
| let values = await validate(); | |||
| // 预处理日期数据 | |||
| changeDateValue(values); | |||
| setModalProps({confirmLoading: true}); | |||
| //提交表单 | |||
| await saveOrUpdate(values, isUpdate.value); | |||
| //关闭弹窗 | |||
| closeModal(); | |||
| //刷新列表 | |||
| emit('success'); | |||
| } catch ({ errorFields }) { | |||
| if (errorFields) { | |||
| const firstField = errorFields[0]; | |||
| if (firstField) { | |||
| scrollToField(firstField.name, { behavior: 'smooth', block: 'center' }); | |||
| } | |||
| } | |||
| return Promise.reject(errorFields); | |||
| } finally { | |||
| setModalProps({confirmLoading: false}); | |||
| } | |||
| } | |||
| /** | |||
| * 处理日期值 | |||
| * @param formData 表单数据 | |||
| */ | |||
| const changeDateValue = (formData) => { | |||
| if (formData && fieldPickers) { | |||
| for (let key in fieldPickers) { | |||
| if (formData[key]) { | |||
| formData[key] = getDateByPicker(formData[key], fieldPickers[key]); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| </script> | |||
| <style lang="less" scoped> | |||
| /** 时间和数字输入框样式 */ | |||
| :deep(.ant-input-number) { | |||
| width: 100%; | |||
| } | |||
| :deep(.ant-calendar-picker) { | |||
| width: 100%; | |||
| } | |||
| </style> | |||
| @ -0,0 +1,48 @@ | |||
| import type { AppRouteModule } from '/@/router/types'; | |||
| import { LAYOUT } from '/@/router/constant'; | |||
| import { t } from '/@/hooks/web/useI18n'; | |||
| const applet: AppRouteModule = { | |||
| path: '/applet', | |||
| name: 'Applet', | |||
| component: LAYOUT, | |||
| redirect: '/applet/product', | |||
| meta: { | |||
| orderNo: 1000, | |||
| icon: 'ion:phone-portrait-outline', | |||
| title: '小程序管理', | |||
| }, | |||
| children: [ | |||
| { | |||
| path: 'product', | |||
| name: 'AppletProduct', | |||
| redirect: '/applet/product/list', | |||
| meta: { | |||
| title: '产品管理', | |||
| }, | |||
| children: [ | |||
| { | |||
| path: 'list', | |||
| name: 'AppletProductList', | |||
| component: () => import('/@/views/applet/product/AppletProductList.vue'), | |||
| meta: { | |||
| title: '产品列表', | |||
| }, | |||
| }, | |||
| { | |||
| path: 'spec-config', | |||
| name: 'AppletProductSpecConfig', | |||
| component: () => import('/@/views/applet/product/AppletProductSpecConfig.vue'), | |||
| meta: { | |||
| title: '产品规格配置', | |||
| hideMenu: true, | |||
| currentActiveMenu: '/applet/product/list', | |||
| }, | |||
| }, | |||
| ], | |||
| }, | |||
| ], | |||
| }; | |||
| export default applet; | |||
| @ -1,101 +0,0 @@ | |||
| import {BasicColumn} from '/@/components/Table'; | |||
| import {FormSchema} from '/@/components/Table'; | |||
| import { rules} from '/@/utils/helper/validator'; | |||
| import { render } from '/@/utils/common/renderUtils'; | |||
| //列表数据 | |||
| export const columns: BasicColumn[] = [ | |||
| { | |||
| title: '规格名称', | |||
| align:"center", | |||
| dataIndex: 'specName' | |||
| }, | |||
| // { | |||
| // title: '规格值', | |||
| // align:"center", | |||
| // dataIndex: 'specValue' | |||
| // }, | |||
| { | |||
| title: '价格', | |||
| align:"center", | |||
| dataIndex: 'price' | |||
| }, | |||
| { | |||
| title: '排序', | |||
| align:"center", | |||
| dataIndex: 'sortOrder' | |||
| }, | |||
| { | |||
| title: '创建时间', | |||
| align:"center", | |||
| dataIndex: 'createTime', | |||
| customRender: ({ text }) => { | |||
| return !text ? "" : render.renderDate(text); | |||
| } | |||
| }, | |||
| ]; | |||
| //查询数据 | |||
| export const searchFormSchema: FormSchema[] = [ | |||
| { | |||
| label: "规格名称", | |||
| field: 'specName', | |||
| component: 'Input', | |||
| colProps: {span: 6}, | |||
| }, | |||
| ]; | |||
| //表单数据 | |||
| export const formSchema: FormSchema[] = [ | |||
| { | |||
| label: '规格名称', | |||
| field: 'specName', | |||
| component: 'Input', | |||
| required: true, | |||
| }, | |||
| // { | |||
| // label: '规格值', | |||
| // field: 'specValue', | |||
| // component: 'Input', | |||
| // required: true, | |||
| // }, | |||
| // { | |||
| // label: '价格', | |||
| // field: 'price', | |||
| // component: 'InputNumber', | |||
| // componentProps: { | |||
| // precision: 2, | |||
| // min: 0, | |||
| // }, | |||
| // }, | |||
| { | |||
| label: '排序', | |||
| field: 'sortOrder', | |||
| component: 'InputNumber', | |||
| componentProps: { | |||
| min: 0, | |||
| }, | |||
| }, | |||
| { | |||
| label: '产品ID', | |||
| field: 'productId', | |||
| component: 'Input', | |||
| show: false, | |||
| }, | |||
| // TODO 主键隐藏字段,目前写死为ID | |||
| { | |||
| label: '', | |||
| field: 'id', | |||
| component: 'Input', | |||
| show: false | |||
| }, | |||
| ]; | |||
| /** | |||
| * 流程表单调用这个方法获取formSchema | |||
| * @param param | |||
| */ | |||
| export function getBpmFormSchema(_formData): FormSchema[]{ | |||
| // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema | |||
| return formSchema; | |||
| } | |||
| @ -0,0 +1,94 @@ | |||
| import {defHttp} from '/@/utils/http/axios'; | |||
| import { useMessage } from "/@/hooks/web/useMessage"; | |||
| const { createConfirm } = useMessage(); | |||
| enum Api { | |||
| list = '/appletproductspectype/appletproductspectype/list', | |||
| save = '/appletproductspectype/appletproductspectype/add', | |||
| edit = '/appletproductspectype/appletproductspectype/edit', | |||
| deleteOne = '/appletproductspectype/appletproductspectype/delete', | |||
| deleteBatch = '/appletproductspectype/appletproductspectype/deleteBatch', | |||
| importExcel = '/appletproductspectype/appletproductspectype/importExcel', | |||
| exportXls = '/appletproductspectype/appletproductspectype/exportXls', | |||
| queryById = '/appletproductspectype/appletproductspectype/queryById', | |||
| // 根据产品ID查询规格类型 | |||
| queryByProductId = '/appletproductspectype/appletproductspectype/queryByProductId', | |||
| // 生成规格实体 | |||
| generateSpecs = '/appletproductspectype/appletproductspectype/generateSpecs', | |||
| } | |||
| /** | |||
| * 导出api | |||
| */ | |||
| export const getExportUrl = Api.exportXls; | |||
| /** | |||
| * 导入api | |||
| */ | |||
| export const getImportUrl = Api.importExcel; | |||
| /** | |||
| * 列表接口 | |||
| */ | |||
| export const list = (params) => | |||
| defHttp.get({ | |||
| url: Api.list, | |||
| params, | |||
| }); | |||
| /** | |||
| * 删除单个 | |||
| */ | |||
| export const deleteOne = (params,handleSuccess) => { | |||
| return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { | |||
| handleSuccess(); | |||
| }); | |||
| } | |||
| /** | |||
| * 批量删除 | |||
| */ | |||
| export const batchDelete = (params, handleSuccess) => { | |||
| createConfirm({ | |||
| iconType: 'warning', | |||
| title: '确认删除', | |||
| content: '是否删除选中数据', | |||
| okText: '确认', | |||
| cancelText: '取消', | |||
| onOk: () => { | |||
| return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { | |||
| handleSuccess(); | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| /** | |||
| * 保存或者更新 | |||
| */ | |||
| export const saveOrUpdate = (params, isUpdate) => { | |||
| let url = isUpdate ? Api.edit : Api.save; | |||
| return defHttp.post({url: url, params}, {isTransformResponse: false}); | |||
| } | |||
| /** | |||
| * 根据ID查询 | |||
| */ | |||
| export const queryById = (params) => { | |||
| return defHttp.get({url: Api.queryById, params}); | |||
| } | |||
| /** | |||
| * 根据产品ID查询规格类型和规格项 | |||
| */ | |||
| export const queryByProductId = (params) => { | |||
| return defHttp.get({url: Api.queryByProductId, params}); | |||
| } | |||
| /** | |||
| * 生成规格实体 | |||
| */ | |||
| export const generateSpecs = (params) => { | |||
| return defHttp.post({url: Api.generateSpecs, params}); | |||
| } | |||
| @ -0,0 +1,236 @@ | |||
| import {BasicColumn} from '/@/components/Table'; | |||
| import {FormSchema} from '/@/components/Table'; | |||
| import { rules} from '/@/utils/helper/validator'; | |||
| import { render } from '/@/utils/common/renderUtils'; | |||
| // 规格类型列表数据 | |||
| export const specTypeColumns: BasicColumn[] = [ | |||
| { | |||
| title: '规格类型名称', | |||
| align: "center", | |||
| dataIndex: 'title', | |||
| width: 200, | |||
| }, | |||
| { | |||
| title: '类型', | |||
| align: "center", | |||
| dataIndex: 'type', | |||
| width: 100, | |||
| customRender: ({text}) => { | |||
| return text === '0' ? '规格类型' : '规格项'; | |||
| }, | |||
| }, | |||
| { | |||
| title: '创建时间', | |||
| align: "center", | |||
| dataIndex: 'createTime', | |||
| width: 180, | |||
| }, | |||
| ]; | |||
| // 规格项列表数据 | |||
| export const specItemColumns: BasicColumn[] = [ | |||
| { | |||
| title: '规格项名称', | |||
| align: "center", | |||
| dataIndex: 'title', | |||
| width: 200, | |||
| }, | |||
| { | |||
| title: '创建时间', | |||
| align: "center", | |||
| dataIndex: 'createTime', | |||
| width: 180, | |||
| }, | |||
| ]; | |||
| // 规格实体列表数据 | |||
| export const specEntityColumns: BasicColumn[] = [ | |||
| { | |||
| title: '规格名称', | |||
| align: "center", | |||
| dataIndex: 'specName', | |||
| width: 200, | |||
| }, | |||
| { | |||
| title: '价格', | |||
| align: "center", | |||
| dataIndex: 'price', | |||
| width: 120, | |||
| }, | |||
| { | |||
| title: '库存', | |||
| align: "center", | |||
| dataIndex: 'inventory', | |||
| width: 100, | |||
| }, | |||
| { | |||
| title: '图片', | |||
| align: "center", | |||
| dataIndex: 'image', | |||
| width: 100, | |||
| customRender: render.renderImage, | |||
| }, | |||
| { | |||
| title: '描述', | |||
| align: "center", | |||
| dataIndex: 'info', | |||
| width: 200, | |||
| }, | |||
| { | |||
| title: '上架状态', | |||
| align: "center", | |||
| dataIndex: 'putaway', | |||
| width: 100, | |||
| customRender: ({text}) => { | |||
| return text === 'Y' ? '已上架' : '未上架'; | |||
| }, | |||
| }, | |||
| ]; | |||
| // 规格类型表单 | |||
| export const specTypeFormSchema: FormSchema[] = [ | |||
| { | |||
| label: '规格类型名称', | |||
| field: 'title', | |||
| component: 'Input', | |||
| required: true, | |||
| rules: [ | |||
| { required: true, message: '请输入规格类型名称!' }, | |||
| { | |||
| pattern: /^[^,,]*$/, | |||
| message: '规格类型名称不能包含英文逗号或中文逗号!' | |||
| } | |||
| ], | |||
| }, | |||
| { | |||
| label: '产品ID', | |||
| field: 'productId', | |||
| component: 'Input', | |||
| show: false, | |||
| }, | |||
| { | |||
| label: '类型', | |||
| field: 'type', | |||
| component: 'Input', | |||
| show: false, | |||
| defaultValue: '0', | |||
| }, | |||
| { | |||
| label: '父ID', | |||
| field: 'pid', | |||
| component: 'Input', | |||
| show: false, | |||
| defaultValue: '0', | |||
| }, | |||
| ]; | |||
| // 规格项表单 | |||
| export const specItemFormSchema: FormSchema[] = [ | |||
| { | |||
| label: '规格项名称', | |||
| field: 'title', | |||
| component: 'Input', | |||
| required: true, | |||
| rules: [ | |||
| { required: true, message: '请输入规格项名称!' }, | |||
| { | |||
| pattern: /^[^,,]*$/, | |||
| message: '规格项名称不能包含英文逗号或中文逗号!' | |||
| } | |||
| ], | |||
| }, | |||
| { | |||
| label: '产品ID', | |||
| field: 'productId', | |||
| component: 'Input', | |||
| show: false, | |||
| }, | |||
| { | |||
| label: '类型', | |||
| field: 'type', | |||
| component: 'Input', | |||
| show: false, | |||
| defaultValue: '1', | |||
| }, | |||
| { | |||
| label: '父ID', | |||
| field: 'pid', | |||
| component: 'Input', | |||
| show: false, | |||
| }, | |||
| ]; | |||
| // 规格实体表单 | |||
| export const specEntityFormSchema: FormSchema[] = [ | |||
| { | |||
| label: '规格名称', | |||
| field: 'specName', | |||
| component: 'Input', | |||
| required: true, | |||
| componentProps: { | |||
| disabled: true, | |||
| }, | |||
| }, | |||
| { | |||
| label: '价格', | |||
| field: 'price', | |||
| component: 'InputNumber', | |||
| required: true, | |||
| rules: [ | |||
| { required: true, message: '请输入价格!' }, | |||
| ], | |||
| }, | |||
| { | |||
| label: '库存', | |||
| field: 'inventory', | |||
| component: 'InputNumber', | |||
| required: true, | |||
| rules: [ | |||
| { required: true, message: '请输入库存!' }, | |||
| ], | |||
| }, | |||
| { | |||
| label: '图片', | |||
| field: 'image', | |||
| component: 'JImageUpload', | |||
| componentProps: { | |||
| fileMax: 1 | |||
| }, | |||
| }, | |||
| { | |||
| label: '描述', | |||
| field: 'info', | |||
| component: 'InputTextArea', | |||
| }, | |||
| { | |||
| label: '上架状态', | |||
| field: 'putaway', | |||
| component: 'JSwitch', | |||
| componentProps: { | |||
| checkedValue: 'Y', | |||
| unCheckedValue: 'N', | |||
| }, | |||
| defaultValue: 'N', | |||
| }, | |||
| { | |||
| label: '产品ID', | |||
| field: 'productId', | |||
| component: 'Input', | |||
| show: false, | |||
| }, | |||
| { | |||
| label: '排序', | |||
| field: 'sortOrder', | |||
| component: 'InputNumber', | |||
| defaultValue: 0, | |||
| }, | |||
| ]; | |||
| // 验证规则 | |||
| export const validateRules = { | |||
| noComma: { | |||
| pattern: /^[^,,]*$/, | |||
| message: '不能包含英文逗号或中文逗号!' | |||
| } | |||
| }; | |||
| @ -0,0 +1,647 @@ | |||
| <template> | |||
| <div class="spec-config-container"> | |||
| <PageWrapper dense contentFullHeight fixedHeight contentClass="flex"> | |||
| <!-- 左侧规格类型和规格项 --> | |||
| <div class="spec-left-panel"> | |||
| <a-card title="规格配置" size="small" class="spec-card"> | |||
| <!-- 产品信息 --> | |||
| <div class="product-info"> | |||
| <h4>产品:{{ productInfo.name }}</h4> | |||
| </div> | |||
| <!-- 规格类型管理 --> | |||
| <div class="spec-section"> | |||
| <div class="section-header"> | |||
| <h5>规格类型</h5> | |||
| <a-button type="primary" size="small" @click="handleAddSpecType"> | |||
| <Icon icon="ant-design:plus-outlined" /> | |||
| 添加规格类型 | |||
| </a-button> | |||
| </div> | |||
| <div class="spec-types"> | |||
| <div | |||
| v-for="specType in specTypes" | |||
| :key="specType.id" | |||
| class="spec-type-item" | |||
| :class="{ active: selectedSpecType?.id === specType.id }" | |||
| @click="selectSpecType(specType)" | |||
| > | |||
| <div class="spec-type-content"> | |||
| <span class="spec-type-name">{{ specType.title }}</span> | |||
| <div class="spec-type-actions"> | |||
| <a-button type="text" size="small" @click.stop="handleEditSpecType(specType)"> | |||
| <Icon icon="ant-design:edit-outlined" /> | |||
| </a-button> | |||
| <a-button type="text" size="small" danger @click.stop="handleDeleteSpecType(specType)"> | |||
| <Icon icon="ant-design:delete-outlined" /> | |||
| </a-button> | |||
| </div> | |||
| </div> | |||
| <!-- 规格项列表 --> | |||
| <div v-if="selectedSpecType?.id === specType.id" class="spec-items"> | |||
| <div class="spec-items-header"> | |||
| <span>规格项</span> | |||
| <a-button type="link" size="small" @click="handleAddSpecItem(specType)"> | |||
| <Icon icon="ant-design:plus-outlined" /> | |||
| 添加规格项 | |||
| </a-button> | |||
| </div> | |||
| <div | |||
| v-for="item in getSpecItems(specType.id)" | |||
| :key="item.id" | |||
| class="spec-item" | |||
| > | |||
| <span class="spec-item-name">{{ item.title }}</span> | |||
| <div class="spec-item-actions"> | |||
| <a-button type="text" size="small" @click="handleEditSpecItem(item)"> | |||
| <Icon icon="ant-design:edit-outlined" /> | |||
| </a-button> | |||
| <a-button type="text" size="small" danger @click="handleDeleteSpecItem(item)"> | |||
| <Icon icon="ant-design:delete-outlined" /> | |||
| </a-button> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <!-- 生成规格实体按钮 --> | |||
| <div class="generate-section" v-if="specTypes.length > 0"> | |||
| <a-button type="primary" @click="handleGenerateSpecs" :loading="generateLoading"> | |||
| <Icon icon="ant-design:thunderbolt-outlined" /> | |||
| 生成规格实体 | |||
| </a-button> | |||
| <p class="generate-tip">根据规格类型和规格项自动生成规格实体</p> | |||
| </div> | |||
| </div> | |||
| </a-card> | |||
| </div> | |||
| <!-- 右侧规格实体列表 --> | |||
| <div class="spec-right-panel"> | |||
| <a-card title="规格实体管理" size="small" class="spec-card"> | |||
| <BasicTable @register="registerTable" :rowSelection="rowSelection"> | |||
| <template #tableTitle> | |||
| <a-button type="primary" @click="handleAddSpecEntity" preIcon="ant-design:plus-outlined"> | |||
| 新增规格实体 | |||
| </a-button> | |||
| <a-dropdown v-if="selectedRowKeys.length > 0"> | |||
| <template #overlay> | |||
| <a-menu> | |||
| <a-menu-item key="1" @click="batchHandleDelete"> | |||
| <Icon icon="ant-design:delete-outlined"></Icon> | |||
| 删除 | |||
| </a-menu-item> | |||
| </a-menu> | |||
| </template> | |||
| <a-button>批量操作 | |||
| <Icon icon="mdi:chevron-down"></Icon> | |||
| </a-button> | |||
| </a-dropdown> | |||
| </template> | |||
| <template #action="{ record }"> | |||
| <TableAction :actions="getTableAction(record)" /> | |||
| </template> | |||
| </BasicTable> | |||
| </a-card> | |||
| </div> | |||
| </PageWrapper> | |||
| <!-- 规格类型表单弹窗 --> | |||
| <BasicModal @register="registerSpecTypeModal" title="规格类型" @ok="handleSpecTypeSubmit"> | |||
| <BasicForm @register="registerSpecTypeForm" /> | |||
| </BasicModal> | |||
| <!-- 规格项表单弹窗 --> | |||
| <BasicModal @register="registerSpecItemModal" title="规格项" @ok="handleSpecItemSubmit"> | |||
| <BasicForm @register="registerSpecItemForm" /> | |||
| </BasicModal> | |||
| <!-- 规格实体表单弹窗 --> | |||
| <BasicModal @register="registerSpecEntityModal" title="规格实体" @ok="handleSpecEntitySubmit"> | |||
| <BasicForm @register="registerSpecEntityForm" /> | |||
| </BasicModal> | |||
| </div> | |||
| </template> | |||
| <script lang="ts" setup> | |||
| import { ref, reactive, onMounted, computed } from 'vue'; | |||
| import { useRoute, useRouter } from 'vue-router'; | |||
| import { BasicTable, useTable, TableAction } from '/@/components/Table'; | |||
| import { BasicModal, useModal } from '/@/components/Modal'; | |||
| import { BasicForm, useForm } from '/@/components/Form/index'; | |||
| import { PageWrapper } from '/@/components/Page'; | |||
| import { useMessage } from '/@/hooks/web/useMessage'; | |||
| import { Icon } from '/@/components/Icon'; | |||
| import { | |||
| specEntityColumns, | |||
| specTypeFormSchema, | |||
| specItemFormSchema, | |||
| specEntityFormSchema | |||
| } from './AppletProductSpecConfig.data'; | |||
| import { | |||
| list, | |||
| saveOrUpdate, | |||
| deleteOne, | |||
| batchDelete, | |||
| queryByProductId, | |||
| generateSpecs | |||
| } from './AppletProductSpecConfig.api'; | |||
| import { queryAppletProductSpecByMainId } from './AppletProduct.api'; | |||
| const route = useRoute(); | |||
| const router = useRouter(); | |||
| const { createMessage, createConfirm } = useMessage(); | |||
| // 产品信息 | |||
| const productInfo = ref<any>({}); | |||
| const productId = ref<string>(''); | |||
| // 规格类型和规格项数据 | |||
| const specTypes = ref<any[]>([]); | |||
| const specItems = ref<any[]>([]); | |||
| const selectedSpecType = ref<any>(null); | |||
| const generateLoading = ref(false); | |||
| // 表格相关 | |||
| const [registerTable, { reload, getSelectRows }] = useTable({ | |||
| title: '规格实体列表', | |||
| api: queryAppletProductSpecByMainId, | |||
| columns: specEntityColumns, | |||
| canResize: true, | |||
| actionColumn: { | |||
| width: 120, | |||
| title: '操作', | |||
| dataIndex: 'action', | |||
| fixed: 'right', | |||
| }, | |||
| beforeFetch: (params) => { | |||
| return { id: productId.value }; | |||
| }, | |||
| }); | |||
| const rowSelection = { | |||
| type: 'checkbox', | |||
| }; | |||
| const selectedRowKeys = ref<string[]>([]); | |||
| // 弹窗相关 | |||
| const [registerSpecTypeModal, { openModal: openSpecTypeModal, closeModal: closeSpecTypeModal }] = useModal(); | |||
| const [registerSpecItemModal, { openModal: openSpecItemModal, closeModal: closeSpecItemModal }] = useModal(); | |||
| const [registerSpecEntityModal, { openModal: openSpecEntityModal, closeModal: closeSpecEntityModal }] = useModal(); | |||
| // 表单相关 | |||
| const [registerSpecTypeForm, { resetFields: resetSpecTypeFields, setFieldsValue: setSpecTypeValues, validate: validateSpecType }] = useForm({ | |||
| labelWidth: 100, | |||
| schemas: specTypeFormSchema, | |||
| showActionButtonGroup: false, | |||
| }); | |||
| const [registerSpecItemForm, { resetFields: resetSpecItemFields, setFieldsValue: setSpecItemValues, validate: validateSpecItem }] = useForm({ | |||
| labelWidth: 100, | |||
| schemas: specItemFormSchema, | |||
| showActionButtonGroup: false, | |||
| }); | |||
| const [registerSpecEntityForm, { resetFields: resetSpecEntityFields, setFieldsValue: setSpecEntityValues, validate: validateSpecEntity }] = useForm({ | |||
| labelWidth: 100, | |||
| schemas: specEntityFormSchema, | |||
| showActionButtonGroup: false, | |||
| }); | |||
| // 当前编辑的数据 | |||
| const currentSpecType = ref<any>(null); | |||
| const currentSpecItem = ref<any>(null); | |||
| const currentSpecEntity = ref<any>(null); | |||
| // 获取指定规格类型下的规格项 | |||
| const getSpecItems = (specTypeId: string) => { | |||
| return specItems.value.filter(item => item.pid === specTypeId); | |||
| }; | |||
| // 初始化数据 | |||
| onMounted(() => { | |||
| productId.value = route.query.productId as string; | |||
| if (productId.value) { | |||
| loadProductInfo(); | |||
| loadSpecData(); | |||
| } else { | |||
| createMessage.error('缺少产品ID参数'); | |||
| router.back(); | |||
| } | |||
| }); | |||
| // 加载产品信息 | |||
| const loadProductInfo = () => { | |||
| // 这里可以调用API获取产品信息,暂时使用路由参数 | |||
| productInfo.value = { | |||
| id: productId.value, | |||
| name: route.query.productName || '未知产品' | |||
| }; | |||
| }; | |||
| // 加载规格数据 | |||
| const loadSpecData = async () => { | |||
| try { | |||
| const result = await queryByProductId({ productId: productId.value }); | |||
| if (result) { | |||
| const allSpecs = result || []; | |||
| specTypes.value = allSpecs.filter(item => item.type === '0'); | |||
| specItems.value = allSpecs.filter(item => item.type === '1'); | |||
| } | |||
| } catch (error) { | |||
| console.error('加载规格数据失败:', error); | |||
| } | |||
| }; | |||
| // 选择规格类型 | |||
| const selectSpecType = (specType: any) => { | |||
| selectedSpecType.value = selectedSpecType.value?.id === specType.id ? null : specType; | |||
| }; | |||
| // 添加规格类型 | |||
| const handleAddSpecType = () => { | |||
| currentSpecType.value = null; | |||
| resetSpecTypeFields(); | |||
| setSpecTypeValues({ | |||
| productId: productId.value, | |||
| type: '0', | |||
| pid: '0' | |||
| }); | |||
| openSpecTypeModal(true, { | |||
| title: '添加规格类型' | |||
| }); | |||
| }; | |||
| // 编辑规格类型 | |||
| const handleEditSpecType = (record: any) => { | |||
| currentSpecType.value = record; | |||
| resetSpecTypeFields(); | |||
| setSpecTypeValues(record); | |||
| openSpecTypeModal(true, { | |||
| title: '编辑规格类型' | |||
| }); | |||
| }; | |||
| // 删除规格类型 | |||
| const handleDeleteSpecType = (record: any) => { | |||
| createConfirm({ | |||
| iconType: 'warning', | |||
| title: '确认删除', | |||
| content: '删除规格类型将同时删除其下所有规格项,确认删除吗?', | |||
| onOk: async () => { | |||
| try { | |||
| await deleteOne({ id: record.id }, () => { | |||
| createMessage.success('删除成功'); | |||
| loadSpecData(); | |||
| }); | |||
| } catch (error) { | |||
| createMessage.error('删除失败'); | |||
| } | |||
| } | |||
| }); | |||
| }; | |||
| // 提交规格类型表单 | |||
| const handleSpecTypeSubmit = async () => { | |||
| try { | |||
| const values = await validateSpecType(); | |||
| const isUpdate = !!currentSpecType.value; | |||
| if (isUpdate) { | |||
| values.id = currentSpecType.value.id; | |||
| } | |||
| await saveOrUpdate(values, isUpdate); | |||
| createMessage.success(isUpdate ? '更新成功' : '添加成功'); | |||
| closeSpecTypeModal(); | |||
| loadSpecData(); | |||
| } catch (error) { | |||
| createMessage.error('操作失败'); | |||
| } | |||
| }; | |||
| // 添加规格项 | |||
| const handleAddSpecItem = (specType: any) => { | |||
| currentSpecItem.value = null; | |||
| resetSpecItemFields(); | |||
| setSpecItemValues({ | |||
| productId: productId.value, | |||
| type: '1', | |||
| pid: specType.id | |||
| }); | |||
| openSpecItemModal(true, { | |||
| title: `添加规格项 - ${specType.title}` | |||
| }); | |||
| }; | |||
| // 编辑规格项 | |||
| const handleEditSpecItem = (record: any) => { | |||
| currentSpecItem.value = record; | |||
| resetSpecItemFields(); | |||
| setSpecItemValues(record); | |||
| openSpecItemModal(true, { | |||
| title: '编辑规格项' | |||
| }); | |||
| }; | |||
| // 删除规格项 | |||
| const handleDeleteSpecItem = (record: any) => { | |||
| createConfirm({ | |||
| iconType: 'warning', | |||
| title: '确认删除', | |||
| content: '确认删除该规格项吗?', | |||
| onOk: async () => { | |||
| try { | |||
| await deleteOne({ id: record.id }, () => { | |||
| createMessage.success('删除成功'); | |||
| loadSpecData(); | |||
| }); | |||
| } catch (error) { | |||
| createMessage.error('删除失败'); | |||
| } | |||
| } | |||
| }); | |||
| }; | |||
| // 提交规格项表单 | |||
| const handleSpecItemSubmit = async () => { | |||
| try { | |||
| const values = await validateSpecItem(); | |||
| const isUpdate = !!currentSpecItem.value; | |||
| if (isUpdate) { | |||
| values.id = currentSpecItem.value.id; | |||
| } | |||
| await saveOrUpdate(values, isUpdate); | |||
| createMessage.success(isUpdate ? '更新成功' : '添加成功'); | |||
| closeSpecItemModal(); | |||
| loadSpecData(); | |||
| } catch (error) { | |||
| createMessage.error('操作失败'); | |||
| } | |||
| }; | |||
| // 生成规格实体 | |||
| const handleGenerateSpecs = async () => { | |||
| if (specTypes.value.length === 0) { | |||
| createMessage.warning('请先添加规格类型'); | |||
| return; | |||
| } | |||
| // 检查是否所有规格类型都有规格项 | |||
| const hasEmptySpecType = specTypes.value.some(specType => { | |||
| const items = getSpecItems(specType.id); | |||
| return items.length === 0; | |||
| }); | |||
| if (hasEmptySpecType) { | |||
| createMessage.warning('请为所有规格类型添加规格项'); | |||
| return; | |||
| } | |||
| createConfirm({ | |||
| iconType: 'info', | |||
| title: '生成规格实体', | |||
| content: '将根据现有规格类型和规格项生成规格实体,是否继续?', | |||
| onOk: async () => { | |||
| try { | |||
| generateLoading.value = true; | |||
| await generateSpecs({ productId: productId.value }); | |||
| createMessage.success('规格实体生成成功'); | |||
| reload(); | |||
| } catch (error) { | |||
| createMessage.error('生成规格实体失败'); | |||
| } finally { | |||
| generateLoading.value = false; | |||
| } | |||
| } | |||
| }); | |||
| }; | |||
| // 添加规格实体 | |||
| const handleAddSpecEntity = () => { | |||
| currentSpecEntity.value = null; | |||
| resetSpecEntityFields(); | |||
| setSpecEntityValues({ | |||
| productId: productId.value, | |||
| putaway: 'N', | |||
| sortOrder: 0 | |||
| }); | |||
| openSpecEntityModal(true, { | |||
| title: '添加规格实体' | |||
| }); | |||
| }; | |||
| // 编辑规格实体 | |||
| const handleEditSpecEntity = (record: any) => { | |||
| currentSpecEntity.value = record; | |||
| resetSpecEntityFields(); | |||
| setSpecEntityValues(record); | |||
| openSpecEntityModal(true, { | |||
| title: '编辑规格实体' | |||
| }); | |||
| }; | |||
| // 删除规格实体 | |||
| const handleDeleteSpecEntity = (record: any) => { | |||
| createConfirm({ | |||
| iconType: 'warning', | |||
| title: '确认删除', | |||
| content: '确认删除该规格实体吗?', | |||
| onOk: async () => { | |||
| try { | |||
| await deleteOne({ id: record.id }, () => { | |||
| createMessage.success('删除成功'); | |||
| reload(); | |||
| }); | |||
| } catch (error) { | |||
| createMessage.error('删除失败'); | |||
| } | |||
| } | |||
| }); | |||
| }; | |||
| // 提交规格实体表单 | |||
| const handleSpecEntitySubmit = async () => { | |||
| try { | |||
| const values = await validateSpecEntity(); | |||
| const isUpdate = !!currentSpecEntity.value; | |||
| if (isUpdate) { | |||
| values.id = currentSpecEntity.value.id; | |||
| } | |||
| await saveOrUpdate(values, isUpdate); | |||
| createMessage.success(isUpdate ? '更新成功' : '添加成功'); | |||
| closeSpecEntityModal(); | |||
| reload(); | |||
| } catch (error) { | |||
| createMessage.error('操作失败'); | |||
| } | |||
| }; | |||
| // 批量删除规格实体 | |||
| const batchHandleDelete = async () => { | |||
| const rows = getSelectRows(); | |||
| if (rows.length === 0) { | |||
| createMessage.warning('请选择要删除的数据'); | |||
| return; | |||
| } | |||
| await batchDelete({ ids: rows.map(row => row.id) }, () => { | |||
| createMessage.success('批量删除成功'); | |||
| reload(); | |||
| }); | |||
| }; | |||
| // 表格操作列 | |||
| const getTableAction = (record: any) => { | |||
| return [ | |||
| { | |||
| label: '编辑', | |||
| onClick: handleEditSpecEntity.bind(null, record), | |||
| }, | |||
| { | |||
| label: '删除', | |||
| color: 'error', | |||
| popConfirm: { | |||
| title: '是否确认删除', | |||
| confirm: handleDeleteSpecEntity.bind(null, record), | |||
| }, | |||
| } | |||
| ]; | |||
| }; | |||
| </script> | |||
| <style lang="less" scoped> | |||
| .spec-config-container { | |||
| height: 100%; | |||
| .spec-left-panel { | |||
| width: 400px; | |||
| margin-right: 16px; | |||
| .spec-card { | |||
| height: 100%; | |||
| .product-info { | |||
| margin-bottom: 16px; | |||
| padding: 12px; | |||
| background: #f5f5f5; | |||
| border-radius: 6px; | |||
| h4 { | |||
| margin: 0; | |||
| color: #1890ff; | |||
| } | |||
| } | |||
| .spec-section { | |||
| .section-header { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-bottom: 12px; | |||
| h5 { | |||
| margin: 0; | |||
| font-weight: 600; | |||
| } | |||
| } | |||
| .spec-types { | |||
| .spec-type-item { | |||
| border: 1px solid #d9d9d9; | |||
| border-radius: 6px; | |||
| margin-bottom: 8px; | |||
| cursor: pointer; | |||
| transition: all 0.3s; | |||
| &:hover { | |||
| border-color: #1890ff; | |||
| } | |||
| &.active { | |||
| border-color: #1890ff; | |||
| background: #f6ffed; | |||
| } | |||
| .spec-type-content { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| padding: 12px; | |||
| .spec-type-name { | |||
| font-weight: 500; | |||
| } | |||
| .spec-type-actions { | |||
| display: flex; | |||
| gap: 4px; | |||
| } | |||
| } | |||
| .spec-items { | |||
| border-top: 1px solid #f0f0f0; | |||
| padding: 8px 12px; | |||
| .spec-items-header { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-bottom: 8px; | |||
| font-size: 12px; | |||
| color: #666; | |||
| } | |||
| .spec-item { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| padding: 6px 8px; | |||
| background: #fafafa; | |||
| border-radius: 4px; | |||
| margin-bottom: 4px; | |||
| .spec-item-name { | |||
| font-size: 12px; | |||
| } | |||
| .spec-item-actions { | |||
| display: flex; | |||
| gap: 2px; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .generate-section { | |||
| margin-top: 20px; | |||
| padding: 16px; | |||
| background: #f0f9ff; | |||
| border-radius: 6px; | |||
| text-align: center; | |||
| .generate-tip { | |||
| margin: 8px 0 0 0; | |||
| font-size: 12px; | |||
| color: #666; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .spec-right-panel { | |||
| flex: 1; | |||
| .spec-card { | |||
| height: 100%; | |||
| } | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,70 +1,199 @@ | |||
| <template> | |||
| <div style="min-height: 400px"> | |||
| <BasicForm @register="registerForm"></BasicForm> | |||
| <div style="width: 100%;text-align: center" v-if="!formDisabled"> | |||
| <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button> | |||
| </div> | |||
| <div> | |||
| <!-- 子表单区域 --> | |||
| <a-tabs v-model:activeKey="activeKey" animated @change="handleChangeTabs"> | |||
| <!--主表区域 --> | |||
| <a-tab-pane tab="产品表" :key="refKeys[0]" :forceRender="true" :style="tabsStyle"> | |||
| <BasicForm @register="registerForm" ref="formRef"/> | |||
| </a-tab-pane> | |||
| <!--子表单区域 --> | |||
| <a-tab-pane tab="产品规格表" key="appletProductSpec" :forceRender="true" :style="tabsStyle"> | |||
| <JVxeTable | |||
| keep-source | |||
| resizable | |||
| ref="appletProductSpec" | |||
| v-if="appletProductSpecTable.show" | |||
| :loading="appletProductSpecTable.loading" | |||
| :columns="appletProductSpecTable.columns" | |||
| :dataSource="appletProductSpecTable.dataSource" | |||
| :height="340" | |||
| :disabled="formDisabled" | |||
| :rowNumber="true" | |||
| :rowSelection="true" | |||
| :toolbar="true" | |||
| /> | |||
| </a-tab-pane> | |||
| <a-tab-pane tab="产品套餐" key="appletProductPackage" :forceRender="true" :style="tabsStyle"> | |||
| <JVxeTable | |||
| keep-source | |||
| resizable | |||
| ref="appletProductPackage" | |||
| v-if="appletProductPackageTable.show" | |||
| :loading="appletProductPackageTable.loading" | |||
| :columns="appletProductPackageTable.columns" | |||
| :dataSource="appletProductPackageTable.dataSource" | |||
| :height="340" | |||
| :disabled="formDisabled" | |||
| :rowNumber="true" | |||
| :rowSelection="true" | |||
| :toolbar="true" | |||
| /> | |||
| </a-tab-pane> | |||
| </a-tabs> | |||
| <div style="width: 100%;text-align: center;margin-top: 10px;" v-if="showFlowSubmitButton"> | |||
| <a-button preIcon="ant-design:check-outlined" style="width: 126px" type="primary" @click="handleSubmit">提 交</a-button> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| <script lang="ts"> | |||
| <script lang="ts" setup> | |||
| import { defHttp } from '/@/utils/http/axios'; | |||
| import {ref, computed, unref,reactive, onMounted, defineProps } from 'vue'; | |||
| import {BasicForm, useForm} from '/@/components/Form/index'; | |||
| import {computed, defineComponent} from 'vue'; | |||
| import {defHttp} from '/@/utils/http/axios'; | |||
| import { propTypes } from '/@/utils/propTypes'; | |||
| import {getBpmFormSchema} from '../AppletProduct.data'; | |||
| import {saveOrUpdate} from '../AppletProduct.api'; | |||
| import { JVxeTable } from '/@/components/jeecg/JVxeTable' | |||
| import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts' | |||
| import {formSchema,appletProductSpecColumns,appletProductPackageColumns} from '../AppletProduct.data'; | |||
| import {saveOrUpdate,appletProductSpecList,appletProductPackageList} from '../AppletProduct.api'; | |||
| import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils' | |||
| const refKeys = ref(['appletProduct','appletProductSpec', 'appletProductPackage', ]); | |||
| const activeKey = ref('appletProduct'); | |||
| const appletProductSpec = ref(); | |||
| const appletProductPackage = ref(); | |||
| const tableRefs = {appletProductSpec, appletProductPackage, }; | |||
| const appletProductSpecTable = reactive({ | |||
| loading: false, | |||
| dataSource: [], | |||
| columns:appletProductSpecColumns, | |||
| show: false | |||
| }) | |||
| const appletProductPackageTable = reactive({ | |||
| loading: false, | |||
| dataSource: [], | |||
| columns:appletProductPackageColumns, | |||
| show: false | |||
| }) | |||
| const props = defineProps({ | |||
| formData: { type: Object, default: ()=>{} }, | |||
| formBpm: { type: Boolean, default: true } | |||
| }); | |||
| const formDisabled = computed(()=>{ | |||
| if(props.formBpm === true){ | |||
| if(props.formData.disabled === false){ | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| }); | |||
| // 是否显示提交按钮 | |||
| const showFlowSubmitButton = computed(()=>{ | |||
| if(props.formBpm === true){ | |||
| if(props.formData.disabled === false){ | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| }); | |||
| export default defineComponent({ | |||
| name: "AppletProductForm", | |||
| components:{ | |||
| BasicForm | |||
| }, | |||
| props:{ | |||
| formData: propTypes.object.def({}), | |||
| formBpm: propTypes.bool.def(true), | |||
| }, | |||
| setup(props){ | |||
| const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({ | |||
| labelWidth: 150, | |||
| schemas: getBpmFormSchema(props.formData), | |||
| showActionButtonGroup: false, | |||
| baseColProps: {span: 24} | |||
| }); | |||
| //表单配置 | |||
| const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({ | |||
| labelWidth: 150, | |||
| schemas: formSchema, | |||
| showActionButtonGroup: false, | |||
| baseColProps: {span: 24} | |||
| }); | |||
| const formDisabled = computed(()=>{ | |||
| if(props.formData.disabled === false){ | |||
| return false; | |||
| } | |||
| return true; | |||
| }); | |||
| onMounted(()=>{ | |||
| initFormData(); | |||
| }); | |||
| //渲染流程表单数据 | |||
| const queryByIdUrl = '/appletProduct/appletProduct/queryById'; | |||
| async function initFormData(){ | |||
| if(props.formBpm === true){ | |||
| await reset(); | |||
| let params = {id: props.formData.dataId}; | |||
| const data = await defHttp.get({url: queryByIdUrl, params}); | |||
| //表单赋值 | |||
| await setFieldsValue({ | |||
| ...data | |||
| }); | |||
| requestSubTableData(appletProductSpecList, {id: data.id}, appletProductSpecTable, ()=>{ | |||
| appletProductSpecTable.show = true; | |||
| }) | |||
| requestSubTableData(appletProductPackageList, {id: data.id}, appletProductPackageTable, ()=>{ | |||
| appletProductPackageTable.show = true; | |||
| }) | |||
| // 隐藏底部时禁用整个表单 | |||
| setProps({ disabled: formDisabled.value }) | |||
| } | |||
| } | |||
| let formData = {}; | |||
| const queryByIdUrl = '/appletProduct/appletProduct/queryById'; | |||
| async function initFormData(){ | |||
| let params = {id: props.formData.dataId}; | |||
| const data = await defHttp.get({url: queryByIdUrl, params}); | |||
| formData = {...data} | |||
| //设置表单的值 | |||
| await setFieldsValue(formData); | |||
| //默认是禁用 | |||
| await setProps({disabled: formDisabled.value}) | |||
| } | |||
| //方法配置 | |||
| const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys); | |||
| // 弹窗tabs滚动区域的高度 | |||
| const tabsStyle = computed(() => { | |||
| let height: Nullable<string> = null | |||
| let minHeight = '100px' | |||
| // 弹窗wrapper | |||
| let overflow = 'auto'; | |||
| return {height, minHeight, overflow}; | |||
| }) | |||
| async function submitForm() { | |||
| let data = getFieldsValue(); | |||
| let params = Object.assign({}, formData, data); | |||
| console.log('表单数据', params) | |||
| await saveOrUpdate(params, true) | |||
| } | |||
| async function reset(){ | |||
| await resetFields(); | |||
| activeKey.value = 'appletProduct'; | |||
| appletProductSpecTable.dataSource = []; | |||
| appletProductPackageTable.dataSource = []; | |||
| } | |||
| function classifyIntoFormData(allValues) { | |||
| let main = Object.assign({}, allValues.formValue) | |||
| return { | |||
| ...main, // 展开 | |||
| appletProductSpecList: allValues.tablesValue[0].tableData, | |||
| appletProductPackageList: allValues.tablesValue[1].tableData, | |||
| } | |||
| } | |||
| //表单提交事件 | |||
| async function requestAddOrEdit(values) { | |||
| //提交表单 | |||
| await saveOrUpdate(values, true); | |||
| } | |||
| </script> | |||
| initFormData(); | |||
| return { | |||
| registerForm, | |||
| formDisabled, | |||
| submitForm, | |||
| } | |||
| } | |||
| }); | |||
| </script> | |||
| <style lang="less" scoped> | |||
| /** 时间和数字输入框样式 */ | |||
| :deep(.ant-input-number) { | |||
| width: 100%; | |||
| } | |||
| :deep(.ant-calendar-picker) { | |||
| width: 100%; | |||
| } | |||
| </style> | |||
| <style lang="less"> | |||
| // Online表单Tab风格专属样式 | |||
| .j-cgform-tab-modal { | |||
| .ant-modal-header { | |||
| padding-top: 8px; | |||
| padding-bottom: 8px; | |||
| border-bottom: none !important; | |||
| } | |||
| .ant-modal .ant-modal-body > .scrollbar, | |||
| .ant-tabs-nav .ant-tabs-tab { | |||
| padding-top: 0; | |||
| } | |||
| .ant-tabs-top-bar { | |||
| width: calc(100% - 55px); | |||
| position: relative; | |||
| left: -14px; | |||
| } | |||
| .ant-tabs .ant-tabs-top-content > .ant-tabs-tabpane { | |||
| overflow: hidden auto; | |||
| } | |||
| } | |||
| </style> | |||
| @ -1,73 +0,0 @@ | |||
| <template> | |||
| <BasicModal | |||
| v-bind="$attrs" | |||
| @register="registerModal" | |||
| :title="getTitle" | |||
| @ok="handleSubmit" | |||
| width="800px" | |||
| :height="500" | |||
| destroyOnClose | |||
| > | |||
| <BasicForm @register="registerForm" /> | |||
| </BasicModal> | |||
| </template> | |||
| <script lang="ts" setup> | |||
| import { ref, computed, unref } from 'vue'; | |||
| import { BasicModal, useModalInner } from '/@/components/Modal'; | |||
| import { BasicForm, useForm } from '/@/components/Form/index'; | |||
| import { formSchema } from '../AppletProductSpec.data'; | |||
| import { saveOrUpdate } from '../AppletProductSpec.api'; | |||
| import { useMessage } from '/@/hooks/web/useMessage'; | |||
| const emit = defineEmits(['register', 'success']); | |||
| const { createMessage } = useMessage(); | |||
| const isUpdate = ref(true); | |||
| const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({ | |||
| labelWidth: 100, | |||
| schemas: formSchema, | |||
| showActionButtonGroup: false, | |||
| }); | |||
| const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { | |||
| //重置表单数据 | |||
| await resetFields(); | |||
| setModalProps({ confirmLoading: false }); | |||
| isUpdate.value = !!data?.isUpdate; | |||
| if (unref(isUpdate)) { | |||
| // 编辑 | |||
| setFieldsValue({ | |||
| ...data.record, | |||
| }); | |||
| } else { | |||
| // 新增 | |||
| setFieldsValue({ | |||
| productId: data.productId, | |||
| }); | |||
| } | |||
| }); | |||
| const getTitle = computed(() => (!unref(isUpdate) ? '新增产品规格' : '编辑产品规格')); | |||
| async function handleSubmit() { | |||
| try { | |||
| let values = await validate(); | |||
| setModalProps({ confirmLoading: true }); | |||
| //提交数据 | |||
| await saveOrUpdate(values, unref(isUpdate)); | |||
| closeModal(); | |||
| emit('success'); | |||
| createMessage.success(unref(isUpdate) ? '编辑成功!' : '新增成功!'); | |||
| } finally { | |||
| setModalProps({ confirmLoading: false }); | |||
| } | |||
| } | |||
| </script> | |||
| <style lang="less" scoped> | |||
| :deep(.ant-picker),:deep(.ant-input-number){ | |||
| width: 100%; | |||
| } | |||
| </style> | |||
| @ -1,175 +0,0 @@ | |||
| <template> | |||
| <BasicModal | |||
| v-bind="$attrs" | |||
| @register="registerModal" | |||
| :title="getTitle" | |||
| @ok="handleSubmit" | |||
| width="1200px" | |||
| :height="600" | |||
| destroyOnClose | |||
| > | |||
| <div> | |||
| <a-button type="primary" @click="handleAdd" style="margin-bottom: 16px;">新增规格</a-button> | |||
| <div v-if="loading">加载中...</div> | |||
| <div v-else> | |||
| <a-table :dataSource="tableData" :columns="tableColumns" :pagination="false"> | |||
| <template #bodyCell="{ column, record }"> | |||
| <template v-if="column.key === 'action'"> | |||
| <a-space> | |||
| <a @click="handleEdit(record)">编辑</a> | |||
| <a @click="handleDelete(record)">删除</a> | |||
| </a-space> | |||
| </template> | |||
| </template> | |||
| </a-table> | |||
| </div> | |||
| </div> | |||
| <!-- 表单区域 --> | |||
| <AppletProductSpecForm @register="registerForm" @success="handleSuccess"></AppletProductSpecForm> | |||
| </BasicModal> | |||
| </template> | |||
| <script lang="ts" setup> | |||
| import {ref, computed, unref, onMounted} from 'vue'; | |||
| import {BasicModal, useModalInner} from '/@/components/Modal'; | |||
| import {useModal} from '/@/components/Modal'; | |||
| import AppletProductSpecForm from './AppletProductSpecForm.vue' | |||
| import {list, deleteOne} from '../AppletProductSpec.api'; | |||
| import { useMessage } from '/@/hooks/web/useMessage'; | |||
| const emit = defineEmits(['register', 'success']); | |||
| const { createMessage } = useMessage(); | |||
| //注册model | |||
| const [registerForm, {openModal: openFormModal}] = useModal(); | |||
| const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => { | |||
| console.log('弹窗数据:', data); | |||
| //重置表单数据 | |||
| setModalProps({confirmLoading: false}); | |||
| productId.value = data?.record?.id || ''; | |||
| console.log('设置产品ID:', productId.value); | |||
| // 加载数据 | |||
| loadData(); | |||
| }); | |||
| const productId = ref<string>(''); | |||
| const loading = ref(false); | |||
| const tableData = ref([]); | |||
| const getTitle = computed(() => '产品规格管理'); | |||
| // 表格列配置 | |||
| const tableColumns = [ | |||
| { | |||
| title: '规格名称', | |||
| dataIndex: 'specName', | |||
| key: 'specName', | |||
| }, | |||
| { | |||
| title: '规格值', | |||
| dataIndex: 'specValue', | |||
| key: 'specValue', | |||
| }, | |||
| { | |||
| title: '价格', | |||
| dataIndex: 'price', | |||
| key: 'price', | |||
| }, | |||
| { | |||
| title: '排序', | |||
| dataIndex: 'sortOrder', | |||
| key: 'sortOrder', | |||
| }, | |||
| { | |||
| title: '操作', | |||
| key: 'action', | |||
| width: 150, | |||
| }, | |||
| ]; | |||
| // 加载数据 | |||
| async function loadData() { | |||
| if (!unref(productId)) return; | |||
| loading.value = true; | |||
| try { | |||
| const params = { | |||
| productId: unref(productId), | |||
| pageNo: 1, | |||
| pageSize: 100 | |||
| }; | |||
| console.log('查询参数:', params); | |||
| const result = await list(params); | |||
| console.log('查询结果:', result); | |||
| if (result && result.records) { | |||
| tableData.value = result.records; | |||
| } else { | |||
| tableData.value = []; | |||
| } | |||
| } catch (error) { | |||
| console.error('加载数据失败:', error); | |||
| createMessage.error('加载数据失败!'); | |||
| tableData.value = []; | |||
| } finally { | |||
| loading.value = false; | |||
| } | |||
| } | |||
| /** | |||
| * 新增事件 | |||
| */ | |||
| function handleAdd() { | |||
| console.log('新增规格,产品ID:', unref(productId)); | |||
| openFormModal(true, { | |||
| isUpdate: false, | |||
| showFooter: true, | |||
| productId: unref(productId), | |||
| }); | |||
| } | |||
| /** | |||
| * 编辑事件 | |||
| */ | |||
| function handleEdit(record: Recordable) { | |||
| console.log('编辑规格:', record); | |||
| openFormModal(true, { | |||
| record, | |||
| isUpdate: true, | |||
| showFooter: true, | |||
| productId: unref(productId), | |||
| }); | |||
| } | |||
| /** | |||
| * 删除事件 | |||
| */ | |||
| async function handleDelete(record) { | |||
| console.log('删除规格:', record); | |||
| try { | |||
| await deleteOne({id: record.id}, handleSuccess); | |||
| createMessage.success('删除成功!'); | |||
| } catch (error) { | |||
| console.error('删除失败:', error); | |||
| createMessage.error('删除失败!'); | |||
| } | |||
| } | |||
| /** | |||
| * 成功回调 | |||
| */ | |||
| function handleSuccess() { | |||
| console.log('操作成功,刷新列表'); | |||
| loadData(); | |||
| } | |||
| /** | |||
| * 提交事件 | |||
| */ | |||
| function handleSubmit() { | |||
| closeModal(); | |||
| } | |||
| </script> | |||
| <style lang="less" scoped> | |||
| :deep(.ant-picker),:deep(.ant-input-number){ | |||
| width: 100%; | |||
| } | |||
| </style> | |||