Ver Fonte

feat: 岗位信息CRUD

zhangying há 3 dias atrás
pai
commit
e2e9f5a3c2

+ 190 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/post/controller/PostInfoController.java

@@ -0,0 +1,190 @@
+package org.jeecg.modules.zjrs.post.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.zjrs.post.entity.PostInfo;
+import org.jeecg.modules.zjrs.post.service.IPostInfoService;
+import org.jeecg.modules.zjrs.post.vo.PostInfoVo;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @Description: 岗位信息表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-02
+ * @Version: V1.0
+ */
+@Tag(name = "岗位信息-岗位基本信息")
+@RestController
+@RequestMapping("/post/postInfo")
+@Slf4j
+public class PostInfoController extends JeecgController<PostInfo, IPostInfoService> {
+    @Autowired
+    private IPostInfoService postInfoService;
+
+    /**
+     * 分页列表查询
+     *
+     * @param postInfo
+     * @param pageNo
+     * @param pageSize
+     * @param req
+     * @return
+     */
+    @Operation(summary = "岗位信息-岗位基本信息-分页列表查询")
+    @GetMapping(value = "/list")
+    public Result<IPage<PostInfoVo>> queryPageList(PostInfo postInfo,
+                                                   @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+                                                   @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
+                                                   HttpServletRequest req) {
+        QueryWrapper<PostInfo> queryWrapper = QueryGenerator.initQueryWrapper(postInfo, req.getParameterMap());
+        Page<PostInfoVo> page = new Page<PostInfoVo>(pageNo, pageSize);
+        IPage<PostInfoVo> pageList = postInfoService.queryPageListWithEnterprise(page, queryWrapper);
+        return Result.OK(pageList);
+    }
+
+    /**
+     * 添加
+     *
+     * @param postInfo
+     * @return
+     */
+    @AutoLog(value = "岗位信息-岗位基本信息-添加")
+    @Operation(summary = "岗位信息-岗位基本信息-添加")
+    @RequiresPermissions("post:post_info:add")
+    @PostMapping(value = "/add")
+    public Result<String> add(@RequestBody PostInfo postInfo) {
+        postInfoService.save(postInfo);
+        return Result.OK("添加成功!");
+    }
+
+    /**
+     * 编辑
+     *
+     * @param postInfo
+     * @return
+     */
+    @AutoLog(value = "岗位信息-岗位基本信息-编辑")
+    @Operation(summary = "岗位信息-岗位基本信息-编辑")
+    @RequiresPermissions("post:post_info:edit")
+    @RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
+    public Result<String> edit(@RequestBody PostInfo postInfo) {
+        postInfoService.updateById(postInfo);
+        return Result.OK("编辑成功!");
+    }
+
+    /**
+     * 通过id删除
+     *
+     * @param id
+     * @return
+     */
+    @AutoLog(value = "岗位信息-岗位基本信息-通过id删除")
+    @Operation(summary = "岗位信息-岗位基本信息-通过id删除")
+    @RequiresPermissions("post:post_info:delete")
+    @DeleteMapping(value = "/delete")
+    public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
+        postInfoService.removeById(id);
+        return Result.OK("删除成功!");
+    }
+
+    /**
+     * 批量删除
+     *
+     * @param ids
+     * @return
+     */
+    @AutoLog(value = "岗位信息-岗位基本信息-批量删除")
+    @Operation(summary = "岗位信息-岗位基本信息-批量删除")
+    @RequiresPermissions("post:post_info:deleteBatch")
+    @DeleteMapping(value = "/deleteBatch")
+    public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
+        this.postInfoService.removeByIds(Arrays.asList(ids.split(",")));
+        return Result.OK("批量删除成功!");
+    }
+
+    /**
+     * 通过id查询
+     *
+     * @param id
+     * @return
+     */
+    @Operation(summary = "岗位信息-岗位基本信息-通过id查询")
+    @GetMapping(value = "/queryById")
+    public Result<PostInfo> queryById(@RequestParam(name = "id", required = true) String id) {
+        PostInfo postInfo = postInfoService.getById(id);
+        if (postInfo == null) {
+            return Result.error("未找到对应数据");
+        }
+        return Result.OK(postInfo);
+    }
+
+    /**
+     * 导出excel
+     *
+     * @param request
+     * @param postInfo
+     */
+    @RequiresPermissions("post:post_info:exportXls")
+    @RequestMapping(value = "/exportXls")
+    public ModelAndView exportXls(HttpServletRequest request, PostInfo postInfo) {
+        QueryWrapper<PostInfo> queryWrapper = QueryGenerator.initQueryWrapper(postInfo, request.getParameterMap());
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        String selections = request.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> selectionList = Arrays.asList(selections.split(","));
+            queryWrapper.in("p.id", selectionList);
+        }
+
+        List<PostInfoVo> exportList = postInfoService.queryListWithEnterprise(queryWrapper);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "岗位信息表");
+        mv.addObject(NormalExcelConstants.CLASS, PostInfoVo.class);
+        ExportParams exportParams = new ExportParams("岗位信息报表", "导出人:" + sysUser.getRealname(), "岗位信息", ExcelType.XSSF);
+        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
+        mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
+
+        String exportFields = request.getParameter(NormalExcelConstants.EXPORT_FIELDS);
+        if (oConvertUtils.isNotEmpty(exportFields)) {
+            mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
+        }
+        return mv;
+    }
+
+    /**
+     * 通过excel导入数据
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    @RequiresPermissions("post:post_info:importExcel")
+    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
+    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
+        return super.importExcel(request, response, PostInfo.class);
+    }
+}

+ 199 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/post/entity/PostInfo.java

@@ -0,0 +1,199 @@
+package org.jeecg.modules.zjrs.post.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @Description: 岗位信息表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-02
+ * @Version: V1.0
+ */
+@Data
+@TableName("post_info")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "岗位信息表")
+public class PostInfo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    @Schema(description = "主键ID")
+    private java.lang.String id;
+    /**
+     * 关联企业基本信息ID
+     */
+    @Excel(name = "关联企业基本信息ID", width = 15)
+    @Schema(description = "关联企业基本信息ID")
+    private java.lang.String enterpriseId;
+    /**
+     * 职业
+     */
+    @Excel(name = "职业", width = 15)
+    @Schema(description = "职业")
+    private java.lang.String occupation;
+    /**
+     * 岗位名称
+     */
+    @Excel(name = "岗位名称", width = 15)
+    @Schema(description = "岗位名称")
+    private java.lang.String postName;
+    /**
+     * 工作性质
+     */
+    @Excel(name = "工作性质", width = 15)
+    @Schema(description = "工作性质")
+    private java.lang.String workNature;
+    /**
+     * 薪酬待遇
+     */
+    @Excel(name = "薪酬待遇", width = 15)
+    @Schema(description = "薪酬待遇")
+    private java.lang.String salaryType;
+    /**
+     * 最低薪酬
+     */
+    @Excel(name = "最低薪酬", width = 15)
+    @Schema(description = "最低薪酬")
+    private BigDecimal salaryMin;
+    /**
+     * 最高薪酬
+     */
+    @Excel(name = "最高薪酬", width = 15)
+    @Schema(description = "最高薪酬")
+    private BigDecimal salaryMax;
+    /**
+     * 招聘人数
+     */
+    @Excel(name = "招聘人数", width = 15)
+    @Schema(description = "招聘人数")
+    private java.lang.Integer recruitCount;
+    /**
+     * 岗位有效期
+     */
+    @Excel(name = "岗位有效期", width = 15, format = "yyyy-MM-dd")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @Schema(description = "岗位有效期")
+    private java.util.Date postValidDate;
+    /**
+     * 职位标签
+     */
+    @Excel(name = "职位标签", width = 15)
+    @Schema(description = "职位标签")
+    private java.lang.String postTag;
+    /**
+     * 其他福利
+     */
+    @Excel(name = "其他福利", width = 15)
+    @Schema(description = "其他福利")
+    private java.lang.String otherBenefits;
+    /**
+     * 岗位描述
+     */
+    @Schema(description = "岗位描述")
+    private java.lang.String postDesc;
+    /**
+     * 学历要求
+     */
+    @Excel(name = "学历要求", width = 15)
+    @Schema(description = "学历要求")
+    private java.lang.String educationRequire;
+    /**
+     * 工作经验
+     */
+    @Excel(name = "工作经验", width = 15)
+    @Schema(description = "工作经验")
+    private java.lang.String workExperienceRequire;
+    /**
+     * 职业技能等级
+     */
+    @Excel(name = "职业技能等级", width = 15)
+    @Schema(description = "职业技能等级")
+    private java.lang.String skillLevelRequire;
+    /**
+     * 外语语种
+     */
+    @Excel(name = "外语语种", width = 15)
+    @Schema(description = "外语语种")
+    private java.lang.String foreignLanguage;
+    /**
+     * 外语水平要求
+     */
+    @Excel(name = "外语水平要求", width = 15)
+    @Schema(description = "外语水平要求")
+    private java.lang.String languageLevel;
+    /**
+     * 联系人
+     */
+    @Excel(name = "联系人", width = 15)
+    @Schema(description = "联系人")
+    private java.lang.String contactPerson;
+    /**
+     * 联系电话
+     */
+    @Excel(name = "联系电话", width = 15)
+    @Schema(description = "联系电话")
+    private java.lang.String contactPhone;
+    /**
+     * 工作地点
+     */
+    @Excel(name = "工作地点", width = 15)
+    @Schema(description = "工作地点")
+    private java.lang.String workLocation;
+    /**
+     * 详细地址
+     */
+    @Excel(name = "详细地址", width = 15)
+    @Schema(description = "详细地址")
+    private java.lang.String detailedAddress;
+    /**
+     * 数据来源
+     */
+    @Excel(name = "数据来源", width = 15)
+    @Schema(description = "数据来源")
+    private java.lang.String dataSource;
+    /**
+     * 创建人
+     */
+    @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;
+}

+ 23 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/post/mapper/PostInfoMapper.java

@@ -0,0 +1,23 @@
+package org.jeecg.modules.zjrs.post.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import org.jeecg.modules.zjrs.post.entity.PostInfo;
+import org.jeecg.modules.zjrs.post.vo.PostInfoVo;
+
+import java.util.List;
+
+/**
+ * @Description: 岗位信息表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-02
+ * @Version: V1.0
+ */
+public interface PostInfoMapper extends BaseMapper<PostInfo> {
+
+    IPage<PostInfoVo> queryPageListWithEnterprise(Page<PostInfoVo> page, @Param("ew") com.baomidou.mybatisplus.core.conditions.Wrapper<PostInfo> queryWrapper);
+
+    List<PostInfoVo> queryListWithEnterprise(@Param("ew") com.baomidou.mybatisplus.core.conditions.Wrapper<PostInfo> queryWrapper);
+}

+ 75 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/post/mapper/xml/PostInfoMapper.xml

@@ -0,0 +1,75 @@
+<?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.zjrs.post.mapper.PostInfoMapper">
+
+    <select id="queryPageListWithEnterprise" resultType="org.jeecg.modules.zjrs.post.vo.PostInfoVo">
+        SELECT p.id,
+               p.enterprise_id,
+               p.occupation,
+               p.post_name,
+               p.work_nature,
+               p.salary_type,
+               p.salary_min,
+               p.salary_max,
+               p.recruit_count,
+               p.post_valid_date,
+               p.post_tag,
+               p.other_benefits,
+               p.post_desc,
+               p.education_require,
+               p.work_experience_require,
+               p.skill_level_require,
+               p.foreign_language,
+               p.language_level,
+               p.contact_person,
+               p.contact_phone,
+               p.work_location,
+               p.detailed_address,
+               p.data_source,
+               p.create_by,
+               p.create_time,
+               p.update_by,
+               p.update_time,
+               p.sys_org_code,
+               e.company_name AS company_name
+        FROM post_info p
+                 LEFT JOIN enterprise_info e ON p.enterprise_id = e.id
+            ${ew.customSqlSegment}
+    </select>
+
+    <select id="queryListWithEnterprise" resultType="org.jeecg.modules.zjrs.post.vo.PostInfoVo">
+        SELECT p.id,
+               p.enterprise_id,
+               p.occupation,
+               p.post_name,
+               p.work_nature,
+               p.salary_type,
+               p.salary_min,
+               p.salary_max,
+               p.recruit_count,
+               p.post_valid_date,
+               p.post_tag,
+               p.other_benefits,
+               p.post_desc,
+               p.education_require,
+               p.work_experience_require,
+               p.skill_level_require,
+               p.foreign_language,
+               p.language_level,
+               p.contact_person,
+               p.contact_phone,
+               p.work_location,
+               p.detailed_address,
+               p.data_source,
+               p.create_by,
+               p.create_time,
+               p.update_by,
+               p.update_time,
+               p.sys_org_code,
+               e.company_name AS company_name
+        FROM post_info p
+                 LEFT JOIN enterprise_info e ON p.enterprise_id = e.id
+            ${ew.customSqlSegment}
+    </select>
+
+</mapper>

+ 23 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/post/service/IPostInfoService.java

@@ -0,0 +1,23 @@
+package org.jeecg.modules.zjrs.post.service;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.zjrs.post.entity.PostInfo;
+import org.jeecg.modules.zjrs.post.vo.PostInfoVo;
+
+import java.util.List;
+
+/**
+ * @Description: 岗位信息表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-02
+ * @Version: V1.0
+ */
+public interface IPostInfoService extends IService<PostInfo> {
+
+    IPage<PostInfoVo> queryPageListWithEnterprise(Page<PostInfoVo> page, Wrapper<PostInfo> queryWrapper);
+
+    List<PostInfoVo> queryListWithEnterprise(Wrapper<PostInfo> queryWrapper);
+}

+ 70 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/post/service/impl/PostInfoServiceImpl.java

@@ -0,0 +1,70 @@
+package org.jeecg.modules.zjrs.post.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.jeecg.common.util.AssertUtils;
+import org.jeecg.modules.zjrs.post.entity.PostInfo;
+import org.jeecg.modules.zjrs.post.mapper.PostInfoMapper;
+import org.jeecg.modules.zjrs.post.service.IPostInfoService;
+import org.jeecg.modules.zjrs.post.vo.PostInfoVo;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @Description: 岗位信息表
+ * @Author: jeecg-boot
+ * @Date: 2026-06-02
+ * @Version: V1.0
+ */
+@Service
+public class PostInfoServiceImpl extends ServiceImpl<PostInfoMapper, PostInfo> implements IPostInfoService {
+
+    @Override
+    public IPage<PostInfoVo> queryPageListWithEnterprise(Page<PostInfoVo> page, Wrapper<PostInfo> queryWrapper) {
+        return baseMapper.queryPageListWithEnterprise(page, queryWrapper);
+    }
+
+    @Override
+    public List<PostInfoVo> queryListWithEnterprise(Wrapper<PostInfo> queryWrapper) {
+        return baseMapper.queryListWithEnterprise(queryWrapper);
+    }
+
+    @Override
+    public boolean save(PostInfo postInfo) {
+        validatePostInfo(postInfo);
+        return super.save(postInfo);
+    }
+
+    @Override
+    public boolean updateById(PostInfo postInfo) {
+        validatePostInfo(postInfo);
+        return super.updateById(postInfo);
+    }
+
+    private void validatePostInfo(PostInfo postInfo) {
+        AssertUtils.assertNotEmpty("关联企业不能为空", postInfo.getEnterpriseId());
+        AssertUtils.assertNotEmpty("职业不能为空", postInfo.getOccupation());
+        AssertUtils.assertNotEmpty("岗位名称不能为空", postInfo.getPostName());
+        AssertUtils.assertNotEmpty("工作性质不能为空", postInfo.getWorkNature());
+        AssertUtils.assertNotEmpty("薪酬待遇不能为空", postInfo.getSalaryType());
+        AssertUtils.assertTrue("招聘人数必须大于0", postInfo.getRecruitCount() != null && postInfo.getRecruitCount() > 0);
+        AssertUtils.assertNotEmpty("岗位有效期不能为空", postInfo.getPostValidDate());
+        AssertUtils.assertNotEmpty("职位标签不能为空", postInfo.getPostTag());
+        AssertUtils.assertNotEmpty("岗位描述不能为空", postInfo.getPostDesc());
+        AssertUtils.assertNotEmpty("学历要求不能为空", postInfo.getEducationRequire());
+        AssertUtils.assertNotEmpty("工作经验不能为空", postInfo.getWorkExperienceRequire());
+        AssertUtils.assertNotEmpty("职业技能等级不能为空", postInfo.getSkillLevelRequire());
+        AssertUtils.assertNotEmpty("联系人不能为空", postInfo.getContactPerson());
+        AssertUtils.assertNotEmpty("联系电话不能为空", postInfo.getContactPhone());
+        AssertUtils.assertTrue("联系电话格式不正确", postInfo.getContactPhone() != null && postInfo.getContactPhone().matches("^1[3-9]\\d{9}$"));
+        AssertUtils.assertNotEmpty("工作地点不能为空", postInfo.getWorkLocation());
+        AssertUtils.assertNotEmpty("详细地址不能为空", postInfo.getDetailedAddress());
+        AssertUtils.assertNotEmpty("数据来源不能为空", postInfo.getDataSource());
+        if (postInfo.getSalaryMin() != null && postInfo.getSalaryMax() != null) {
+            AssertUtils.assertTrue("最高薪酬不能小于最低薪酬", postInfo.getSalaryMax().compareTo(postInfo.getSalaryMin()) >= 0);
+        }
+    }
+}

+ 197 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/post/vo/PostInfoVo.java

@@ -0,0 +1,197 @@
+package org.jeecg.modules.zjrs.post.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+
+/**
+ * @Description: 岗位信息(关联单位名称)
+ * @Author: jeecg-boot
+ * @Date: 2026-06-02
+ * @Version: V1.0
+ */
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@Schema(description = "岗位信息(关联单位名称)")
+public class PostInfoVo {
+
+    /**
+     * 主键ID
+     */
+    @Schema(description = "主键ID")
+    private java.lang.String id;
+    /**
+     * 关联企业基本信息ID
+     */
+    @Schema(description = "关联企业基本信息ID")
+    private java.lang.String enterpriseId;
+    /**
+     * 单位名称(关联enterprise_info表)
+     */
+    @Excel(name = "单位名称", width = 15)
+    @Schema(description = "单位名称")
+    private java.lang.String companyName;
+    /**
+     * 职业
+     */
+    @Excel(name = "职业", width = 15)
+    @Schema(description = "职业")
+    private java.lang.String occupation;
+    /**
+     * 岗位名称
+     */
+    @Excel(name = "岗位名称", width = 15)
+    @Schema(description = "岗位名称")
+    private java.lang.String postName;
+    /**
+     * 工作性质
+     */
+    @Excel(name = "工作性质", width = 15)
+    @Schema(description = "工作性质")
+    private java.lang.String workNature;
+    /**
+     * 薪酬待遇
+     */
+    @Excel(name = "薪酬待遇", width = 15)
+    @Schema(description = "薪酬待遇")
+    private java.lang.String salaryType;
+    /**
+     * 最低薪酬
+     */
+    @Excel(name = "最低薪酬", width = 15)
+    @Schema(description = "最低薪酬")
+    private BigDecimal salaryMin;
+    /**
+     * 最高薪酬
+     */
+    @Excel(name = "最高薪酬", width = 15)
+    @Schema(description = "最高薪酬")
+    private BigDecimal salaryMax;
+    /**
+     * 招聘人数
+     */
+    @Excel(name = "招聘人数", width = 15)
+    @Schema(description = "招聘人数")
+    private java.lang.Integer recruitCount;
+    /**
+     * 岗位有效期
+     */
+    @Excel(name = "岗位有效期", width = 15, format = "yyyy-MM-dd")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @Schema(description = "岗位有效期")
+    private java.util.Date postValidDate;
+    /**
+     * 职位标签
+     */
+    @Excel(name = "职位标签", width = 15)
+    @Schema(description = "职位标签")
+    private java.lang.String postTag;
+    /**
+     * 其他福利
+     */
+    @Excel(name = "其他福利", width = 15)
+    @Schema(description = "其他福利")
+    private java.lang.String otherBenefits;
+    /**
+     * 岗位描述
+     */
+    @Schema(description = "岗位描述")
+    private java.lang.String postDesc;
+    /**
+     * 学历要求
+     */
+    @Excel(name = "学历要求", width = 15)
+    @Schema(description = "学历要求")
+    private java.lang.String educationRequire;
+    /**
+     * 工作经验
+     */
+    @Excel(name = "工作经验", width = 15)
+    @Schema(description = "工作经验")
+    private java.lang.String workExperienceRequire;
+    /**
+     * 职业技能等级
+     */
+    @Excel(name = "职业技能等级", width = 15)
+    @Schema(description = "职业技能等级")
+    private java.lang.String skillLevelRequire;
+    /**
+     * 外语语种
+     */
+    @Excel(name = "外语语种", width = 15)
+    @Schema(description = "外语语种")
+    private java.lang.String foreignLanguage;
+    /**
+     * 外语水平要求
+     */
+    @Excel(name = "外语水平要求", width = 15)
+    @Schema(description = "外语水平要求")
+    private java.lang.String languageLevel;
+    /**
+     * 联系人
+     */
+    @Excel(name = "联系人", width = 15)
+    @Schema(description = "联系人")
+    private java.lang.String contactPerson;
+    /**
+     * 联系电话
+     */
+    @Excel(name = "联系电话", width = 15)
+    @Schema(description = "联系电话")
+    private java.lang.String contactPhone;
+    /**
+     * 工作地点
+     */
+    @Excel(name = "工作地点", width = 15)
+    @Schema(description = "工作地点")
+    private java.lang.String workLocation;
+    /**
+     * 详细地址
+     */
+    @Excel(name = "详细地址", width = 15)
+    @Schema(description = "详细地址")
+    private java.lang.String detailedAddress;
+    /**
+     * 数据来源
+     */
+    @Excel(name = "数据来源", width = 15)
+    @Schema(description = "数据来源")
+    private java.lang.String dataSource;
+    /**
+     * 创建人
+     */
+    @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;
+}

+ 93 - 0
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V20260603_2__menu_insert_PostInfo.sql

@@ -0,0 +1,93 @@
+-- 注意:该页面对应的前台目录为views/recruitment/post文件夹下
+-- 如果你想更改到其他目录,请修改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 ('178028566404001', '2061767672122322946', '岗位信息', '/recruitment/postInfoList',
+        'recruitment/post/PostInfoList', NULL, NULL, 1, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1',
+        0,
+        0, 'admin', '2026-06-03 00:00:00', NULL, NULL, 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 ('178028566404002', '178028566404001', '添加岗位信息', NULL, NULL, 0, NULL, NULL, 2,
+        'post:post_info:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-03 00:00:00', 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 ('178028566404003', '178028566404001', '编辑岗位信息', NULL, NULL, 0, NULL, NULL, 2,
+        'post:post_info:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-03 00:00:00', 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 ('178028566404004', '178028566404001', '删除岗位信息', NULL, NULL, 0, NULL, NULL, 2,
+        'post:post_info:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-03 00:00:00', 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 ('178028566404005', '178028566404001', '批量删除岗位信息', NULL, NULL, 0, NULL, NULL, 2,
+        'post:post_info:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-03 00:00:00',
+        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 ('178028566404006', '178028566404001', '导出excel', NULL, NULL, 0, NULL, NULL, 2,
+        'post:post_info:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-03 00:00:00',
+        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 ('178028566404007', '178028566404001', '导入excel', NULL, NULL, 0, NULL, NULL, 2,
+        'post:post_info:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2026-06-03 00:00:00',
+        NULL, NULL, 0, 0, '1', 0);
+
+-- 角色授权(以 admin 角色为例,role_id 可替换)
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178028566404008', 'f6817f48af4fb3af11b9e8bf182f618b', '178028566404001', NULL, '2026-06-03 00:00:00',
+        '127.0.0.1');
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178028566404009', 'f6817f48af4fb3af11b9e8bf182f618b', '178028566404002', NULL, '2026-06-03 00:00:00',
+        '127.0.0.1');
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178028566404010', 'f6817f48af4fb3af11b9e8bf182f618b', '178028566404003', NULL, '2026-06-03 00:00:00',
+        '127.0.0.1');
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178028566404011', 'f6817f48af4fb3af11b9e8bf182f618b', '178028566404004', NULL, '2026-06-03 00:00:00',
+        '127.0.0.1');
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178028566404012', 'f6817f48af4fb3af11b9e8bf182f618b', '178028566404005', NULL, '2026-06-03 00:00:00',
+        '127.0.0.1');
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178028566404013', 'f6817f48af4fb3af11b9e8bf182f618b', '178028566404006', NULL, '2026-06-03 00:00:00',
+        '127.0.0.1');
+INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip)
+VALUES ('178028566404014', 'f6817f48af4fb3af11b9e8bf182f618b', '178028566404007', NULL, '2026-06-03 00:00:00',
+        '127.0.0.1');

+ 72 - 0
jeecgboot-vue3/src/views/recruitment/post/PostInfo.api.ts

@@ -0,0 +1,72 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/post/postInfo/list',
+  save = '/post/postInfo/add',
+  edit = '/post/postInfo/edit',
+  deleteOne = '/post/postInfo/delete',
+  deleteBatch = '/post/postInfo/deleteBatch',
+  importExcel = '/post/postInfo/importExcel',
+  exportXls = '/post/postInfo/exportXls',
+}
+
+/**
+ * 导出api
+ * @param params
+ */
+export const getExportUrl = Api.exportXls;
+
+/**
+ * 导入api
+ */
+export const getImportUrl = Api.importExcel;
+
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });
+
+/**
+ * 删除单个
+ * @param params
+ * @param handleSuccess
+ */
+export const deleteOne = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
+    handleSuccess();
+  });
+};
+
+/**
+ * 批量删除
+ * @param params
+ * @param 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();
+      });
+    },
+  });
+};
+
+/**
+ * 保存或者更新
+ * @param params
+ * @param isUpdate
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({ url: url, params }, { isTransformResponse: false });
+};

+ 96 - 0
jeecgboot-vue3/src/views/recruitment/post/PostInfo.data.ts

@@ -0,0 +1,96 @@
+import { BasicColumn } from '/@/components/Table';
+
+//列表数据
+export const columns: BasicColumn[] = [
+  {
+    title: '单位名称',
+    align: 'center',
+    dataIndex: 'companyName',
+  },
+  {
+    title: '职业',
+    align: 'center',
+    dataIndex: 'occupation',
+  },
+  {
+    title: '岗位名称',
+    align: 'center',
+    dataIndex: 'postName',
+  },
+  {
+    title: '工作性质',
+    align: 'center',
+    dataIndex: 'workNature',
+  },
+  {
+    title: '薪酬待遇',
+    align: 'center',
+    dataIndex: 'salaryType',
+  },
+  {
+    title: '最低薪酬',
+    align: 'center',
+    dataIndex: 'salaryMin',
+  },
+  {
+    title: '最高薪酬',
+    align: 'center',
+    dataIndex: 'salaryMax',
+  },
+  {
+    title: '招聘人数',
+    align: 'center',
+    dataIndex: 'recruitCount',
+  },
+  {
+    title: '岗位有效期',
+    align: 'center',
+    dataIndex: 'postValidDate',
+  },
+  {
+    title: '学历要求',
+    align: 'center',
+    dataIndex: 'educationRequire',
+  },
+  {
+    title: '联系人',
+    align: 'center',
+    dataIndex: 'contactPerson',
+  },
+  {
+    title: '联系电话',
+    align: 'center',
+    dataIndex: 'contactPhone',
+  },
+  {
+    title: '工作地点',
+    align: 'center',
+    dataIndex: 'workLocation',
+  },
+];
+
+// 高级查询数据
+export const superQuerySchema = {
+  enterpriseId: { title: '关联企业', order: 0, view: 'text', type: 'string' },
+  occupation: { title: '职业', order: 1, view: 'text', type: 'string' },
+  postName: { title: '岗位名称', order: 2, view: 'text', type: 'string' },
+  workNature: { title: '工作性质', order: 3, view: 'text', type: 'string' },
+  salaryType: { title: '薪酬待遇', order: 4, view: 'text', type: 'string' },
+  salaryMin: { title: '最低薪酬', order: 5, view: 'text', type: 'number' },
+  salaryMax: { title: '最高薪酬', order: 6, view: 'text', type: 'number' },
+  recruitCount: { title: '招聘人数', order: 7, view: 'text', type: 'number' },
+  postValidDate: { title: '岗位有效期', order: 8, view: 'text', type: 'string' },
+  postTag: { title: '职位标签', order: 9, view: 'text', type: 'string' },
+  otherBenefits: { title: '其他福利', order: 10, view: 'text', type: 'string' },
+  postDesc: { title: '岗位描述', order: 11, view: 'text', type: 'string' },
+  educationRequire: { title: '学历要求', order: 12, view: 'text', type: 'string' },
+  workExperienceRequire: { title: '工作经验', order: 13, view: 'text', type: 'string' },
+  skillLevelRequire: { title: '职业技能等级', order: 14, view: 'text', type: 'string' },
+  foreignLanguage: { title: '外语语种', order: 15, view: 'text', type: 'string' },
+  languageLevel: { title: '外语水平要求', order: 16, view: 'text', type: 'string' },
+  contactPerson: { title: '联系人', order: 17, view: 'text', type: 'string' },
+  contactPhone: { title: '联系电话', order: 18, view: 'text', type: 'string' },
+  workLocation: { title: '工作地点', order: 19, view: 'text', type: 'string' },
+  detailedAddress: { title: '详细地址', order: 20, view: 'text', type: 'string' },
+  dataSource: { title: '数据来源', order: 21, view: 'text', type: 'string' },
+};

+ 269 - 0
jeecgboot-vue3/src/views/recruitment/post/PostInfoList.vue

@@ -0,0 +1,269 @@
+<template>
+  <div class="p-2">
+    <!--查询区域-->
+    <div class="jeecg-basic-table-form-container">
+      <a-form ref="formRef" :label-col="labelCol" :model="queryParam" :wrapper-col="wrapperCol" @keyup.enter.native="searchQuery">
+        <a-row :gutter="24">
+          <a-col :lg="6">
+            <a-form-item label="岗位名称" name="postName">
+              <a-input v-model:value="queryParam.postName" placeholder="请输入岗位名称"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="6">
+            <a-form-item label="单位名称" name="companyName">
+              <a-input v-model:value="queryParam.companyName" placeholder="请输入单位名称"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="6">
+            <a-form-item label="工作地点" name="workLocation">
+              <a-input v-model:value="queryParam.workLocation" placeholder="请输入工作地点"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="6">
+            <a-form-item label="学历要求" name="educationRequire">
+              <a-input v-model:value="queryParam.educationRequire" placeholder="请输入学历要求"></a-input>
+            </a-form-item>
+          </a-col>
+          <a-col :lg="7" :md="8" :sm="24" :xl="6">
+            <a-button preIcon="ant-design:search-outlined" type="primary" @click="searchQuery">查询</a-button>
+            <a-button preIcon="ant-design:reload-outlined" style="margin-left: 8px" type="primary" @click="searchReset">重置</a-button>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+    <!--引用表格-->
+    <BasicTable :rowSelection="rowSelection" @register="registerTable">
+      <!--插槽:table标题-->
+      <template #tableTitle>
+        <a-button v-auth="'post:post_info:add'" preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd"> 新增</a-button>
+        <a-button v-auth="'post:post_info:exportXls'" preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
+        <j-upload-button v-auth="'post:post_info:importExcel'" preIcon="ant-design:import-outlined" type="primary" @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="'post:post_info: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>
+    <!-- 表单区域 -->
+    <PostInfoModal ref="registerModal" @success="handleSuccess"></PostInfoModal>
+  </div>
+</template>
+
+<script lang="ts" name="post-postInfo" setup>
+  import { reactive, ref } from 'vue';
+  import { BasicTable, TableAction } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { columns, superQuerySchema } from './PostInfo.data';
+  import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './PostInfo.api';
+  import PostInfoModal from './components/PostInfoModal.vue';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { getDateByPicker } from '/@/utils';
+
+  const fieldPickers = reactive({});
+
+  const formRef = ref();
+  const queryParam = reactive<any>({});
+  const toggleSearchStatus = ref<boolean>(false);
+  const registerModal = ref();
+  const userStore = useUserStore();
+  const { createMessage } = useMessage();
+  //注册table数据
+  const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
+    tableProps: {
+      title: '岗位信息-岗位基本信息',
+      api: list,
+      columns,
+      canResize: true,
+      useSearchForm: false,
+      actionColumn: {
+        width: 120,
+        fixed: 'right',
+      },
+      beforeFetch: async (params) => {
+        for (let key in fieldPickers) {
+          if (queryParam[key] && fieldPickers[key]) {
+            queryParam[key] = getDateByPicker(queryParam[key], fieldPickers[key]);
+          }
+        }
+        return Object.assign(params, queryParam);
+      },
+    },
+    exportConfig: {
+      name: '岗位信息-岗位基本信息',
+      url: getExportUrl,
+      params: queryParam,
+    },
+    importConfig: {
+      url: getImportUrl,
+      success: handleSuccess,
+    },
+  });
+  const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
+    tableContext;
+  const labelCol = reactive({
+    xs: 24,
+    sm: 4,
+    xl: 6,
+    xxl: 4,
+  });
+  const wrapperCol = reactive({
+    xs: 24,
+    sm: 20,
+  });
+
+  // 高级查询配置
+  const superQueryConfig = reactive(superQuerySchema);
+
+  /**
+   * 高级查询事件
+   */
+  function handleSuperQuery(params) {
+    Object.keys(params).map((k) => {
+      queryParam[k] = params[k];
+    });
+    searchQuery();
+  }
+
+  /**
+   * 新增事件
+   */
+  function handleAdd() {
+    registerModal.value.disableSubmit = false;
+    registerModal.value.add();
+  }
+
+  /**
+   * 编辑事件
+   */
+  function handleEdit(record: Recordable) {
+    registerModal.value.disableSubmit = false;
+    registerModal.value.edit(record);
+  }
+
+  /**
+   * 详情
+   */
+  function handleDetail(record: Recordable) {
+    registerModal.value.disableSubmit = true;
+    registerModal.value.edit(record);
+  }
+
+  /**
+   * 删除事件
+   */
+  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: 'post:post_info:edit',
+      },
+    ];
+  }
+
+  /**
+   * 下拉操作栏
+   */
+  function getDropDownAction(record) {
+    return [
+      {
+        label: '详情',
+        onClick: handleDetail.bind(null, record),
+      },
+      {
+        label: '删除',
+        popConfirm: {
+          title: '是否确认删除',
+          confirm: handleDelete.bind(null, record),
+          placement: 'topLeft',
+        },
+        auth: 'post:post_info:delete',
+      },
+    ];
+  }
+
+  /**
+   * 查询
+   */
+  function searchQuery() {
+    reload();
+  }
+
+  /**
+   * 重置
+   */
+  function searchReset() {
+    formRef.value.resetFields();
+    selectedRowKeys.value = [];
+    //刷新数据
+    reload();
+  }
+</script>
+
+<style lang="less" scoped>
+  .jeecg-basic-table-form-container {
+    padding: 0;
+    .table-page-search-submitButtons {
+      display: block;
+      margin-bottom: 24px;
+      white-space: nowrap;
+    }
+    .query-group-cust {
+      min-width: 100px !important;
+    }
+    .query-group-split-cust {
+      width: 30px;
+      display: inline-block;
+      text-align: center;
+    }
+    .ant-form-item:not(.ant-form-item-with-help) {
+      margin-bottom: 16px;
+      height: 32px;
+    }
+    :deep(.ant-picker),
+    :deep(.ant-input-number) {
+      width: 100%;
+    }
+  }
+</style>

+ 339 - 0
jeecgboot-vue3/src/views/recruitment/post/components/PostInfoForm.vue

@@ -0,0 +1,339 @@
+<template>
+  <a-spin :spinning="confirmLoading">
+    <JFormContainer :disabled="disabled">
+      <template #detail>
+        <a-form ref="formRef" :labelCol="labelCol" :wrapperCol="wrapperCol" class="antd-modal-form" name="PostInfoForm">
+          <a-row>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-enterpriseId" label="关联企业" name="enterpriseId" v-bind="validateInfos.enterpriseId">
+                <JSearchSelect v-model:value="formData.enterpriseId" dict="enterprise_info,company_name,id" placeholder="请选择关联企业" />
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-occupation" label="职业" name="occupation" v-bind="validateInfos.occupation">
+                <a-input v-model:value="formData.occupation" allow-clear placeholder="请输入职业"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-postName" label="岗位名称" name="postName" v-bind="validateInfos.postName">
+                <a-input v-model:value="formData.postName" allow-clear placeholder="请输入岗位名称"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-workNature" label="工作性质" name="workNature" v-bind="validateInfos.workNature">
+                <a-input v-model:value="formData.workNature" allow-clear placeholder="请输入工作性质"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-salaryType" label="薪酬待遇" name="salaryType" v-bind="validateInfos.salaryType">
+                <a-input v-model:value="formData.salaryType" allow-clear placeholder="请输入薪酬待遇"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-salaryMin" label="最低薪酬" name="salaryMin" v-bind="validateInfos.salaryMin">
+                <a-input-number
+                  v-model:value="formData.salaryMin"
+                  :min="0"
+                  :precision="2"
+                  allow-clear
+                  placeholder="请输入最低薪酬"
+                  style="width: 100%"
+                />
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-salaryMax" label="最高薪酬" name="salaryMax" v-bind="validateInfos.salaryMax">
+                <a-input-number
+                  v-model:value="formData.salaryMax"
+                  :min="0"
+                  :precision="2"
+                  allow-clear
+                  placeholder="请输入最高薪酬"
+                  style="width: 100%"
+                />
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-recruitCount" label="招聘人数" name="recruitCount" v-bind="validateInfos.recruitCount">
+                <a-input-number v-model:value="formData.recruitCount" :min="1" allow-clear placeholder="请输入招聘人数" style="width: 100%" />
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-postValidDate" label="岗位有效期" name="postValidDate" v-bind="validateInfos.postValidDate">
+                <a-date-picker
+                  v-model:value="formData.postValidDate"
+                  allow-clear
+                  placeholder="请选择岗位有效期"
+                  style="width: 100%"
+                  value-format="YYYY-MM-DD"
+                />
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-postTag" label="职位标签" name="postTag" v-bind="validateInfos.postTag">
+                <a-input v-model:value="formData.postTag" allow-clear placeholder="请输入职位标签"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-otherBenefits" label="其他福利" name="otherBenefits" v-bind="validateInfos.otherBenefits">
+                <a-textarea v-model:value="formData.otherBenefits" :rows="3" allow-clear placeholder="请输入其他福利"></a-textarea>
+              </a-form-item>
+            </a-col>
+            <a-col :span="24">
+              <a-form-item
+                id="PostInfoForm-postDesc"
+                :labelCol="{ xs: { span: 24 }, sm: { span: 3 } }"
+                :wrapperCol="{ xs: { span: 24 }, sm: { span: 20 } }"
+                label="岗位描述"
+                name="postDesc"
+                v-bind="validateInfos.postDesc"
+              >
+                <a-textarea v-model:value="formData.postDesc" :rows="4" allow-clear placeholder="请输入岗位描述"></a-textarea>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-educationRequire" label="学历要求" name="educationRequire" v-bind="validateInfos.educationRequire">
+                <a-input v-model:value="formData.educationRequire" allow-clear placeholder="请输入学历要求"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item
+                id="PostInfoForm-workExperienceRequire"
+                label="工作经验"
+                name="workExperienceRequire"
+                v-bind="validateInfos.workExperienceRequire"
+              >
+                <a-input v-model:value="formData.workExperienceRequire" allow-clear placeholder="请输入工作经验"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-skillLevelRequire" label="职业技能等级" name="skillLevelRequire" v-bind="validateInfos.skillLevelRequire">
+                <a-input v-model:value="formData.skillLevelRequire" allow-clear placeholder="请输入职业技能等级"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-foreignLanguage" label="外语语种" name="foreignLanguage" v-bind="validateInfos.foreignLanguage">
+                <a-input v-model:value="formData.foreignLanguage" allow-clear placeholder="请输入外语语种"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-languageLevel" label="外语水平要求" name="languageLevel" v-bind="validateInfos.languageLevel">
+                <a-input v-model:value="formData.languageLevel" allow-clear placeholder="请输入外语水平要求"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-contactPerson" label="联系人" name="contactPerson" v-bind="validateInfos.contactPerson">
+                <a-input v-model:value="formData.contactPerson" allow-clear placeholder="请输入联系人"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-contactPhone" label="联系电话" name="contactPhone" v-bind="validateInfos.contactPhone">
+                <a-input v-model:value="formData.contactPhone" allow-clear placeholder="请输入联系电话"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-workLocation" label="工作地点" name="workLocation" v-bind="validateInfos.workLocation">
+                <a-input v-model:value="formData.workLocation" allow-clear placeholder="请输入工作地点"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-detailedAddress" label="详细地址" name="detailedAddress" v-bind="validateInfos.detailedAddress">
+                <a-input v-model:value="formData.detailedAddress" allow-clear placeholder="请输入详细地址"></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item id="PostInfoForm-dataSource" label="数据来源" name="dataSource" v-bind="validateInfos.dataSource">
+                <a-input v-model:value="formData.dataSource" allow-clear placeholder="请输入数据来源"></a-input>
+              </a-form-item>
+            </a-col>
+          </a-row>
+        </a-form>
+      </template>
+    </JFormContainer>
+  </a-spin>
+</template>
+
+<script lang="ts" setup>
+  import { computed, defineExpose, defineProps, nextTick, reactive, ref } from 'vue';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  import { getDateByPicker, getValueType } from '/@/utils';
+  import { saveOrUpdate } from '../PostInfo.api';
+  import JSearchSelect from '/@/components/Form/src/jeecg/components/JSearchSelect.vue';
+  import { Form } from 'ant-design-vue';
+  import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
+
+  const props = defineProps({
+    formDisabled: { type: Boolean, default: false },
+    formData: { type: Object, default: () => ({}) },
+    formBpm: { type: Boolean, default: true },
+  });
+  const formRef = ref();
+  const useForm = Form.useForm;
+  const emit = defineEmits(['register', 'ok']);
+  const formData = reactive<Record<string, any>>({
+    id: '',
+    enterpriseId: '',
+    occupation: '',
+    postName: '',
+    workNature: '',
+    salaryType: '',
+    salaryMin: undefined,
+    salaryMax: undefined,
+    recruitCount: undefined,
+    postValidDate: undefined,
+    postTag: '',
+    otherBenefits: '',
+    postDesc: '',
+    educationRequire: '',
+    workExperienceRequire: '',
+    skillLevelRequire: '',
+    foreignLanguage: '',
+    languageLevel: '',
+    contactPerson: '',
+    contactPhone: '',
+    workLocation: '',
+    detailedAddress: '',
+    dataSource: '',
+  });
+  const { createMessage } = useMessage();
+  const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 6 } });
+  const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
+  const confirmLoading = ref<boolean>(false);
+
+  //表单验证
+  const validatorRules = reactive({
+    enterpriseId: [{ required: true, message: '请选择关联企业' }],
+    occupation: [{ required: true, message: '请输入职业' }],
+    postName: [{ required: true, message: '请输入岗位名称' }],
+    workNature: [{ required: true, message: '请输入工作性质' }],
+    salaryType: [{ required: true, message: '请输入薪酬待遇' }],
+    recruitCount: [{ required: true, message: '请输入招聘人数' }],
+    postValidDate: [{ required: true, message: '请选择岗位有效期' }],
+    postTag: [{ required: true, message: '请输入职位标签' }],
+    postDesc: [{ required: true, message: '请输入岗位描述' }],
+    educationRequire: [{ required: true, message: '请输入学历要求' }],
+    workExperienceRequire: [{ required: true, message: '请输入工作经验' }],
+    skillLevelRequire: [{ required: true, message: '请输入职业技能等级' }],
+    contactPerson: [{ required: true, message: '请输入联系人' }],
+    contactPhone: [
+      { required: true, message: '请输入联系电话' },
+      { pattern: /^1[3-9]\d{9}$/, message: '联系电话格式不正确' },
+    ],
+    workLocation: [{ required: true, message: '请输入工作地点' }],
+    detailedAddress: [{ required: true, message: '请输入详细地址' }],
+    dataSource: [{ required: true, message: '请输入数据来源' }],
+    salaryMax: [
+      {
+        validator: (_, value) => {
+          if (value !== undefined && value !== null && formData.salaryMin !== undefined && formData.salaryMin !== null) {
+            if (value <= formData.salaryMin) {
+              return Promise.reject('最高薪酬必须大于最低薪酬');
+            }
+          }
+          return Promise.resolve();
+        },
+      },
+    ],
+  });
+  const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
+  //日期个性化选择
+  const fieldPickers = reactive({});
+
+  // 表单禁用
+  const disabled = computed(() => {
+    if (props.formBpm === true) {
+      if (props.formData.disabled === false) {
+        return false;
+      } else {
+        return true;
+      }
+    }
+    return props.formDisabled;
+  });
+
+  /**
+   * 新增
+   */
+  function add() {
+    edit({});
+  }
+
+  /**
+   * 编辑
+   */
+  function edit(record) {
+    nextTick(() => {
+      resetFields();
+      const tmpData = {};
+      Object.keys(formData).forEach((key) => {
+        if (record.hasOwnProperty(key)) {
+          tmpData[key] = record[key];
+        }
+      });
+      //赋值
+      Object.assign(formData, tmpData);
+    });
+  }
+
+  /**
+   * 提交数据
+   */
+  async function submitForm() {
+    try {
+      // 触发表单验证
+      await validate();
+    } catch ({ errorFields }) {
+      if (errorFields) {
+        const firstField = errorFields[0];
+        if (firstField) {
+          formRef.value.scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
+        }
+      }
+      return Promise.reject(errorFields);
+    }
+    confirmLoading.value = true;
+    const isUpdate = ref<boolean>(false);
+    //时间格式化
+    let model = formData;
+    if (model.id) {
+      isUpdate.value = true;
+    }
+    //循环数据
+    for (let data in model) {
+      // 更新个性化日期选择器的值
+      model[data] = getDateByPicker(model[data], fieldPickers[data]);
+      //如果该数据是数组并且是字符串类型
+      if (model[data] instanceof Array) {
+        let valueType = getValueType(formRef.value.getProps, data);
+        //如果是字符串类型的需要变成以逗号分割的字符串
+        if (valueType === 'string') {
+          model[data] = model[data].join(',');
+        }
+      }
+    }
+    await saveOrUpdate(model, isUpdate.value)
+      .then((res) => {
+        if (res.success) {
+          createMessage.success(res.message);
+          emit('ok');
+        } else {
+          createMessage.warning(res.message);
+        }
+      })
+      .finally(() => {
+        confirmLoading.value = false;
+      });
+  }
+
+  defineExpose({
+    add,
+    edit,
+    submitForm,
+  });
+</script>
+
+<style lang="less" scoped>
+  .antd-modal-form {
+    padding: 14px 20px;
+  }
+</style>

+ 92 - 0
jeecgboot-vue3/src/views/recruitment/post/components/PostInfoModal.vue

@@ -0,0 +1,92 @@
+<template>
+  <j-modal
+    :fullscreen="true"
+    :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }"
+    :title="title"
+    :visible="visible"
+    :width="800"
+    cancelText="关闭"
+    maxHeight="500px"
+    @cancel="handleCancel"
+    @ok="handleOk"
+  >
+    <PostInfoForm ref="registerForm" :formBpm="false" :formDisabled="disableSubmit" @ok="submitCallback"></PostInfoForm>
+    <template #footer>
+      <a-button @click="handleCancel">取消</a-button>
+      <a-button :class="{ 'jee-hidden': disableSubmit }" type="primary" @click="handleOk">确认</a-button>
+    </template>
+  </j-modal>
+</template>
+
+<script lang="ts" setup>
+  import { defineExpose, nextTick, ref } from 'vue';
+  import PostInfoForm from './PostInfoForm.vue';
+  import JModal from '/@/components/Modal/src/JModal/JModal.vue';
+  import { useMessage } from '/@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+  const title = ref<string>('');
+  const width = ref<number>(800);
+  const visible = ref<boolean>(false);
+  const disableSubmit = ref<boolean>(false);
+  const registerForm = ref();
+  const emit = defineEmits(['register', 'success']);
+
+  /**
+   * 新增
+   */
+  function add() {
+    title.value = '新增';
+    visible.value = true;
+    nextTick(() => {
+      registerForm.value.add();
+    });
+  }
+
+  /**
+   * 编辑
+   * @param record
+   */
+  function edit(record) {
+    title.value = disableSubmit.value ? '详情' : '编辑';
+    visible.value = true;
+    nextTick(() => {
+      registerForm.value.edit(record);
+    });
+  }
+
+  /**
+   * 确定按钮点击事件
+   */
+  function handleOk() {
+    registerForm.value.submitForm();
+  }
+
+  /**
+   * form保存回调事件
+   */
+  function submitCallback() {
+    handleCancel();
+    emit('success');
+  }
+
+  /**
+   * 取消按钮回调事件
+   */
+  function handleCancel() {
+    visible.value = false;
+  }
+  defineExpose({
+    add,
+    edit,
+    disableSubmit,
+  });
+</script>
+
+<style lang="less">
+  /**隐藏样式-modal确定按钮 */
+  .jee-hidden {
+    display: none !important;
+  }
+</style>
+<style lang="less" scoped></style>