按需求调整

This commit is contained in:
wanglongjie 2026-02-03 15:37:25 +08:00
parent 8aa3af986f
commit daacd7d742
17 changed files with 602 additions and 165 deletions

View File

@ -55,6 +55,11 @@ public class Constants {
*/ */
public static final int REPORT_STATUS_EXPIRED = 3; public static final int REPORT_STATUS_EXPIRED = 3;
/**
* 报备状态 - 已作废
*/
public static final int REPORT_STATUS_CANCELED = 4;
/** /**
* 用户角色 - 管理员 * 用户角色 - 管理员
*/ */

View File

@ -5,6 +5,7 @@ import com.bycrm.common.Result;
import com.bycrm.dto.PageQuery; import com.bycrm.dto.PageQuery;
import com.bycrm.dto.ReportAuditDTO; import com.bycrm.dto.ReportAuditDTO;
import com.bycrm.dto.ReportDTO; import com.bycrm.dto.ReportDTO;
import com.bycrm.dto.ReportUpdateDTO;
import com.bycrm.service.ReportService; import com.bycrm.service.ReportService;
import com.bycrm.vo.ReportVO; import com.bycrm.vo.ReportVO;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@ -72,6 +73,20 @@ public class ReportController {
return Result.success(); return Result.success();
} }
/**
* 更新报备
*/
@ApiOperation("更新报备")
@PutMapping("/{id}")
public Result<Void> updateReport(
@ApiParam("报备ID") @PathVariable Long id,
@RequestBody ReportUpdateDTO updateDTO,
HttpServletRequest request) {
Long currentUserId = (Long) request.getAttribute("currentUserId");
reportService.updateReport(id, updateDTO, currentUserId);
return Result.success();
}
/** /**
* 审核报备 * 审核报备
*/ */

View File

@ -2,7 +2,7 @@ package com.bycrm.dto;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotBlank;
import java.io.Serializable; import java.io.Serializable;
/** /**
@ -14,10 +14,27 @@ public class ReportDTO implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 客户ID * 学校ID可选从数据源中选择时提供
*/ */
@NotNull(message = "客户ID不能为空") private Long schoolId;
private Long customerId;
/**
* 学校名称必填
*/
@NotBlank(message = "学校名称不能为空")
private String schoolName;
/**
* 所属产品
*/
@NotBlank(message = "所属产品不能为空")
private String product;
/**
* 项目类型
*/
@NotBlank(message = "项目类型不能为空")
private String projectType;
/** /**
* 报备说明 * 报备说明

View File

@ -0,0 +1,44 @@
package com.bycrm.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 报备更新 DTO
*/
@Data
public class ReportUpdateDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 所属产品
*/
@NotBlank(message = "所属产品不能为空")
private String product;
/**
* 项目类型
*/
@NotBlank(message = "项目类型不能为空")
private String projectType;
/**
* 报备说明
*/
private String description;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
private Integer status;
/**
* 作废原因当状态为作废时必填
*/
private String cancelReason;
}

View File

@ -26,9 +26,24 @@ public class Report implements Serializable {
private Long dealerId; private Long dealerId;
/** /**
* 客户ID * 学校ID crm_school 选择
*/ */
private Long customerId; private Long schoolId;
/**
* 学校名称冗余存储方便查询
*/
private String schoolName;
/**
* 所属产品
*/
private String product;
/**
* 项目类型
*/
private String projectType;
/** /**
* 报备说明 * 报备说明
@ -36,7 +51,7 @@ public class Report implements Serializable {
private String description; private String description;
/** /**
* 状态0-待审核 1-已通过 2-已驳回 3-已失效 * 状态0-待审核 1-已通过 2-已驳回 3-已失效 4-已作废
*/ */
private Integer status; private Integer status;
@ -45,6 +60,11 @@ public class Report implements Serializable {
*/ */
private String rejectReason; private String rejectReason;
/**
* 作废原因
*/
private String cancelReason;
/** /**
* 保护期开始日期 * 保护期开始日期
*/ */
@ -73,14 +93,4 @@ public class Report implements Serializable {
* 关联查询字段 - 经销商名称 * 关联查询字段 - 经销商名称
*/ */
private String dealerName; private String dealerName;
/**
* 关联查询字段 - 客户名称
*/
private String customerName;
/**
* 关联查询字段 - 客户电话
*/
private String customerPhone;
} }

View File

@ -19,9 +19,18 @@ public interface ReportMapper {
Report selectById(@Param("id") Long id); Report selectById(@Param("id") Long id);
/** /**
* 查询客户的有效报备 * 查询学校+产品+项目类型的有效报备根据学校ID
*/ */
Report selectValidByCustomerId(@Param("customerId") Long customerId); Report selectValidBySchoolAndProduct(@Param("schoolId") Long schoolId,
@Param("product") String product,
@Param("projectType") String projectType);
/**
* 查询学校名称+产品+项目类型的有效报备根据学校名称
*/
Report selectValidBySchoolNameAndProduct(@Param("schoolName") String schoolName,
@Param("product") String product,
@Param("projectType") String projectType);
/** /**
* 分页查询报备 * 分页查询报备

View File

@ -12,6 +12,11 @@ import java.util.List;
@Mapper @Mapper
public interface SchoolMapper { public interface SchoolMapper {
/**
* 根据ID查询学校
*/
School selectById(@Param("id") Long id);
/** /**
* 插入学校 * 插入学校
*/ */

View File

@ -4,6 +4,7 @@ import com.bycrm.common.PageResult;
import com.bycrm.dto.PageQuery; import com.bycrm.dto.PageQuery;
import com.bycrm.dto.ReportAuditDTO; import com.bycrm.dto.ReportAuditDTO;
import com.bycrm.dto.ReportDTO; import com.bycrm.dto.ReportDTO;
import com.bycrm.dto.ReportUpdateDTO;
import com.bycrm.vo.ReportVO; import com.bycrm.vo.ReportVO;
/** /**
@ -27,6 +28,11 @@ public interface ReportService {
*/ */
void createReport(ReportDTO reportDTO, Long currentUserId); void createReport(ReportDTO reportDTO, Long currentUserId);
/**
* 更新报备
*/
void updateReport(Long id, ReportUpdateDTO updateDTO, Long currentUserId);
/** /**
* 审核报备 * 审核报备
*/ */

View File

@ -5,12 +5,13 @@ import com.bycrm.common.PageResult;
import com.bycrm.dto.PageQuery; import com.bycrm.dto.PageQuery;
import com.bycrm.dto.ReportAuditDTO; import com.bycrm.dto.ReportAuditDTO;
import com.bycrm.dto.ReportDTO; import com.bycrm.dto.ReportDTO;
import com.bycrm.dto.ReportUpdateDTO;
import com.bycrm.entity.Report; import com.bycrm.entity.Report;
import com.bycrm.entity.Customer; import com.bycrm.entity.School;
import com.bycrm.entity.User; import com.bycrm.entity.User;
import com.bycrm.exception.BusinessException; import com.bycrm.exception.BusinessException;
import com.bycrm.mapper.CustomerMapper;
import com.bycrm.mapper.ReportMapper; import com.bycrm.mapper.ReportMapper;
import com.bycrm.mapper.SchoolMapper;
import com.bycrm.mapper.UserMapper; import com.bycrm.mapper.UserMapper;
import com.bycrm.service.ReportService; import com.bycrm.service.ReportService;
import com.bycrm.service.SystemConfigService; import com.bycrm.service.SystemConfigService;
@ -32,16 +33,16 @@ import java.util.stream.Collectors;
public class ReportServiceImpl implements ReportService { public class ReportServiceImpl implements ReportService {
private final ReportMapper reportMapper; private final ReportMapper reportMapper;
private final CustomerMapper customerMapper; private final SchoolMapper schoolMapper;
private final UserMapper userMapper; private final UserMapper userMapper;
private final SystemConfigService systemConfigService; private final SystemConfigService systemConfigService;
public ReportServiceImpl(ReportMapper reportMapper, public ReportServiceImpl(ReportMapper reportMapper,
CustomerMapper customerMapper, SchoolMapper schoolMapper,
UserMapper userMapper, UserMapper userMapper,
SystemConfigService systemConfigService) { SystemConfigService systemConfigService) {
this.reportMapper = reportMapper; this.reportMapper = reportMapper;
this.customerMapper = customerMapper; this.schoolMapper = schoolMapper;
this.userMapper = userMapper; this.userMapper = userMapper;
this.systemConfigService = systemConfigService; this.systemConfigService = systemConfigService;
} }
@ -86,23 +87,47 @@ public class ReportServiceImpl implements ReportService {
throw new BusinessException("您未关联经销商,无法提交报备"); throw new BusinessException("您未关联经销商,无法提交报备");
} }
// 检查客户是否存在 // 防撞单校验根据是否有 schoolId 选择不同的校验方式
Customer customer = customerMapper.selectById(reportDTO.getCustomerId()); Report existingReport = null;
if (customer == null) { if (reportDTO.getSchoolId() != null) {
throw new BusinessException("客户不存在"); // 有学校ID按学校ID+产品+项目类型校验
existingReport = reportMapper.selectValidBySchoolAndProduct(
reportDTO.getSchoolId(),
reportDTO.getProduct(),
reportDTO.getProjectType()
);
} else {
// 没有学校ID按学校名称+产品+项目类型校验
existingReport = reportMapper.selectValidBySchoolNameAndProduct(
reportDTO.getSchoolName(),
reportDTO.getProduct(),
reportDTO.getProjectType()
);
} }
// 防撞单校验检查该客户是否已存在有效报备
Report existingReport = reportMapper.selectValidByCustomerId(reportDTO.getCustomerId());
Boolean allowOverlap = systemConfigService.getBooleanConfigValue("report.allow.overlap", false); Boolean allowOverlap = systemConfigService.getBooleanConfigValue("report.allow.overlap", false);
if (existingReport != null && !allowOverlap) { if (existingReport != null && !allowOverlap) {
throw new BusinessException("该客户已被其他经销商报备,无法重复报备"); throw new BusinessException("该项目已报备");
}
// 如果有学校ID查询学校信息
String schoolName = reportDTO.getSchoolName();
Long schoolId = reportDTO.getSchoolId();
if (schoolId != null) {
School school = schoolMapper.selectById(schoolId);
if (school != null) {
schoolName = school.getSchoolName();
}
} }
// 创建报备 // 创建报备
Report report = new Report(); Report report = new Report();
report.setDealerId(currentUser.getDealerId()); report.setDealerId(currentUser.getDealerId());
report.setCustomerId(reportDTO.getCustomerId()); report.setSchoolId(schoolId); // 可以为 NULL
report.setSchoolName(schoolName);
report.setProduct(reportDTO.getProduct());
report.setProjectType(reportDTO.getProjectType());
report.setDescription(reportDTO.getDescription()); report.setDescription(reportDTO.getDescription());
report.setStatus(Constants.REPORT_STATUS_PENDING); report.setStatus(Constants.REPORT_STATUS_PENDING);
report.setCreatedAt(LocalDateTime.now()); report.setCreatedAt(LocalDateTime.now());
@ -138,11 +163,6 @@ public class ReportServiceImpl implements ReportService {
report.setProtectStartDate(today); report.setProtectStartDate(today);
Integer protectDays = systemConfigService.getIntegerConfigValue("report.protect.days", Constants.DEFAULT_PROTECT_DAYS); Integer protectDays = systemConfigService.getIntegerConfigValue("report.protect.days", Constants.DEFAULT_PROTECT_DAYS);
report.setProtectEndDate(today.plusDays(protectDays)); report.setProtectEndDate(today.plusDays(protectDays));
// 更新客户状态为保护中
Customer customer = customerMapper.selectById(report.getCustomerId());
customer.setStatus(Constants.CUSTOMER_STATUS_PROTECTED);
customerMapper.update(customer);
} else { } else {
// 审核驳回 // 审核驳回
report.setStatus(Constants.REPORT_STATUS_REJECTED); report.setStatus(Constants.REPORT_STATUS_REJECTED);
@ -153,6 +173,43 @@ public class ReportServiceImpl implements ReportService {
reportMapper.update(report); reportMapper.update(report);
} }
@Override
@Transactional(rollbackFor = Exception.class)
public void updateReport(Long id, ReportUpdateDTO updateDTO, Long currentUserId) {
// 获取当前用户
User currentUser = userMapper.selectById(currentUserId);
if (currentUser.getRole() != Constants.USER_ROLE_ADMIN) {
throw new BusinessException("只有管理员才能编辑报备");
}
Report report = reportMapper.selectById(id);
if (report == null) {
throw new BusinessException("报备不存在");
}
// 只有已通过的报备才能编辑
if (report.getStatus() != Constants.REPORT_STATUS_APPROVED) {
throw new BusinessException("只能编辑已通过的报备");
}
// 如果状态改为作废必须填写作废原因
if (updateDTO.getStatus() == Constants.REPORT_STATUS_CANCELED) {
if (updateDTO.getCancelReason() == null || updateDTO.getCancelReason().trim().isEmpty()) {
throw new BusinessException("作废时必须填写作废原因");
}
}
// 更新报备信息
report.setProduct(updateDTO.getProduct());
report.setProjectType(updateDTO.getProjectType());
report.setDescription(updateDTO.getDescription());
report.setStatus(updateDTO.getStatus());
report.setCancelReason(updateDTO.getCancelReason());
report.setUpdatedAt(LocalDateTime.now());
reportMapper.update(report);
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void withdrawReport(Long id, Long currentUserId) { public void withdrawReport(Long id, Long currentUserId) {
@ -189,15 +246,6 @@ public class ReportServiceImpl implements ReportService {
// 批量更新报备状态 // 批量更新报备状态
List<Long> ids = expiringReports.stream().map(Report::getId).collect(Collectors.toList()); List<Long> ids = expiringReports.stream().map(Report::getId).collect(Collectors.toList());
reportMapper.batchUpdateExpired(ids); reportMapper.batchUpdateExpired(ids);
// 批量更新客户状态为可报备
expiringReports.forEach(report -> {
Customer customer = customerMapper.selectById(report.getCustomerId());
if (customer != null && customer.getStatus() == Constants.CUSTOMER_STATUS_PROTECTED) {
customer.setStatus(Constants.CUSTOMER_STATUS_AVAILABLE);
customerMapper.update(customer);
}
});
} }
@Override @Override
@ -236,6 +284,9 @@ public class ReportServiceImpl implements ReportService {
case Constants.REPORT_STATUS_EXPIRED: case Constants.REPORT_STATUS_EXPIRED:
vo.setStatusDesc("已失效"); vo.setStatusDesc("已失效");
break; break;
case Constants.REPORT_STATUS_CANCELED:
vo.setStatusDesc("已作废");
break;
default: default:
vo.setStatusDesc("未知"); vo.setStatusDesc("未知");
} }

View File

@ -31,19 +31,24 @@ public class ReportVO implements Serializable {
private String dealerName; private String dealerName;
/** /**
* 客户ID * 学校ID
*/ */
private Long customerId; private Long schoolId;
/** /**
* 客户名称 * 学校名称
*/ */
private String customerName; private String schoolName;
/** /**
* 客户电话 * 所属产品
*/ */
private String customerPhone; private String product;
/**
* 项目类型
*/
private String projectType;
/** /**
* 报备说明 * 报备说明
@ -51,7 +56,7 @@ public class ReportVO implements Serializable {
private String description; private String description;
/** /**
* 状态0-待审核 1-已通过 2-已驳回 3-已失效 * 状态0-待审核 1-已通过 2-已驳回 3-已失效 4-已作废
*/ */
private Integer status; private Integer status;
@ -65,6 +70,11 @@ public class ReportVO implements Serializable {
*/ */
private String rejectReason; private String rejectReason;
/**
* 作废原因
*/
private String cancelReason;
/** /**
* 保护期开始日期 * 保护期开始日期
*/ */

View File

@ -6,45 +6,52 @@
<resultMap id="BaseResultMap" type="com.bycrm.entity.Report"> <resultMap id="BaseResultMap" type="com.bycrm.entity.Report">
<id column="id" property="id"/> <id column="id" property="id"/>
<result column="dealer_id" property="dealerId"/> <result column="dealer_id" property="dealerId"/>
<result column="customer_id" property="customerId"/> <result column="school_id" property="schoolId"/>
<result column="school_name" property="schoolName"/>
<result column="product" property="product"/>
<result column="project_type" property="projectType"/>
<result column="description" property="description"/> <result column="description" property="description"/>
<result column="status" property="status"/> <result column="status" property="status"/>
<result column="reject_reason" property="rejectReason"/> <result column="reject_reason" property="rejectReason"/>
<result column="cancel_reason" property="cancelReason"/>
<result column="protect_start_date" property="protectStartDate"/> <result column="protect_start_date" property="protectStartDate"/>
<result column="protect_end_date" property="protectEndDate"/> <result column="protect_end_date" property="protectEndDate"/>
<result column="created_at" property="createdAt"/> <result column="created_at" property="createdAt"/>
<result column="updated_at" property="updatedAt"/> <result column="updated_at" property="updatedAt"/>
<result column="dealer_name" property="dealerName"/> <result column="dealer_name" property="dealerName"/>
<result column="customer_name" property="customerName"/>
<result column="customer_phone" property="customerPhone"/>
</resultMap> </resultMap>
<select id="selectById" resultMap="BaseResultMap"> <select id="selectById" resultMap="BaseResultMap">
SELECT r.*, SELECT r.*,
d.name AS dealer_name, d.name AS dealer_name
c.name AS customer_name,
c.phone AS customer_phone
FROM crm_report r FROM crm_report r
LEFT JOIN crm_dealer d ON r.dealer_id = d.id LEFT JOIN crm_dealer d ON r.dealer_id = d.id
LEFT JOIN crm_customer c ON r.customer_id = c.id
WHERE r.id = #{id} WHERE r.id = #{id}
</select> </select>
<select id="selectValidByCustomerId" resultMap="BaseResultMap"> <select id="selectValidBySchoolAndProduct" resultMap="BaseResultMap">
SELECT * FROM crm_report SELECT * FROM crm_report
WHERE customer_id = #{customerId} WHERE school_id = #{schoolId}
AND product = #{product}
AND project_type = #{projectType}
AND status IN (0, 1)
LIMIT 1
</select>
<select id="selectValidBySchoolNameAndProduct" resultMap="BaseResultMap">
SELECT * FROM crm_report
WHERE school_name = #{schoolName}
AND product = #{product}
AND project_type = #{projectType}
AND status IN (0, 1) AND status IN (0, 1)
LIMIT 1 LIMIT 1
</select> </select>
<select id="selectPage" resultMap="BaseResultMap"> <select id="selectPage" resultMap="BaseResultMap">
SELECT r.*, SELECT r.*,
d.name AS dealer_name, d.name AS dealer_name
c.name AS customer_name,
c.phone AS customer_phone
FROM crm_report r FROM crm_report r
LEFT JOIN crm_dealer d ON r.dealer_id = d.id LEFT JOIN crm_dealer d ON r.dealer_id = d.id
LEFT JOIN crm_customer c ON r.customer_id = c.id
<where> <where>
<if test="dealerId != null"> <if test="dealerId != null">
AND r.dealer_id = #{dealerId} AND r.dealer_id = #{dealerId}
@ -53,7 +60,7 @@
AND d.name LIKE CONCAT('%', #{dealerName}, '%') AND d.name LIKE CONCAT('%', #{dealerName}, '%')
</if> </if>
<if test="customerName != null and customerName != ''"> <if test="customerName != null and customerName != ''">
AND c.name LIKE CONCAT('%', #{customerName}, '%') AND r.school_name LIKE CONCAT('%', #{customerName}, '%')
</if> </if>
<if test="status != null"> <if test="status != null">
AND r.status = #{status} AND r.status = #{status}
@ -67,7 +74,6 @@
SELECT COUNT(*) SELECT COUNT(*)
FROM crm_report r FROM crm_report r
LEFT JOIN crm_dealer d ON r.dealer_id = d.id LEFT JOIN crm_dealer d ON r.dealer_id = d.id
LEFT JOIN crm_customer c ON r.customer_id = c.id
<where> <where>
<if test="dealerId != null"> <if test="dealerId != null">
AND r.dealer_id = #{dealerId} AND r.dealer_id = #{dealerId}
@ -76,7 +82,7 @@
AND d.name LIKE CONCAT('%', #{dealerName}, '%') AND d.name LIKE CONCAT('%', #{dealerName}, '%')
</if> </if>
<if test="customerName != null and customerName != ''"> <if test="customerName != null and customerName != ''">
AND c.name LIKE CONCAT('%', #{customerName}, '%') AND r.school_name LIKE CONCAT('%', #{customerName}, '%')
</if> </if>
<if test="status != null"> <if test="status != null">
AND r.status = #{status} AND r.status = #{status}
@ -85,17 +91,22 @@
</select> </select>
<insert id="insert" parameterType="com.bycrm.entity.Report" useGeneratedKeys="true" keyProperty="id"> <insert id="insert" parameterType="com.bycrm.entity.Report" useGeneratedKeys="true" keyProperty="id">
INSERT INTO crm_report (dealer_id, customer_id, description, status, protect_start_date, protect_end_date) INSERT INTO crm_report (dealer_id, school_id, school_name, product, project_type, description, status, protect_start_date, protect_end_date)
VALUES (#{dealerId}, #{customerId}, #{description}, #{status}, #{protectStartDate}, #{protectEndDate}) VALUES (#{dealerId}, #{schoolId}, #{schoolName}, #{product}, #{projectType}, #{description}, #{status}, #{protectStartDate}, #{protectEndDate})
</insert> </insert>
<update id="update" parameterType="com.bycrm.entity.Report"> <update id="update" parameterType="com.bycrm.entity.Report">
UPDATE crm_report UPDATE crm_report
<set> <set>
<if test="product != null">product = #{product},</if>
<if test="projectType != null">project_type = #{projectType},</if>
<if test="description != null">description = #{description},</if>
<if test="status != null">status = #{status},</if> <if test="status != null">status = #{status},</if>
<if test="rejectReason != null">reject_reason = #{rejectReason},</if> <if test="rejectReason != null">reject_reason = #{rejectReason},</if>
<if test="cancelReason != null">cancel_reason = #{cancelReason},</if>
<if test="protectStartDate != null">protect_start_date = #{protectStartDate},</if> <if test="protectStartDate != null">protect_start_date = #{protectStartDate},</if>
<if test="protectEndDate != null">protect_end_date = #{protectEndDate},</if> <if test="protectEndDate != null">protect_end_date = #{protectEndDate},</if>
updated_at = NOW()
</set> </set>
WHERE id = #{id} WHERE id = #{id}
</update> </update>

View File

@ -12,6 +12,12 @@
<result column="updated_at" property="updatedAt"/> <result column="updated_at" property="updatedAt"/>
</resultMap> </resultMap>
<select id="selectById" resultMap="BaseResultMap">
SELECT id, school_code, school_name, location, created_at, updated_at
FROM crm_school
WHERE id = #{id}
</select>
<insert id="insert" parameterType="com.bycrm.entity.School" useGeneratedKeys="true" keyProperty="id"> <insert id="insert" parameterType="com.bycrm.entity.School" useGeneratedKeys="true" keyProperty="id">
INSERT INTO crm_school (school_code, school_name, location, created_at, updated_at) INSERT INTO crm_school (school_code, school_name, location, created_at, updated_at)
VALUES (#{schoolCode}, #{schoolName}, #{location}, #{createdAt}, #{updatedAt}) VALUES (#{schoolCode}, #{schoolName}, #{location}, #{createdAt}, #{updatedAt})

View File

@ -1,5 +1,5 @@
import { http } from '@/utils/request' import { http } from '@/utils/request'
import type { Report, ReportForm, ReportAuditForm, PageQuery, PageResult } from '@/types' import type { Report, ReportForm, ReportAuditForm, ReportUpdateForm, PageQuery, PageResult } from '@/types'
// 分页查询报备 // 分页查询报备
export const getReportPage = (params: PageQuery & { export const getReportPage = (params: PageQuery & {
@ -21,6 +21,11 @@ export const createReport = (data: ReportForm) => {
return http.post('/report', data) return http.post('/report', data)
} }
// 更新报备
export const updateReport = (id: number, data: ReportUpdateForm) => {
return http.put(`/report/${id}`, data)
}
// 审核报备 // 审核报备
export const auditReport = (id: number, data: ReportAuditForm) => { export const auditReport = (id: number, data: ReportAuditForm) => {
return http.put(`/report/${id}/audit`, data) return http.put(`/report/${id}/audit`, data)

View File

@ -60,13 +60,15 @@ export interface Report {
id: number id: number
dealerId: number dealerId: number
dealerName: string dealerName: string
customerId: number schoolId?: number
customerName: string schoolName?: string
customerPhone?: string product?: string
projectType?: string
description?: string description?: string
status: number status: number
statusDesc: string statusDesc: string
rejectReason?: string rejectReason?: string
cancelReason?: string
protectStartDate?: string protectStartDate?: string
protectEndDate?: string protectEndDate?: string
remainDays?: number remainDays?: number
@ -75,7 +77,10 @@ export interface Report {
} }
export interface ReportForm { export interface ReportForm {
customerId: number | null schoolId?: number
schoolName: string
product: string
projectType: string
description?: string description?: string
} }
@ -84,6 +89,14 @@ export interface ReportAuditForm {
rejectReason?: string rejectReason?: string
} }
export interface ReportUpdateForm {
product: string
projectType: string
description?: string
status: number
cancelReason?: string
}
// 分页相关类型 // 分页相关类型
export interface PageQuery { export interface PageQuery {
current: number current: number
@ -104,22 +117,6 @@ export interface DictItem {
value: string | number value: string | number
} }
// 报备进展记录相关类型
export interface ReportProgress {
id: number
reportId: number
progressContent: string
createdBy: number
creatorName?: string
createdAt: string
updatedAt: string
}
export interface ReportProgressForm {
reportId: number
progressContent: string
}
// 系统配置相关类型 // 系统配置相关类型
export interface SystemConfig { export interface SystemConfig {
id: number id: number

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="dashboard"> <div class="dashboard">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6"> <!-- <el-col :span="userStore.isAdmin ? 6 : 8">
<el-card class="stat-card"> <el-card class="stat-card">
<div class="stat-content"> <div class="stat-content">
<div class="stat-icon" style="background-color: #409eff"> <div class="stat-icon" style="background-color: #409eff">
@ -14,8 +14,9 @@
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="6"> -->
<el-card class="stat-card"> <el-col :span="userStore.isAdmin ? 8 : 12">
<el-card class="stat-card" @click="handleReportCountClick">
<div class="stat-content"> <div class="stat-content">
<div class="stat-icon" style="background-color: #67c23a"> <div class="stat-icon" style="background-color: #67c23a">
<el-icon :size="30"><Document /></el-icon> <el-icon :size="30"><Document /></el-icon>
@ -27,8 +28,8 @@
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="userStore.isAdmin ? 8 : 12">
<el-card class="stat-card"> <el-card class="stat-card" @click="handlePendingClick">
<div class="stat-content"> <div class="stat-content">
<div class="stat-icon" style="background-color: #e6a23c"> <div class="stat-icon" style="background-color: #e6a23c">
<el-icon :size="30"><Clock /></el-icon> <el-icon :size="30"><Clock /></el-icon>
@ -40,8 +41,8 @@
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="8" v-if="userStore.isAdmin">
<el-card class="stat-card"> <el-card class="stat-card" @click="handleDealerCountClick">
<div class="stat-content"> <div class="stat-content">
<div class="stat-icon" style="background-color: #f56c6c"> <div class="stat-icon" style="background-color: #f56c6c">
<el-icon :size="30"><Shop /></el-icon> <el-icon :size="30"><Shop /></el-icon>
@ -57,18 +58,20 @@
<el-card class="welcome-card" style="margin-top: 20px"> <el-card class="welcome-card" style="margin-top: 20px">
<h3>欢迎使用泊云智销通</h3> <h3>欢迎使用泊云智销通</h3>
<p v-if="userStore.isAdmin">您是管理员可以管理所有客户报备和经销商信息</p> <p v-if="userStore.isAdmin">您是管理员可以管理所有报备和经销商信息</p>
<p v-else>您是经销商用户可以管理客户和提交报备申请</p> <p v-else>您是经销商用户可以提交报备申请</p>
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { getStatistics } from '@/api/dashboard' import { getStatistics } from '@/api/dashboard'
const router = useRouter()
const userStore = useUserStore() const userStore = useUserStore()
const stats = ref({ const stats = ref({
@ -78,6 +81,32 @@ const stats = ref({
dealerCount: 0 dealerCount: 0
}) })
//
const handleReportCountClick = () => {
router.push({
path: '/report',
query: {}
})
}
//
const handlePendingClick = () => {
router.push({
path: '/report',
query: {
status: '0'
}
})
}
//
const handleDealerCountClick = () => {
router.push({
path: '/dealer',
query: {}
})
}
// //
const fetchStatistics = async () => { const fetchStatistics = async () => {
try { try {

View File

@ -13,10 +13,10 @@
<el-icon><HomeFilled /></el-icon> <el-icon><HomeFilled /></el-icon>
<span>首页</span> <span>首页</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="/customer"> <!-- <el-menu-item index="/customer">
<el-icon><User /></el-icon> <el-icon><User /></el-icon>
<span>客户管理</span> <span>客户管理</span>
</el-menu-item> </el-menu-item> -->
<el-menu-item index="/report"> <el-menu-item index="/report">
<el-icon><Document /></el-icon> <el-icon><Document /></el-icon>
<span>报备管理</span> <span>报备管理</span>
@ -43,11 +43,11 @@
<el-dropdown> <el-dropdown>
<span class="user-name"> <span class="user-name">
<el-icon><Avatar /></el-icon> <el-icon><Avatar /></el-icon>
{{ userStore.userInfo?.realName }} {{ userStore.userInfo?.dealerName }}
<el-tag v-if="userStore.isAdmin" type="danger" size="small" style="margin-left: 8px"> <el-tag v-if="userStore.isAdmin" type="danger" size="small" style="margin-left: 8px">
管理员 管理员
</el-tag> </el-tag>
<el-tag v-else type="success" size="small" style="margin-left: 8px">经销商</el-tag> <!-- <el-tag v-else type="success" size="small" style="margin-left: 8px">经销商</el-tag> -->
</span> </span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>

View File

@ -18,8 +18,8 @@
<!-- 查询表单 --> <!-- 查询表单 -->
<el-form :inline="true" :model="queryForm" class="query-form"> <el-form :inline="true" :model="queryForm" class="query-form">
<el-form-item label="客户名称"> <el-form-item label="学校名称">
<el-input v-model="queryForm.customerName" placeholder="请输入客户名称" clearable /> <el-input v-model="queryForm.customerName" placeholder="请输入学校名称" clearable />
</el-form-item> </el-form-item>
<el-form-item label="状态" style="width: 180px;"> <el-form-item label="状态" style="width: 180px;">
<el-select v-model="queryForm.status" placeholder="请选择状态" clearable> <el-select v-model="queryForm.status" placeholder="请选择状态" clearable>
@ -27,6 +27,7 @@
<el-option label="已通过" :value="1" /> <el-option label="已通过" :value="1" />
<el-option label="已驳回" :value="2" /> <el-option label="已驳回" :value="2" />
<el-option label="已失效" :value="3" /> <el-option label="已失效" :value="3" />
<el-option label="已作废" :value="4" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -40,16 +41,18 @@
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table :data="tableData" border stripe v-loading="loading"> <el-table :data="tableData" border stripe v-loading="loading">
<el-table-column prop="dealerName" label="经销商" /> <el-table-column prop="dealerName" label="经销商" v-if="userStore.isAdmin"/>
<el-table-column prop="customerName" label="客户名称" /> <el-table-column prop="schoolName" label="学校名称" />
<el-table-column prop="customerPhone" label="联系电话" /> <el-table-column prop="product" label="所属产品" show-overflow-tooltip />
<el-table-column prop="projectType" label="项目类型" show-overflow-tooltip />
<el-table-column prop="description" label="报备说明" show-overflow-tooltip /> <el-table-column prop="description" label="报备说明" show-overflow-tooltip />
<el-table-column prop="status" label="状态"> <el-table-column prop="status" label="状态">
<template #default="{ row }"> <template #default="{ row }">
<el-tag v-if="row.status === 0" type="warning">待审核</el-tag> <el-tag v-if="row.status === 0" type="warning">待审核</el-tag>
<el-tag v-else-if="row.status === 1" type="success">已通过</el-tag> <el-tag v-else-if="row.status === 1" type="success">已通过</el-tag>
<el-tag v-else-if="row.status === 2" type="danger">已驳回</el-tag> <el-tag v-else-if="row.status === 2" type="danger">已驳回</el-tag>
<el-tag v-else type="info">已失效</el-tag> <el-tag v-else-if="row.status === 3" type="info">已失效</el-tag>
<el-tag v-else-if="row.status === 4" type="warning">已作废</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="protectEndDate" label="保护期截止" width="180"> <el-table-column prop="protectEndDate" label="保护期截止" width="180">
@ -58,7 +61,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="createdAt" label="创建时间" width="180" /> <el-table-column prop="createdAt" label="创建时间" width="180" />
<el-table-column label="操作" width="250" fixed="right"> <el-table-column label="操作" width="280" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="primary" @click="handleView(row)">查看</el-button> <el-button link type="primary" @click="handleView(row)">查看</el-button>
<el-button <el-button
@ -77,6 +80,14 @@
> >
审核 审核
</el-button> </el-button>
<el-button
v-if="userStore.isAdmin && row.status === 1"
link
type="warning"
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button <el-button
v-if="!userStore.isAdmin && row.status === 0" v-if="!userStore.isAdmin && row.status === 0"
link link
@ -109,23 +120,36 @@
<!-- 提交报备对话框 --> <!-- 提交报备对话框 -->
<el-dialog v-model="dialogVisible" title="提交报备" width="600px" @close="handleDialogClose"> <el-dialog v-model="dialogVisible" title="提交报备" width="600px" @close="handleDialogClose">
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px"> <el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="客户" prop="customerId"> <el-form-item label="学校" prop="schoolName">
<el-select <el-autocomplete
v-model="formData.customerId" v-model="formData.schoolName"
placeholder="请选择客户" :fetch-suggestions="searchSchool"
filterable placeholder="请输入学校名称"
remote :trigger-on-focus="false"
:remote-method="searchCustomer"
:loading="searchLoading"
style="width: 100%" style="width: 100%"
@select="handleSchoolSelect"
value-key="schoolName"
clearable
> >
<el-option <template #default="{ item }">
v-for="item in customerOptions" <div>{{ item.schoolName }}</div>
:key="item.id" <div style="font-size: 12px; color: #999;">{{ item.location || '无地址信息' }}</div>
:label="item.name" </template>
:value="item.id" </el-autocomplete>
</el-form-item>
<el-form-item label="所属产品" prop="product">
<el-input
v-model="formData.product"
placeholder="请输入所属产品"
clearable
/>
</el-form-item>
<el-form-item label="项目类型" prop="projectType">
<el-input
v-model="formData.projectType"
placeholder="请输入项目类型"
clearable
/> />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="报备说明" prop="description"> <el-form-item label="报备说明" prop="description">
<el-input <el-input
@ -166,6 +190,109 @@
</template> </template>
</el-dialog> </el-dialog>
<!-- 编辑报备对话框 -->
<el-dialog v-model="editDialogVisible" title="编辑报备" width="600px" @close="handleEditDialogClose">
<el-form ref="editFormRef" :model="editFormData" :rules="editRules" label-width="100px">
<el-form-item label="学校名称">
<el-input :model-value="currentReport?.schoolName" disabled />
</el-form-item>
<el-form-item label="所属产品" prop="product">
<el-input
v-model="editFormData.product"
placeholder="请输入所属产品"
clearable
/>
</el-form-item>
<el-form-item label="项目类型" prop="projectType">
<el-input
v-model="editFormData.projectType"
placeholder="请输入项目类型"
clearable
/>
</el-form-item>
<el-form-item label="报备说明" prop="description">
<el-input
v-model="editFormData.description"
type="textarea"
:rows="4"
placeholder="请输入报备说明"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="editFormData.status" placeholder="请选择状态" style="width: 100%">
<el-option label="已通过" :value="1" />
<el-option label="已作废" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="作废原因" prop="cancelReason" v-if="editFormData.status === 4">
<el-input
v-model="editFormData.cancelReason"
type="textarea"
:rows="4"
placeholder="请填写作废原因"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleEditSubmit">确定</el-button>
</template>
</el-dialog>
<!-- 报备详情对话框 -->
<el-dialog v-model="detailDialogVisible" title="报备详情" width="700px">
<el-descriptions :column="2" border>
<el-descriptions-item label="经销商">
{{ currentReport?.dealerName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="学校名称">
{{ currentReport?.schoolName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="所属产品">
{{ currentReport?.product || '-' }}
</el-descriptions-item>
<el-descriptions-item label="项目类型">
{{ currentReport?.projectType || '-' }}
</el-descriptions-item>
<el-descriptions-item label="状态" :span="2">
<el-tag v-if="currentReport?.status === 0" type="warning">待审核</el-tag>
<el-tag v-else-if="currentReport?.status === 1" type="success">已通过</el-tag>
<el-tag v-else-if="currentReport?.status === 2" type="danger">已驳回</el-tag>
<el-tag v-else-if="currentReport?.status === 3" type="info">已失效</el-tag>
<el-tag v-else-if="currentReport?.status === 4" type="warning">已作废</el-tag>
</el-descriptions-item>
<el-descriptions-item label="保护期开始" :span="1">
{{ formatDate(currentReport?.protectStartDate) }}
</el-descriptions-item>
<el-descriptions-item label="保护期结束" :span="1">
{{ formatDate(currentReport?.protectEndDate) }}
</el-descriptions-item>
<el-descriptions-item label="剩余保护天数" v-if="currentReport?.status === 1 && currentReport?.remainDays !== undefined">
<el-tag :type="currentReport.remainDays > 7 ? 'success' : currentReport.remainDays > 0 ? 'warning' : 'danger'">
{{ currentReport.remainDays }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="驳回原因" v-if="currentReport?.status === 2" :span="2">
<span style="color: #f56c6c;">{{ currentReport?.rejectReason || '-' }}</span>
</el-descriptions-item>
<el-descriptions-item label="作废原因" v-if="currentReport?.status === 4" :span="2">
<span style="color: #e6a23c;">{{ currentReport?.cancelReason || '-' }}</span>
</el-descriptions-item>
<el-descriptions-item label="报备说明" :span="2">
<div style="white-space: pre-wrap;">{{ currentReport?.description || '-' }}</div>
</el-descriptions-item>
<el-descriptions-item label="创建时间" :span="2">
{{ currentReport?.createdAt || '-' }}
</el-descriptions-item>
<el-descriptions-item label="更新时间" :span="2">
{{ currentReport?.updatedAt || '-' }}
</el-descriptions-item>
</el-descriptions>
<template #footer>
<el-button type="primary" @click="detailDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
<!-- 进展记录对话框 --> <!-- 进展记录对话框 -->
<el-dialog v-model="progressDialogVisible" title="报备进展记录" width="1000px" @close="handleProgressDialogClose"> <el-dialog v-model="progressDialogVisible" title="报备进展记录" width="1000px" @close="handleProgressDialogClose">
<el-card> <el-card>
@ -215,15 +342,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus' import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs' import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { getReportPage, createReport, auditReport, withdrawReport } from '@/api/report' import { getReportPage, createReport, auditReport, withdrawReport, updateReport } from '@/api/report'
import { getProgressByReportId, createProgress, updateProgress } from '@/api/report-progress' import { getProgressByReportId, createProgress, updateProgress } from '@/api/report-progress'
import { searchCustomerByName } from '@/api/customer' import { searchSchoolByName } from '@/api/school'
import { getConfigValue } from '@/api/system' import { getConfigValue } from '@/api/system'
import type { Report, ReportForm, ReportProgress, ReportProgressForm } from '@/types' import type { Report, ReportForm, ReportProgress, ReportProgressForm, ReportUpdateForm } from '@/types'
const route = useRoute()
const userStore = useUserStore() const userStore = useUserStore()
const protectDays = ref<number>(90) const protectDays = ref<number>(90)
@ -233,10 +362,14 @@ const tableData = ref<Report[]>([])
const total = ref(0) const total = ref(0)
const dialogVisible = ref(false) const dialogVisible = ref(false)
const auditDialogVisible = ref(false) const auditDialogVisible = ref(false)
const detailDialogVisible = ref(false)
const editDialogVisible = ref(false)
const auditReportId = ref<number>() const auditReportId = ref<number>()
const editingReportId = ref<number>()
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const searchLoading = ref(false) const editFormRef = ref<FormInstance>()
const customerOptions = ref<Array<{ id: number; name: string }>>([]) const selectedSchool = ref<any>(null)
const currentReport = ref<Report | null>(null)
// //
const progressDialogVisible = ref(false) const progressDialogVisible = ref(false)
@ -261,7 +394,10 @@ const queryForm = reactive({
}) })
const formData = reactive<ReportForm>({ const formData = reactive<ReportForm>({
customerId: null, schoolId: undefined,
schoolName: '',
product: '',
projectType: '',
description: '' description: ''
}) })
@ -270,8 +406,25 @@ const auditForm = reactive({
rejectReason: '' rejectReason: ''
}) })
const editFormData = reactive<ReportUpdateForm>({
product: '',
projectType: '',
description: '',
status: 1,
cancelReason: ''
})
const rules: FormRules = { const rules: FormRules = {
customerId: [{ required: true, message: '请选择客户', trigger: 'change' }] schoolName: [{ required: true, message: '请输入学校名称', trigger: 'blur' }],
product: [{ required: true, message: '请输入所属产品', trigger: 'blur' }],
projectType: [{ required: true, message: '请输入项目类型', trigger: 'blur' }]
}
const editRules: FormRules = {
product: [{ required: true, message: '请输入所属产品', trigger: 'blur' }],
projectType: [{ required: true, message: '请输入项目类型', trigger: 'blur' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
cancelReason: [{ required: true, message: '请填写作废原因', trigger: 'blur' }]
} }
// //
@ -365,18 +518,29 @@ const fetchData = async () => {
} }
} }
// //
const searchCustomer = async (query: string) => { const searchSchool = async (queryString: string, cb: any) => {
if (!query) return if (!queryString || queryString.trim().length < 1) {
searchLoading.value = true cb([])
try { return
const res = await searchCustomerByName(query)
customerOptions.value = res
} catch (error) {
console.error('搜索客户失败', error)
} finally {
searchLoading.value = false
} }
try {
const schools = await searchSchoolByName(queryString)
const suggestions = schools.map((school: any) => ({
...school,
value: school.schoolName
}))
cb(suggestions)
} catch (error) {
console.error('搜索学校失败', error)
cb([])
}
}
//
const handleSchoolSelect = (item: any) => {
selectedSchool.value = item
formData.schoolId = item.id
} }
// //
@ -400,24 +564,8 @@ const handleAdd = () => {
// //
const handleView = (row: Report) => { const handleView = (row: Report) => {
ElMessageBox.alert( currentReport.value = row
` detailDialogVisible.value = true
<div style="line-height: 2;">
<p><strong>经销商</strong>${row.dealerName}</p>
<p><strong>客户名称</strong>${row.customerName}</p>
<p><strong>联系电话</strong>${row.customerPhone || '-'}</p>
<p><strong>报备说明</strong>${row.description || '-'}</p>
<p><strong>状态</strong>${row.statusDesc}</p>
<p><strong>驳回原因</strong>${row.rejectReason || '-'}</p>
<p><strong>保护期</strong>${formatDate(row.protectStartDate)} ~ ${formatDate(row.protectEndDate)}</p>
</div>
`,
'报备详情',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '关闭'
}
)
} }
// //
@ -428,6 +576,51 @@ const handleAudit = (row: Report) => {
auditDialogVisible.value = true auditDialogVisible.value = true
} }
//
const handleEdit = (row: Report) => {
editingReportId.value = row.id
currentReport.value = row
editFormData.product = row.product || ''
editFormData.projectType = row.projectType || ''
editFormData.description = row.description || ''
editFormData.status = row.status
editFormData.cancelReason = row.cancelReason || ''
editDialogVisible.value = true
}
//
const handleEditDialogClose = () => {
editFormRef.value?.resetFields()
Object.assign(editFormData, {
product: '',
projectType: '',
description: '',
status: 1,
cancelReason: ''
})
editingReportId.value = undefined
}
//
const handleEditSubmit = async () => {
if (!editingReportId.value) return
//
if (editFormData.status === 4 && !editFormData.cancelReason) {
ElMessage.warning('请填写作废原因')
return
}
try {
await updateReport(editingReportId.value, editFormData)
ElMessage.success('编辑成功')
editDialogVisible.value = false
fetchData()
} catch (error) {
console.error('编辑失败', error)
}
}
// //
const handleWithdraw = async (row: Report) => { const handleWithdraw = async (row: Report) => {
try { try {
@ -486,10 +679,13 @@ const handleAuditSubmit = async () => {
const handleDialogClose = () => { const handleDialogClose = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
Object.assign(formData, { Object.assign(formData, {
customerId: null, schoolId: undefined,
schoolName: '',
product: '',
projectType: '',
description: '' description: ''
}) })
customerOptions.value = [] selectedSchool.value = null
} }
// //
@ -504,6 +700,13 @@ const fetchProtectDays = async () => {
onMounted(() => { onMounted(() => {
fetchProtectDays() fetchProtectDays()
//
const { status } = route.query
if (status !== undefined) {
queryForm.status = status === '' ? undefined : parseInt(status as string)
}
fetchData() fetchData()
}) })
</script> </script>
@ -522,4 +725,18 @@ onMounted(() => {
.query-form { .query-form {
margin-bottom: 20px; margin-bottom: 20px;
} }
/* 详情对话框样式优化 */
:deep(.el-descriptions) {
margin-top: 10px;
}
:deep(.el-descriptions__label) {
font-weight: 600;
background-color: #fafafa;
}
:deep(.el-descriptions__content) {
color: #333;
}
</style> </style>