Merge branch 'master' of http://47.99.164.66:3000/hzdyjs-xg/all-project
This commit is contained in:
commit
ea739d328b
19 files changed
+3510
-39
No files matched your search
+37
-2
@@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
@@ -22,6 +23,8 @@ import org.dromara.zsjy.domain.vo.ZsjyEnrollmentInfoVo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentInfoBo;
|
||||
import org.dromara.zsjy.service.IZsjyEnrollmentInfoService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.xg.service.IXgSysDeptService;
|
||||
import org.dromara.xg.domain.vo.XgSysDeptVo;
|
||||
|
||||
/**
|
||||
* 招生报名信息Controller
|
||||
@@ -36,6 +39,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
|
||||
private final IZsjyEnrollmentInfoService enrollmentInfoService;
|
||||
private final IXgSysDeptService xgSysDeptService;
|
||||
|
||||
/**
|
||||
* 查询招生报名信息列表
|
||||
@@ -101,6 +105,7 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
/**
|
||||
* 根据身份证号和准考证号查询报名信息
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/query")
|
||||
public R<ZsjyEnrollmentInfoVo> queryByIdCardAndExamNumber(
|
||||
@RequestParam(required = false) String idCard,
|
||||
@@ -112,7 +117,7 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
/**
|
||||
* 提交报名信息(供学生端使用)
|
||||
*/
|
||||
@SaCheckPermission("zsjy:mobile:enrollment:apply")
|
||||
@SaIgnore
|
||||
@Log(title = "学生报名", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/submit")
|
||||
@@ -123,7 +128,7 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
/**
|
||||
* 查询我的报名状态(供学生端使用)
|
||||
*/
|
||||
@SaCheckPermission("zsjy:mobile:enrollment:status")
|
||||
@SaIgnore
|
||||
@GetMapping("/myStatus")
|
||||
public R<ZsjyEnrollmentInfoVo> getMyEnrollmentStatus(
|
||||
@RequestParam String idCard,
|
||||
@@ -153,6 +158,36 @@ public class ZsjyEnrollmentInfoController extends BaseController {
|
||||
return R.fail("请提供要更新的报名信息ID");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学院列表(供移动端公开访问)
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/colleges")
|
||||
public R<List<XgSysDeptVo>> getColleges() {
|
||||
try {
|
||||
// 调用学院部门服务获取学院列表
|
||||
List<XgSysDeptVo> colleges = xgSysDeptService.selectCollegeList();
|
||||
return R.ok(colleges);
|
||||
} catch (Exception e) {
|
||||
return R.fail("获取学院列表异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学院ID获取专业列表(供移动端公开访问)
|
||||
*/
|
||||
@SaIgnore
|
||||
@GetMapping("/majors/{collegeId}")
|
||||
public R<List<XgSysDeptVo>> getMajorsByCollegeId(@PathVariable Long collegeId) {
|
||||
try {
|
||||
// 调用学院部门服务获取专业列表
|
||||
List<XgSysDeptVo> majors = xgSysDeptService.selectMajorListByCollegeId(collegeId);
|
||||
return R.ok(majors);
|
||||
} catch (Exception e) {
|
||||
return R.fail("获取专业列表异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package org.dromara.zsjy.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyQrCodeBo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyQrCodeVo;
|
||||
import org.dromara.zsjy.service.IZsjyQrCodeService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 二维码管理Controller
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/zsjy/qrcode")
|
||||
@Tag(name = "二维码管理", description = "招生二维码管理功能")
|
||||
public class ZsjyQrCodeController extends BaseController {
|
||||
|
||||
private final IZsjyQrCodeService qrCodeService;
|
||||
|
||||
/**
|
||||
* 查询二维码信息列表
|
||||
*/
|
||||
@Operation(summary = "查询二维码列表", description = "分页查询二维码信息列表")
|
||||
@SaCheckPermission("zsjy:qrcode:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<ZsjyQrCodeVo> list(@Validated ZsjyQrCodeBo bo, PageQuery pageQuery) {
|
||||
return qrCodeService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出二维码信息列表
|
||||
*/
|
||||
@Operation(summary = "导出二维码列表", description = "导出二维码信息到Excel")
|
||||
@SaCheckPermission("zsjy:qrcode:export")
|
||||
@Log(title = "二维码管理", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(@Validated ZsjyQrCodeBo bo, HttpServletResponse response) {
|
||||
List<ZsjyQrCodeVo> list = qrCodeService.queryList(bo);
|
||||
ExcelUtil.exportExcel(list, "二维码信息", ZsjyQrCodeVo.class, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二维码信息详细信息
|
||||
*/
|
||||
@Operation(summary = "获取二维码详情", description = "根据ID获取二维码详细信息")
|
||||
@SaCheckPermission("zsjy:qrcode:query")
|
||||
@GetMapping("/{id}")
|
||||
public R<ZsjyQrCodeVo> getInfo(
|
||||
@Parameter(description = "二维码ID", required = true)
|
||||
@PathVariable @NotNull(message = "主键不能为空") Long id) {
|
||||
return R.ok(qrCodeService.queryById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增二维码信息
|
||||
*/
|
||||
@Operation(summary = "新增二维码", description = "创建新的二维码")
|
||||
@SaCheckPermission("zsjy:qrcode:add")
|
||||
@Log(title = "二维码管理", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ZsjyQrCodeBo bo) {
|
||||
return toAjax(qrCodeService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改二维码信息
|
||||
*/
|
||||
@Operation(summary = "修改二维码", description = "修改二维码信息")
|
||||
@SaCheckPermission("zsjy:qrcode:edit")
|
||||
@Log(title = "二维码管理", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ZsjyQrCodeBo bo) {
|
||||
return toAjax(qrCodeService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除二维码信息
|
||||
*/
|
||||
@Operation(summary = "删除二维码", description = "批量删除二维码信息")
|
||||
@SaCheckPermission("zsjy:qrcode:remove")
|
||||
@Log(title = "二维码管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(
|
||||
@Parameter(description = "二维码ID数组", required = true)
|
||||
@PathVariable @NotEmpty(message = "主键不能为空") Long[] ids) {
|
||||
return toAjax(qrCodeService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证二维码Token有效性
|
||||
*/
|
||||
@Operation(summary = "验证Token", description = "验证二维码访问令牌是否有效")
|
||||
@GetMapping("/validate/{token}")
|
||||
public R<Boolean> validateToken(
|
||||
@Parameter(description = "访问令牌", required = true)
|
||||
@PathVariable String token) {
|
||||
return R.ok(qrCodeService.validateToken(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Token获取二维码信息
|
||||
*/
|
||||
@Operation(summary = "根据Token获取二维码", description = "通过访问令牌获取二维码信息")
|
||||
@GetMapping("/token/{token}")
|
||||
public R<ZsjyQrCodeVo> getByToken(
|
||||
@Parameter(description = "访问令牌", required = true)
|
||||
@PathVariable String token) {
|
||||
return R.ok(qrCodeService.queryByAccessToken(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加二维码使用次数
|
||||
*/
|
||||
@Operation(summary = "增加使用次数", description = "增加二维码使用次数")
|
||||
@PostMapping("/increment/{id}")
|
||||
public R<Void> incrementUsage(
|
||||
@Parameter(description = "二维码ID", required = true)
|
||||
@PathVariable @NotNull(message = "主键不能为空") Long id) {
|
||||
return toAjax(qrCodeService.incrementUsage(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的访问令牌
|
||||
*/
|
||||
@Operation(summary = "生成Token", description = "生成新的访问令牌")
|
||||
@SaCheckPermission("zsjy:qrcode:token")
|
||||
@PostMapping("/generate-token")
|
||||
public R<String> generateToken() {
|
||||
return R.ok(qrCodeService.generateAccessToken());
|
||||
}
|
||||
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.dromara.zsjy.domain;
|
||||
|
||||
import org.dromara.common.tenant.core.TenantEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 二维码管理对象 zsjy_qr_code
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("zsjy_qr_code")
|
||||
public class ZsjyQrCode extends TenantEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 二维码名称
|
||||
*/
|
||||
private String codeName;
|
||||
|
||||
/**
|
||||
* 二维码类型(1-招生报名)
|
||||
*/
|
||||
private Integer codeType;
|
||||
|
||||
/**
|
||||
* 二维码内容(URL)
|
||||
*/
|
||||
private String qrContent;
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
|
||||
/**
|
||||
* 最大使用次数(0-无限制)
|
||||
*/
|
||||
private Integer maxUsage;
|
||||
|
||||
/**
|
||||
* 当前使用次数
|
||||
*/
|
||||
private Integer currentUsage;
|
||||
|
||||
/**
|
||||
* 状态(0-禁用 1-启用)
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package org.dromara.zsjy.domain.bo;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyQrCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 二维码管理业务对象 zsjy_qr_code
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ZsjyQrCode.class, reverseConvertGenerate = false)
|
||||
public class ZsjyQrCodeBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 二维码名称
|
||||
*/
|
||||
@NotBlank(message = "二维码名称不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(max = 100, message = "二维码名称长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String codeName;
|
||||
|
||||
/**
|
||||
* 二维码类型(1-招生报名)
|
||||
*/
|
||||
@NotNull(message = "二维码类型不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer codeType;
|
||||
|
||||
/**
|
||||
* 二维码内容(URL)
|
||||
*/
|
||||
@NotBlank(message = "二维码内容不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String qrContent;
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
@NotBlank(message = "访问令牌不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(max = 255, message = "访问令牌长度不能超过255个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expireTime;
|
||||
|
||||
/**
|
||||
* 最大使用次数(0-无限制)
|
||||
*/
|
||||
@NotNull(message = "最大使用次数不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Min(value = 0, message = "最大使用次数不能小于0", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer maxUsage;
|
||||
|
||||
/**
|
||||
* 当前使用次数
|
||||
*/
|
||||
private Integer currentUsage;
|
||||
|
||||
/**
|
||||
* 状态(0-禁用 1-启用)
|
||||
*/
|
||||
@NotNull(message = "状态不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer status;
|
||||
|
||||
// VO类基础字段(强制性规范)
|
||||
private Long createBy;
|
||||
private Date createTime;
|
||||
private Long updateBy;
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package org.dromara.zsjy.domain.vo;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyQrCode;
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 二维码管理视图对象 zsjy_qr_code
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = ZsjyQrCode.class)
|
||||
public class ZsjyQrCodeVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 二维码名称
|
||||
*/
|
||||
@ExcelProperty(value = "二维码名称")
|
||||
private String codeName;
|
||||
|
||||
/**
|
||||
* 二维码类型(1-招生报名)
|
||||
*/
|
||||
@ExcelProperty(value = "二维码类型")
|
||||
private Integer codeType;
|
||||
|
||||
/**
|
||||
* 二维码内容(URL)
|
||||
*/
|
||||
@ExcelProperty(value = "二维码内容")
|
||||
private String qrContent;
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
@ExcelProperty(value = "访问令牌")
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
@ExcelProperty(value = "过期时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expireTime;
|
||||
|
||||
/**
|
||||
* 最大使用次数(0-无限制)
|
||||
*/
|
||||
@ExcelProperty(value = "最大使用次数")
|
||||
private Integer maxUsage;
|
||||
|
||||
/**
|
||||
* 当前使用次数
|
||||
*/
|
||||
@ExcelProperty(value = "当前使用次数")
|
||||
private Integer currentUsage;
|
||||
|
||||
/**
|
||||
* 状态(0-禁用 1-启用)
|
||||
*/
|
||||
@ExcelProperty(value = "状态")
|
||||
private Integer status;
|
||||
|
||||
// VO类基础字段(强制性规范)
|
||||
private Long createBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
private Long updateBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package org.dromara.zsjy.mapper;
|
||||
|
||||
import org.dromara.zsjy.domain.ZsjyQrCode;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyQrCodeVo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyQrCodeBo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 二维码管理 Mapper 接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
public interface ZsjyQrCodeMapper extends BaseMapperPlus<ZsjyQrCode, ZsjyQrCodeVo> {
|
||||
|
||||
/**
|
||||
* 分页查询二维码列表
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param bo 查询条件
|
||||
* @return 二维码列表
|
||||
*/
|
||||
IPage<ZsjyQrCodeVo> selectQrCodePageList(@Param("page") Page<?> page, @Param("bo") ZsjyQrCodeBo bo);
|
||||
|
||||
/**
|
||||
* 查询二维码列表(非分页)
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 二维码列表
|
||||
*/
|
||||
List<ZsjyQrCodeVo> selectQrCodeVoList(ZsjyQrCodeBo bo);
|
||||
|
||||
/**
|
||||
* 根据访问令牌查询二维码信息
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 二维码信息
|
||||
*/
|
||||
ZsjyQrCodeVo selectByAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 更新二维码使用次数
|
||||
*
|
||||
* @param id 二维码ID
|
||||
* @return 更新结果
|
||||
*/
|
||||
int updateUsageCount(@Param("id") Long id);
|
||||
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package org.dromara.zsjy.service;
|
||||
|
||||
import org.dromara.zsjy.domain.bo.ZsjyQrCodeBo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyQrCodeVo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 二维码管理Service接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
public interface IZsjyQrCodeService {
|
||||
|
||||
/**
|
||||
* 查询二维码信息
|
||||
*
|
||||
* @param id 二维码信息主键
|
||||
* @return 二维码信息
|
||||
*/
|
||||
ZsjyQrCodeVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 查询二维码信息列表
|
||||
*
|
||||
* @param bo 二维码信息
|
||||
* @param pageQuery 分页参数
|
||||
* @return 二维码信息集合
|
||||
*/
|
||||
TableDataInfo<ZsjyQrCodeVo> queryPageList(ZsjyQrCodeBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询二维码信息列表
|
||||
*
|
||||
* @param bo 二维码信息
|
||||
* @return 二维码信息集合
|
||||
*/
|
||||
List<ZsjyQrCodeVo> queryList(ZsjyQrCodeBo bo);
|
||||
|
||||
/**
|
||||
* 新增二维码信息
|
||||
*
|
||||
* @param bo 二维码信息
|
||||
* @return 结果
|
||||
*/
|
||||
Boolean insertByBo(ZsjyQrCodeBo bo);
|
||||
|
||||
/**
|
||||
* 修改二维码信息
|
||||
*
|
||||
* @param bo 二维码信息
|
||||
* @return 结果
|
||||
*/
|
||||
Boolean updateByBo(ZsjyQrCodeBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除二维码信息
|
||||
*
|
||||
* @param ids 需要删除的二维码信息主键集合
|
||||
* @param isValid 是否校验,true-删除前校验,false-不校验
|
||||
* @return 结果
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 根据访问令牌查询二维码信息
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 二维码信息
|
||||
*/
|
||||
ZsjyQrCodeVo queryByAccessToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 验证二维码Token有效性
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 是否有效
|
||||
*/
|
||||
Boolean validateToken(String accessToken);
|
||||
|
||||
/**
|
||||
* 增加二维码使用次数
|
||||
*
|
||||
* @param id 二维码ID
|
||||
* @return 结果
|
||||
*/
|
||||
Boolean incrementUsage(Long id);
|
||||
|
||||
/**
|
||||
* 生成唯一的访问令牌
|
||||
*
|
||||
* @return 访问令牌
|
||||
*/
|
||||
String generateAccessToken();
|
||||
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
package org.dromara.zsjy.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.zsjy.domain.ZsjyQrCode;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyQrCodeBo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyQrCodeVo;
|
||||
import org.dromara.zsjy.mapper.ZsjyQrCodeMapper;
|
||||
import org.dromara.zsjy.service.IZsjyQrCodeService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 二维码管理Service业务层处理
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ZsjyQrCodeServiceImpl implements IZsjyQrCodeService {
|
||||
|
||||
private final ZsjyQrCodeMapper baseMapper;
|
||||
|
||||
/**
|
||||
* 查询二维码信息
|
||||
*/
|
||||
@Override
|
||||
public ZsjyQrCodeVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询二维码信息列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<ZsjyQrCodeVo> queryPageList(ZsjyQrCodeBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<ZsjyQrCode> lqw = buildQueryWrapper(bo);
|
||||
Page<ZsjyQrCodeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询二维码信息列表
|
||||
*/
|
||||
@Override
|
||||
public List<ZsjyQrCodeVo> queryList(ZsjyQrCodeBo bo) {
|
||||
LambdaQueryWrapper<ZsjyQrCode> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<ZsjyQrCode> buildQueryWrapper(ZsjyQrCodeBo bo) {
|
||||
LambdaQueryWrapper<ZsjyQrCode> lqw = Wrappers.lambdaQuery();
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCodeName()), ZsjyQrCode::getCodeName, bo.getCodeName());
|
||||
lqw.eq(ObjectUtil.isNotNull(bo.getCodeType()), ZsjyQrCode::getCodeType, bo.getCodeType());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getQrContent()), ZsjyQrCode::getQrContent, bo.getQrContent());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getAccessToken()), ZsjyQrCode::getAccessToken, bo.getAccessToken());
|
||||
lqw.eq(ObjectUtil.isNotNull(bo.getExpireTime()), ZsjyQrCode::getExpireTime, bo.getExpireTime());
|
||||
lqw.eq(ObjectUtil.isNotNull(bo.getMaxUsage()), ZsjyQrCode::getMaxUsage, bo.getMaxUsage());
|
||||
lqw.eq(ObjectUtil.isNotNull(bo.getCurrentUsage()), ZsjyQrCode::getCurrentUsage, bo.getCurrentUsage());
|
||||
lqw.eq(ObjectUtil.isNotNull(bo.getStatus()), ZsjyQrCode::getStatus, bo.getStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增二维码信息
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(ZsjyQrCodeBo bo) {
|
||||
ZsjyQrCode add = BeanUtil.toBean(bo, ZsjyQrCode.class);
|
||||
validEntityBeforeSave(add);
|
||||
|
||||
// 如果没有提供访问令牌,则自动生成
|
||||
if (StrUtil.isBlank(add.getAccessToken())) {
|
||||
add.setAccessToken(generateAccessToken());
|
||||
}
|
||||
|
||||
// 初始化当前使用次数
|
||||
if (add.getCurrentUsage() == null) {
|
||||
add.setCurrentUsage(0);
|
||||
}
|
||||
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改二维码信息
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(ZsjyQrCodeBo bo) {
|
||||
ZsjyQrCode update = BeanUtil.toBean(bo, ZsjyQrCode.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(ZsjyQrCode entity) {
|
||||
// 校验二维码名称唯一性
|
||||
if (StringUtils.isNotBlank(entity.getCodeName())) {
|
||||
LambdaQueryWrapper<ZsjyQrCode> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(ZsjyQrCode::getCodeName, entity.getCodeName());
|
||||
if (entity.getId() != null) {
|
||||
lqw.ne(ZsjyQrCode::getId, entity.getId());
|
||||
}
|
||||
boolean exists = baseMapper.exists(lqw);
|
||||
if (exists) {
|
||||
throw new RuntimeException("二维码名称已存在");
|
||||
}
|
||||
}
|
||||
|
||||
// 校验访问令牌唯一性
|
||||
if (StringUtils.isNotBlank(entity.getAccessToken())) {
|
||||
LambdaQueryWrapper<ZsjyQrCode> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(ZsjyQrCode::getAccessToken, entity.getAccessToken());
|
||||
if (entity.getId() != null) {
|
||||
lqw.ne(ZsjyQrCode::getId, entity.getId());
|
||||
}
|
||||
boolean exists = baseMapper.exists(lqw);
|
||||
if (exists) {
|
||||
throw new RuntimeException("访问令牌已存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除二维码信息
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// 删除前校验逻辑(如果需要的话)
|
||||
}
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据访问令牌查询二维码信息
|
||||
*/
|
||||
@Override
|
||||
public ZsjyQrCodeVo queryByAccessToken(String accessToken) {
|
||||
if (StrUtil.isBlank(accessToken)) {
|
||||
return null;
|
||||
}
|
||||
LambdaQueryWrapper<ZsjyQrCode> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(ZsjyQrCode::getAccessToken, accessToken);
|
||||
return baseMapper.selectVoOne(lqw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证二维码Token有效性
|
||||
*/
|
||||
@Override
|
||||
public Boolean validateToken(String accessToken) {
|
||||
ZsjyQrCodeVo qrCode = queryByAccessToken(accessToken);
|
||||
if (qrCode == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查状态
|
||||
if (qrCode.getStatus() == null || qrCode.getStatus() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查过期时间
|
||||
if (qrCode.getExpireTime() != null && qrCode.getExpireTime().before(new Date())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查使用次数限制
|
||||
if (qrCode.getMaxUsage() != null && qrCode.getMaxUsage() > 0) {
|
||||
if (qrCode.getCurrentUsage() != null && qrCode.getCurrentUsage() >= qrCode.getMaxUsage()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加二维码使用次数
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean incrementUsage(Long id) {
|
||||
return baseMapper.updateUsageCount(id) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的访问令牌
|
||||
*/
|
||||
@Override
|
||||
public String generateAccessToken() {
|
||||
String token;
|
||||
int maxAttempts = 10;
|
||||
int attempts = 0;
|
||||
|
||||
do {
|
||||
token = "qr_" + IdUtil.fastSimpleUUID();
|
||||
attempts++;
|
||||
|
||||
// 检查令牌是否已存在
|
||||
LambdaQueryWrapper<ZsjyQrCode> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(ZsjyQrCode::getAccessToken, token);
|
||||
boolean exists = baseMapper.exists(lqw);
|
||||
|
||||
if (!exists) {
|
||||
return token;
|
||||
}
|
||||
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
throw new RuntimeException("生成访问令牌失败,请重试");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<?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.dromara.zsjy.mapper.ZsjyQrCodeMapper">
|
||||
|
||||
<resultMap type="org.dromara.zsjy.domain.vo.ZsjyQrCodeVo" id="ZsjyQrCodeVoResult">
|
||||
<result property="id" column="id"/>
|
||||
<result property="codeName" column="code_name"/>
|
||||
<result property="codeType" column="code_type"/>
|
||||
<result property="qrContent" column="qr_content"/>
|
||||
<result property="accessToken" column="access_token"/>
|
||||
<result property="expireTime" column="expire_time"/>
|
||||
<result property="maxUsage" column="max_usage"/>
|
||||
<result property="currentUsage" column="current_usage"/>
|
||||
<result property="status" column="status"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectZsjyQrCodeVo">
|
||||
select id, code_name, code_type, qr_content, access_token, expire_time,
|
||||
max_usage, current_usage, status, create_by, create_time,
|
||||
update_by, update_time
|
||||
from zsjy_qr_code
|
||||
</sql>
|
||||
|
||||
<!-- 分页查询二维码列表 -->
|
||||
<select id="selectQrCodePageList" parameterType="org.dromara.zsjy.domain.bo.ZsjyQrCodeBo" resultMap="ZsjyQrCodeVoResult">
|
||||
<include refid="selectZsjyQrCodeVo"/>
|
||||
<where>
|
||||
deleted = 0
|
||||
<if test="bo.codeName != null and bo.codeName != ''">
|
||||
AND code_name like concat('%', #{bo.codeName}, '%')
|
||||
</if>
|
||||
<if test="bo.codeType != null">
|
||||
AND code_type = #{bo.codeType}
|
||||
</if>
|
||||
<if test="bo.qrContent != null and bo.qrContent != ''">
|
||||
AND qr_content like concat('%', #{bo.qrContent}, '%')
|
||||
</if>
|
||||
<if test="bo.accessToken != null and bo.accessToken != ''">
|
||||
AND access_token = #{bo.accessToken}
|
||||
</if>
|
||||
<if test="bo.expireTime != null">
|
||||
AND expire_time = #{bo.expireTime}
|
||||
</if>
|
||||
<if test="bo.maxUsage != null">
|
||||
AND max_usage = #{bo.maxUsage}
|
||||
</if>
|
||||
<if test="bo.currentUsage != null">
|
||||
AND current_usage = #{bo.currentUsage}
|
||||
</if>
|
||||
<if test="bo.status != null">
|
||||
AND status = #{bo.status}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询二维码列表(非分页) -->
|
||||
<select id="selectQrCodeVoList" parameterType="org.dromara.zsjy.domain.bo.ZsjyQrCodeBo" resultMap="ZsjyQrCodeVoResult">
|
||||
<include refid="selectZsjyQrCodeVo"/>
|
||||
<where>
|
||||
deleted = 0
|
||||
<if test="codeName != null and codeName != ''">
|
||||
AND code_name like concat('%', #{codeName}, '%')
|
||||
</if>
|
||||
<if test="codeType != null">
|
||||
AND code_type = #{codeType}
|
||||
</if>
|
||||
<if test="qrContent != null and qrContent != ''">
|
||||
AND qr_content like concat('%', #{qrContent}, '%')
|
||||
</if>
|
||||
<if test="accessToken != null and accessToken != ''">
|
||||
AND access_token = #{accessToken}
|
||||
</if>
|
||||
<if test="expireTime != null">
|
||||
AND expire_time = #{expireTime}
|
||||
</if>
|
||||
<if test="maxUsage != null">
|
||||
AND max_usage = #{maxUsage}
|
||||
</if>
|
||||
<if test="currentUsage != null">
|
||||
AND current_usage = #{currentUsage}
|
||||
</if>
|
||||
<if test="status != null">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据访问令牌查询二维码信息 -->
|
||||
<select id="selectByAccessToken" parameterType="String" resultMap="ZsjyQrCodeVoResult">
|
||||
<include refid="selectZsjyQrCodeVo"/>
|
||||
WHERE access_token = #{accessToken} AND deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 更新二维码使用次数 -->
|
||||
<update id="updateUsageCount" parameterType="Long">
|
||||
UPDATE zsjy_qr_code
|
||||
SET current_usage = IFNULL(current_usage, 0) + 1,
|
||||
update_time = NOW()
|
||||
WHERE id = #{id} AND deleted = 0
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,125 @@
|
||||
// 移动端新生报名相关API接口
|
||||
import { request } from '@/common/request.js'
|
||||
|
||||
/**
|
||||
* 新生报名相关API
|
||||
*/
|
||||
export const enrollmentApi = {
|
||||
/**
|
||||
* 提交报名信息(供学生端使用)
|
||||
* @param {Object} data 报名表单数据
|
||||
*/
|
||||
submitApplication: (data) => {
|
||||
return request({
|
||||
url: '/zsjy/enrollment/info/submit',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询我的报名状态(供学生端使用)
|
||||
* @param {Object} params 查询参数 { idCard?, examNumber? }
|
||||
*/
|
||||
queryMyStatus: (params) => {
|
||||
return request({
|
||||
url: '/zsjy/enrollment/info/myStatus',
|
||||
method: 'GET',
|
||||
params: params,
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据身份证号和准考证号查询报名信息
|
||||
* @param {string} idCard 身份证号
|
||||
* @param {string} examNumber 准考证号
|
||||
*/
|
||||
queryByInfo: (idCard, examNumber) => {
|
||||
return request({
|
||||
url: '/zsjy/enrollment/info/query',
|
||||
method: 'GET',
|
||||
params: {
|
||||
idCard,
|
||||
examNumber
|
||||
},
|
||||
header: {
|
||||
isToken: false // 不需要登录token
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取学院列表
|
||||
*/
|
||||
getCollegeList: () => {
|
||||
return request({
|
||||
url: '/zsjy/enrollment/info/colleges',
|
||||
method: 'GET',
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据学院ID获取专业列表
|
||||
* @param {string|number} collegeId 学院ID
|
||||
*/
|
||||
getMajorsByCollegeId: (collegeId) => {
|
||||
return request({
|
||||
url: `/zsjy/enrollment/info/majors/${collegeId}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
isToken: false // 不需要登录token,支持对外公开访问
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法 - 提交报名申请
|
||||
* @param {Object} formData 报名表单数据
|
||||
*/
|
||||
export const submitEnrollmentApplication = (formData) => {
|
||||
return enrollmentApi.submitApplication(formData)
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法 - 查询报名状态
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
export const queryMyEnrollmentStatus = (params) => {
|
||||
return enrollmentApi.queryMyStatus(params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法 - 根据证件信息查询
|
||||
* @param {string} idCard 身份证号
|
||||
* @param {string} examNumber 准考证号
|
||||
*/
|
||||
export const queryEnrollmentByInfo = (idCard, examNumber) => {
|
||||
return enrollmentApi.queryByInfo(idCard, examNumber)
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法 - 获取学院列表
|
||||
*/
|
||||
export const getCollegeList = () => {
|
||||
return enrollmentApi.getCollegeList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法 - 根据学院ID获取专业列表
|
||||
* @param {string|number} collegeId 学院ID
|
||||
*/
|
||||
export const getMajorsByCollegeId = (collegeId) => {
|
||||
return enrollmentApi.getMajorsByCollegeId(collegeId)
|
||||
}
|
||||
|
||||
export default enrollmentApi
|
||||
@@ -1,5 +1,5 @@
|
||||
// 移动端字典数据获取公共方法 - 适配Vue 2.6.14 + uni-app 2.x
|
||||
import { get } from './request.js';
|
||||
import { request } from './request.js';
|
||||
|
||||
/**
|
||||
* 字典数据缓存管理
|
||||
@@ -76,8 +76,12 @@ const dictCache = {
|
||||
* @returns {Promise} 字典数据Promise
|
||||
*/
|
||||
export const getDicts = (dictType) => {
|
||||
return get(`/system/dict/data/type/${dictType}`, null, {
|
||||
isToken: false, // 字典数据不需要token认证
|
||||
return request({
|
||||
url: `/system/dict/data/type/${dictType}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
isToken: false // 字典数据不需要token认证
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,17 @@ function getToken() {
|
||||
function handleUnauthorized(message = '登录已失效,请重新登录') {
|
||||
console.log('🔒 处理401未授权响应:', message);
|
||||
|
||||
// 获取当前页面路径
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const currentPath = '/' + currentPage.route;
|
||||
|
||||
// 如果是新生报名页面,不跳转到登录页,只是静默处理
|
||||
if (currentPath === '/pages/enrollment/index') {
|
||||
console.log('📋 新生报名页面401错误,使用兜底数据,不跳转登录页');
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除登录信息
|
||||
try {
|
||||
uni.removeStorageSync('access_token');
|
||||
@@ -53,11 +64,6 @@ function handleUnauthorized(message = '登录已失效,请重新登录') {
|
||||
|
||||
// 延迟跳转,确保Toast显示
|
||||
setTimeout(() => {
|
||||
// 获取当前页面路径
|
||||
const pages = getCurrentPages();
|
||||
const currentPage = pages[pages.length - 1];
|
||||
const currentPath = '/' + currentPage.route;
|
||||
|
||||
// 如果当前不是登录页,则跳转到登录页
|
||||
if (currentPath !== '/pages/login/index') {
|
||||
console.log('🔄 从', currentPath, '跳转到登录页');
|
||||
|
||||
@@ -215,6 +215,13 @@
|
||||
"navigationBarBackgroundColor": "#0051d9",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/enrollment/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "新生报名登记",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -27,6 +27,7 @@
|
||||
"@fullcalendar/timegrid": "^6.1.18",
|
||||
"@fullcalendar/vue3": "^6.1.18",
|
||||
"@highlightjs/vue-plugin": "2.1.0",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "13.1.0",
|
||||
"animate.css": "4.1.1",
|
||||
@@ -45,6 +46,7 @@
|
||||
"jspdf-font": "^1.0.7",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "3.0.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"screenfull": "6.0.2",
|
||||
"vue": "3.5.13",
|
||||
"vue-cropper": "1.1.1",
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 二维码管理 VO
|
||||
*/
|
||||
export interface QrCodeVO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 二维码名称
|
||||
*/
|
||||
codeName?: string;
|
||||
|
||||
/**
|
||||
* 二维码类型(1-招生报名)
|
||||
*/
|
||||
codeType?: number;
|
||||
|
||||
/**
|
||||
* 二维码内容(URL)
|
||||
*/
|
||||
qrContent?: string;
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
accessToken?: string;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
expireTime?: string;
|
||||
|
||||
/**
|
||||
* 最大使用次数(0-无限制)
|
||||
*/
|
||||
maxUsage?: number;
|
||||
|
||||
/**
|
||||
* 当前使用次数
|
||||
*/
|
||||
currentUsage?: number;
|
||||
|
||||
/**
|
||||
* 状态(0-禁用 1-启用)
|
||||
*/
|
||||
status?: number;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
createBy?: string | number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: string;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
updateBy?: string | number;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二维码管理 BO
|
||||
*/
|
||||
export interface QrCodeBO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 二维码名称
|
||||
*/
|
||||
codeName?: string;
|
||||
|
||||
/**
|
||||
* 二维码类型(1-招生报名)
|
||||
*/
|
||||
codeType?: number;
|
||||
|
||||
/**
|
||||
* 二维码内容(URL)
|
||||
*/
|
||||
qrContent?: string;
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
accessToken?: string;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
expireTime?: string;
|
||||
|
||||
/**
|
||||
* 最大使用次数(0-无限制)
|
||||
*/
|
||||
maxUsage?: number;
|
||||
|
||||
/**
|
||||
* 当前使用次数
|
||||
*/
|
||||
currentUsage?: number;
|
||||
|
||||
/**
|
||||
* 状态(0-禁用 1-启用)
|
||||
*/
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二维码查询参数
|
||||
*/
|
||||
export interface QrCodeQuery {
|
||||
/**
|
||||
* 二维码名称
|
||||
*/
|
||||
codeName?: string;
|
||||
|
||||
/**
|
||||
* 二维码类型
|
||||
*/
|
||||
codeType?: number;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
status?: number;
|
||||
|
||||
/**
|
||||
* 创建时间范围
|
||||
*/
|
||||
params?: {
|
||||
beginTime?: string;
|
||||
endTime?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询二维码列表
|
||||
*/
|
||||
export function listQrCode(query?: QrCodeQuery) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询二维码详细
|
||||
*/
|
||||
export function getQrCode(id: string | number) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增二维码
|
||||
*/
|
||||
export function addQrCode(data: QrCodeBO) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改二维码
|
||||
*/
|
||||
export function updateQrCode(data: QrCodeBO) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除二维码
|
||||
*/
|
||||
export function delQrCode(ids: string | number | Array<string | number>) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出二维码
|
||||
*/
|
||||
export function exportQrCode(query?: QrCodeQuery) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/export',
|
||||
method: 'post',
|
||||
data: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证二维码Token有效性
|
||||
*/
|
||||
export function validateToken(token: string) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/validate/' + token,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Token获取二维码信息
|
||||
*/
|
||||
export function getQrCodeByToken(token: string) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/token/' + token,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加二维码使用次数
|
||||
*/
|
||||
export function incrementUsage(id: string | number) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/increment/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的访问令牌
|
||||
*/
|
||||
export function generateToken() {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/generate-token',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 二维码类型选项
|
||||
*/
|
||||
export const qrCodeTypeOptions = [
|
||||
{
|
||||
label: '招生报名',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 状态选项
|
||||
*/
|
||||
export const statusOptions = [
|
||||
{
|
||||
label: '禁用',
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: '启用',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
@@ -159,6 +159,20 @@
|
||||
<el-icon><Star /></el-icon>
|
||||
报考信息
|
||||
</h3>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属学院" prop="collegeName" required>
|
||||
<el-select v-model="collegeSelectValue" placeholder="请选择所属学院" style="width: 100%" @change="onCollegeChange">
|
||||
<el-option v-for="college in collegeOptions" :key="college.deptId" :label="college.deptName" :value="college.deptId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学院代码" prop="collegeId">
|
||||
<el-input v-model="applyForm.collegeId" placeholder="学院代码将自动填入" readonly />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="报考专业" prop="appliedMajor" required>
|
||||
@@ -271,17 +285,20 @@
|
||||
import { reactive, ref, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick } from 'vue';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { submitEnrollmentApplication, queryMyEnrollmentStatus } from '@/api/zsjy/enrollment';
|
||||
import { EnrollmentInfoForm } from '@/api/zsjy/enrollment/types';
|
||||
import type { EnrollmentInfoForm } from '@/api/zsjy/enrollment/types';
|
||||
import { listColleges, listMajorsByCollegeId } from '@/api/xg/xsjcxx/classMsg/college';
|
||||
import { User, School, Star, Document, Check, Refresh } from '@element-plus/icons-vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { zsjy_gender, sys_yes_no, zsjy_major, zsjy_uniform_size } = toRefs<any>(
|
||||
proxy?.useDict('zsjy_gender', 'sys_yes_no', 'zsjy_major', 'zsjy_uniform_size')
|
||||
(proxy as any)?.useDict('zsjy_gender', 'sys_yes_no', 'zsjy_major', 'zsjy_uniform_size')
|
||||
);
|
||||
|
||||
const applyFormRef = ref<InstanceType<typeof ElForm>>();
|
||||
const submitLoading = ref(false);
|
||||
const queryLoading = ref(false);
|
||||
const collegeOptions = ref<Array<{ deptId: number; deptName: string; deptCategory: string }>>([]);
|
||||
const collegeSelectValue = ref<number | undefined>(undefined);
|
||||
const majorOptions = ref<Array<{ value: string; label: string; majorCode: string }>>([]);
|
||||
|
||||
const statusDialog = reactive({
|
||||
@@ -311,6 +328,8 @@ const applyForm = reactive<EnrollmentInfoForm>({
|
||||
scienceScore: undefined,
|
||||
socialScore: undefined,
|
||||
peScore: undefined,
|
||||
collegeName: undefined,
|
||||
collegeId: undefined,
|
||||
appliedMajor: undefined,
|
||||
majorCode: undefined,
|
||||
uniformSize: undefined,
|
||||
@@ -340,6 +359,7 @@ const rules = {
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
|
||||
],
|
||||
phone2: [{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }],
|
||||
collegeName: [{ required: true, message: '所属学院不能为空', trigger: 'change' }],
|
||||
appliedMajor: [{ required: true, message: '报考专业不能为空', trigger: 'change' }],
|
||||
totalScoreFull: [{ type: 'number', min: 100, max: 1000, message: '中考满分应在100-1000之间', trigger: 'blur' }],
|
||||
totalScore: [{ type: 'number', min: 0, message: '中考成绩不能小于0', trigger: 'blur' }],
|
||||
@@ -383,6 +403,55 @@ const onIdCardChange = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 学院选择变化处理 */
|
||||
const onCollegeChange = async (selectedCollegeId: number) => {
|
||||
if (selectedCollegeId) {
|
||||
// 根据选择的学院ID找到学院对象
|
||||
const college = collegeOptions.value.find((item) => item.deptId === selectedCollegeId);
|
||||
if (college) {
|
||||
// 设置学院信息
|
||||
applyForm.collegeId = college.deptCategory || college.deptId.toString();
|
||||
applyForm.collegeName = college.deptName;
|
||||
|
||||
// 根据学院加载专业选项
|
||||
await loadMajorsByCollegeId(selectedCollegeId);
|
||||
|
||||
// 清空专业选择
|
||||
applyForm.appliedMajor = undefined;
|
||||
applyForm.majorCode = undefined;
|
||||
|
||||
console.log('学院选择变化:', {
|
||||
collegeId: applyForm.collegeId,
|
||||
collegeName: applyForm.collegeName
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 清空学院和专业相关字段
|
||||
applyForm.collegeName = undefined;
|
||||
applyForm.collegeId = undefined;
|
||||
applyForm.appliedMajor = undefined;
|
||||
applyForm.majorCode = undefined;
|
||||
majorOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
/** 根据学院ID加载专业 */
|
||||
const loadMajorsByCollegeId = async (collegeId: number) => {
|
||||
try {
|
||||
const response = await listMajorsByCollegeId(collegeId);
|
||||
if (response.data) {
|
||||
majorOptions.value = response.data.map((major: any) => ({
|
||||
value: major.deptId.toString(),
|
||||
label: major.deptName,
|
||||
majorCode: major.deptCategory || major.deptId.toString()
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载专业数据失败:', error);
|
||||
majorOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
/** 专业选择变化处理 */
|
||||
const onMajorChange = (selectedMajor: string) => {
|
||||
const major = majorOptions.value.find((item) => item.label === selectedMajor);
|
||||
@@ -448,10 +517,10 @@ const submitApplication = () => {
|
||||
}
|
||||
|
||||
await submitEnrollmentApplication(applyForm);
|
||||
proxy?.$modal.msgSuccess('报名申请提交成功!');
|
||||
(proxy as any)?.$modal.msgSuccess('报名申请提交成功!');
|
||||
resetForm();
|
||||
} catch (error) {
|
||||
proxy?.$modal.msgError('提交失败,请稍后重试');
|
||||
(proxy as any)?.$modal.msgError('提交失败,请稍后重试');
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
}
|
||||
@@ -470,7 +539,7 @@ const resetForm = () => {
|
||||
/** 查询报名状态 */
|
||||
const queryStatus = async () => {
|
||||
if (!queryForm.idCard && !queryForm.examNumber) {
|
||||
proxy?.$modal.msgWarning('请输入身份证号或准考证号');
|
||||
(proxy as any)?.$modal.msgWarning('请输入身份证号或准考证号');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -485,16 +554,31 @@ const queryStatus = async () => {
|
||||
statusInfo.value = response.data;
|
||||
statusDialog.visible = true;
|
||||
} else {
|
||||
proxy?.$modal.msgWarning('未找到相关报名信息');
|
||||
(proxy as any)?.$modal.msgWarning('未找到相关报名信息');
|
||||
}
|
||||
} catch (error) {
|
||||
proxy?.$modal.msgError('查询失败,请稍后重试');
|
||||
(proxy as any)?.$modal.msgError('查询失败,请稍后重试');
|
||||
} finally {
|
||||
queryLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取学院选项 */
|
||||
const getCollegeOptions = async () => {
|
||||
try {
|
||||
const response = await listColleges();
|
||||
if (response.data) {
|
||||
collegeOptions.value = response.data;
|
||||
console.log('学院数据加载成功:', collegeOptions.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('学院数据加载失败:', error);
|
||||
collegeOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getCollegeOptions();
|
||||
getMajorOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -365,15 +365,7 @@
|
||||
<el-col :span="12">
|
||||
<el-form-item label="校服尺码" prop="uniformSize">
|
||||
<el-select v-model="form.uniformSize" placeholder="请选择校服尺码" style="width: 100%">
|
||||
<el-option value="150/84A" label="150/84A(身高150cm)" />
|
||||
<el-option value="155/86A" label="155/86A(身高155cm)" />
|
||||
<el-option value="160/88A" label="160/88A(身高160cm)" />
|
||||
<el-option value="165/90A" label="165/90A(身高165cm)" />
|
||||
<el-option value="170/92A" label="170/92A(身高170cm)" />
|
||||
<el-option value="175/94A" label="175/94A(身高175cm)" />
|
||||
<el-option value="180/96A" label="180/96A(身高180cm)" />
|
||||
<el-option value="185/98A" label="185/98A(身高185cm)" />
|
||||
<el-option value="190/100A" label="190/100A(身高190cm)" />
|
||||
<el-option v-for="dict in zsjy_uniform_size" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -1214,18 +1206,7 @@ const onSearchCollegeChange = (selectedCollegeId: string) => {
|
||||
|
||||
// 专业选项由学院选择后动态生成
|
||||
|
||||
// 校服尺码数据,全局常量
|
||||
const UNIFORM_SIZES = [
|
||||
{ value: '150/84A', label: '150/84A(身高150cm)', height: '150cm' },
|
||||
{ value: '155/86A', label: '155/86A(身高155cm)', height: '155cm' },
|
||||
{ value: '160/88A', label: '160/88A(身高160cm)', height: '160cm' },
|
||||
{ value: '165/90A', label: '165/90A(身高165cm)', height: '165cm' },
|
||||
{ value: '170/92A', label: '170/92A(身高170cm)', height: '170cm' },
|
||||
{ value: '175/94A', label: '175/94A(身高175cm)', height: '175cm' },
|
||||
{ value: '180/96A', label: '180/96A(身高180cm)', height: '180cm' },
|
||||
{ value: '185/98A', label: '185/98A(身高185cm)', height: '185cm' },
|
||||
{ value: '190/100A', label: '190/100A(身高190cm)', height: '190cm' }
|
||||
];
|
||||
// 校服尺码数据使用字典 zsjy_uniform_size
|
||||
|
||||
/** 根据学院代码查找学院名称 */
|
||||
const getCollegeName = (code: string | undefined) => {
|
||||
@@ -1281,7 +1262,8 @@ const getCollegeNameEnhanced = (student: any) => {
|
||||
/** 获取校服尺码显示名称 */
|
||||
const getUniformSizeLabel = (value: string | undefined) => {
|
||||
if (!value) return '-';
|
||||
const size = UNIFORM_SIZES.find((item) => item.value === value);
|
||||
// 使用字典数据查找校服尺码
|
||||
const size = zsjy_uniform_size.value?.find((item: any) => item.value === value);
|
||||
return size ? size.label : value;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<div class="p-2 page-container" v-hasPermi="['zsjy:qrcode:view']">
|
||||
<!-- 二维码展示卡片 -->
|
||||
<el-card shadow="never" class="qrcode-card">
|
||||
<div class="qrcode-content">
|
||||
<!-- 页面标题 -->
|
||||
<div class="qrcode-header">
|
||||
<h2 class="qrcode-title">新生报名二维码</h2>
|
||||
<p class="qrcode-description">请扫码进行新生报名信息填写</p>
|
||||
</div>
|
||||
|
||||
<!-- 二维码区域 -->
|
||||
<div class="qrcode-wrapper">
|
||||
<div class="qrcode-container">
|
||||
<canvas ref="qrcodeCanvas" class="qrcode-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- 二维码URL显示 -->
|
||||
<div class="qrcode-url">
|
||||
<el-input
|
||||
v-model="qrcodeUrl"
|
||||
placeholder="请输入二维码链接地址"
|
||||
clearable
|
||||
@change="generateQrcode"
|
||||
>
|
||||
<template #prepend>链接地址</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="qrcode-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="Refresh"
|
||||
@click="generateQrcode"
|
||||
:loading="generating"
|
||||
v-hasPermi="['zsjy:qrcode:view']"
|
||||
>
|
||||
重新生成
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
icon="Download"
|
||||
@click="downloadQrcode"
|
||||
:disabled="!qrcodeGenerated"
|
||||
v-hasPermi="['zsjy:qrcode:view']"
|
||||
>
|
||||
下载二维码
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
icon="Printer"
|
||||
@click="printQrcode"
|
||||
:disabled="!qrcodeGenerated"
|
||||
v-hasPermi="['zsjy:qrcode:view']"
|
||||
>
|
||||
打印二维码
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="QrCode">
|
||||
import { ref, onMounted, nextTick, getCurrentInstance } from 'vue'
|
||||
import QRCode from 'qrcode'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
// 响应式数据
|
||||
const qrcodeCanvas = ref<HTMLCanvasElement>()
|
||||
const qrcodeUrl = ref<string>('') // 二维码链接地址,可手动配置
|
||||
const generating = ref<boolean>(false)
|
||||
const qrcodeGenerated = ref<boolean>(false)
|
||||
|
||||
// 默认二维码URL(后续可手动配置)
|
||||
const getDefaultUrl = () => {
|
||||
// 使用 window.location 来判断环境
|
||||
const isProduction = window.location.hostname !== 'localhost'
|
||||
return isProduction
|
||||
? 'https://domain.com/mobile/#/pages/enrollment/form'
|
||||
: 'http://localhost:8081/#/pages/enrollment/form'
|
||||
}
|
||||
|
||||
/** 生成二维码 */
|
||||
const generateQrcode = async () => {
|
||||
if (!qrcodeUrl.value.trim()) {
|
||||
ElMessage.warning('请输入有效的链接地址')
|
||||
return
|
||||
}
|
||||
|
||||
generating.value = true
|
||||
try {
|
||||
await nextTick()
|
||||
|
||||
if (qrcodeCanvas.value) {
|
||||
// 生成二维码到canvas
|
||||
await QRCode.toCanvas(qrcodeCanvas.value, qrcodeUrl.value, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
color: {
|
||||
dark: '#000000',
|
||||
light: '#FFFFFF'
|
||||
},
|
||||
errorCorrectionLevel: 'M'
|
||||
})
|
||||
|
||||
qrcodeGenerated.value = true
|
||||
ElMessage.success('二维码生成成功')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成二维码失败:', error)
|
||||
ElMessage.error('生成二维码失败,请检查链接地址是否正确')
|
||||
} finally {
|
||||
generating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 下载二维码 */
|
||||
const downloadQrcode = () => {
|
||||
if (!qrcodeCanvas.value || !qrcodeGenerated.value) {
|
||||
ElMessage.warning('请先生成二维码')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建下载链接
|
||||
const link = document.createElement('a')
|
||||
link.download = `新生报到二维码_${new Date().getTime()}.png`
|
||||
link.href = qrcodeCanvas.value.toDataURL('image/png')
|
||||
|
||||
// 触发下载
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
|
||||
ElMessage.success('二维码下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载二维码失败:', error)
|
||||
ElMessage.error('下载失败')
|
||||
}
|
||||
}
|
||||
|
||||
/** 打印二维码 */
|
||||
const printQrcode = () => {
|
||||
if (!qrcodeCanvas.value || !qrcodeGenerated.value) {
|
||||
ElMessage.warning('请先生成二维码')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建打印窗口
|
||||
const printWindow = window.open('', '_blank')
|
||||
if (!printWindow) {
|
||||
ElMessage.error('无法打开打印窗口,请检查浏览器设置')
|
||||
return
|
||||
}
|
||||
|
||||
// 获取二维码图片数据
|
||||
const qrcodeDataUrl = qrcodeCanvas.value.toDataURL('image/png')
|
||||
|
||||
// 生成打印页面HTML
|
||||
const scriptTag = 'script'
|
||||
const printContent = [
|
||||
'<!DOCTYPE html>',
|
||||
'<html>',
|
||||
'<head>',
|
||||
'<meta charset="utf-8">',
|
||||
'<title>新生报到二维码</title>',
|
||||
'<style>',
|
||||
'body { margin: 0; padding: 20px; text-align: center; font-family: Arial, sans-serif; }',
|
||||
'.print-header { margin-bottom: 30px; }',
|
||||
'.print-title { font-size: 24px; font-weight: bold; color: #333; margin-bottom: 10px; }',
|
||||
'.print-description { font-size: 16px; color: #666; margin-bottom: 20px; }',
|
||||
'.print-qrcode { margin: 30px 0; }',
|
||||
'.print-qrcode img { border: 1px solid #ddd; padding: 10px; background: white; }',
|
||||
'@media print { body { margin: 0; } }',
|
||||
'</style>',
|
||||
'</head>',
|
||||
'<body>',
|
||||
'<div class="print-header">',
|
||||
'<h1 class="print-title">新生报到二维码</h1>',
|
||||
'<p class="print-description">请扫码进行新生报名信息填写</p>',
|
||||
'</div>',
|
||||
'<div class="print-qrcode">',
|
||||
`<img src="${qrcodeDataUrl}" alt="新生报到二维码" />`,
|
||||
'</div>',
|
||||
`<${scriptTag}>`,
|
||||
'window.onload = function() {',
|
||||
'window.print();',
|
||||
'window.onafterprint = function() { window.close(); }',
|
||||
'}',
|
||||
`</${scriptTag}>`,
|
||||
'</body>',
|
||||
'</html>'
|
||||
].join('')
|
||||
|
||||
// 写入打印内容
|
||||
printWindow.document.write(printContent)
|
||||
printWindow.document.close()
|
||||
|
||||
} catch (error) {
|
||||
console.error('打印二维码失败:', error)
|
||||
ElMessage.error('打印失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载后初始化
|
||||
onMounted(() => {
|
||||
// 设置默认URL
|
||||
qrcodeUrl.value = getDefaultUrl()
|
||||
// 自动生成二维码
|
||||
nextTick(() => {
|
||||
generateQrcode()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
.qrcode-card {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
|
||||
.qrcode-content {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.qrcode-header {
|
||||
margin-bottom: 40px;
|
||||
|
||||
.qrcode-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 12px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.qrcode-description {
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.qrcode-wrapper {
|
||||
margin-bottom: 40px;
|
||||
|
||||
.qrcode-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border: 2px dashed #e4e7ed;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
background-color: #f0f9ff;
|
||||
}
|
||||
|
||||
.qrcode-canvas {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.qrcode-url {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.qrcode-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.el-button {
|
||||
min-width: 120px;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
.page-container {
|
||||
.qrcode-card {
|
||||
margin: 0;
|
||||
|
||||
.qrcode-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.qrcode-header {
|
||||
margin-bottom: 30px;
|
||||
|
||||
.qrcode-title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.qrcode-description {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.qrcode-actions {
|
||||
.el-button {
|
||||
min-width: 100px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user