feat(approval): 新增业务审批流程及配置管理
- 新增审批配置主子表(biz_approval_config / biz_approval_config_user),支持或签 - 5 个业务模块接入审批: 采购订单/客户报价/供应商报价/发货单/订单异议 - 统一审批动作接口(提交/通过/驳回),status=10 表示审批中 - 新增"待我审批"聚合页面,按业务类型筛选 - 修复 logback 写本地路径报错,去除文件 appender - 修复 Redis SSL 配置在 Spring Boot 4 下需对象格式 - 补齐部分业务表缺失的 update_by/update_time 审计列 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package com.ruoyi.web.controller.bid;
|
||||
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.system.mapper.bid.BizApprovalPendingMapper;
|
||||
import com.ruoyi.system.service.bid.IBizApprovalActionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bid/approval/action")
|
||||
public class BizApprovalActionController extends BaseController {
|
||||
|
||||
@Autowired private IBizApprovalActionService service;
|
||||
@Autowired private BizApprovalPendingMapper pendingMapper;
|
||||
|
||||
@GetMapping("/pending")
|
||||
public AjaxResult pending() {
|
||||
return success(pendingMapper.selectPendingForUser(SecurityUtils.getUserId()));
|
||||
}
|
||||
|
||||
@Log(title = "提交审批", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/submit/{bizType}/{id}")
|
||||
public AjaxResult submit(@PathVariable String bizType, @PathVariable Long id) {
|
||||
return toAjax(service.submit(bizType, id, getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "审批通过", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/approve/{bizType}/{id}")
|
||||
public AjaxResult approve(@PathVariable String bizType, @PathVariable Long id) {
|
||||
return toAjax(service.approve(bizType, id, SecurityUtils.getUserId(), getUsername()));
|
||||
}
|
||||
|
||||
@Log(title = "审批驳回", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/reject/{bizType}/{id}")
|
||||
public AjaxResult reject(@PathVariable String bizType, @PathVariable Long id,
|
||||
@RequestBody(required = false) Map<String, String> body) {
|
||||
String reason = body != null ? body.get("reason") : null;
|
||||
return toAjax(service.reject(bizType, id, SecurityUtils.getUserId(), getUsername(), reason));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.ruoyi.web.controller.bid;
|
||||
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.system.domain.bid.BizApprovalConfig;
|
||||
import com.ruoyi.system.service.bid.IBizApprovalConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/bid/approval")
|
||||
public class BizApprovalConfigController extends BaseController {
|
||||
@Autowired private IBizApprovalConfigService service;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:approval:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(BizApprovalConfig q) {
|
||||
startPage();
|
||||
return getDataTable(service.selectList(q));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:approval:query')")
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable Long id) {
|
||||
return success(service.selectById(id));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:approval:add')")
|
||||
@Log(title = "审批配置", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody BizApprovalConfig c) {
|
||||
c.setCreateBy(getUsername());
|
||||
return toAjax(service.insert(c));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:approval:edit')")
|
||||
@Log(title = "审批配置", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody BizApprovalConfig c) {
|
||||
c.setUpdateBy(getUsername());
|
||||
return toAjax(service.update(c));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('bid:approval:remove')")
|
||||
@Log(title = "审批配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ids}")
|
||||
public AjaxResult remove(@PathVariable Long[] ids) {
|
||||
return toAjax(service.deleteByIds(ids));
|
||||
}
|
||||
}
|
||||
@@ -72,15 +72,18 @@ spring:
|
||||
# redis 配置
|
||||
redis:
|
||||
# 地址
|
||||
host: localhost
|
||||
host: 49.232.154.205
|
||||
# 端口,默认为6379
|
||||
port: 6379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
# 密码
|
||||
password:
|
||||
password: WANGyu11!
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
timeout: 30s
|
||||
# 是否开启ssl
|
||||
ssl:
|
||||
enabled: false
|
||||
lettuce:
|
||||
pool:
|
||||
# 连接池中的最小空闲连接
|
||||
@@ -92,6 +95,28 @@ spring:
|
||||
# #连接池最大阻塞等待时间(使用负值表示没有限制)
|
||||
max-wait: -1ms
|
||||
|
||||
redisson:
|
||||
# redis key前缀
|
||||
keyPrefix:
|
||||
# 线程池数量
|
||||
threads: 16
|
||||
# Netty线程池数量
|
||||
nettyThreads: 32
|
||||
# 单节点配置
|
||||
singleServerConfig:
|
||||
# 客户端名称
|
||||
clientName: ${ruoyi.name}
|
||||
# 最小空闲连接数
|
||||
connectionMinimumIdleSize: 32
|
||||
# 连接池大小
|
||||
connectionPoolSize: 64
|
||||
# 连接空闲超时,单位:毫秒
|
||||
idleConnectionTimeout: 10000
|
||||
# 命令等待超时,单位:毫秒
|
||||
timeout: 3000
|
||||
# 发布和订阅连接池大小
|
||||
subscriptionConnectionPoolSize: 50
|
||||
|
||||
# token配置
|
||||
token:
|
||||
# 令牌自定义标识
|
||||
|
||||
@@ -1,93 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="/home/ruoyi/logs" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/sys-info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/sys-error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 用户访问日志输出 -->
|
||||
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/sys-user.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 按天回滚 daily -->
|
||||
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.ruoyi" level="info" />
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
|
||||
<!--系统用户操作日志-->
|
||||
<logger name="sys-user" level="info">
|
||||
<appender-ref ref="sys-user"/>
|
||||
</logger>
|
||||
</configuration>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="com.ruoyi" level="info" />
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<logger name="sys-user" level="info">
|
||||
<appender-ref ref="console" />
|
||||
</logger>
|
||||
</configuration>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ruoyi.system.domain.bid;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import java.util.List;
|
||||
|
||||
public class BizApprovalConfig extends BaseEntity {
|
||||
private Long id;
|
||||
private String bizType;
|
||||
private String bizName;
|
||||
private String signType;
|
||||
private String enabled;
|
||||
private List<Long> userIds;
|
||||
private List<String> userNames;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getBizType() { return bizType; }
|
||||
public void setBizType(String bizType) { this.bizType = bizType; }
|
||||
public String getBizName() { return bizName; }
|
||||
public void setBizName(String bizName) { this.bizName = bizName; }
|
||||
public String getSignType() { return signType; }
|
||||
public void setSignType(String signType) { this.signType = signType; }
|
||||
public String getEnabled() { return enabled; }
|
||||
public void setEnabled(String enabled) { this.enabled = enabled; }
|
||||
public List<Long> getUserIds() { return userIds; }
|
||||
public void setUserIds(List<Long> userIds) { this.userIds = userIds; }
|
||||
public List<String> getUserNames() { return userNames; }
|
||||
public void setUserNames(List<String> userNames) { this.userNames = userNames; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ruoyi.system.mapper.bid;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface BizApprovalActionMapper {
|
||||
/** 仅当当前状态在 fromStatuses 中时更新业务单据状态 */
|
||||
int updateStatus(@Param("table") String table,
|
||||
@Param("pk") String pk,
|
||||
@Param("statusCol") String statusCol,
|
||||
@Param("id") Long id,
|
||||
@Param("newStatus") String newStatus,
|
||||
@Param("fromStatuses") java.util.List<String> fromStatuses,
|
||||
@Param("updateBy") String updateBy);
|
||||
|
||||
String selectStatus(@Param("table") String table,
|
||||
@Param("pk") String pk,
|
||||
@Param("statusCol") String statusCol,
|
||||
@Param("id") Long id);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ruoyi.system.mapper.bid;
|
||||
|
||||
import com.ruoyi.system.domain.bid.BizApprovalConfig;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
|
||||
public interface BizApprovalConfigMapper {
|
||||
List<BizApprovalConfig> selectList(BizApprovalConfig query);
|
||||
BizApprovalConfig selectById(Long id);
|
||||
BizApprovalConfig selectByBizType(String bizType);
|
||||
int insert(BizApprovalConfig record);
|
||||
int update(BizApprovalConfig record);
|
||||
int deleteById(Long id);
|
||||
|
||||
List<Long> selectUserIds(@Param("configId") Long configId);
|
||||
List<String> selectUserNames(@Param("configId") Long configId);
|
||||
int deleteUsers(@Param("configId") Long configId);
|
||||
int insertUser(@Param("configId") Long configId, @Param("userId") Long userId, @Param("sortNo") Integer sortNo);
|
||||
|
||||
int existsUser(@Param("bizType") String bizType, @Param("userId") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.ruoyi.system.mapper.bid;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface BizApprovalPendingMapper {
|
||||
/** 查询某用户作为审批人的、所有业务表中 status='10' 的单据 */
|
||||
List<Map<String, Object>> selectPendingForUser(@Param("userId") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ruoyi.system.service.bid;
|
||||
|
||||
public interface IBizApprovalActionService {
|
||||
/** 提交审批: 业务单据 status -> 10 */
|
||||
int submit(String bizType, Long id, String username);
|
||||
|
||||
/** 通过审批(或签): 任一审批人通过即生效, 业务单据 status -> 通过状态 */
|
||||
int approve(String bizType, Long id, Long userId, String username);
|
||||
|
||||
/** 驳回审批: 业务单据 status -> rejected, 备注记录原因 */
|
||||
int reject(String bizType, Long id, Long userId, String username, String reason);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ruoyi.system.service.bid;
|
||||
|
||||
import com.ruoyi.system.domain.bid.BizApprovalConfig;
|
||||
import java.util.List;
|
||||
|
||||
public interface IBizApprovalConfigService {
|
||||
List<BizApprovalConfig> selectList(BizApprovalConfig query);
|
||||
BizApprovalConfig selectById(Long id);
|
||||
BizApprovalConfig selectByBizType(String bizType);
|
||||
int insert(BizApprovalConfig record);
|
||||
int update(BizApprovalConfig record);
|
||||
int deleteByIds(Long[] ids);
|
||||
|
||||
/** 校验当前用户是否为该业务的审批人(或签) */
|
||||
boolean canApprove(String bizType, Long userId);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.ruoyi.system.service.bid.impl;
|
||||
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.system.mapper.bid.BizApprovalActionMapper;
|
||||
import com.ruoyi.system.service.bid.IBizApprovalActionService;
|
||||
import com.ruoyi.system.service.bid.IBizApprovalConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class BizApprovalActionServiceImpl implements IBizApprovalActionService {
|
||||
|
||||
@Autowired private BizApprovalActionMapper mapper;
|
||||
@Autowired private IBizApprovalConfigService configService;
|
||||
|
||||
/** 业务类型 -> 表/主键/状态列/审批通过的目标状态/可提交的初始状态 */
|
||||
static class Meta {
|
||||
final String table, pk, statusCol, approvedStatus;
|
||||
final List<String> draftStatuses;
|
||||
Meta(String t, String pk, String sc, String approved, List<String> drafts) {
|
||||
this.table = t; this.pk = pk; this.statusCol = sc;
|
||||
this.approvedStatus = approved; this.draftStatuses = drafts;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<String, Meta> META = new HashMap<>();
|
||||
static {
|
||||
META.put("PURCHASE_ORDER", new Meta("biz_purchase_order", "po_id", "status", "confirmed", Arrays.asList("draft")));
|
||||
META.put("CLIENT_QUOTE", new Meta("biz_client_quote", "quote_id", "status", "confirmed", Arrays.asList("draft")));
|
||||
META.put("QUOTATION", new Meta("biz_quotation", "quotation_id", "status", "accepted", Arrays.asList("draft", "submitted")));
|
||||
META.put("DELIVERY_ORDER", new Meta("biz_delivery_order", "do_id", "delivery_status", "confirmed", Arrays.asList("pending")));
|
||||
META.put("ORDER_OBJECTION", new Meta("biz_order_objection", "objection_id", "status", "resolved", Arrays.asList("pending")));
|
||||
}
|
||||
|
||||
private Meta meta(String bizType) {
|
||||
Meta m = META.get(bizType);
|
||||
if (m == null) throw new ServiceException("不支持的业务类型: " + bizType);
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int submit(String bizType, Long id, String username) {
|
||||
Meta m = meta(bizType);
|
||||
int rows = mapper.updateStatus(m.table, m.pk, m.statusCol, id, "10", m.draftStatuses, username);
|
||||
if (rows == 0) throw new ServiceException("当前状态不允许提交审批");
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int approve(String bizType, Long id, Long userId, String username) {
|
||||
Meta m = meta(bizType);
|
||||
if (!configService.canApprove(bizType, userId)) {
|
||||
throw new ServiceException("无权审批: 当前用户不是该业务的审批人");
|
||||
}
|
||||
int rows = mapper.updateStatus(m.table, m.pk, m.statusCol, id,
|
||||
m.approvedStatus, Collections.singletonList("10"), username);
|
||||
if (rows == 0) throw new ServiceException("单据非审批中, 无法通过");
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int reject(String bizType, Long id, Long userId, String username, String reason) {
|
||||
Meta m = meta(bizType);
|
||||
if (!configService.canApprove(bizType, userId)) {
|
||||
throw new ServiceException("无权审批: 当前用户不是该业务的审批人");
|
||||
}
|
||||
int rows = mapper.updateStatus(m.table, m.pk, m.statusCol, id,
|
||||
"rejected", Collections.singletonList("10"), username);
|
||||
if (rows == 0) throw new ServiceException("单据非审批中, 无法驳回");
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.ruoyi.system.service.bid.impl;
|
||||
|
||||
import com.ruoyi.system.domain.bid.BizApprovalConfig;
|
||||
import com.ruoyi.system.mapper.bid.BizApprovalConfigMapper;
|
||||
import com.ruoyi.system.service.bid.IBizApprovalConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class BizApprovalConfigServiceImpl implements IBizApprovalConfigService {
|
||||
@Autowired private BizApprovalConfigMapper mapper;
|
||||
|
||||
@Override
|
||||
public List<BizApprovalConfig> selectList(BizApprovalConfig query) {
|
||||
List<BizApprovalConfig> list = mapper.selectList(query);
|
||||
for (BizApprovalConfig c : list) {
|
||||
c.setUserIds(mapper.selectUserIds(c.getId()));
|
||||
c.setUserNames(mapper.selectUserNames(c.getId()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BizApprovalConfig selectById(Long id) {
|
||||
BizApprovalConfig c = mapper.selectById(id);
|
||||
if (c != null) {
|
||||
c.setUserIds(mapper.selectUserIds(id));
|
||||
c.setUserNames(mapper.selectUserNames(id));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BizApprovalConfig selectByBizType(String bizType) {
|
||||
BizApprovalConfig c = mapper.selectByBizType(bizType);
|
||||
if (c != null) {
|
||||
c.setUserIds(mapper.selectUserIds(c.getId()));
|
||||
c.setUserNames(mapper.selectUserNames(c.getId()));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public int insert(BizApprovalConfig r) {
|
||||
int rows = mapper.insert(r);
|
||||
saveUsers(r);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public int update(BizApprovalConfig r) {
|
||||
int rows = mapper.update(r);
|
||||
mapper.deleteUsers(r.getId());
|
||||
saveUsers(r);
|
||||
return rows;
|
||||
}
|
||||
|
||||
private void saveUsers(BizApprovalConfig r) {
|
||||
if (r.getUserIds() == null) return;
|
||||
int sort = 0;
|
||||
for (Long uid : r.getUserIds()) {
|
||||
if (uid != null) mapper.insertUser(r.getId(), uid, sort++);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public int deleteByIds(Long[] ids) {
|
||||
int n = 0;
|
||||
for (Long id : ids) {
|
||||
mapper.deleteUsers(id);
|
||||
n += mapper.deleteById(id);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canApprove(String bizType, Long userId) {
|
||||
if (bizType == null || userId == null) return false;
|
||||
return mapper.existsUser(bizType, userId) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.bid.BizApprovalActionMapper">
|
||||
|
||||
<update id="updateStatus">
|
||||
UPDATE ${table}
|
||||
SET ${statusCol} = #{newStatus},
|
||||
update_by = #{updateBy},
|
||||
update_time = NOW()
|
||||
WHERE ${pk} = #{id}
|
||||
<if test="fromStatuses != null and !fromStatuses.isEmpty()">
|
||||
AND ${statusCol} IN
|
||||
<foreach collection="fromStatuses" item="s" open="(" separator="," close=")">#{s}</foreach>
|
||||
</if>
|
||||
</update>
|
||||
|
||||
<select id="selectStatus" resultType="java.lang.String">
|
||||
SELECT ${statusCol} FROM ${table} WHERE ${pk}=#{id}
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.bid.BizApprovalConfigMapper">
|
||||
|
||||
<resultMap id="BaseRM" type="com.ruoyi.system.domain.bid.BizApprovalConfig">
|
||||
<id property="id" column="id"/>
|
||||
<result property="bizType" column="biz_type"/>
|
||||
<result property="bizName" column="biz_name"/>
|
||||
<result property="signType" column="sign_type"/>
|
||||
<result property="enabled" column="enabled"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<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>
|
||||
|
||||
<select id="selectList" resultMap="BaseRM">
|
||||
SELECT * FROM biz_approval_config
|
||||
<where>
|
||||
<if test="bizType != null and bizType != ''"> AND biz_type LIKE CONCAT('%',#{bizType},'%')</if>
|
||||
<if test="bizName != null and bizName != ''"> AND biz_name LIKE CONCAT('%',#{bizName},'%')</if>
|
||||
<if test="enabled != null and enabled != ''"> AND enabled=#{enabled}</if>
|
||||
</where>
|
||||
ORDER BY id ASC
|
||||
</select>
|
||||
|
||||
<select id="selectById" resultMap="BaseRM">
|
||||
SELECT * FROM biz_approval_config WHERE id=#{id}
|
||||
</select>
|
||||
|
||||
<select id="selectByBizType" resultMap="BaseRM">
|
||||
SELECT * FROM biz_approval_config WHERE biz_type=#{bizType}
|
||||
</select>
|
||||
|
||||
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
|
||||
INSERT INTO biz_approval_config(biz_type,biz_name,sign_type,enabled,remark,create_by,create_time)
|
||||
VALUES(#{bizType},#{bizName},#{signType},#{enabled},#{remark},#{createBy},NOW())
|
||||
</insert>
|
||||
|
||||
<update id="update">
|
||||
UPDATE biz_approval_config
|
||||
<set>
|
||||
<if test="bizName != null">biz_name=#{bizName},</if>
|
||||
<if test="signType != null">sign_type=#{signType},</if>
|
||||
<if test="enabled != null">enabled=#{enabled},</if>
|
||||
<if test="remark != null">remark=#{remark},</if>
|
||||
update_by=#{updateBy}, update_time=NOW()
|
||||
</set>
|
||||
WHERE id=#{id}
|
||||
</update>
|
||||
|
||||
<delete id="deleteById">DELETE FROM biz_approval_config WHERE id=#{id}</delete>
|
||||
|
||||
<select id="selectUserIds" resultType="java.lang.Long">
|
||||
SELECT user_id FROM biz_approval_config_user WHERE config_id=#{configId} ORDER BY sort_no, id
|
||||
</select>
|
||||
|
||||
<select id="selectUserNames" resultType="java.lang.String">
|
||||
SELECT u.nick_name FROM biz_approval_config_user cu
|
||||
JOIN sys_user u ON u.user_id=cu.user_id
|
||||
WHERE cu.config_id=#{configId} ORDER BY cu.sort_no, cu.id
|
||||
</select>
|
||||
|
||||
<delete id="deleteUsers">DELETE FROM biz_approval_config_user WHERE config_id=#{configId}</delete>
|
||||
|
||||
<insert id="insertUser">
|
||||
INSERT INTO biz_approval_config_user(config_id,user_id,sort_no)
|
||||
VALUES(#{configId},#{userId},#{sortNo})
|
||||
</insert>
|
||||
|
||||
<select id="existsUser" resultType="int">
|
||||
SELECT COUNT(1) FROM biz_approval_config c
|
||||
JOIN biz_approval_config_user cu ON cu.config_id=c.id
|
||||
WHERE c.biz_type=#{bizType} AND c.enabled='1' AND cu.user_id=#{userId}
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.bid.BizApprovalPendingMapper">
|
||||
|
||||
<select id="selectPendingForUser" resultType="java.util.LinkedHashMap">
|
||||
SELECT * FROM (
|
||||
SELECT 'PURCHASE_ORDER' AS bizType, '采购订单' AS bizName,
|
||||
po_id AS id, po_no AS bizNo,
|
||||
CAST(total_amount AS CHAR) AS amount,
|
||||
create_by AS createBy, create_time AS createTime
|
||||
FROM biz_purchase_order
|
||||
WHERE status = '10'
|
||||
AND EXISTS (SELECT 1 FROM biz_approval_config c
|
||||
JOIN biz_approval_config_user cu ON cu.config_id = c.id
|
||||
WHERE c.biz_type = 'PURCHASE_ORDER' AND c.enabled = '1' AND cu.user_id = #{userId})
|
||||
|
||||
UNION ALL
|
||||
SELECT 'CLIENT_QUOTE', '客户报价',
|
||||
quote_id, quote_no, CAST(total_amount AS CHAR),
|
||||
create_by, create_time
|
||||
FROM biz_client_quote
|
||||
WHERE status = '10'
|
||||
AND EXISTS (SELECT 1 FROM biz_approval_config c
|
||||
JOIN biz_approval_config_user cu ON cu.config_id = c.id
|
||||
WHERE c.biz_type = 'CLIENT_QUOTE' AND c.enabled = '1' AND cu.user_id = #{userId})
|
||||
|
||||
UNION ALL
|
||||
SELECT 'QUOTATION', '供应商报价',
|
||||
quotation_id, quote_no, CAST(total_amount AS CHAR),
|
||||
create_by, create_time
|
||||
FROM biz_quotation
|
||||
WHERE status = '10'
|
||||
AND EXISTS (SELECT 1 FROM biz_approval_config c
|
||||
JOIN biz_approval_config_user cu ON cu.config_id = c.id
|
||||
WHERE c.biz_type = 'QUOTATION' AND c.enabled = '1' AND cu.user_id = #{userId})
|
||||
|
||||
UNION ALL
|
||||
SELECT 'DELIVERY_ORDER', '发货单',
|
||||
do_id, do_no, CAST(total_amount AS CHAR),
|
||||
create_by, create_time
|
||||
FROM biz_delivery_order
|
||||
WHERE delivery_status = '10'
|
||||
AND EXISTS (SELECT 1 FROM biz_approval_config c
|
||||
JOIN biz_approval_config_user cu ON cu.config_id = c.id
|
||||
WHERE c.biz_type = 'DELIVERY_ORDER' AND c.enabled = '1' AND cu.user_id = #{userId})
|
||||
|
||||
UNION ALL
|
||||
SELECT 'ORDER_OBJECTION', '订单异议',
|
||||
objection_id, CAST(objection_id AS CHAR), NULL,
|
||||
create_by, create_time
|
||||
FROM biz_order_objection
|
||||
WHERE status = '10'
|
||||
AND EXISTS (SELECT 1 FROM biz_approval_config c
|
||||
JOIN biz_approval_config_user cu ON cu.config_id = c.id
|
||||
WHERE c.biz_type = 'ORDER_OBJECTION' AND c.enabled = '1' AND cu.user_id = #{userId})
|
||||
) t
|
||||
ORDER BY t.createTime DESC
|
||||
</select>
|
||||
</mapper>
|
||||
12
ruoyi-ui/public/favicon.svg
Normal file
12
ruoyi-ui/public/favicon.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#e4393c"/>
|
||||
<stop offset="1" stop-color="#c81623"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="1" y="1" width="30" height="30" rx="7" fill="url(#g)"/>
|
||||
<path d="M10 8 h14 v3.5 h-10 v4 h8.5 v3.5 h-8.5 v5.5 h-4 z" fill="#fff"/>
|
||||
<circle cx="23" cy="22" r="3" fill="#fff"/>
|
||||
<circle cx="23" cy="22" r="1.3" fill="#e4393c"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 520 B |
@@ -5,7 +5,8 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<link rel="icon" type="image/svg+xml" href="<%= BASE_URL %>favicon.svg">
|
||||
<link rel="alternate icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||
<style>
|
||||
|
||||
7
ruoyi-ui/src/api/bid/approval.js
Normal file
7
ruoyi-ui/src/api/bid/approval.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import request from '@/utils/request'
|
||||
const baseUrl = '/bid/approval'
|
||||
export const listApproval = (params) => request({ url: baseUrl + '/list', method: 'get', params })
|
||||
export const getApproval = (id) => request({ url: baseUrl + '/' + id, method: 'get' })
|
||||
export const addApproval = (data) => request({ url: baseUrl, method: 'post', data })
|
||||
export const updateApproval = (data) => request({ url: baseUrl, method: 'put', data })
|
||||
export const delApproval = (ids) => request({ url: baseUrl + '/' + ids, method: 'delete' })
|
||||
6
ruoyi-ui/src/api/bid/approvalAction.js
Normal file
6
ruoyi-ui/src/api/bid/approvalAction.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
const base = '/bid/approval/action'
|
||||
export const listPending = () => request({ url: `${base}/pending`, method: 'get' })
|
||||
export const submitApproval = (bizType, id) => request({ url: `${base}/submit/${bizType}/${id}`, method: 'post' })
|
||||
export const approveBiz = (bizType, id) => request({ url: `${base}/approve/${bizType}/${id}`, method: 'post' })
|
||||
export const rejectBiz = (bizType, id, reason) => request({ url: `${base}/reject/${bizType}/${id}`, method: 'post', data: { reason } })
|
||||
12
ruoyi-ui/src/assets/logo/logo.svg
Normal file
12
ruoyi-ui/src/assets/logo/logo.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<defs>
|
||||
<linearGradient id="lg-red" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#e4393c"/>
|
||||
<stop offset="1" stop-color="#c81623"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="10" y="10" width="180" height="180" rx="36" fill="url(#lg-red)"/>
|
||||
<path d="M62 56 h78 v18 h-56 v22 h48 v18 h-48 v32 h-22 z" fill="#ffffff"/>
|
||||
<circle cx="146" cy="138" r="14" fill="#ffffff"/>
|
||||
<circle cx="146" cy="138" r="6" fill="#e4393c"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 546 B |
@@ -21,7 +21,7 @@ $--border-color-light: #f0f0f0;
|
||||
$--border-color-lighter: #f0f0f0;
|
||||
|
||||
$--table-border: 1px solid #e5e5e5;
|
||||
$--table-header-background: #f5f5f5;
|
||||
$--table-header-background: #ffffff;
|
||||
$--table-header-font-color: #666666;
|
||||
$--table-row-hover-background: #fff5f5;
|
||||
|
||||
@@ -30,7 +30,7 @@ $--button-default-border-color: #e5e5e5;
|
||||
$--button-default-font-color: #666666;
|
||||
|
||||
/* 背景 */
|
||||
$--background-color-base: #f5f5f5;
|
||||
$--background-color-base: #ffffff;
|
||||
|
||||
/* icon font path, required */
|
||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
// ═══ 京东品牌色 ═══
|
||||
$jd-red: #e4393c;
|
||||
$jd-red-dark: #c81623;
|
||||
$jd-red-light: #fff5f5;
|
||||
$jd-red-bg: #fff0f0;
|
||||
$jd-red-light: #fafafa;
|
||||
$jd-red-bg: #ffffff;
|
||||
|
||||
// ═══ 灰色系 ═══
|
||||
$bg-gray: #f5f5f5;
|
||||
$bg-gray: #ffffff;
|
||||
$bg-dark: #333;
|
||||
$border-gray: #e5e5e5;
|
||||
$border-light: #f0f0f0;
|
||||
@@ -21,7 +21,7 @@ $text-medium: #666666;
|
||||
$text-light: #999999;
|
||||
|
||||
// ═══ 功能色 ═══
|
||||
$link-blue: #005ea7;
|
||||
$link-blue: #e4393c;
|
||||
$color-success: #67c23a;
|
||||
$color-warning: #e6a23c;
|
||||
$color-danger: #f56c6c;
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
color: #333;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.detail-card-title i { margin-right: 5px; color: #409EFF; }
|
||||
.detail-card-title i { margin-right: 5px; color: #e4393c; }
|
||||
|
||||
.detail-row { padding: 0 8px; }
|
||||
|
||||
@@ -357,7 +357,7 @@
|
||||
}
|
||||
.code-action .el-button:hover {
|
||||
background: #ffffff;
|
||||
border-color: #409EFF;
|
||||
border-color: #e4393c;
|
||||
}
|
||||
.code-pre {
|
||||
margin: 0;
|
||||
@@ -507,8 +507,8 @@
|
||||
|
||||
/* 搜索按钮特殊色(浅蓝) */
|
||||
.search-btn {
|
||||
background: #409EFF;
|
||||
border-color: #409EFF;
|
||||
background: #e4393c;
|
||||
border-color: #e4393c;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* ═══════════════════════════════════════════════════════════════
|
||||
京东风格 — 全局主题(CSS 变量 + Element UI 覆盖)
|
||||
主色:#e4393c 深红:#c81623 背景灰:#f5f5f5
|
||||
主色:#e4393c 深红:#c81623 背景灰:#ffffff
|
||||
═══════════════════════════════════════════════════════════════ */
|
||||
|
||||
:root {
|
||||
@@ -8,11 +8,11 @@
|
||||
--jd-red: #e4393c;
|
||||
--jd-red-dark: #c81623;
|
||||
--jd-red-hover: #e55a5d;
|
||||
--jd-red-light: #fff5f5;
|
||||
--jd-red-bg: #fff0f0;
|
||||
--jd-red-light: #fafafa;
|
||||
--jd-red-bg: #ffffff;
|
||||
|
||||
/* ═══ 灰色系 ═══ */
|
||||
--bg-gray: #f5f5f5;
|
||||
--bg-gray: #ffffff;
|
||||
--bg-white: #ffffff;
|
||||
--border-gray: #e5e5e5;
|
||||
--border-light: #f0f0f0;
|
||||
@@ -23,7 +23,7 @@
|
||||
--text-light: #999999;
|
||||
|
||||
/* ═══ 功能色 ═══ */
|
||||
--link-blue: #005ea7;
|
||||
--link-blue: #e4393c;
|
||||
--color-success: #67c23a;
|
||||
--color-warning: #e6a23c;
|
||||
--color-danger: #f56c6c;
|
||||
@@ -61,7 +61,7 @@
|
||||
--space-xl: 20px;
|
||||
|
||||
/* ═══ 表格 ═══ */
|
||||
--table-header-bg: #f5f5f5;
|
||||
--table-header-bg: #ffffff;
|
||||
--table-header-text: #666666;
|
||||
--table-row-hover: #f5f7fa;
|
||||
--table-stripe: #fafafa;
|
||||
@@ -69,12 +69,12 @@
|
||||
|
||||
/* ═══ 导航 ═══ */
|
||||
--nav-height: 50px;
|
||||
--nav-bg: #f5f5f5;
|
||||
--nav-bg: #ffffff;
|
||||
--nav-text: #333333;
|
||||
--nav-hover: rgba(0, 0, 0, 0.04);
|
||||
|
||||
/* ═══ 侧栏 ═══ */
|
||||
--sidebar-width: 200px;
|
||||
--sidebar-width: 192px;
|
||||
--sidebar-bg: #ffffff;
|
||||
--sidebar-text: #666666;
|
||||
--sidebar-text-active: #e4393c;
|
||||
@@ -107,11 +107,21 @@ body {
|
||||
顶部导航栏
|
||||
═══════════════════════════════════════════ */
|
||||
.navbar {
|
||||
background: #f5f5f5 !important;
|
||||
border-bottom: 1px solid #e5e5e5 !important;
|
||||
box-shadow: none !important;
|
||||
background: #ffffff !important;
|
||||
border-bottom: 1px solid #ececec !important;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04) !important;
|
||||
height: var(--nav-height) !important;
|
||||
color: #333 !important;
|
||||
position: relative;
|
||||
|
||||
/* 顶部红色品牌细条 */
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #e4393c 0%, #c81623 60%, #e4393c 100%);
|
||||
}
|
||||
|
||||
.hamburger-container svg {
|
||||
color: #666 !important;
|
||||
@@ -119,7 +129,8 @@ body {
|
||||
}
|
||||
|
||||
.hamburger-container:hover {
|
||||
background: rgba(0, 0, 0, 0.04) !important;
|
||||
background: rgba(228, 57, 60, 0.06) !important;
|
||||
svg { fill: #e4393c !important; color: #e4393c !important; }
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
@@ -127,12 +138,15 @@ body {
|
||||
.el-breadcrumb__inner a,
|
||||
.el-breadcrumb__separator {
|
||||
color: #999 !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
.el-breadcrumb__inner.is-link {
|
||||
font-weight: 400 !important;
|
||||
&:hover {
|
||||
color: #e4393c !important;
|
||||
}
|
||||
&:hover { color: #e4393c !important; }
|
||||
}
|
||||
.el-breadcrumb__item:last-child .el-breadcrumb__inner {
|
||||
color: #333 !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,36 +156,25 @@ body {
|
||||
font-size: 16px !important;
|
||||
|
||||
&.hover-effect:hover {
|
||||
color: #333 !important;
|
||||
background: #f0f0f0 !important;
|
||||
color: #e4393c !important;
|
||||
background: rgba(228, 57, 60, 0.06) !important;
|
||||
}
|
||||
|
||||
svg,
|
||||
i {
|
||||
color: #666 !important;
|
||||
}
|
||||
&:hover svg,
|
||||
&:hover i {
|
||||
color: #333 !important;
|
||||
}
|
||||
svg, i { color: #666 !important; }
|
||||
&:hover svg, &:hover i { color: #e4393c !important; }
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
.user-nickname {
|
||||
color: #333 !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
.avatar-wrapper {
|
||||
margin-top: 6px !important;
|
||||
}
|
||||
.user-nickname { color: #333 !important; font-weight: 500 !important; }
|
||||
.avatar-wrapper { margin-top: 6px !important; }
|
||||
}
|
||||
|
||||
.el-dropdown {
|
||||
color: #333 !important;
|
||||
}
|
||||
.el-dropdown { color: #333 !important; }
|
||||
|
||||
.el-badge__content {
|
||||
border-color: #fff !important;
|
||||
background: #e4393c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +186,7 @@ body {
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
color: #666 !important;
|
||||
color: #555 !important;
|
||||
background: transparent !important;
|
||||
border-bottom: none !important;
|
||||
height: var(--nav-height) !important;
|
||||
@@ -191,12 +194,13 @@ body {
|
||||
|
||||
&:hover {
|
||||
color: #e4393c !important;
|
||||
background: rgba(0, 0, 0, 0.03) !important;
|
||||
background: rgba(228, 57, 60, 0.04) !important;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: #e4393c !important;
|
||||
background: transparent !important;
|
||||
border-bottom: 2px solid #e4393c !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,6 +273,21 @@ body {
|
||||
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: var(--sidebar-text-active) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* 强制清除 element-ui 默认 hover 黄色背景 */
|
||||
.el-submenu__title:hover,
|
||||
.el-submenu__title:focus,
|
||||
.el-menu-item:focus {
|
||||
background: var(--sidebar-hover-bg) !important;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
/* 子菜单展开时 inline-style 的浅色背景一并覆盖 */
|
||||
.el-menu .el-menu-item,
|
||||
.el-menu .el-submenu__title {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* 子菜单 */
|
||||
@@ -277,12 +296,23 @@ body {
|
||||
}
|
||||
|
||||
.el-submenu .el-menu-item {
|
||||
padding-left: 42px !important;
|
||||
padding-left: 32px !important;
|
||||
height: 34px !important;
|
||||
line-height: 34px !important;
|
||||
font-size: 12px !important;
|
||||
color: var(--text-medium) !important;
|
||||
|
||||
/* 二级菜单图标隐藏 */
|
||||
.svg-icon,
|
||||
> i,
|
||||
> .el-icon-,
|
||||
[class^="el-icon-"],
|
||||
[class*=" el-icon-"] {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--text-medium) !important;
|
||||
background: var(--sidebar-hover-bg) !important;
|
||||
@@ -322,38 +352,39 @@ body {
|
||||
/* ═══════════════════════════════════════════
|
||||
按钮样式 — 银灰风格
|
||||
═══════════════════════════════════════════ */
|
||||
/* 主按钮 — 银灰色 */
|
||||
/* 主按钮 — 京东红 */
|
||||
.el-button--primary {
|
||||
background: #909399 !important;
|
||||
border: 1px solid #909399 !important;
|
||||
background: var(--jd-red) !important;
|
||||
border: 1px solid var(--jd-red) !important;
|
||||
color: #fff !important;
|
||||
border-radius: var(--radius-base) !important;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: #6b7280 !important;
|
||||
border-color: #6b7280 !important;
|
||||
background: var(--jd-red-dark) !important;
|
||||
border-color: var(--jd-red-dark) !important;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #4b5563 !important;
|
||||
border-color: #4b5563 !important;
|
||||
background: #a30f1c !important;
|
||||
border-color: #a30f1c !important;
|
||||
}
|
||||
|
||||
&.is-disabled,
|
||||
&.is-disabled:hover {
|
||||
background: #d1d5db !important;
|
||||
border-color: #d1d5db !important;
|
||||
background: #fbcecf !important;
|
||||
border-color: #fbcecf !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
&.is-plain {
|
||||
background: #fff !important;
|
||||
border-color: #909399 !important;
|
||||
color: #909399 !important;
|
||||
border-color: var(--jd-red) !important;
|
||||
color: var(--jd-red) !important;
|
||||
|
||||
&:hover {
|
||||
background: #909399 !important;
|
||||
background: var(--jd-red) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
@@ -744,8 +775,8 @@ body {
|
||||
color: #e6a23c !important;
|
||||
}
|
||||
.el-tag--primary {
|
||||
background: #ecf5ff !important;
|
||||
color: #409eff !important;
|
||||
background: var(--jd-red-light) !important;
|
||||
color: var(--jd-red) !important;
|
||||
}
|
||||
.el-tag--success {
|
||||
background: #f0f9eb !important;
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
// base color - ERPNext inspired palette
|
||||
$blue:#4A6FA5;
|
||||
$light-blue:#6B8FB5;
|
||||
$red:#C03639;
|
||||
// base color - 京东红 palette
|
||||
$blue:#e4393c;
|
||||
$light-blue:#f88;
|
||||
$red:#e4393c;
|
||||
$pink: #E65D6E;
|
||||
$green: #30B08F;
|
||||
$tiffany: #4AB7BD;
|
||||
$yellow:#FEC171;
|
||||
$panGreen: #30B08F;
|
||||
|
||||
// 默认菜单主题风格(ERPNext风格:白色边栏,蓝色主色)
|
||||
// 默认菜单主题风格(京东风格)
|
||||
$base-menu-color: #555c70;
|
||||
$base-menu-color-active: #4A6FA5;
|
||||
$base-menu-color-active: #e4393c;
|
||||
$base-menu-background: #1a2332;
|
||||
$base-logo-title-color: #ffffff;
|
||||
|
||||
$base-menu-light-color: rgba(0,0,0,.65);
|
||||
$base-menu-light-background: #ffffff;
|
||||
$base-logo-light-title-color: #4A6FA5;
|
||||
$base-logo-light-title-color: #e4393c;
|
||||
|
||||
$base-sub-menu-background: #111a27;
|
||||
$base-sub-menu-hover: rgba(17,113,196,.08);
|
||||
$base-sub-menu-hover: rgba(228,57,60,.08);
|
||||
|
||||
$base-sidebar-width: 200px;
|
||||
$base-sidebar-width: 192px;
|
||||
|
||||
:export {
|
||||
menuColor: $base-menu-color;
|
||||
|
||||
@@ -63,7 +63,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.jd-filter-bar {
|
||||
background: var(--bg-gray, #f5f5f5);
|
||||
background: var(--bg-gray, #ffffff);
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--radius-base, 2px);
|
||||
margin-bottom: 12px;
|
||||
|
||||
@@ -582,7 +582,7 @@ export default {
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
background: #ecf5ff;
|
||||
}
|
||||
}
|
||||
@@ -608,7 +608,7 @@ export default {
|
||||
gap: 5px;
|
||||
|
||||
i {
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
@@ -630,7 +630,7 @@ export default {
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
background: #ecf5ff;
|
||||
}
|
||||
}
|
||||
@@ -668,17 +668,17 @@ export default {
|
||||
margin-bottom: 1px;
|
||||
|
||||
&:hover {
|
||||
background: #f0f7ff;
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-tree-node.is-current > .el-tree-node__content {
|
||||
background: #e6f0fd;
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
font-weight: 600;
|
||||
|
||||
.node-icon {
|
||||
color: #409eff !important;
|
||||
color: #e4393c !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ export default {
|
||||
}
|
||||
.notice-popover .notice-mark-all {
|
||||
font-size: 12px;
|
||||
color: #409EFF;
|
||||
color: #e4393c;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ export default {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
border-bottom: 1px solid #ffffff;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ export default {
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #f5f5f5;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
<template>
|
||||
<div class="sidebar-logo-container" :class="{ collapse: collapse }"
|
||||
:style="{ backgroundColor: sideTheme === 'theme-dark' && navType !== 3 ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<div class="sidebar-logo-container" :class="{ collapse: collapse }">
|
||||
<transition name="sidebarLogoFade">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo"
|
||||
style="width:32px;height:32px;object-fit:contain;flex-shrink:0;display:block" />
|
||||
<h1 v-else class="sidebar-title"
|
||||
:style="{ color: sideTheme === 'theme-dark' && navType !== 3 ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo-img" />
|
||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo"
|
||||
style="width:32px;height:32px;object-fit:contain;flex-shrink:0;margin-right:10px;display:block" />
|
||||
<h1 class="sidebar-title"
|
||||
:style="{ color: sideTheme === 'theme-dark' && navType !== 3 ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo-img expand" />
|
||||
<h1 class="sidebar-title">{{ title }}</h1>
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import logoImg from "@/assets/logo/logo.png"
|
||||
import logoImg from "@/assets/logo/logo.svg"
|
||||
import variables from "@/assets/styles/variables.scss"
|
||||
export default {
|
||||
name: "SidebarLogo",
|
||||
@@ -31,18 +26,57 @@ export default {
|
||||
data() { return { title: process.env.VUE_APP_TITLE, logo: logoImg } }
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
<style lang="scss">
|
||||
.sidebarLogoFade-enter-active { transition: opacity 1.5s; }
|
||||
.sidebarLogoFade-enter, .sidebarLogoFade-leave-to { opacity: 0; }
|
||||
.sidebar-logo-container {
|
||||
height: 50px; background: #2b2f3a; text-align: center;
|
||||
height: 50px !important;
|
||||
background: #ffffff !important;
|
||||
text-align: left !important;
|
||||
overflow: hidden !important;
|
||||
border-bottom: 1px solid #e5e5e5 !important;
|
||||
white-space: nowrap !important;
|
||||
|
||||
.sidebar-logo-link {
|
||||
height: 100%; width: 100%; display: flex; align-items: center;
|
||||
justify-content: center; text-decoration: none; padding: 0 12px;
|
||||
height: 50px !important;
|
||||
width: 100% !important;
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-start !important;
|
||||
text-decoration: none !important;
|
||||
padding: 0 12px !important;
|
||||
box-sizing: border-box !important;
|
||||
white-space: nowrap !important;
|
||||
|
||||
.sidebar-logo-img {
|
||||
width: 26px !important;
|
||||
height: 26px !important;
|
||||
min-width: 26px !important;
|
||||
object-fit: contain !important;
|
||||
flex: 0 0 26px !important;
|
||||
display: inline-block !important;
|
||||
vertical-align: middle !important;
|
||||
margin: 0 !important;
|
||||
&.expand { margin-right: 10px !important; }
|
||||
}
|
||||
.sidebar-title {
|
||||
color: #fff; font-weight: 600; font-size: 14px; margin: 0;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
color: #333 !important;
|
||||
font-weight: 700 !important;
|
||||
font-size: 13px !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
line-height: 50px !important;
|
||||
flex: 1 1 auto !important;
|
||||
min-width: 0 !important;
|
||||
display: inline-block !important;
|
||||
vertical-align: middle !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
}
|
||||
}
|
||||
&.collapse .sidebar-logo-link { justify-content: center !important; padding: 0 !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -105,9 +105,9 @@ export default {
|
||||
},
|
||||
chromeVars() {
|
||||
if (this.tagsViewStyle !== 'chrome') return {}
|
||||
const primary = this.theme || '#409EFF'
|
||||
const primary = this.theme || '#e4393c'
|
||||
return {
|
||||
'--chrome-tab-active-bg': this.mixHexWithWhite(primary, 0.15),
|
||||
'--chrome-tab-active-bg': '#ffffff',
|
||||
'--chrome-tab-text-active': primary,
|
||||
'--chrome-wing-r': '14px'
|
||||
}
|
||||
|
||||
150
ruoyi-ui/src/views/bid/approval/index.vue
Normal file
150
ruoyi-ui/src/views/bid/approval/index.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" size="small" :inline="true" ref="queryForm">
|
||||
<el-form-item label="业务名称" prop="bizName">
|
||||
<el-input v-model="queryParams.bizName" placeholder="业务名称" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="启用状态" prop="enabled">
|
||||
<el-select v-model="queryParams.enabled" placeholder="全部" clearable style="width:110px">
|
||||
<el-option label="启用" value="1"/>
|
||||
<el-option label="停用" value="0"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['bid:approval:add']">新增配置</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="业务类型" prop="bizType" width="180"/>
|
||||
<el-table-column label="业务名称" prop="bizName" width="160"/>
|
||||
<el-table-column label="审批方式" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.signType==='1'?'':'warning'">
|
||||
{{ scope.row.signType==='1' ? '或签' : '会签' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批人" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-for="n in scope.row.userNames" :key="n" size="small" style="margin:2px">{{ n }}</el-tag>
|
||||
<span v-if="!scope.row.userNames || !scope.row.userNames.length" style="color:#f56c6c">未配置</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.enabled==='1' ? 'success' : 'info'">
|
||||
{{ scope.row.enabled==='1' ? '启用' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" :show-overflow-tooltip="true"/>
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['bid:approval:edit']">编辑</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['bid:approval:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<el-dialog :title="title" :visible.sync="open" width="640px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item label="业务类型" prop="bizType">
|
||||
<el-input v-model="form.bizType" :disabled="!!form.id" placeholder="如 PURCHASE_ORDER"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务名称" prop="bizName">
|
||||
<el-input v-model="form.bizName" placeholder="如 采购订单"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批方式" prop="signType">
|
||||
<el-radio-group v-model="form.signType">
|
||||
<el-radio label="1">或签(任一审批人通过即生效)</el-radio>
|
||||
<el-radio label="2" disabled>会签(预留)</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批人" prop="userIds">
|
||||
<el-select v-model="form.userIds" multiple filterable placeholder="选择审批人" style="width:100%">
|
||||
<el-option v-for="u in userOptions" :key="u.userId" :label="u.nickName" :value="u.userId"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="启用" prop="enabled">
|
||||
<el-radio-group v-model="form.enabled">
|
||||
<el-radio label="1">启用</el-radio>
|
||||
<el-radio label="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注"><el-input v-model="form.remark" type="textarea" rows="2"/></el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="open=false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listApproval, getApproval, addApproval, updateApproval, delApproval } from "@/api/bid/approval";
|
||||
import { listUser } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "ApprovalConfig",
|
||||
data() {
|
||||
return {
|
||||
loading: false, total: 0, list: [],
|
||||
open: false, title: "",
|
||||
userOptions: [],
|
||||
queryParams: { pageNum: 1, pageSize: 10, bizName: null, enabled: null },
|
||||
form: { signType: "1", enabled: "1", userIds: [] },
|
||||
rules: {
|
||||
bizType: [{ required: true, message: "请输入业务类型", trigger: "blur" }],
|
||||
bizName: [{ required: true, message: "请输入业务名称", trigger: "blur" }],
|
||||
userIds: [{ required: true, type: "array", min: 1, message: "至少选择一个审批人", trigger: "change" }]
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
listUser({ pageSize: 500 }).then(r => { this.userOptions = r.rows || []; });
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listApproval(this.queryParams).then(r => { this.list = r.rows; this.total = r.total; this.loading = false; });
|
||||
},
|
||||
handleQuery() { this.queryParams.pageNum = 1; this.getList(); },
|
||||
resetQuery() { this.resetForm("queryForm"); this.handleQuery(); },
|
||||
handleAdd() {
|
||||
this.form = { signType: "1", enabled: "1", userIds: [] };
|
||||
this.open = true; this.title = "新增审批配置";
|
||||
},
|
||||
handleUpdate(row) {
|
||||
getApproval(row.id).then(r => {
|
||||
this.form = Object.assign({}, r.data, { userIds: r.data.userIds || [] });
|
||||
this.open = true; this.title = "编辑审批配置";
|
||||
});
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$modal.confirm("确认删除该配置?").then(() => delApproval(row.id))
|
||||
.then(() => { this.getList(); this.$modal.msgSuccess("删除成功"); });
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) return;
|
||||
const action = this.form.id ? updateApproval : addApproval;
|
||||
action(this.form).then(() => { this.$modal.msgSuccess("保存成功"); this.open = false; this.getList(); });
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
86
ruoyi-ui/src/views/bid/approval/pending.vue
Normal file
86
ruoyi-ui/src/views/bid/approval/pending.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div style="margin-bottom:12px;display:flex;align-items:center;gap:8px">
|
||||
<span style="font-size:16px;font-weight:600">待我审批</span>
|
||||
<el-tag type="warning" size="small">共 {{ list.length }} 条</el-tag>
|
||||
<el-button size="mini" icon="el-icon-refresh" @click="getList" style="margin-left:auto">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-form :inline="true" size="small">
|
||||
<el-form-item label="业务类型">
|
||||
<el-select v-model="filterBizType" placeholder="全部" clearable style="width:160px">
|
||||
<el-option label="采购订单" value="PURCHASE_ORDER"/>
|
||||
<el-option label="客户报价" value="CLIENT_QUOTE"/>
|
||||
<el-option label="供应商报价" value="QUOTATION"/>
|
||||
<el-option label="发货单" value="DELIVERY_ORDER"/>
|
||||
<el-option label="订单异议" value="ORDER_OBJECTION"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="filteredList" border>
|
||||
<el-table-column label="业务类型" width="140">
|
||||
<template slot-scope="s">
|
||||
<el-tag size="small">{{ s.row.bizName }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单号" prop="bizNo" width="200"/>
|
||||
<el-table-column label="金额" prop="amount" width="140" align="right">
|
||||
<template slot-scope="s">
|
||||
<span v-if="s.row.amount" style="color:#e4393c;font-weight:600">¥{{ s.row.amount }}</span>
|
||||
<span v-else style="color:#999">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交人" prop="createBy" width="120"/>
|
||||
<el-table-column label="提交时间" prop="createTime" width="180"/>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template><el-tag type="warning">审批中</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="180">
|
||||
<template slot-scope="s">
|
||||
<el-button size="mini" type="text" style="color:#67C23A" @click="handleApprove(s.row)">通过</el-button>
|
||||
<el-button size="mini" type="text" style="color:#F56C6C" @click="handleReject(s.row)">驳回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template slot="empty">
|
||||
<div style="padding:24px;color:#999">暂无待审批单据</div>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listPending, approveBiz, rejectBiz } from "@/api/bid/approvalAction";
|
||||
|
||||
export default {
|
||||
name: "ApprovalPending",
|
||||
data() {
|
||||
return { loading: false, list: [], filterBizType: null };
|
||||
},
|
||||
computed: {
|
||||
filteredList() {
|
||||
if (!this.filterBizType) return this.list;
|
||||
return this.list.filter(r => r.bizType === this.filterBizType);
|
||||
}
|
||||
},
|
||||
created() { this.getList(); },
|
||||
methods: {
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listPending().then(r => { this.list = r.data || []; this.loading = false; })
|
||||
.catch(() => { this.loading = false; });
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.$modal.confirm(`确认通过【${row.bizName} · ${row.bizNo}】?`)
|
||||
.then(() => approveBiz(row.bizType, row.id))
|
||||
.then(() => { this.$modal.msgSuccess("审批通过"); this.getList(); });
|
||||
},
|
||||
handleReject(row) {
|
||||
this.$prompt("请输入驳回原因", "驳回", { inputPattern: /.+/, inputErrorMessage: "请填写原因" })
|
||||
.then(({ value }) => rejectBiz(row.bizType, row.id, value))
|
||||
.then(() => { this.$modal.msgSuccess("已驳回"); this.getList(); })
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -277,7 +277,7 @@ export default {
|
||||
/* ═══════ 整体布局 ═══════ */
|
||||
.client-manage {
|
||||
padding: 12px;
|
||||
background: #f5f5f5;
|
||||
background: #ffffff;
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
.client-manage ::v-deep .el-tabs__header {
|
||||
@@ -342,7 +342,7 @@ export default {
|
||||
.dl {
|
||||
width: 90px;
|
||||
flex-shrink: 0;
|
||||
background: #f5f5f5;
|
||||
background: #ffffff;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
@@ -357,7 +357,7 @@ export default {
|
||||
}
|
||||
.detail-remark {
|
||||
padding: 8px 12px;
|
||||
background: #fff5f5;
|
||||
background: #fafafa;
|
||||
border: 1px solid #fce4e4;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
|
||||
@@ -17,11 +17,20 @@
|
||||
<el-table-column label="金额" width="120" align="right"><template slot-scope="s"><span class="amount">¥{{ s.row.totalAmount }}</span></template></el-table-column>
|
||||
<el-table-column label="交货期" prop="deliveryDate" width="95" align="center" />
|
||||
<el-table-column label="物料" prop="itemCount" width="55" align="center" />
|
||||
<el-table-column label="状态" width="90" align="center"><template slot-scope="s"><el-tag type="warning" size="small" effect="dark">待发</el-tag></template></el-table-column>
|
||||
<el-table-column label="操作" width="180" align="center">
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template slot-scope="s">
|
||||
<el-tag :type="s.row.deliveryStatus==='10'?'warning':(s.row.deliveryStatus==='rejected'?'danger':'warning')" size="small" effect="dark">
|
||||
{{ {pending:'待发','10':'审批中',rejected:'已驳回',confirmed:'已确认'}[s.row.deliveryStatus] || '待发' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="300" align="center">
|
||||
<template slot-scope="s">
|
||||
<el-button size="mini" type="text" @click="handleView(s.row)">详情</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" @click="handleShip(s.row)">发货确认</el-button>
|
||||
<el-button size="mini" type="text" style="color:#E6A23C" @click="handleSubmitApproval(s.row)" v-if="s.row.deliveryStatus==='pending' || s.row.deliveryStatus==='rejected'">提交审批</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" @click="handleApprove(s.row)" v-if="s.row.deliveryStatus==='10'">通过</el-button>
|
||||
<el-button size="mini" type="text" style="color:#F56C6C" @click="handleReject(s.row)" v-if="s.row.deliveryStatus==='10'">驳回</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" @click="handleShip(s.row)" v-if="s.row.deliveryStatus==='pending' || s.row.deliveryStatus==='confirmed'">发货确认</el-button>
|
||||
<el-button size="mini" type="text" style="color:#f56c6c" @click="handleDelete(s.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -32,7 +41,7 @@
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><span class="dl">发货单号</span><span class="dv"><b>{{ detailData ? detailData.doNo : '' }}</b></span></div>
|
||||
<div class="detail-item"><span class="dl">甲方客户</span><span class="dv">{{ (detailData && detailData.clientName) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#409EFF">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#e4393c">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="warning" size="small" effect="dark">待发</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">交货期</span><span class="dv">{{ (detailData && detailData.deliveryDate) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">备注</span><span class="dv">{{ (detailData && detailData.remark) || '-' }}</span></div>
|
||||
@@ -53,11 +62,12 @@
|
||||
</template>
|
||||
<script>
|
||||
import { listDelivery, getDelivery, delDelivery, shipDelivery } from "@/api/bid/delivery"
|
||||
import { submitApproval, approveBiz, rejectBiz } from "@/api/bid/approvalAction"
|
||||
export default {
|
||||
name: "ClientDeliveryPending",
|
||||
data() { return {
|
||||
loading: false, list: [], total: 0,
|
||||
q: { pageNum: 1, pageSize: 20, type: "client", deliveryStatus: "pending", doNo: "", clientName: "" },
|
||||
q: { pageNum: 1, pageSize: 20, type: "client", doNo: "", clientName: "" },
|
||||
detailOpen: false, detailData: null
|
||||
}},
|
||||
created() { this.getList() },
|
||||
@@ -66,22 +76,25 @@ export default {
|
||||
search() { this.q.pageNum=1; this.getList() }, resetSearch() { this.q.doNo=""; this.q.clientName=""; this.search() },
|
||||
handleView(row) { getDelivery(row.doId).then(r=>{this.detailData=r.data;this.detailOpen=true}).catch(()=>{}) },
|
||||
handleShip(row) { this.$modal.confirm("确认发货?").then(()=>shipDelivery(row.doId)).then(()=>{this.$modal.msgSuccess("已发货");this.getList()}).catch(()=>{}) },
|
||||
handleDelete(row) { this.$modal.confirm("确认删除?").then(()=>delDelivery(row.doId)).then(()=>{this.$modal.msgSuccess("已删除");this.getList()}).catch(()=>{}) }
|
||||
handleDelete(row) { this.$modal.confirm("确认删除?").then(()=>delDelivery(row.doId)).then(()=>{this.$modal.msgSuccess("已删除");this.getList()}).catch(()=>{}) },
|
||||
handleSubmitApproval(row) { this.$modal.confirm("确认提交审批?").then(()=>submitApproval("DELIVERY_ORDER", row.doId)).then(()=>{this.$modal.msgSuccess("已提交审批");this.getList()}).catch(()=>{}) },
|
||||
handleApprove(row) { this.$modal.confirm("确认通过该发货单?").then(()=>approveBiz("DELIVERY_ORDER", row.doId)).then(()=>{this.$modal.msgSuccess("审批通过");this.getList()}).catch(()=>{}) },
|
||||
handleReject(row) { this.$prompt("请输入驳回原因","驳回",{inputPattern:/.+/,inputErrorMessage:"请填写原因"}).then(({value})=>rejectBiz("DELIVERY_ORDER", row.doId, value)).then(()=>{this.$modal.msgSuccess("已驳回");this.getList()}).catch(()=>{}) }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.order-page { background:#f5f7fa; padding:12px; min-height:calc(100vh - 84px); }
|
||||
.page-header { background:#fff; padding:12px 16px; border-radius:4px; margin-bottom:12px; display:flex; align-items:center; gap:12px; }
|
||||
.page-title { font-size:16px; font-weight:700; color:#1a2c4e; }
|
||||
.page-title { font-size:16px; font-weight:700; color:#333333; }
|
||||
.search-bar { background:#fff; padding:12px 16px; border-radius:4px; margin-bottom:12px; display:flex; align-items:center; gap:8px; }
|
||||
.sr { margin-left:auto; }
|
||||
.amount { color:#409EFF; font-weight:700; }
|
||||
.amount { color:#e4393c; font-weight:700; }
|
||||
.detail-grid { display:grid; grid-template-columns:1fr 1fr; gap:0; border:1px solid #ebeef5; border-radius:4px; margin-bottom:16px; }
|
||||
.detail-item { display:flex; border-bottom:1px solid #ebeef5; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom:none; }
|
||||
.detail-item:nth-child(odd) { border-right:1px solid #ebeef5; }
|
||||
.dl { width:90px; flex-shrink:0; background:#f5f7fa; padding:10px 12px; font-size:12px; color:#606266; font-weight:600; border-right:1px solid #ebeef5; }
|
||||
.dv { padding:10px 12px; font-size:13px; color:#303133; flex:1; }
|
||||
.section-bar { font-size:13px; font-weight:700; color:#1a2c4e; padding:6px 0 6px 10px; margin-bottom:10px; border-left:4px solid #4A6FA5; }
|
||||
.section-bar { font-size:13px; font-weight:700; color:#333333; padding:6px 0 6px 10px; margin-bottom:10px; border-left:4px solid #4A6FA5; }
|
||||
</style>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><span class="dl">发货单号</span><span class="dv"><b>{{ detailData ? detailData.doNo : '' }}</b></span></div>
|
||||
<div class="detail-item"><span class="dl">甲方客户</span><span class="dv">{{ (detailData && detailData.clientName) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#409EFF">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#e4393c">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="success" size="small" effect="dark">已签收</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">交货期</span><span class="dv">{{ (detailData && detailData.deliveryDate) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">签收日期</span><span class="dv">{{ (detailData && detailData.actualCloseDate) || '-' }}</span></div>
|
||||
@@ -63,17 +63,17 @@ export default {
|
||||
<style scoped>
|
||||
.order-page { background:#f5f7fa; padding:12px; min-height:calc(100vh - 84px); }
|
||||
.page-header { background:#fff; padding:12px 16px; border-radius:4px; margin-bottom:12px; display:flex; align-items:center; gap:12px; }
|
||||
.page-title { font-size:16px; font-weight:700; color:#1a2c4e; }
|
||||
.page-title { font-size:16px; font-weight:700; color:#333333; }
|
||||
.search-bar { background:#fff; padding:12px 16px; border-radius:4px; margin-bottom:12px; display:flex; align-items:center; gap:8px; }
|
||||
.sr { margin-left:auto; }
|
||||
.amount { color:#409EFF; font-weight:700; }
|
||||
.amount { color:#e4393c; font-weight:700; }
|
||||
.detail-grid { display:grid; grid-template-columns:1fr 1fr; gap:0; border:1px solid #ebeef5; border-radius:4px; margin-bottom:16px; }
|
||||
.detail-item { display:flex; border-bottom:1px solid #ebeef5; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom:none; }
|
||||
.detail-item:nth-child(odd) { border-right:1px solid #ebeef5; }
|
||||
.dl { width:90px; flex-shrink:0; background:#f5f7fa; padding:10px 12px; font-size:12px; color:#606266; font-weight:600; border-right:1px solid #ebeef5; }
|
||||
.dv { padding:10px 12px; font-size:13px; color:#303133; flex:1; }
|
||||
.section-bar { font-size:13px; font-weight:700; color:#1a2c4e; padding:6px 0 6px 10px; margin-bottom:10px; border-left:4px solid #4A6FA5; }
|
||||
.section-bar { font-size:13px; font-weight:700; color:#333333; padding:6px 0 6px 10px; margin-bottom:10px; border-left:4px solid #4A6FA5; }
|
||||
.diff-early { color:#67c23a; font-weight:600; }
|
||||
.diff-ontime { color:#909399; font-weight:600; }
|
||||
.diff-late { color:#f56c6c; font-weight:600; }
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><span class="dl">发货单号</span><span class="dv"><b>{{ detailData ? detailData.doNo : '' }}</b></span></div>
|
||||
<div class="detail-item"><span class="dl">甲方客户</span><span class="dv">{{ (detailData && detailData.clientName) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#409EFF">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">总金额</span><span class="dv" style="color:#e4393c">¥{{ detailData ? detailData.totalAmount : 0 }}</span></div>
|
||||
<div class="detail-item"><span class="dl">状态</span><span class="dv"><el-tag type="primary" size="small" effect="dark">运输中</el-tag></span></div>
|
||||
<div class="detail-item"><span class="dl">交货期</span><span class="dv">{{ (detailData && detailData.deliveryDate) || '-' }}</span></div>
|
||||
<div class="detail-item"><span class="dl">备注</span><span class="dv">{{ (detailData && detailData.remark) || '-' }}</span></div>
|
||||
@@ -65,15 +65,15 @@ export default {
|
||||
<style scoped>
|
||||
.order-page { background:#f5f7fa; padding:12px; min-height:calc(100vh - 84px); }
|
||||
.page-header { background:#fff; padding:12px 16px; border-radius:4px; margin-bottom:12px; display:flex; align-items:center; gap:12px; }
|
||||
.page-title { font-size:16px; font-weight:700; color:#1a2c4e; }
|
||||
.page-title { font-size:16px; font-weight:700; color:#333333; }
|
||||
.search-bar { background:#fff; padding:12px 16px; border-radius:4px; margin-bottom:12px; display:flex; align-items:center; gap:8px; }
|
||||
.sr { margin-left:auto; }
|
||||
.amount { color:#409EFF; font-weight:700; }
|
||||
.amount { color:#e4393c; font-weight:700; }
|
||||
.detail-grid { display:grid; grid-template-columns:1fr 1fr; gap:0; border:1px solid #ebeef5; border-radius:4px; margin-bottom:16px; }
|
||||
.detail-item { display:flex; border-bottom:1px solid #ebeef5; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom:none; }
|
||||
.detail-item:nth-child(odd) { border-right:1px solid #ebeef5; }
|
||||
.dl { width:90px; flex-shrink:0; background:#f5f7fa; padding:10px 12px; font-size:12px; color:#606266; font-weight:600; border-right:1px solid #ebeef5; }
|
||||
.dv { padding:10px 12px; font-size:13px; color:#303133; flex:1; }
|
||||
.section-bar { font-size:13px; font-weight:700; color:#1a2c4e; padding:6px 0 6px 10px; margin-bottom:10px; border-left:4px solid #4A6FA5; }
|
||||
.section-bar { font-size:13px; font-weight:700; color:#333333; padding:6px 0 6px 10px; margin-bottom:10px; border-left:4px solid #4A6FA5; }
|
||||
</style>
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="75" align="right">
|
||||
<template slot-scope="s">
|
||||
<strong style="color:#409EFF">¥{{ s.row.totalPrice || '0.00' }}</strong>
|
||||
<strong style="color:#e4393c">¥{{ s.row.totalPrice || '0.00' }}</strong>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="毛利率" width="60" align="center">
|
||||
@@ -167,7 +167,7 @@
|
||||
|
||||
<div class="total-bar">
|
||||
<span>合计:</span>
|
||||
<strong style="font-size:20px;color:#409EFF">¥{{ grandTotal }}</strong>
|
||||
<strong style="font-size:20px;color:#e4393c">¥{{ grandTotal }}</strong>
|
||||
<span style="margin-left:20px;color:#909399;font-size:13px">共 {{ form.items.length }} 项</span>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -293,7 +293,7 @@ import { getClientQuote, addClientQuote, updateClientQuote, getClientQuoteHistor
|
||||
import { listClient } from "@/api/bid/client";
|
||||
import { createRfqFromQuote } from "@/api/bid/rfq";
|
||||
import { listMaterial, getSupplierQuoteReference } from "@/api/bid/material";
|
||||
import logoImg from "@/assets/logo/logo.png";
|
||||
import logoImg from "@/assets/logo/logo.svg";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
|
||||
@@ -577,7 +577,7 @@ export default {
|
||||
.page-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; padding:16px 0; border-bottom:1px solid #f0f2f5; flex-wrap:wrap; gap:10px; overflow:hidden; }
|
||||
.page-header-left { display:flex; align-items:center; gap:12px; }
|
||||
.page-header-right { display:flex; gap:10px; }
|
||||
.page-title { font-size:18px; font-weight:700; color:#1a2c4e; }
|
||||
.page-title { font-size:18px; font-weight:700; color:#333333; }
|
||||
.info-card ::v-deep .el-card__body { padding: 20px; }
|
||||
.total-bar { display:flex; align-items:center; justify-content:flex-end; padding:16px 20px; border-top:1px solid #f0f2f5; background:#fafbfc; }
|
||||
|
||||
@@ -606,7 +606,7 @@ export default {
|
||||
/* RFQ已生成信息 */
|
||||
.rfq-generated-info { padding: 4px 0; }
|
||||
.rfq-item { display:flex; align-items:center; gap:8px; padding:8px 10px; border:1px solid #e4e7ed; border-radius:6px; cursor:pointer; transition:all .2s; }
|
||||
.rfq-item:hover { border-color:#409eff; background:#f0f7ff; }
|
||||
.rfq-item:hover { border-color:#e4393c; background:#fafafa; }
|
||||
.rfq-title { font-size:12px; color:#606266; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:120px; }
|
||||
|
||||
/* 物料搜索建议下拉 */
|
||||
@@ -671,7 +671,7 @@ export default {
|
||||
}
|
||||
|
||||
.ms-brand {
|
||||
color: #409EFF;
|
||||
color: #e4393c;
|
||||
background: #ecf5ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
@@ -695,21 +695,21 @@ export default {
|
||||
.pdf-header { display:flex; align-items:flex-start; padding-bottom:12px; }
|
||||
.pdf-logo { width:52px; height:52px; object-fit:contain; margin-right:14px; }
|
||||
.pdf-header-text { flex:1; }
|
||||
.pdf-company { font-size:22px; font-weight:700; color:#1171c4; }
|
||||
.pdf-company { font-size:22px; font-weight:700; color:#e4393c; }
|
||||
.pdf-doc-type { font-size:14px; color:#333; margin-top:4px; font-weight:600; }
|
||||
.pdf-header-meta { font-size:12px; color:#888; text-align:right; }
|
||||
.pdf-divider { border-top:2px solid #1171c4; margin:0 0 16px; }
|
||||
.pdf-divider { border-top:2px solid #e4393c; margin:0 0 16px; }
|
||||
.pdf-meta-table { width:100%; border-collapse:collapse; margin-bottom:16px; td { padding:7px 10px; border:1px solid #e4e7ed; } }
|
||||
.meta-label { background:#f5f7fa; color:#606266; font-weight:600; width:90px; }
|
||||
.meta-val { color:#303133; }
|
||||
.pdf-section-title { font-size:14px; font-weight:700; color:#1a2c4e; margin:0 0 10px; padding-left:8px; border-left:4px solid #1171c4; }
|
||||
.pdf-section-title { font-size:14px; font-weight:700; color:#333333; margin:0 0 10px; padding-left:8px; border-left:4px solid #e4393c; }
|
||||
.pdf-items-table { width:100%; border-collapse:collapse; margin-bottom:20px;
|
||||
th { background:#1171c4; color:#fff; padding:8px; text-align:center; font-size:12px; font-weight:600; }
|
||||
th { background:#e4393c; color:#fff; padding:8px; text-align:center; font-size:12px; font-weight:600; }
|
||||
td { border:1px solid #e4e7ed; padding:7px 8px; text-align:center; font-size:12px; }
|
||||
tbody tr:nth-child(even) td { background:#f9fbff; }
|
||||
}
|
||||
.amount-cell { color:#409EFF; font-weight:600; }
|
||||
.total-cell { font-size:14px; background:#f0f7ff !important; font-weight:700; }
|
||||
.pdf-note { background:#fffbf0; border:1px solid #faecd8; border-radius:4px; padding:10px 14px; font-size:12px; color:#606266; margin-bottom:16px; }
|
||||
.amount-cell { color:#e4393c; font-weight:600; }
|
||||
.total-cell { font-size:14px; background:#fafafa !important; font-weight:700; }
|
||||
.pdf-note { background:#fafafa; border:1px solid #faecd8; border-radius:4px; padding:10px 14px; font-size:12px; color:#606266; margin-bottom:16px; }
|
||||
.pdf-footer { text-align:center; font-size:11px; color:#aaa; border-top:1px solid #f0f2f5; padding-top:12px; }
|
||||
</style>
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="总金额" width="130" align="right">
|
||||
<template slot-scope="s">
|
||||
<strong style="color:#409EFF">¥{{ s.row.totalAmount | money }}</strong>
|
||||
<strong style="color:#e4393c">¥{{ s.row.totalAmount | money }}</strong>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="币种" prop="currency" width="70" align="center" />
|
||||
@@ -134,13 +134,13 @@
|
||||
<el-descriptions-item label="有效期">{{ detailForm.validityDate | date }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联询价单">{{ detailForm.rfqNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总金额" :span="3">
|
||||
<strong style="color:#409EFF;font-size:16px">¥{{ detailForm.totalAmount | money }}</strong>
|
||||
<strong style="color:#e4393c;font-size:16px">¥{{ detailForm.totalAmount | money }}</strong>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="3">{{ detailForm.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<!-- 明细表格 -->
|
||||
<div style="margin-top:16px;font-weight:600;font-size:14px;color:#303133;padding:8px 0;border-bottom:2px solid #409EFF">
|
||||
<div style="margin-top:16px;font-weight:600;font-size:14px;color:#303133;padding:8px 0;border-bottom:2px solid #e4393c">
|
||||
报价明细
|
||||
</div>
|
||||
<el-table :data="detailForm.items || []" border size="small" style="margin-top:12px">
|
||||
@@ -157,7 +157,7 @@
|
||||
<template slot-scope="s">¥{{ s.row.unitPrice | money }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="110" align="right">
|
||||
<template slot-scope="s"><strong style="color:#409EFF">¥{{ s.row.totalPrice | money }}</strong></template>
|
||||
<template slot-scope="s"><strong style="color:#e4393c">¥{{ s.row.totalPrice | money }}</strong></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交期(天)" prop="deliveryDays" width="80" align="center" />
|
||||
</el-table>
|
||||
|
||||
@@ -93,6 +93,9 @@
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="quickCreate">快速新建</el-dropdown-item>
|
||||
<el-dropdown-item command="createRfq">生成RFQ</el-dropdown-item>
|
||||
<el-dropdown-item command="submitApproval" v-if="s.row.status==='draft' || s.row.status==='rejected'">提交审批</el-dropdown-item>
|
||||
<el-dropdown-item command="approve" v-if="s.row.status==='10'">审批通过</el-dropdown-item>
|
||||
<el-dropdown-item command="reject" v-if="s.row.status==='10'">审批驳回</el-dropdown-item>
|
||||
<el-dropdown-item command="createDelivery" v-if="s.row.status==='confirmed'">生成发货单</el-dropdown-item>
|
||||
<el-dropdown-item command="delete" v-if="s.row.status==='draft'" divided>删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -213,7 +216,7 @@
|
||||
<el-descriptions-item label="币种">{{ detailData.currency || 'CNY' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="有效期">{{ detailData.validityDate | dateFmt }}</el-descriptions-item>
|
||||
<el-descriptions-item label="RFQ数量">
|
||||
<span v-if="detailRfqList.length > 0" style="color:#409eff;font-weight:600">{{ detailRfqList.length }} 个</span>
|
||||
<span v-if="detailRfqList.length > 0" style="color:#e4393c;font-weight:600">{{ detailRfqList.length }} 个</span>
|
||||
<span v-else style="color:#c0c4cc">-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总金额" :span="3">
|
||||
@@ -269,6 +272,7 @@ import { listClientQuote, getClientQuote, addClientQuote, updateClientQuote, del
|
||||
import { listRfq, createRfqFromQuote } from "@/api/bid/rfq";
|
||||
import { listMaterial } from "@/api/bid/material";
|
||||
import { addDelivery } from "@/api/bid/delivery";
|
||||
import { submitApproval, approveBiz, rejectBiz } from "@/api/bid/approvalAction";
|
||||
|
||||
export default {
|
||||
name: "ClientQuote",
|
||||
@@ -331,8 +335,25 @@ export default {
|
||||
if (cmd === 'quickCreate') this.handleQuickCreate(row);
|
||||
else if (cmd === 'createRfq') this.handleCreateRfq(row);
|
||||
else if (cmd === 'createDelivery') this.handleCreateDelivery(row);
|
||||
else if (cmd === 'submitApproval') this.handleSubmitApproval(row);
|
||||
else if (cmd === 'approve') this.handleApprove(row);
|
||||
else if (cmd === 'reject') this.handleReject(row);
|
||||
else if (cmd === 'delete') this.handleDelete(row);
|
||||
},
|
||||
handleSubmitApproval(row) {
|
||||
this.$modal.confirm("确认提交审批?").then(() => submitApproval("CLIENT_QUOTE", row.quoteId))
|
||||
.then(() => { this.$modal.msgSuccess("已提交审批"); this.getList(); this.getStats(); });
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.$modal.confirm("确认通过该客户报价?").then(() => approveBiz("CLIENT_QUOTE", row.quoteId))
|
||||
.then(() => { this.$modal.msgSuccess("审批通过"); this.getList(); this.getStats(); });
|
||||
},
|
||||
handleReject(row) {
|
||||
this.$prompt("请输入驳回原因", "驳回", { inputPattern: /.+/, inputErrorMessage: "请填写原因" })
|
||||
.then(({ value }) => rejectBiz("CLIENT_QUOTE", row.quoteId, value))
|
||||
.then(() => { this.$modal.msgSuccess("已驳回"); this.getList(); this.getStats(); })
|
||||
.catch(() => {});
|
||||
},
|
||||
handleDelete(row) { this.$modal.confirm("确认删除【" + row.quoteNo + "】?").then(() => delClientQuote(row.quoteId)).then(() => { this.$modal.msgSuccess("删除成功"); this.getList(); this.getStats(); }); },
|
||||
loadRfqForDetail(quoteId) { this.detailRfqLoading = true; listRfq({ clientQuoteId: quoteId, pageSize: 50 }).then(r => { this.detailRfqList = r.rows || []; this.detailRfqLoading = false; }).catch(() => { this.detailRfqLoading = false; }); },
|
||||
viewRfqDetail(rfq) { this.detailOpen = false; this.$router.push({ path: '/bid/rfq/detail', query: { rfqId: rfq.rfqId } }); },
|
||||
@@ -348,22 +369,22 @@ export default {
|
||||
itemTotal(row) { return ((parseFloat(row.quantity)||0) * (parseFloat(row.unitPrice)||0)).toFixed(2); },
|
||||
calcMargin(row) { const c = parseFloat(row.costPrice)||0; const p = parseFloat(row.unitPrice)||0; if (!p) return "0.0"; return (((p-c)/p)*100).toFixed(1); },
|
||||
marginColor(row) { const m = parseFloat(this.calcMargin(row)); return m >= 20 ? "#67c23a" : m >= 10 ? "#e6a23c" : "#f56c6c"; },
|
||||
statusType(s) { return { draft:"info", sent:"primary", confirmed:"success", rejected:"danger" }[s] || ""; },
|
||||
statusLabel(s) { return { draft:"草稿", sent:"已发送", confirmed:"已确认", rejected:"已拒绝" }[s] || s; }
|
||||
statusType(s) { return { draft:"info", "10":"warning", sent:"primary", confirmed:"success", rejected:"danger" }[s] || ""; },
|
||||
statusLabel(s) { return { draft:"草稿", "10":"审批中", sent:"已发送", confirmed:"已确认", rejected:"已拒绝" }[s] || s; }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ═══════ 页面容器 ═══════ */
|
||||
.cq-page { background: #f5f5f5; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
.cq-page { background: #ffffff; padding: 12px; min-height: calc(100vh - 84px); }
|
||||
|
||||
/* ═══════ 统计卡片 ═══════ */
|
||||
.stat-row { margin-bottom: 12px !important; }
|
||||
|
||||
/* ═══════ 搜索栏 ═══════ */
|
||||
.search-bar {
|
||||
background: #f5f5f5; padding: 10px 16px; border-radius: 2px;
|
||||
background: #ffffff; padding: 10px 16px; border-radius: 2px;
|
||||
margin-bottom: 12px;
|
||||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||||
}
|
||||
@@ -371,7 +392,7 @@ export default {
|
||||
|
||||
/* ═══════ 表格 ═══════ */
|
||||
.cq-table { }
|
||||
.link-text { color: #005ea7; cursor: pointer; }
|
||||
.link-text { color: #e4393c; cursor: pointer; }
|
||||
.link-text:hover { color: #e4393c; text-decoration: underline; }
|
||||
.amount { color: #e4393c; font-weight: 700; }
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plan-reason"><i class="el-icon-info" style="color:#409EFF;margin-right:4px"></i>{{ plan.clusterReason }}</div>
|
||||
<div class="plan-reason"><i class="el-icon-info" style="color:#e4393c;margin-right:4px"></i>{{ plan.clusterReason }}</div>
|
||||
|
||||
<el-table :data="plan.items" border size="small" class="plan-table"
|
||||
@selection-change="(sel) => onItemSelect(plan, sel)">
|
||||
@@ -93,7 +93,7 @@
|
||||
<template slot-scope="s">¥{{ s.row.unitPrice }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额(元)" width="120" align="right">
|
||||
<template slot-scope="s"><strong style="color:#409EFF">¥{{ s.row.totalPrice }}</strong></template>
|
||||
<template slot-scope="s"><strong style="color:#e4393c">¥{{ s.row.totalPrice }}</strong></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交期(天)" prop="deliveryDays" width="85" align="center" />
|
||||
</el-table>
|
||||
@@ -150,7 +150,7 @@
|
||||
|
||||
<script>
|
||||
import { compareRfq } from "@/api/bid/comparison";
|
||||
import logoImg from "@/assets/logo/logo.png";
|
||||
import logoImg from "@/assets/logo/logo.svg";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
|
||||
@@ -243,11 +243,11 @@ export default {
|
||||
.comparison-detail { padding-bottom: 40px; }
|
||||
.page-header { display:flex; align-items:center; margin-bottom:20px; padding:16px 0; border-bottom:1px solid #f0f2f5; }
|
||||
.page-header-left { display:flex; align-items:center; gap:12px; }
|
||||
.page-title { font-size:18px; font-weight:700; color:#1a2c4e; }
|
||||
.page-title { font-size:18px; font-weight:700; color:#333333; }
|
||||
.loading-box { text-align:center; padding:80px; color:#909399; font-size:15px; i { font-size:24px; margin-right:8px; } }
|
||||
.main-tabs { ::v-deep .el-tabs__content { padding: 20px; } }
|
||||
.item-block { margin-bottom: 32px; }
|
||||
.item-header { background:linear-gradient(90deg,#1171c4,#22a4ff); color:#fff; padding:10px 16px; border-radius:6px 6px 0 0; display:flex; align-items:center; gap:10px; }
|
||||
.item-header { background:linear-gradient(90deg,#e4393c,#e4393c); color:#fff; padding:10px 16px; border-radius:6px 6px 0 0; display:flex; align-items:center; gap:10px; }
|
||||
.item-badge { background:rgba(255,255,255,.25); padding:2px 10px; border-radius:12px; font-size:12px; }
|
||||
.item-name { font-size:15px; font-weight:700; }
|
||||
.item-spec { color:rgba(255,255,255,.8); font-size:13px; }
|
||||
@@ -256,33 +256,33 @@ export default {
|
||||
.supplier-cards { display:flex; gap:12px; padding:16px 0; overflow-x:auto; }
|
||||
.supplier-card { flex:0 0 200px; border:1px solid #e4e7ed; border-radius:8px; padding:14px; background:#fff; &:hover { box-shadow:0 4px 16px rgba(0,0,0,.1); } }
|
||||
.card-supplier { font-size:13px; font-weight:600; color:#303133; margin-bottom:6px; }
|
||||
.card-price { font-size:13px; color:#409EFF; font-weight:600; margin-bottom:10px; }
|
||||
.card-price { font-size:13px; color:#e4393c; font-weight:600; margin-bottom:10px; }
|
||||
.card-meta { font-size:11px; color:#c0c4cc; margin-top:8px; }
|
||||
.price-low { color:#67c23a; font-weight:700; }
|
||||
.plan-card { border:1px solid #e4e7ed; border-radius:8px; margin-bottom:24px; overflow:hidden; background:#fff; position:relative; }
|
||||
.plan-header { display:flex; align-items:center; justify-content:space-between; padding:14px 20px; background:linear-gradient(90deg,#f0f7ff,#fff); border-bottom:1px solid #e4e7ed; flex-wrap:wrap; gap:10px; }
|
||||
.plan-header { display:flex; align-items:center; justify-content:space-between; padding:14px 20px; background:linear-gradient(90deg,#fafafa,#fff); border-bottom:1px solid #e4e7ed; flex-wrap:wrap; gap:10px; }
|
||||
.plan-title { display:flex; align-items:center; gap:10px; }
|
||||
.plan-rank { background:#1171c4; color:#fff; padding:3px 10px; border-radius:12px; font-size:12px; }
|
||||
.plan-supplier { font-size:16px; font-weight:700; color:#1a2c4e; }
|
||||
.plan-rank { background:#e4393c; color:#fff; padding:3px 10px; border-radius:12px; font-size:12px; }
|
||||
.plan-supplier { font-size:16px; font-weight:700; color:#333333; }
|
||||
.plan-actions { display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
|
||||
.plan-total { font-size:14px; color:#606266; strong { font-size:18px; color:#409EFF; } }
|
||||
.plan-total { font-size:14px; color:#606266; strong { font-size:18px; color:#e4393c; } }
|
||||
.plan-reason { padding:8px 20px; font-size:13px; color:#606266; background:#f9fbff; border-bottom:1px solid #f0f2f5; }
|
||||
/* PDF */
|
||||
.pdf-area { padding:28px; background:#fff; font-family:"Microsoft YaHei","Noto Sans SC",Arial,sans-serif; font-size:13px; color:#222; }
|
||||
.pdf-header { display:flex; align-items:flex-start; padding-bottom:12px; }
|
||||
.pdf-logo { width:52px; height:52px; object-fit:contain; margin-right:14px; }
|
||||
.pdf-header-text { flex:1; }
|
||||
.pdf-company { font-size:22px; font-weight:700; color:#1171c4; }
|
||||
.pdf-company { font-size:22px; font-weight:700; color:#e4393c; }
|
||||
.pdf-doc-type { font-size:13px; color:#666; margin-top:2px; }
|
||||
.pdf-header-meta { font-size:12px; color:#888; text-align:right; }
|
||||
.pdf-divider { border-top:2px solid #1171c4; margin:0 0 16px; }
|
||||
.pdf-divider { border-top:2px solid #e4393c; margin:0 0 16px; }
|
||||
.pdf-meta-table { width:100%; border-collapse:collapse; margin-bottom:16px; td { padding:7px 10px; border:1px solid #e4e7ed; } }
|
||||
.meta-label { background:#f5f7fa; color:#606266; font-weight:600; width:90px; }
|
||||
.meta-val { color:#303133; }
|
||||
.amount-big { color:#409EFF; font-weight:700; font-size:16px; }
|
||||
.pdf-section-title { font-size:14px; font-weight:700; color:#1a2c4e; margin:0 0 10px; padding-left:8px; border-left:4px solid #1171c4; }
|
||||
.pdf-items-table { width:100%; border-collapse:collapse; margin-bottom:20px; th { background:#1171c4; color:#fff; padding:8px; text-align:center; font-weight:600; font-size:12px; } td { border:1px solid #e4e7ed; padding:7px 8px; text-align:center; font-size:12px; } tbody tr:nth-child(even) td { background:#f9fbff; } }
|
||||
.amount-cell { color:#409EFF; font-weight:600; }
|
||||
.total-cell { font-size:14px; background:#f0f7ff !important; font-weight:700; }
|
||||
.amount-big { color:#e4393c; font-weight:700; font-size:16px; }
|
||||
.pdf-section-title { font-size:14px; font-weight:700; color:#333333; margin:0 0 10px; padding-left:8px; border-left:4px solid #e4393c; }
|
||||
.pdf-items-table { width:100%; border-collapse:collapse; margin-bottom:20px; th { background:#e4393c; color:#fff; padding:8px; text-align:center; font-weight:600; font-size:12px; } td { border:1px solid #e4e7ed; padding:7px 8px; text-align:center; font-size:12px; } tbody tr:nth-child(even) td { background:#f9fbff; } }
|
||||
.amount-cell { color:#e4393c; font-weight:600; }
|
||||
.total-cell { font-size:14px; background:#fafafa !important; font-weight:700; }
|
||||
.pdf-footer { text-align:center; font-size:11px; color:#aaa; margin-top:16px; border-top:1px solid #f0f2f5; padding-top:12px; }
|
||||
</style>
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" style="margin-top:12px">
|
||||
<div slot="header" style="font-weight:700;color:#1a2c4e;font-size:15px">
|
||||
<i class="el-icon-data-analysis" style="margin-right:6px;color:#1171c4"></i>
|
||||
<div slot="header" style="font-weight:700;color:#333333;font-size:15px">
|
||||
<i class="el-icon-data-analysis" style="margin-right:6px;color:#e4393c"></i>
|
||||
选择询价单进行智慧比价
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="rfqList" border>
|
||||
|
||||
@@ -418,7 +418,7 @@ export default {
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 14px;
|
||||
background: linear-gradient(180deg, #409eff 0%, #2c3e50 100%);
|
||||
background: linear-gradient(180deg, #e4393c 0%, #2c3e50 100%);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ export default {
|
||||
.mat-name {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #1a2c4e;
|
||||
color: #333333;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.mat-subtitle {
|
||||
@@ -567,7 +567,7 @@ export default {
|
||||
gap: 4px;
|
||||
}
|
||||
.divider-text i {
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@@ -617,7 +617,7 @@ export default {
|
||||
}
|
||||
.quote-row.is-fastest {
|
||||
background: linear-gradient(90deg, #ecf5ff 0%, #d9ecff 100%);
|
||||
border-left: 3px solid #409eff;
|
||||
border-left: 3px solid #e4393c;
|
||||
}
|
||||
.quote-row.is-lowest.is-fastest {
|
||||
background: linear-gradient(90deg, #f0f9eb 0%, #e8f5e0 50%, #ecf5ff 100%);
|
||||
|
||||
@@ -183,7 +183,7 @@ export default {
|
||||
|
||||
.supplier-contact .phone {
|
||||
margin-left: 8px;
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
}
|
||||
|
||||
/* 价格单元格 */
|
||||
|
||||
@@ -249,7 +249,7 @@ export default {
|
||||
<style scoped>
|
||||
/* ========= 顶部标题 ========= */
|
||||
.detail-header {
|
||||
background: linear-gradient(135deg, #1a2c4e 0%, #2c3e50 100%);
|
||||
background: linear-gradient(135deg, #333333 0%, #2c3e50 100%);
|
||||
padding: 18px 24px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
@@ -315,7 +315,7 @@ export default {
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
background: linear-gradient(180deg, #409eff 0%, #2c3e50 100%);
|
||||
background: linear-gradient(180deg, #e4393c 0%, #2c3e50 100%);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ export default {
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
.grid-value-wrap >>> .el-input__inner:focus {
|
||||
border-color: #409eff;
|
||||
border-color: #e4393c;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.15);
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ export default {
|
||||
}
|
||||
>>> .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active {
|
||||
background: #fff;
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
font-weight: 600;
|
||||
}
|
||||
>>> .el-tabs--border-card > .el-tabs__content {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
:data="materialList"
|
||||
@selection-change="handleSelectionChange"
|
||||
border stripe style="width:100%"
|
||||
:header-cell-style="{ background: '#f5f5f5', color: '#666', fontWeight: 500, fontSize: '12px' }"
|
||||
:header-cell-style="{ background: '#ffffff', color: '#666', fontWeight: 500, fontSize: '12px' }"
|
||||
:cell-style="{ fontSize: '12px', color: '#333' }"
|
||||
size="small">
|
||||
<el-table-column type="selection" width="44" align="center" />
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
<el-table-column label="异议原因" prop="reason" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="{ pending:'warning', processing:'primary', resolved:'success', rejected:'danger' }[scope.row.status]">
|
||||
{{ { pending:'待处理', processing:'处理中', resolved:'已解决', rejected:'已拒绝' }[scope.row.status] || scope.row.status }}
|
||||
<el-tag :type="{ pending:'warning', processing:'primary', '10':'warning', resolved:'success', rejected:'danger' }[scope.row.status]">
|
||||
{{ { pending:'待处理', processing:'处理中', '10':'审批中', resolved:'已解决', rejected:'已拒绝' }[scope.row.status] || scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -35,6 +35,9 @@
|
||||
<el-table-column label="操作" align="center" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" @click="handleResolve(scope.row)" v-if="scope.row.status==='pending'||scope.row.status==='processing'" style="color:#67C23A">处理</el-button>
|
||||
<el-button size="mini" type="text" @click="handleSubmitApproval(scope.row)" v-if="scope.row.status==='pending'" style="color:#E6A23C">提交审批</el-button>
|
||||
<el-button size="mini" type="text" @click="handleApprove(scope.row)" v-if="scope.row.status==='10'" style="color:#67C23A">通过</el-button>
|
||||
<el-button size="mini" type="text" @click="handleReject(scope.row)" v-if="scope.row.status==='10'" style="color:#F56C6C">驳回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -76,6 +79,7 @@
|
||||
<script>
|
||||
import { listObjection, addObjection, updateObjection } from "@/api/bid/objection";
|
||||
import { listSupplier } from "@/api/bid/supplier";
|
||||
import { submitApproval, approveBiz, rejectBiz } from "@/api/bid/approvalAction";
|
||||
export default {
|
||||
name: "Objection",
|
||||
data() {
|
||||
@@ -100,6 +104,20 @@ export default {
|
||||
handleResolve(row) { this.resolveForm = { objectionId: row.objectionId, status: "resolved" }; this.resolveOpen = true; },
|
||||
submitResolve() {
|
||||
updateObjection(this.resolveForm).then(() => { this.$modal.msgSuccess("处理成功"); this.resolveOpen = false; this.getList(); });
|
||||
},
|
||||
handleSubmitApproval(row) {
|
||||
this.$modal.confirm("确认提交审批?").then(() => submitApproval("ORDER_OBJECTION", row.objectionId))
|
||||
.then(() => { this.$modal.msgSuccess("已提交审批"); this.getList(); });
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.$modal.confirm("确认通过该异议?").then(() => approveBiz("ORDER_OBJECTION", row.objectionId))
|
||||
.then(() => { this.$modal.msgSuccess("审批通过"); this.getList(); });
|
||||
},
|
||||
handleReject(row) {
|
||||
this.$prompt("请输入驳回原因", "驳回", { inputPattern: /.+/, inputErrorMessage: "请填写原因" })
|
||||
.then(({ value }) => rejectBiz("ORDER_OBJECTION", row.objectionId, value))
|
||||
.then(() => { this.$modal.msgSuccess("已驳回"); this.getList(); })
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -131,7 +131,7 @@ export default {
|
||||
return map[b] || ("类型" + b)
|
||||
},
|
||||
actionColor(b) {
|
||||
const map = { 1: "#67c23a", 2: "#e6a23c", 3: "#f56c6c", 5: "#409EFF" }
|
||||
const map = { 1: "#67c23a", 2: "#e6a23c", 3: "#f56c6c", 5: "#e4393c" }
|
||||
return map[b] || "#909399"
|
||||
},
|
||||
formatParam(param) {
|
||||
@@ -150,7 +150,7 @@ export default {
|
||||
background: #fff; padding: 12px 16px; border-radius: 4px; margin-bottom: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06); display: flex; align-items: center; gap: 12px;
|
||||
}
|
||||
.page-title { font-size: 16px; font-weight: 700; color: #1a2c4e; }
|
||||
.page-title { font-size: 16px; font-weight: 700; color: #333333; }
|
||||
.page-subtitle { font-size: 11px; color: #c0c4cc; letter-spacing: 1px; }
|
||||
.header-right { margin-left: auto; }
|
||||
.total-info { font-size: 12px; color: #909399; }
|
||||
|
||||
@@ -184,7 +184,7 @@ export default {
|
||||
|
||||
.stat-row { margin-bottom: 12px !important; }
|
||||
|
||||
.jd-filter { display: flex; align-items: center; background: #f5f5f5; padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
|
||||
.jd-filter { display: flex; align-items: center; background: #ffffff; padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
|
||||
.filter-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.filter-right { margin-left: auto; }
|
||||
.filter-input { width: 130px; }
|
||||
@@ -202,17 +202,17 @@ export default {
|
||||
.right-section { margin-bottom: 20px; }
|
||||
.rs-header { font-size: 12px; color: #666; margin-bottom: 8px; }
|
||||
.rs-list { max-height: 150px; overflow-y: auto; border: 1px solid #e5e5e5; border-radius: 2px; padding: 4px; }
|
||||
.rs-item { padding: 4px 8px; font-size: 12px; color: #333; border-bottom: 1px solid #f5f5f5; }
|
||||
.rs-item { padding: 4px 8px; font-size: 12px; color: #333; border-bottom: 1px solid #ffffff; }
|
||||
.rs-item:last-child { border-bottom: none; }
|
||||
.rs-empty { text-align: center; padding: 20px; color: #999; font-size: 12px; }
|
||||
.rs-date-row { display: flex; gap: 6px; margin-bottom: 8px; }
|
||||
.rs-quick { display: flex; gap: 6px; }
|
||||
.rs-warn { font-size: 11px; color: #f56c6c; margin-top: 6px; }
|
||||
|
||||
.order-link { color: #005ea7; cursor: pointer; }
|
||||
.order-link { color: #e4393c; cursor: pointer; }
|
||||
.order-link:hover { color: #e4393c; }
|
||||
|
||||
::v-deep .selected-row td { background: #fff5f5 !important; }
|
||||
::v-deep .selected-row td { background: #fafafa !important; }
|
||||
|
||||
.diff-early { color: #67c23a; font-weight: 600; }
|
||||
.diff-late { color: #f56c6c; font-weight: 600; }
|
||||
|
||||
@@ -238,7 +238,7 @@ export default {
|
||||
.jd-tab.active::after { content: ''; position: absolute; bottom: 0; left: 20px; right: 20px; height: 2px; background: #e4393c; }
|
||||
.tab-count { margin-left: 4px; font-size: 12px; color: #999; font-weight: 400; }
|
||||
|
||||
.jd-filter { display: flex; align-items: center; background: #f5f5f5; padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
|
||||
.jd-filter { display: flex; align-items: center; background: #ffffff; padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
|
||||
.filter-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.filter-right { margin-left: auto; }
|
||||
.filter-input { width: 150px; }
|
||||
@@ -247,7 +247,7 @@ export default {
|
||||
.jd-table { border: none !important; }
|
||||
|
||||
.amount { color: #e4393c; font-weight: 700; }
|
||||
.order-link { color: #005ea7; cursor: pointer; transition: color 0.2s; }
|
||||
.order-link { color: #e4393c; cursor: pointer; transition: color 0.2s; }
|
||||
.order-link:hover { color: #e4393c; text-decoration: underline; }
|
||||
|
||||
.diff-early { color: #67c23a; font-weight: 600; }
|
||||
@@ -258,7 +258,7 @@ export default {
|
||||
.detail-item { display: flex; border-bottom: 1px solid #f0f0f0; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f5f5; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #ffffff; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #333; flex: 1; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #333; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #e4393c; }
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ export default {
|
||||
.jd-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #f5f5f5;
|
||||
background: #ffffff;
|
||||
padding: 10px 16px;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 12px;
|
||||
@@ -291,9 +291,9 @@ export default {
|
||||
.detail-item { display: flex; border-bottom: 1px solid #f0f0f0; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f5f5; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #ffffff; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #333; flex: 1; }
|
||||
.detail-remark { padding: 8px 12px; background: #fff5f5; border: 1px solid #fce4e4; border-radius: 2px; font-size: 12px; color: #e6a23c; margin-bottom: 16px; }
|
||||
.detail-remark { padding: 8px 12px; background: #fafafa; border: 1px solid #fce4e4; border-radius: 2px; font-size: 12px; color: #e6a23c; margin-bottom: 16px; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #333; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #e4393c; }
|
||||
|
||||
/* ═══ Amount ═══ */
|
||||
@@ -303,7 +303,7 @@ export default {
|
||||
|
||||
/* ═══ Order Link ═══ */
|
||||
.order-link {
|
||||
color: #005ea7;
|
||||
color: #e4393c;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ export default {
|
||||
|
||||
/* ═══ JD Filter ═══ */
|
||||
.jd-filter {
|
||||
display: flex; align-items: center; background: #f5f5f5;
|
||||
display: flex; align-items: center; background: #ffffff;
|
||||
padding: 10px 16px; border-radius: 2px; margin-bottom: 12px; flex-wrap: wrap; gap: 8px;
|
||||
}
|
||||
.filter-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
@@ -283,15 +283,15 @@ export default {
|
||||
.detail-item { display: flex; border-bottom: 1px solid #f0f0f0; }
|
||||
.detail-item:nth-last-child(-n+2) { border-bottom: none; }
|
||||
.detail-item:nth-child(odd) { border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #f5f5f5; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dl { width: 90px; flex-shrink: 0; background: #ffffff; padding: 10px 12px; font-size: 12px; color: #666; font-weight: 600; border-right: 1px solid #f0f0f0; }
|
||||
.dv { padding: 10px 12px; font-size: 13px; color: #333; flex: 1; }
|
||||
.detail-remark { padding: 8px 12px; background: #fff5f5; border: 1px solid #fce4e4; border-radius: 2px; font-size: 12px; color: #e6a23c; margin-bottom: 16px; }
|
||||
.detail-remark { padding: 8px 12px; background: #fafafa; border: 1px solid #fce4e4; border-radius: 2px; font-size: 12px; color: #e6a23c; margin-bottom: 16px; }
|
||||
.section-bar { font-size: 13px; font-weight: 700; color: #333; padding: 6px 0 6px 10px; margin-bottom: 10px; border-left: 4px solid #e4393c; }
|
||||
|
||||
/* ═══ Urgency ═══ */
|
||||
.amount { color: #e4393c; font-weight: 700; }
|
||||
.urgent-overdue { color: #f56c6c; font-weight: 700; font-size: 12px; }
|
||||
.urgent-soon { color: #e6a23c; font-weight: 700; font-size: 12px; }
|
||||
.order-link { color: #005ea7; cursor: pointer; transition: color 0.2s; }
|
||||
.order-link { color: #e4393c; cursor: pointer; transition: color 0.2s; }
|
||||
.order-link:hover { color: #e4393c; text-decoration: underline; }
|
||||
</style>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width:110px">
|
||||
<el-option label="草稿" value="draft"/>
|
||||
<el-option label="审批中" value="10"/>
|
||||
<el-option label="已驳回" value="rejected"/>
|
||||
<el-option label="已确认" value="confirmed"/>
|
||||
<el-option label="已交付" value="delivered"/>
|
||||
<el-option label="已关闭" value="closed"/>
|
||||
@@ -29,7 +31,7 @@
|
||||
<el-table-column label="供应商" prop="supplierName" />
|
||||
<el-table-column label="RFQ" prop="rfqTitle" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="总金额" prop="totalAmount" width="130" align="right">
|
||||
<template slot-scope="scope"><span style="color:#409EFF;font-weight:bold">¥{{ scope.row.totalAmount }}</span></template>
|
||||
<template slot-scope="scope"><span style="color:#e4393c;font-weight:bold">¥{{ scope.row.totalAmount }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交货日期" prop="deliveryDate" width="110" />
|
||||
<el-table-column label="状态" width="100">
|
||||
@@ -38,10 +40,13 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" width="160" />
|
||||
<el-table-column label="操作" align="center" width="220">
|
||||
<el-table-column label="操作" align="center" width="320">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">详情</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-if="scope.row.status==='draft'">编辑</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-if="scope.row.status==='draft' || scope.row.status==='rejected'">编辑</el-button>
|
||||
<el-button size="mini" type="text" style="color:#E6A23C" @click="handleSubmitApproval(scope.row)" v-if="scope.row.status==='draft' || scope.row.status==='rejected'">提交审批</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" @click="handleApprove(scope.row)" v-if="scope.row.status==='10'">通过</el-button>
|
||||
<el-button size="mini" type="text" style="color:#F56C6C" @click="handleReject(scope.row)" v-if="scope.row.status==='10'">驳回</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" @click="handleConfirm(scope.row)" v-if="scope.row.status==='draft'">确认</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-if="scope.row.status==='draft'">删除</el-button>
|
||||
</template>
|
||||
@@ -90,7 +95,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="120" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span style="color:#409EFF">{{ ((scope.row.quantity||0)*(scope.row.unitPrice||0)).toFixed(2) }}</span>
|
||||
<span style="color:#e4393c">{{ ((scope.row.quantity||0)*(scope.row.unitPrice||0)).toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="60" align="center">
|
||||
@@ -174,8 +179,9 @@
|
||||
|
||||
<script>
|
||||
import { listPurchaseorder, getPurchaseorder, addPurchaseorder, updatePurchaseorder, confirmPurchaseorder, delPurchaseorder } from "@/api/bid/purchaseorder";
|
||||
import { submitApproval, approveBiz, rejectBiz } from "@/api/bid/approvalAction";
|
||||
import { listSupplier } from "@/api/bid/supplier";
|
||||
import logoImg from "@/assets/logo/logo.png";
|
||||
import logoImg from "@/assets/logo/logo.svg";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
|
||||
@@ -208,6 +214,20 @@ export default {
|
||||
this.$modal.confirm("确认提交采购单?").then(() => confirmPurchaseorder(row.poId))
|
||||
.then(() => { this.$modal.msgSuccess("已确认"); this.getList(); });
|
||||
},
|
||||
handleSubmitApproval(row) {
|
||||
this.$modal.confirm("确认提交审批?提交后单据状态变为审批中。").then(() => submitApproval("PURCHASE_ORDER", row.poId))
|
||||
.then(() => { this.$modal.msgSuccess("已提交审批"); this.getList(); });
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.$modal.confirm("确认通过该采购单?").then(() => approveBiz("PURCHASE_ORDER", row.poId))
|
||||
.then(() => { this.$modal.msgSuccess("审批通过"); this.getList(); });
|
||||
},
|
||||
handleReject(row) {
|
||||
this.$prompt("请输入驳回原因", "驳回", { inputPattern: /.+/, inputErrorMessage: "请填写原因" })
|
||||
.then(({ value }) => rejectBiz("PURCHASE_ORDER", row.poId, value))
|
||||
.then(() => { this.$modal.msgSuccess("已驳回"); this.getList(); })
|
||||
.catch(() => {});
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$modal.confirm("确认删除?").then(() => delPurchaseorder(row.poId)).then(() => { this.getList(); this.$modal.msgSuccess("删除成功"); });
|
||||
},
|
||||
@@ -240,8 +260,8 @@ export default {
|
||||
this.pdfLoading = false;
|
||||
}
|
||||
},
|
||||
statusType(s) { return { draft:"info", confirmed:"success", delivered:"", closed:"", disputed:"danger" }[s] || ""; },
|
||||
statusLabel(s) { return { draft:"草稿", confirmed:"已确认", delivered:"已交付", closed:"已关闭", disputed:"异议中" }[s] || s; }
|
||||
statusType(s) { return { draft:"info", "10":"warning", rejected:"danger", confirmed:"success", delivered:"", closed:"", disputed:"danger" }[s] || ""; },
|
||||
statusLabel(s) { return { draft:"草稿", "10":"审批中", rejected:"已驳回", confirmed:"已确认", delivered:"已交付", closed:"已关闭", disputed:"异议中" }[s] || s; }
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -271,7 +291,7 @@ export default {
|
||||
.pdf-company {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1171c4;
|
||||
color: #e4393c;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.pdf-doc-type {
|
||||
@@ -284,7 +304,7 @@ export default {
|
||||
color: #888;
|
||||
}
|
||||
.pdf-divider {
|
||||
border-top: 2px solid #1171c4;
|
||||
border-top: 2px solid #e4393c;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.pdf-meta-table {
|
||||
@@ -306,17 +326,17 @@ export default {
|
||||
color: #303133;
|
||||
}
|
||||
.amount {
|
||||
color: #409EFF;
|
||||
color: #e4393c;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
}
|
||||
.pdf-section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #1a2c4e;
|
||||
color: #333333;
|
||||
margin: 0 0 10px;
|
||||
padding-left: 8px;
|
||||
border-left: 4px solid #1171c4;
|
||||
border-left: 4px solid #e4393c;
|
||||
}
|
||||
.pdf-items-table {
|
||||
width: 100%;
|
||||
@@ -324,7 +344,7 @@ export default {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.pdf-items-table th {
|
||||
background: #1171c4;
|
||||
background: #e4393c;
|
||||
color: #fff;
|
||||
padding: 8px 10px;
|
||||
text-align: center;
|
||||
@@ -339,12 +359,12 @@ export default {
|
||||
background: #f9fbff;
|
||||
}
|
||||
.amount-cell {
|
||||
color: #409EFF;
|
||||
color: #e4393c;
|
||||
font-weight: 600;
|
||||
}
|
||||
.total-cell {
|
||||
font-size: 15px;
|
||||
background: #f0f7ff !important;
|
||||
background: #fafafa !important;
|
||||
}
|
||||
.pdf-footer {
|
||||
text-align: right;
|
||||
|
||||
@@ -120,6 +120,12 @@
|
||||
v-if="s.row.status==='draft'">编辑</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" icon="el-icon-upload2"
|
||||
@click="handleSubmit(s.row)" v-if="s.row.status==='draft'">提交</el-button>
|
||||
<el-button size="mini" type="text" style="color:#E6A23C" icon="el-icon-s-check"
|
||||
@click="handleSubmitApproval(s.row)" v-if="s.row.status==='draft' || s.row.status==='submitted'">提交审批</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A"
|
||||
@click="handleApprove(s.row)" v-if="s.row.status==='10'">审批通过</el-button>
|
||||
<el-button size="mini" type="text" style="color:#F56C6C"
|
||||
@click="handleApprovalReject(s.row)" v-if="s.row.status==='10'">审批驳回</el-button>
|
||||
<el-button size="mini" type="text" style="color:#67C23A" icon="el-icon-check"
|
||||
@click="handleAccept(s.row)" v-if="s.row.status==='submitted' && !isSupplier">采纳</el-button>
|
||||
<el-button size="mini" type="text" style="color:#F56C6C" icon="el-icon-close"
|
||||
@@ -185,7 +191,7 @@
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">
|
||||
<span style="font-weight:700;color:#1a2c4e">报价明细</span>
|
||||
<span style="font-weight:700;color:#333333">报价明细</span>
|
||||
<el-tooltip content="选择RFQ后可自动加载物料需求" placement="top">
|
||||
<i class="el-icon-question" style="margin-left:6px;color:#909399;cursor:pointer"></i>
|
||||
</el-tooltip>
|
||||
@@ -372,7 +378,8 @@ import { listQuotation, getQuotation, addQuotation, updateQuotation,
|
||||
import { listRfq, getRfqItems } from "@/api/bid/rfq";
|
||||
import { listSupplier } from "@/api/bid/supplier";
|
||||
import { addDelivery } from "@/api/bid/delivery";
|
||||
import logoImg from "@/assets/logo/logo.png";
|
||||
import { submitApproval, approveBiz, rejectBiz } from "@/api/bid/approvalAction";
|
||||
import logoImg from "@/assets/logo/logo.svg";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
|
||||
@@ -495,6 +502,20 @@ export default {
|
||||
this.$modal.confirm("确认提交报价?提交后不可修改").then(() => submitQuotation(row.quotationId))
|
||||
.then(() => { this.$modal.msgSuccess("提交成功"); this.getList(); });
|
||||
},
|
||||
handleSubmitApproval(row) {
|
||||
this.$modal.confirm("确认提交审批?").then(() => submitApproval("QUOTATION", row.quotationId))
|
||||
.then(() => { this.$modal.msgSuccess("已提交审批"); this.getList(); });
|
||||
},
|
||||
handleApprove(row) {
|
||||
this.$modal.confirm("确认通过该报价审批?").then(() => approveBiz("QUOTATION", row.quotationId))
|
||||
.then(() => { this.$modal.msgSuccess("审批通过"); this.getList(); });
|
||||
},
|
||||
handleApprovalReject(row) {
|
||||
this.$prompt("请输入驳回原因", "驳回", { inputPattern: /.+/, inputErrorMessage: "请填写原因" })
|
||||
.then(({ value }) => rejectBiz("QUOTATION", row.quotationId, value))
|
||||
.then(() => { this.$modal.msgSuccess("已驳回"); this.getList(); })
|
||||
.catch(() => {});
|
||||
},
|
||||
handleAccept(row) {
|
||||
this.$modal.confirm("确认采纳此报价?").then(() => acceptQuotation(row.quotationId))
|
||||
.then(() => { this.$modal.msgSuccess("已采纳"); this.getList(); });
|
||||
@@ -591,8 +612,8 @@ export default {
|
||||
} catch(e) { this.$message.error("导出失败:" + e.message); }
|
||||
finally { this.pdfLoading = false; }
|
||||
},
|
||||
statusType(s) { return { draft: "info", submitted: "warning", accepted: "success", rejected: "danger" }[s] || ""; },
|
||||
statusLabel(s) { return { draft: "草稿", submitted: "已提交", accepted: "已采纳", rejected: "已拒绝" }[s] || s; },
|
||||
statusType(s) { return { draft: "info", submitted: "warning", "10": "warning", accepted: "success", rejected: "danger" }[s] || ""; },
|
||||
statusLabel(s) { return { draft: "草稿", submitted: "已提交", "10": "审批中", accepted: "已采纳", rejected: "已拒绝" }[s] || s; },
|
||||
statusIcon(s) { return { draft: "el-icon-edit-outline", submitted: "el-icon-time", accepted: "el-icon-circle-check", rejected: "el-icon-circle-close" }[s] || "el-icon-document"; }
|
||||
}
|
||||
};
|
||||
@@ -606,7 +627,7 @@ export default {
|
||||
|
||||
/* ── 搜索 ── */
|
||||
.jd-filter-bar {
|
||||
background: #f5f5f5;
|
||||
background: #ffffff;
|
||||
padding: 12px 16px 4px;
|
||||
border-radius: 2px;
|
||||
::v-deep .el-form-item {
|
||||
@@ -677,6 +698,6 @@ export default {
|
||||
tbody tr:nth-child(even) td { background: #f9fbff; }
|
||||
}
|
||||
.amount-cell { color: #e4393c; font-weight: 600; }
|
||||
.total-cell { font-size: 15px; background: #fff5f5 !important; font-weight: 700; }
|
||||
.total-cell { font-size: 15px; background: #fafafa !important; font-weight: 700; }
|
||||
.pdf-footer { text-align: right; font-size: 11px; color: #aaa; margin-top: 10px; border-top: 1px solid #f0f2f5; padding-top: 8px; }
|
||||
</style>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const COLORS = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399']
|
||||
const COLORS = ['#e4393c', '#67C23A', '#E6A23C', '#F56C6C', '#909399']
|
||||
const CARD_NAMES = ['采购总额', 'RFQ总数', '采购单数', '活跃供应商']
|
||||
|
||||
export default {
|
||||
@@ -59,7 +59,7 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.kpi-card {
|
||||
border-left: 4px solid var(--kpi-accent, #409EFF);
|
||||
border-left: 4px solid var(--kpi-accent, #e4393c);
|
||||
border-radius: 6px;
|
||||
transition: transform .2s;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<el-card shadow="hover" style="margin-bottom:16px">
|
||||
<div slot="header" class="card-header">
|
||||
<span><i class="el-icon-data-board" style="color:#409EFF"></i> 月度预算 vs 实际成本</span>
|
||||
<span><i class="el-icon-data-board" style="color:#e4393c"></i> 月度预算 vs 实际成本</span>
|
||||
</div>
|
||||
<div ref="costTrendChart" style="height:350px;width:100%"></div>
|
||||
</el-card>
|
||||
@@ -104,7 +104,7 @@ export default {
|
||||
if (!this.data || !this.data.summary) return []
|
||||
const s = this.data.summary
|
||||
return [
|
||||
{ label: '预算总额', value: '¥' + this.formatMoney(s.totalExpected), color: '#409EFF' },
|
||||
{ label: '预算总额', value: '¥' + this.formatMoney(s.totalExpected), color: '#e4393c' },
|
||||
{ label: '实际采购', value: '¥' + this.formatMoney(s.totalActual), color: '#67C23A' },
|
||||
{ label: '节省金额', value: '¥' + this.formatMoney(s.savedAmount), color: Number(s.savedAmount) > 0 ? '#E6A23C' : '#C0C4CC', sub: Number(s.savedAmount) > 0 ? '为您节省了开支' : '' },
|
||||
{ label: '节省比例', value: (Number(s.savedRate) || 0) + '%', color: '#F56C6C', sub: '相比预算' }
|
||||
@@ -182,7 +182,7 @@ export default {
|
||||
{
|
||||
name: '实际金额', type: 'bar',
|
||||
data: items.map(d => Number(d.actualAmount) || 0),
|
||||
itemStyle: { color: '#409EFF', borderRadius: [4,4,0,0] },
|
||||
itemStyle: { color: '#e4393c', borderRadius: [4,4,0,0] },
|
||||
barWidth: '35%',
|
||||
label: { show: true, position: 'top', formatter: p => '¥' + Number(p.value).toLocaleString(), fontSize: 10 }
|
||||
}
|
||||
@@ -206,7 +206,7 @@ export default {
|
||||
center: ['50%', '55%'],
|
||||
label: { show: true, formatter: '{b}\n{d}%', fontSize: 11 },
|
||||
data: list.length ? list : [{ name: '暂无', value: 1 }],
|
||||
color: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#22a4ff']
|
||||
color: ['#e4393c', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#e4393c']
|
||||
}]
|
||||
})
|
||||
},
|
||||
@@ -258,12 +258,12 @@ export default {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
.page-title { font-size: 20px; font-weight: 700; color: #1a2c4e; }
|
||||
.page-title { font-size: 20px; font-weight: 700; color: #333333; }
|
||||
.loading-box { text-align: center; padding: 120px 0; color: #909399; font-size: 15px; i { font-size: 24px; margin-right: 8px; } }
|
||||
.card-header { font-weight: 600; color: #303133; font-size: 14px; }
|
||||
.cost-content { min-height: 400px; }
|
||||
.summary-card {
|
||||
border-left: 4px solid var(--s-accent, #409EFF);
|
||||
border-left: 4px solid var(--s-accent, #e4393c);
|
||||
border-radius: 6px;
|
||||
.s-label { font-size: 14px; color: #909399; margin-bottom: 8px; }
|
||||
.s-value { font-size: 24px; font-weight: 700; margin-bottom: 4px; }
|
||||
@@ -271,7 +271,7 @@ export default {
|
||||
}
|
||||
.cell-expected { color: #C0C4CC; }
|
||||
.cell-lowest { color: #E6A23C; font-weight: 600; }
|
||||
.cell-actual { color: #409EFF; font-weight: 600; }
|
||||
.cell-actual { color: #e4393c; font-weight: 600; }
|
||||
.cell-saved { color: #67C23A; font-weight: 700; }
|
||||
.cell-none { color: #C0C4CC; }
|
||||
.no-data { text-align: center; color: #C0C4CC; }
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<el-col :xs="24" :sm="14" style="margin-bottom:16px">
|
||||
<el-card shadow="hover">
|
||||
<div slot="header" class="card-header">
|
||||
<span><i class="el-icon-data-line" style="color:#409EFF"></i> 月度采购趋势</span>
|
||||
<span><i class="el-icon-data-line" style="color:#e4393c"></i> 月度采购趋势</span>
|
||||
</div>
|
||||
<div ref="trendChart" style="height:320px;width:100%"></div>
|
||||
</el-card>
|
||||
@@ -173,7 +173,7 @@ export default {
|
||||
series: [
|
||||
{
|
||||
name: '采购金额', type: 'bar', data: list.map(d => Number(d.amount) || 0),
|
||||
itemStyle: { color: '#409EFF', borderRadius: [4,4,0,0] },
|
||||
itemStyle: { color: '#e4393c', borderRadius: [4,4,0,0] },
|
||||
barWidth: '40%',
|
||||
label: { show: true, position: 'top', formatter: p => '¥' + Number(p.value).toLocaleString(), fontSize: 10 }
|
||||
},
|
||||
@@ -206,7 +206,7 @@ export default {
|
||||
label: { show: true, formatter: '{b}\n{d}%', fontSize: 11 },
|
||||
emphasis: { label: { show: true, fontSize: 14, fontWeight: 'bold' } },
|
||||
data: list.length ? list : [{ name: '暂无数据', value: 1 }],
|
||||
color: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399']
|
||||
color: ['#e4393c', '#67C23A', '#E6A23C', '#F56C6C', '#909399']
|
||||
}]
|
||||
})
|
||||
},
|
||||
@@ -246,8 +246,8 @@ export default {
|
||||
value: v,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#409EFF' },
|
||||
{ offset: 1, color: '#22a4ff' }
|
||||
{ offset: 0, color: '#e4393c' },
|
||||
{ offset: 1, color: '#e4393c' }
|
||||
]),
|
||||
borderRadius: [0, 6, 6, 0]
|
||||
}
|
||||
@@ -278,7 +278,7 @@ export default {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
.page-title { font-size: 20px; font-weight: 700; color: #1a2c4e; }
|
||||
.page-title { font-size: 20px; font-weight: 700; color: #333333; }
|
||||
.page-tip { font-size: 13px; color: #C0C4CC; }
|
||||
.loading-box { text-align: center; padding: 120px 0; color: #909399; font-size: 15px; i { font-size: 24px; margin-right: 8px; } }
|
||||
.card-header { font-weight: 600; color: #303133; font-size: 14px; }
|
||||
@@ -287,7 +287,7 @@ export default {
|
||||
.activity-list { max-height: 280px; overflow-y: auto; }
|
||||
.activity-item {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 0; border-bottom: 1px solid #f5f5f5;
|
||||
padding: 10px 0; border-bottom: 1px solid #ffffff;
|
||||
&:last-child { border-bottom: none; }
|
||||
}
|
||||
.act-time { font-size: 12px; color: #C0C4CC; white-space: nowrap; min-width: 120px; }
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div v-show="!loading && data" class="supplier-content">
|
||||
<el-card shadow="hover" style="margin-bottom:16px">
|
||||
<div slot="header" class="card-header">
|
||||
<span><i class="el-icon-s-custom" style="color:#409EFF"></i> 供应商综合评分排名</span>
|
||||
<span><i class="el-icon-s-custom" style="color:#e4393c"></i> 供应商综合评分排名</span>
|
||||
</div>
|
||||
<el-table :data="data.rankings || []" border size="small"
|
||||
highlight-current-row @current-change="onRowClick" style="cursor:pointer">
|
||||
@@ -47,7 +47,7 @@
|
||||
<el-col :xs="24" :sm="12" style="margin-bottom:16px">
|
||||
<el-card shadow="hover">
|
||||
<div slot="header" class="card-header">
|
||||
<span><i class="el-icon-s-marketing" style="color:#409EFF"></i> 供应商评价雷达图</span>
|
||||
<span><i class="el-icon-s-marketing" style="color:#e4393c"></i> 供应商评价雷达图</span>
|
||||
<span style="float:right;font-size:12px;color:#C0C4CC">点击表格行切换</span>
|
||||
</div>
|
||||
<div ref="radarChart" style="height:320px;width:100%"></div>
|
||||
@@ -166,7 +166,7 @@ export default {
|
||||
{ name: '服务', max: 5 },
|
||||
{ name: '价格', max: 5 }
|
||||
]
|
||||
const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#22a4ff']
|
||||
const colors = ['#e4393c', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#e4393c']
|
||||
|
||||
this.radarChart.setOption({
|
||||
legend: {
|
||||
@@ -296,7 +296,7 @@ export default {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
.page-title { font-size: 20px; font-weight: 700; color: #1a2c4e; }
|
||||
.page-title { font-size: 20px; font-weight: 700; color: #333333; }
|
||||
.loading-box { text-align: center; padding: 120px 0; color: #909399; font-size: 15px; i { font-size: 24px; margin-right: 8px; } }
|
||||
.card-header { font-weight: 600; color: #303133; font-size: 14px; }
|
||||
.supplier-content { min-height: 400px; }
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="参考来源" width="90" align="center">
|
||||
<template slot-scope="s">
|
||||
<span v-if="s.row._fromClient" style="color:#409eff;font-size:12px">甲方报价</span>
|
||||
<span v-if="s.row._fromClient" style="color:#e4393c;font-size:12px">甲方报价</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="60" align="center">
|
||||
@@ -198,7 +198,7 @@
|
||||
<script>
|
||||
import { getRfq, updateRfq } from "@/api/bid/rfq";
|
||||
import { listQuotation } from "@/api/bid/quotation";
|
||||
import logoImg from "@/assets/logo/logo.png";
|
||||
import logoImg from "@/assets/logo/logo.svg";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
|
||||
@@ -371,7 +371,7 @@ export default {
|
||||
.pdf-company {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #1171c4;
|
||||
color: #e4393c;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.pdf-doc-type {
|
||||
@@ -381,7 +381,7 @@ export default {
|
||||
}
|
||||
.pdf-header-no { font-size: 13px; color: #888; }
|
||||
.pdf-divider {
|
||||
border-top: 2px solid #1171c4;
|
||||
border-top: 2px solid #e4393c;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.pdf-meta-table {
|
||||
@@ -403,10 +403,10 @@ export default {
|
||||
.pdf-section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #1a2c4e;
|
||||
color: #333333;
|
||||
margin: 0 0 12px;
|
||||
padding-left: 8px;
|
||||
border-left: 4px solid #1171c4;
|
||||
border-left: 4px solid #e4393c;
|
||||
}
|
||||
.pdf-items-table {
|
||||
width: 100%;
|
||||
@@ -414,7 +414,7 @@ export default {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.pdf-items-table th {
|
||||
background: #1171c4;
|
||||
background: #e4393c;
|
||||
color: #fff;
|
||||
padding: 9px 12px;
|
||||
text-align: center;
|
||||
@@ -426,7 +426,7 @@ export default {
|
||||
text-align: center;
|
||||
}
|
||||
.pdf-items-table tbody tr:nth-child(even) td { background: #f9fbff; }
|
||||
.amount-cell { color: #409EFF; font-weight: 600; }
|
||||
.amount-cell { color: #e4393c; font-weight: 600; }
|
||||
.pdf-footer {
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<el-tooltip placement="top" effect="light" style="width:100%">
|
||||
<div slot="content">
|
||||
<div style="max-width:300px;line-height:1.8">
|
||||
<strong style="color:#409EFF">💡 使用说明:</strong><br/>
|
||||
<strong style="color:#e4393c">💡 使用说明:</strong><br/>
|
||||
1. 从下拉框选择已存在的甲方报价单<br/>
|
||||
2. 系统将<strong>自动导入</strong>该报价单中的所有物料明细<br/>
|
||||
3. 物料名称、规格、数量、预期单价会自动填充<br/>
|
||||
@@ -156,7 +156,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="参考来源" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row._fromClient" style="color:#409eff;font-size:12px">甲方报价</span>
|
||||
<span v-if="scope.row._fromClient" style="color:#e4393c;font-size:12px">甲方报价</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="60" align="center" v-if="!viewOnly">
|
||||
@@ -236,7 +236,7 @@
|
||||
<el-table-column type="index" width="44" label="#" />
|
||||
<el-table-column label="供应商" prop="supplierName" min-width="160">
|
||||
<template slot-scope="s">
|
||||
<el-avatar :size="26" style="background:#1171c4;margin-right:6px;vertical-align:middle;display:inline-flex">
|
||||
<el-avatar :size="26" style="background:#e4393c;margin-right:6px;vertical-align:middle;display:inline-flex">
|
||||
{{ (s.row.supplierName||'?').charAt(0) }}
|
||||
</el-avatar>
|
||||
<span style="vertical-align:middle">{{ s.row.supplierName }}</span>
|
||||
@@ -251,7 +251,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="130" align="right">
|
||||
<template slot-scope="s">
|
||||
<strong style="color:#409EFF">¥{{ s.row.totalAmount | money }}</strong>
|
||||
<strong style="color:#e4393c">¥{{ s.row.totalAmount | money }}</strong>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交期" prop="deliveryDays" width="70" align="center">
|
||||
@@ -494,13 +494,13 @@ export default {
|
||||
text-align: center;
|
||||
padding: 14px 10px;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, #f0f7ff, #e8f4fd);
|
||||
background: linear-gradient(135deg, #fafafa, #e8f4fd);
|
||||
border: 1px solid #d9ecff;
|
||||
}
|
||||
.summary-stats .stat-num {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #1171c4;
|
||||
color: #e4393c;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.summary-stats .stat-lbl {
|
||||
|
||||
@@ -680,7 +680,7 @@ export default {
|
||||
|
||||
.supplier-item.active {
|
||||
background-color: #ecf5ff;
|
||||
border-left: 3px solid #409eff;
|
||||
border-left: 3px solid #e4393c;
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
@@ -830,14 +830,14 @@ export default {
|
||||
}
|
||||
|
||||
.detail-tabs >>> .el-tabs__item.is-active {
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
background: #ecf5ff;
|
||||
border-radius: 4px 4px 0 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail-tabs >>> .el-tabs__item:hover {
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
}
|
||||
|
||||
.detail-tabs >>> .el-tabs__active-bar {
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<el-table-column label="采购单号" prop="poNo" width="150" />
|
||||
<el-table-column label="金额" prop="amount" width="130" align="right">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.amount" style="color:#409EFF">¥{{ scope.row.amount }}</span>
|
||||
<span v-if="scope.row.amount" style="color:#e4393c">¥{{ scope.row.amount }}</span>
|
||||
<span v-else style="color:#909399">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -103,7 +103,7 @@ export default {
|
||||
},
|
||||
supplierQuickActions() {
|
||||
return [
|
||||
{ label: "报价请求", icon: "el-icon-document", color: "#1171c4", path: "/rfq" },
|
||||
{ label: "报价请求", icon: "el-icon-document", color: "#e4393c", path: "/rfq" },
|
||||
{ label: "我的报价", icon: "el-icon-money", color: "#67c23a", path: "/quotation" }
|
||||
];
|
||||
}
|
||||
@@ -115,18 +115,18 @@ export default {
|
||||
recentPos: [],
|
||||
suppliers: [],
|
||||
statCards: [
|
||||
{ key: "suppliers", label: "供应商", icon: "el-icon-s-custom", color: "#1171c4" },
|
||||
{ key: "materials", label: "物料数", icon: "el-icon-goods", color: "#67c23a" },
|
||||
{ key: "rfqs", label: "询价单", icon: "el-icon-document", color: "#e6a23c" },
|
||||
{ key: "pos", label: "采购单", icon: "el-icon-shopping-cart-full", color: "#f56c6c" }
|
||||
{ key: "suppliers", label: "供应商", icon: "el-icon-s-custom", color: "#e4393c" },
|
||||
{ key: "materials", label: "物料数", icon: "el-icon-goods", color: "#e4393c" },
|
||||
{ key: "rfqs", label: "询价单", icon: "el-icon-document", color: "#e4393c" },
|
||||
{ key: "pos", label: "采购单", icon: "el-icon-shopping-cart-full", color: "#e4393c" }
|
||||
],
|
||||
quickActions: [
|
||||
{ label: "新建询价单", icon: "el-icon-edit", color: "#1171c4", path: "/rfq" },
|
||||
{ label: "物料管理", icon: "el-icon-goods", color: "#67c23a", path: "/material" },
|
||||
{ label: "智慧比价", icon: "el-icon-data-analysis", color: "#e6a23c", path: "/comparison" },
|
||||
{ label: "采购单", icon: "el-icon-shopping-cart-full", color: "#f56c6c", path: "/purchaseorder" },
|
||||
{ label: "供应商", icon: "el-icon-s-cooperation", color: "#9b59b6", path: "/supplier" },
|
||||
{ label: "供应商评价", icon: "el-icon-star-off", color: "#e67e22", path: "/evaluation" }
|
||||
{ label: "新建询价单", icon: "el-icon-edit", color: "#e4393c", path: "/rfq" },
|
||||
{ label: "物料管理", icon: "el-icon-goods", color: "#e4393c", path: "/material" },
|
||||
{ label: "智慧比价", icon: "el-icon-data-analysis", color: "#e4393c", path: "/comparison" },
|
||||
{ label: "采购单", icon: "el-icon-shopping-cart-full", color: "#e4393c", path: "/purchaseorder" },
|
||||
{ label: "供应商", icon: "el-icon-s-cooperation", color: "#e4393c", path: "/supplier" },
|
||||
{ label: "供应商评价", icon: "el-icon-star-off", color: "#e4393c", path: "/evaluation" }
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -159,10 +159,10 @@ export default {
|
||||
this.stats.pos = r.total || 0
|
||||
}).catch(() => {})
|
||||
},
|
||||
rfqStatusType(s) { return s === "open" ? "primary" : s === "closed" ? "info" : s === "awarded" ? "success" : "warning" },
|
||||
rfqStatusText(s) { return s === "open" ? "询价中" : s === "closed" ? "已关闭" : s === "awarded" ? "已定标" : (s || "-") },
|
||||
poStatusType(s) { return s === "approved" ? "success" : s === "pending" ? "warning" : s === "rejected" ? "danger" : "info" },
|
||||
poStatusText(s) { return s === "approved" ? "已审批" : s === "pending" ? "待审批" : s === "rejected" ? "已拒绝" : (s || "-") },
|
||||
rfqStatusType(s) { return ({ draft:"info", published:"warning", "10":"warning", closed:"", completed:"success", awarded:"success", open:"primary", rejected:"danger" })[s] || "info" },
|
||||
rfqStatusText(s) { return ({ draft:"草稿", published:"已发布", "10":"审批中", closed:"已关闭", completed:"已完成", awarded:"已定标", open:"询价中", rejected:"已驳回" })[s] || (s || "-") },
|
||||
poStatusType(s) { return ({ draft:"info", "10":"warning", rejected:"danger", confirmed:"success", delivered:"", closed:"info", disputed:"danger" })[s] || "info" },
|
||||
poStatusText(s) { return ({ draft:"草稿", "10":"审批中", rejected:"已驳回", confirmed:"已确认", delivered:"已交付", closed:"已关闭", disputed:"异议中" })[s] || (s || "-") },
|
||||
gradeType(g) { return g === "A" ? "success" : g === "B" ? "primary" : g === "C" ? "warning" : "info" }
|
||||
}
|
||||
}
|
||||
@@ -184,10 +184,10 @@ export default {
|
||||
}
|
||||
.panel-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
font-size: 14px; font-weight: 600; color: #1a2c4e;
|
||||
i { margin-right: 6px; color: #1171c4; }
|
||||
font-size: 14px; font-weight: 600; color: #333;
|
||||
i { margin-right: 6px; color: #e4393c; }
|
||||
}
|
||||
.panel-more { font-size: 12px; color: #1171c4; text-decoration: none; }
|
||||
.panel-more { font-size: 12px; color: #e4393c; text-decoration: none; }
|
||||
.panel-more:hover { text-decoration: underline; }
|
||||
.quick-actions { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; padding: 4px 0; }
|
||||
.quick-btn { display: flex; flex-direction: column; align-items: center; gap: 8px; text-decoration: none; padding: 16px 8px; border-radius: 8px; transition: background 0.2s; }
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<!-- Left branding panel -->
|
||||
<!-- 背景图 + 红色遮罩 -->
|
||||
<div class="login-bg"></div>
|
||||
<div class="login-mask"></div>
|
||||
|
||||
<!-- 左侧品牌介绍 -->
|
||||
<div class="login-left">
|
||||
<div class="brand-wrap">
|
||||
<div class="brand-logo-box">
|
||||
<img src="@/assets/logo/logo.png" class="brand-logo-img" alt="logo" />
|
||||
<img src="@/assets/logo/logo.svg" class="brand-logo-img" alt="logo" />
|
||||
</div>
|
||||
<h1 class="brand-name">福安德智慧报价平台</h1>
|
||||
<p class="brand-slogan">高效 · 精准 · 智能采购管理</p>
|
||||
@@ -23,19 +27,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="brand-circles">
|
||||
<div class="circle c1"></div>
|
||||
<div class="circle c2"></div>
|
||||
<div class="circle c3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right form panel -->
|
||||
<!-- 右侧表单 -->
|
||||
<div class="login-right">
|
||||
<div class="form-card">
|
||||
<div class="form-header">
|
||||
<h2 class="form-title">欢迎登录</h2>
|
||||
<p class="form-subtitle">福安德智慧报价系统</p>
|
||||
<div class="title-bar"></div>
|
||||
</div>
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on">
|
||||
<el-form-item prop="username">
|
||||
@@ -77,15 +77,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-form-item class="remember-row">
|
||||
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:loading="loading"
|
||||
type="primary"
|
||||
size="medium"
|
||||
style="width: 100%;"
|
||||
class="login-btn"
|
||||
@click.native.prevent="handleLogin"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
@@ -192,143 +191,173 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ---- Left panel ---- */
|
||||
/* 背景图(来源:Unsplash 商业摄影) */
|
||||
.login-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&w=1920&q=80");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
filter: blur(1px);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
/* 红色品牌渐变遮罩 */
|
||||
.login-mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(120deg, rgba(200, 22, 35, 0.88) 0%, rgba(228, 57, 60, 0.78) 55%, rgba(228, 57, 60, 0.35) 100%);
|
||||
}
|
||||
|
||||
/* ---- 左侧 ---- */
|
||||
.login-left {
|
||||
flex: 0 0 55%;
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #0d2b6b 0%, #1171c4 55%, #22a4ff 100%);
|
||||
z-index: 2;
|
||||
flex: 0 0 55%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.brand-wrap {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.brand-logo-box {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 20px;
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 24px;
|
||||
backdrop-filter: blur(4px);
|
||||
margin: 0 auto 26px;
|
||||
backdrop-filter: blur(6px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.brand-logo-img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
color: #ffffff;
|
||||
font-size: 28px;
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
margin: 0 0 10px;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
letter-spacing: 3px;
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.brand-slogan {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 15px;
|
||||
margin: 0 0 40px;
|
||||
letter-spacing: 4px;
|
||||
margin: 0 0 44px;
|
||||
letter-spacing: 6px;
|
||||
}
|
||||
|
||||
.brand-features {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.feature-row {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
i {
|
||||
font-size: 18px;
|
||||
color: #a8d8ff;
|
||||
font-size: 20px;
|
||||
color: #ffd9da;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decorative circles */
|
||||
.brand-circles {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.circle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
.c1 { width: 400px; height: 400px; bottom: -120px; left: -100px; }
|
||||
.c2 { width: 250px; height: 250px; top: -60px; right: -60px; }
|
||||
.c3 { width: 150px; height: 150px; top: 40%; right: 10%; background: rgba(255,255,255,0.04); }
|
||||
|
||||
/* ---- Right panel ---- */
|
||||
/* ---- 右侧 ---- */
|
||||
.login-right {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
flex: 1;
|
||||
background: #f5f7fa;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
width: 400px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 40px 40px 32px;
|
||||
box-shadow: 0 8px 32px rgba(17, 113, 196, 0.10);
|
||||
background: rgba(255, 255, 255, 0.97);
|
||||
border-radius: 14px;
|
||||
padding: 40px 40px 28px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.form-header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
margin-bottom: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #1a2c4e;
|
||||
color: #333;
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
.form-subtitle {
|
||||
font-size: 13px;
|
||||
color: #8c97a8;
|
||||
margin: 0;
|
||||
color: #999;
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
width: 40px;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #e4393c, #c81623);
|
||||
border-radius: 2px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
.el-form-item {
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.el-input__inner {
|
||||
height: 42px;
|
||||
::v-deep .el-input__inner {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
border-radius: 8px;
|
||||
padding-left: 38px;
|
||||
border: 1px solid #e5e5e5;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover { border-color: #e4393c; }
|
||||
&:focus { border-color: #e4393c; box-shadow: 0 0 0 3px rgba(228, 57, 60, 0.08); }
|
||||
}
|
||||
::v-deep .el-input__prefix {
|
||||
left: 12px;
|
||||
color: #c0c4cc;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,9 +365,7 @@ export default {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
.el-form-item, .el-input {
|
||||
flex: 1;
|
||||
}
|
||||
.el-input { flex: 1; }
|
||||
}
|
||||
|
||||
.code-img-box {
|
||||
@@ -346,23 +373,66 @@ export default {
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
height: 42px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.code-img {
|
||||
height: 42px;
|
||||
height: 44px;
|
||||
width: 120px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.remember-row {
|
||||
::v-deep .el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background: #e4393c;
|
||||
border-color: #e4393c;
|
||||
}
|
||||
::v-deep .el-checkbox__input.is-checked + .el-checkbox__label {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
/* 登录按钮 — 京东红渐变 */
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 6px;
|
||||
border: none !important;
|
||||
border-radius: 8px;
|
||||
color: #fff !important;
|
||||
background: linear-gradient(90deg, #e4393c 0%, #c81623 100%) !important;
|
||||
box-shadow: 0 4px 12px rgba(228, 57, 60, 0.35);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(90deg, #c81623 0%, #a30f1c 100%) !important;
|
||||
box-shadow: 0 6px 16px rgba(228, 57, 60, 0.45);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.right-footer {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
font-size: 12px;
|
||||
color: #b0bac6;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
letter-spacing: 0.5px;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 900px) {
|
||||
.login-left { display: none; }
|
||||
.login-right { flex: 1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -102,7 +102,7 @@ export default {
|
||||
line-height: 28px;
|
||||
}
|
||||
.read-stat strong {
|
||||
color: #409eff;
|
||||
color: #e4393c;
|
||||
font-size: 15px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
@@ -902,7 +902,7 @@ export default {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.select-item.sortable-chosen {
|
||||
border: 1px dashed #409eff;
|
||||
border: 1px dashed #e4393c;
|
||||
}
|
||||
.select-line-icon {
|
||||
line-height: 32px;
|
||||
@@ -929,7 +929,7 @@ export default {
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
background: #409eff;
|
||||
background: #e4393c;
|
||||
z-index: 1;
|
||||
border-radius: 0 0 6px 0;
|
||||
text-align: center;
|
||||
|
||||
@@ -147,7 +147,7 @@ import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/
|
||||
import { makeUpJs } from '@/utils/generator/js'
|
||||
import { makeUpCss } from '@/utils/generator/css'
|
||||
import { drawingDefaultValue, initDrawingDefaultValue, cleanDrawingDefaultValue } from '@/utils/generator/drawingDefault'
|
||||
import logo from '@/assets/logo/logo.png'
|
||||
import logo from '@/assets/logo/logo.svg'
|
||||
import CodeTypeDialog from './CodeTypeDialog'
|
||||
import DraggableItem from './DraggableItem'
|
||||
|
||||
@@ -482,7 +482,7 @@ export default {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.el-icon-plus{
|
||||
color: #409EFF;
|
||||
color: #e4393c;
|
||||
}
|
||||
.el-icon-delete{
|
||||
color: #157a0c;
|
||||
@@ -502,7 +502,7 @@ export default {
|
||||
}
|
||||
|
||||
$selectedColor: #f6f7ff;
|
||||
$lighterBlue: #409EFF;
|
||||
$lighterBlue: #e4393c;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
|
||||
55
sql/biz_approval_config.sql
Normal file
55
sql/biz_approval_config.sql
Normal file
@@ -0,0 +1,55 @@
|
||||
-- ----------------------------
|
||||
-- 审批负责人配置(或签)
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS biz_approval_config_user;
|
||||
DROP TABLE IF EXISTS biz_approval_config;
|
||||
|
||||
CREATE TABLE biz_approval_config (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
biz_type VARCHAR(64) NOT NULL COMMENT '业务类型: PURCHASE_ORDER/CLIENT_QUOTE/QUOTATION/DELIVERY_ORDER/ORDER_OBJECTION',
|
||||
biz_name VARCHAR(64) NOT NULL COMMENT '业务名称',
|
||||
sign_type CHAR(1) DEFAULT '1' COMMENT '审批方式: 1或签 2会签(预留)',
|
||||
enabled CHAR(1) DEFAULT '1' COMMENT '是否启用 0停用 1启用',
|
||||
remark VARCHAR(255) DEFAULT NULL,
|
||||
create_by VARCHAR(64) DEFAULT '',
|
||||
create_time DATETIME DEFAULT NULL,
|
||||
update_by VARCHAR(64) DEFAULT '',
|
||||
update_time DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_biz_type (biz_type)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审批负责人配置';
|
||||
|
||||
CREATE TABLE biz_approval_config_user (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
config_id BIGINT NOT NULL COMMENT 'biz_approval_config.id',
|
||||
user_id BIGINT NOT NULL COMMENT 'sys_user.user_id',
|
||||
sort_no INT DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_config (config_id),
|
||||
UNIQUE KEY uk_config_user (config_id, user_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='审批人(或签)';
|
||||
|
||||
-- 种子数据
|
||||
INSERT INTO biz_approval_config(biz_type,biz_name,sign_type,enabled,create_by,create_time) VALUES
|
||||
('PURCHASE_ORDER', '采购订单', '1','1','admin',NOW()),
|
||||
('CLIENT_QUOTE', '客户报价', '1','1','admin',NOW()),
|
||||
('QUOTATION', '供应商报价', '1','1','admin',NOW()),
|
||||
('DELIVERY_ORDER', '发货单', '1','1','admin',NOW()),
|
||||
('ORDER_OBJECTION', '订单异议', '1','1','admin',NOW());
|
||||
|
||||
-- 字典:审批状态(业务表 status 复用,10=审批中)
|
||||
DELETE FROM sys_dict_type WHERE dict_type='biz_approval_status';
|
||||
INSERT INTO sys_dict_type(dict_name,dict_type,status,create_by,create_time,remark)
|
||||
VALUES('审批状态','biz_approval_status','0','admin',NOW(),'业务单据审批中状态');
|
||||
|
||||
DELETE FROM sys_dict_data WHERE dict_type='biz_approval_status';
|
||||
INSERT INTO sys_dict_data(dict_sort,dict_label,dict_value,dict_type,css_class,list_class,is_default,status,create_by,create_time) VALUES
|
||||
(1,'审批中','10','biz_approval_status','','warning','N','0','admin',NOW()),
|
||||
(2,'已通过','confirmed','biz_approval_status','','success','N','0','admin',NOW()),
|
||||
(3,'已驳回','rejected','biz_approval_status','','danger','N','0','admin',NOW());
|
||||
|
||||
-- 菜单:审批配置
|
||||
DELETE FROM sys_menu WHERE menu_name='审批配置' AND parent_id=2000;
|
||||
INSERT INTO sys_menu(menu_name,parent_id,order_num,path,component,is_frame,is_cache,menu_type,visible,status,perms,icon,create_by,create_time)
|
||||
VALUES('审批配置',(SELECT menu_id FROM (SELECT menu_id FROM sys_menu WHERE menu_name='系统管理' AND parent_id=0 LIMIT 1) t),100,
|
||||
'approval','bid/approval/index',1,0,'C','0','0','bid:approval:list','tree','admin',NOW());
|
||||
80
sql/menu_reorganize.sql
Normal file
80
sql/menu_reorganize.sql
Normal file
@@ -0,0 +1,80 @@
|
||||
-- ═══════════════════════════════════════════════════════════════════
|
||||
-- 菜单重组:按业务模块分组到统一父菜单 + 新增审批配置
|
||||
-- ═══════════════════════════════════════════════════════════════════
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- ───────────────────────────────────────────────
|
||||
-- 1. 新建一级业务父菜单(M 类型 = 目录)
|
||||
-- ───────────────────────────────────────────────
|
||||
INSERT IGNORE INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
VALUES
|
||||
(2100, '基础数据', 0, 10, 'basedata', NULL, 1, 0, 'M', '0', '0', '', 'tree-table', 'admin', NOW()),
|
||||
(2110, '报价采购', 0, 20, 'quote', NULL, 1, 0, 'M', '0', '0', '', 'money', 'admin', NOW()),
|
||||
(2120, '订单履约', 0, 30, 'fulfill', NULL, 1, 0, 'M', '0', '0', '', 'shopping', 'admin', NOW()),
|
||||
(2130, '系统配置', 0, 90, 'bizconfig',NULL, 1, 0, 'M', '0', '0', '', 'tool', 'admin', NOW());
|
||||
|
||||
-- 统计分析 2019 已存在,保持 order_num
|
||||
UPDATE sys_menu SET order_num = 40 WHERE menu_id = 2019;
|
||||
|
||||
-- ───────────────────────────────────────────────
|
||||
-- 2. 已有子菜单按业务归类到新父菜单
|
||||
-- 用 perms 标识 ,避免依赖具体 menu_id
|
||||
-- ───────────────────────────────────────────────
|
||||
|
||||
-- 基础数据
|
||||
UPDATE sys_menu SET parent_id = 2100, order_num = 1 WHERE perms = 'bid:material:list';
|
||||
UPDATE sys_menu SET parent_id = 2100, order_num = 2 WHERE perms = 'bid:category:list';
|
||||
UPDATE sys_menu SET parent_id = 2100, order_num = 3 WHERE perms = 'bid:client:list';
|
||||
UPDATE sys_menu SET parent_id = 2100, order_num = 4 WHERE perms = 'bid:supplier:list';
|
||||
|
||||
-- 报价采购
|
||||
UPDATE sys_menu SET parent_id = 2110, order_num = 1 WHERE perms = 'bid:rfq:list';
|
||||
UPDATE sys_menu SET parent_id = 2110, order_num = 2 WHERE perms = 'bid:quotation:list';
|
||||
UPDATE sys_menu SET parent_id = 2110, order_num = 3 WHERE perms = 'bid:comparison:list';
|
||||
UPDATE sys_menu SET parent_id = 2110, order_num = 4 WHERE perms = 'bid:purchaseorder:list';
|
||||
UPDATE sys_menu SET parent_id = 2110, order_num = 5 WHERE perms = 'bid:clientquote:list';
|
||||
|
||||
-- 订单履约
|
||||
UPDATE sys_menu SET parent_id = 2120, order_num = 1 WHERE perms = 'bid:order:pending';
|
||||
UPDATE sys_menu SET parent_id = 2120, order_num = 2 WHERE perms = 'bid:order:transit';
|
||||
UPDATE sys_menu SET parent_id = 2120, order_num = 3 WHERE perms = 'bid:order:history';
|
||||
UPDATE sys_menu SET parent_id = 2120, order_num = 4 WHERE perms = 'bid:order:closeDate';
|
||||
UPDATE sys_menu SET parent_id = 2120, order_num = 5 WHERE perms = 'bid:objection:list';
|
||||
UPDATE sys_menu SET parent_id = 2120, order_num = 6 WHERE perms = 'bid:clientDelivery:list';
|
||||
|
||||
-- 兼容旧 perms:order:list(订单履约旧索引页)若存在,放履约组顶部
|
||||
UPDATE sys_menu SET parent_id = 2120, order_num = 0 WHERE perms = 'bid:order:list';
|
||||
|
||||
-- 系统配置 / 业务配置
|
||||
UPDATE sys_menu SET parent_id = 2130, order_num = 2 WHERE perms = 'bid:evaluation:list';
|
||||
UPDATE sys_menu SET parent_id = 2130, order_num = 3 WHERE perms = 'bid:transaction:list';
|
||||
UPDATE sys_menu SET parent_id = 2130, order_num = 4 WHERE perms = 'bid:operationlog:list';
|
||||
UPDATE sys_menu SET parent_id = 2130, order_num = 5 WHERE perms = 'bid:tenant:list';
|
||||
|
||||
-- ───────────────────────────────────────────────
|
||||
-- 3. 新增:审批配置(挂在系统配置下)
|
||||
-- ───────────────────────────────────────────────
|
||||
DELETE FROM sys_menu WHERE perms = 'bid:approval:list';
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
VALUES (2131, '审批配置', 2130, 1, 'approval', 'bid/approval/index', 1, 0, 'C', '0', '0', 'bid:approval:list', 'tree', 'admin', NOW());
|
||||
|
||||
-- 审批配置按钮权限
|
||||
DELETE FROM sys_menu WHERE perms IN ('bid:approval:edit','bid:approval:query');
|
||||
INSERT INTO sys_menu(menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time)
|
||||
VALUES
|
||||
('查询', 2131, 1, '', NULL, 1, 0, 'F', '0', '0', 'bid:approval:query', '#', 'admin', NOW()),
|
||||
('编辑', 2131, 2, '', NULL, 1, 0, 'F', '0', '0', 'bid:approval:edit', '#', 'admin', NOW());
|
||||
|
||||
-- 给 admin 角色(role_id=1)授权
|
||||
INSERT IGNORE INTO sys_role_menu(role_id, menu_id)
|
||||
SELECT 1, menu_id FROM sys_menu
|
||||
WHERE perms IN ('bid:approval:list','bid:approval:query','bid:approval:edit')
|
||||
OR menu_id IN (2100, 2110, 2120, 2130);
|
||||
|
||||
-- ───────────────────────────────────────────────
|
||||
-- 4. 验证:列出当前一级菜单
|
||||
-- ───────────────────────────────────────────────
|
||||
SELECT menu_id, menu_name, order_num, menu_type, perms
|
||||
FROM sys_menu
|
||||
WHERE parent_id = 0 AND visible = '0' AND status = '0'
|
||||
ORDER BY order_num;
|
||||
Reference in New Issue
Block a user