同步前端代码并提交相关修复
@@ -9,7 +9,9 @@ CONSOLE_LOG="$LOG_DIR/backend-console.log"
|
|||||||
PID_FILE="$LOG_DIR/backend-java.pid"
|
PID_FILE="$LOG_DIR/backend-java.pid"
|
||||||
TARGET_DIR="$ROOT_DIR/ruoyi-admin/target"
|
TARGET_DIR="$ROOT_DIR/ruoyi-admin/target"
|
||||||
JAR_NAME="ruoyi-admin.jar"
|
JAR_NAME="ruoyi-admin.jar"
|
||||||
SERVER_PORT=8080
|
JAVA_HOME_1_8="/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home"
|
||||||
|
JAVA_BIN="$JAVA_HOME_1_8/bin/java"
|
||||||
|
SERVER_PORT=63310
|
||||||
STOP_WAIT_SECONDS=30
|
STOP_WAIT_SECONDS=30
|
||||||
APP_MARKER="-Dloan.pricing.backend.root=$ROOT_DIR"
|
APP_MARKER="-Dloan.pricing.backend.root=$ROOT_DIR"
|
||||||
JAVA_OPTS="$APP_MARKER -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"
|
JAVA_OPTS="$APP_MARKER -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"
|
||||||
@@ -42,6 +44,18 @@ ensure_command() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setup_java_env() {
|
||||||
|
if [ ! -x "$JAVA_BIN" ]; then
|
||||||
|
log_error "未找到 JDK 1.8: $JAVA_BIN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
JAVA_HOME="$JAVA_HOME_1_8"
|
||||||
|
export JAVA_HOME
|
||||||
|
PATH="$JAVA_HOME/bin:$PATH"
|
||||||
|
export PATH
|
||||||
|
}
|
||||||
|
|
||||||
is_managed_backend_pid() {
|
is_managed_backend_pid() {
|
||||||
pid="$1"
|
pid="$1"
|
||||||
if [ -z "${pid:-}" ] || ! kill -0 "$pid" 2>/dev/null; then
|
if [ -z "${pid:-}" ] || ! kill -0 "$pid" 2>/dev/null; then
|
||||||
@@ -175,7 +189,7 @@ start_backend() {
|
|||||||
|
|
||||||
(
|
(
|
||||||
cd "$TARGET_DIR"
|
cd "$TARGET_DIR"
|
||||||
nohup java $JAVA_OPTS -jar "$JAR_NAME" >> "$CONSOLE_LOG" 2>&1 &
|
nohup "$JAVA_BIN" $JAVA_OPTS -jar "$JAR_NAME" >> "$CONSOLE_LOG" 2>&1 &
|
||||||
echo $! > "$PID_FILE"
|
echo $! > "$PID_FILE"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -232,8 +246,8 @@ restart_action() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
|
setup_java_env
|
||||||
ensure_command mvn
|
ensure_command mvn
|
||||||
ensure_command java
|
|
||||||
ensure_command lsof
|
ensure_command lsof
|
||||||
ensure_command ps
|
ensure_command ps
|
||||||
ensure_command tail
|
ensure_command tail
|
||||||
|
|||||||
17
doc/2026-04-15-AGENTS中文化实施记录.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# AGENTS 中文化实施记录
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
- 将根目录 `AGENTS.md` 中英文版仓库协作指南完整转换为中文表述。
|
||||||
|
- 保留原有章节结构与约束语义,仅调整为中文描述,未改变规则内容。
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
- 影响文件:`/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/AGENTS.md`
|
||||||
|
- 本次实施记录:`/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/doc/2026-04-15-AGENTS中文化实施记录.md`
|
||||||
|
|
||||||
|
## 说明
|
||||||
|
- 本次修改仅涉及文档文本,不涉及前端、后端逻辑或配置变更。
|
||||||
|
- 未执行构建与测试,原因是本次仅为文档中文化调整。
|
||||||
|
|
||||||
|
## 保存路径确认
|
||||||
|
- `AGENTS.md` 保存路径正确:`/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/AGENTS.md`
|
||||||
|
- 实施记录保存路径正确:`/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/doc/2026-04-15-AGENTS中文化实施记录.md`
|
||||||
19
doc/2026-04-15-后端启动脚本固定JDK18实施文档.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# 后端启动脚本固定 JDK 1.8 实施文档
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
- 在 `bin/restart_java_backend.sh` 中新增固定的 JDK 1.8 路径 `/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home`。
|
||||||
|
- 脚本启动时统一设置 `JAVA_HOME` 和 `PATH`,确保脚本内执行的 `mvn` 与 `java -jar` 都使用 JDK 1.8。
|
||||||
|
- 启动 Java 进程时改为显式调用固定路径下的 `bin/java`,避免落回系统默认的 JDK 21。
|
||||||
|
|
||||||
|
## 适配依据
|
||||||
|
- 当前机器默认 `JAVA_HOME` 为 JDK 21,不满足本项目需要固定 JDK 1.8 的要求。
|
||||||
|
- 当前机器已安装可用的 JDK 1.8:`/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home`。
|
||||||
|
- 本次按“写死当前机器 JDK 1.8 路径”的方案实现,不引入额外的自动探测逻辑。
|
||||||
|
|
||||||
|
## 验证方式
|
||||||
|
- 执行 `sh -n bin/restart_java_backend.sh` 校验脚本语法。
|
||||||
|
- 执行 `/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/bin/java -version` 校验固定路径下的 Java 为 1.8。
|
||||||
|
|
||||||
|
## 保存路径确认
|
||||||
|
- 脚本路径:`/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/bin/restart_java_backend.sh`
|
||||||
|
- 本次实施文档路径:`/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/doc/2026-04-15-后端启动脚本固定JDK18实施文档.md`
|
||||||
25
doc/2026-04-15-后端端口改为63310实施记录.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# 后端端口改为 `63310` 实施记录
|
||||||
|
|
||||||
|
## 修改时间
|
||||||
|
|
||||||
|
- 2026-04-15
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
- 将重启脚本 [bin/restart_java_backend.sh](/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/bin/restart_java_backend.sh) 中的 `SERVER_PORT` 从 `8080` 调整为 `63310`
|
||||||
|
- 核对后端环境配置,确认 [application-dev.yml](/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/ruoyi-admin/src/main/resources/application-dev.yml)、[application-pro.yml](/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/ruoyi-admin/src/main/resources/application-pro.yml)、[application-uat.yml](/Users/wkc/Desktop/loan-pricing/loan-pricing-jdk-1.8/ruoyi-admin/src/main/resources/application-uat.yml) 已经使用 `63310`
|
||||||
|
|
||||||
|
## 影响范围
|
||||||
|
|
||||||
|
- 后端重启脚本的端口监听检测
|
||||||
|
- 后端脚本状态查询结果
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 通过源码检索确认后端主环境配置和脚本统一指向 `63310`
|
||||||
|
- 通过 `bash -n bin/restart_java_backend.sh` 确认脚本语法正常
|
||||||
|
|
||||||
|
## 说明
|
||||||
|
|
||||||
|
- 当前开发、生产、UAT 环境配置原本已经是 `63310`
|
||||||
|
- 本次主要修正的是脚本里仍残留的 `8080`
|
||||||
16
doc/2026-04-15-直接复制源分支前端代码实施记录.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 2026-04-15 直接复制源分支前端代码实施记录
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
- 按用户要求,直接将 `origin/892-without-redis` 的 `ruoyi-ui` 整体复制到当前分支。
|
||||||
|
- 覆盖了源分支已有的前端页面、路由、API、布局、样式、构建配置与依赖锁文件。
|
||||||
|
- 同步删除了当前分支中源分支不存在的前端文件,确保前端代码基线与源分支保持一致。
|
||||||
|
|
||||||
|
## 涉及范围
|
||||||
|
|
||||||
|
- `ruoyi-ui/`
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 使用 `nvm` 切换 Node 版本后执行 `npm install`
|
||||||
|
- 执行 `npm run build:prod`
|
||||||
16
doc/2026-04-15-贷款定价密钥统一为密码传输配置后端实施记录.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 2026-04-15 贷款定价密钥统一为密码传输配置后端实施记录
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
- 将贷款定价敏感字段加解密服务统一为只读取 `security.password-transfer.key`。
|
||||||
|
- 删除对 `loan-pricing.sensitive.key` 的依赖,避免出现双配置源。
|
||||||
|
- 调整定向单元测试,校验未配置时抛出的错误信息改为 `security.password-transfer.key 未配置`。
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
|
||||||
|
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/SensitiveFieldCryptoService.java`
|
||||||
|
- `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/SensitiveFieldCryptoServiceTest.java`
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 执行 `mvn -pl ruoyi-admin -am -DskipTests package` 验证后端整体打包。
|
||||||
14
doc/2026-04-15-贷款定价敏感字段密钥配置修复后端实施记录.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# 2026-04-15 贷款定价敏感字段密钥配置修复后端实施记录
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
- 修复贷款定价首页列表请求返回 `loan-pricing.sensitive.key 未配置` 的问题。
|
||||||
|
- 在 `application.yml` 中新增 `loan-pricing.sensitive.key`,并直接复用现有的 `security.password-transfer.key`,保证贷款定价敏感字段加解密与密码传输使用同一把密钥。
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
|
||||||
|
- `ruoyi-admin/src/main/resources/application.yml`
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 执行 `mvn -pl ruoyi-admin -am -DskipTests package`,验证后端配置修改后可正常完成打包。
|
||||||
14
doc/2026-04-15-首页改为利率测算列表前端实施记录.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# 2026-04-15 首页改为利率测算列表前端实施记录
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
- 将前端默认首页路由从 `@/views/index` 调整为 `@/views/loanPricing/workflow/index`,使登录后的首页直接进入利率测算列表。
|
||||||
|
- 为利率测算详情页补充显式命名路由 `LoanPricingWorkflowDetail`,保证首页列表点击“查看”后可以正常进入详情页。
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
|
||||||
|
- `ruoyi-ui/src/router/index.js`
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 使用 `nvm` 切换 Node 版本后执行 `npm run build:prod`,验证前端路由配置可以正常通过生产构建。
|
||||||
18
doc/2026-04-15-首页面包屑与贷款定价密钥异常修复实施记录.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 2026-04-15 首页面包屑与贷款定价密钥异常修复实施记录
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
- 修复首页访问 `/index` 时面包屑重复注入首页节点,导致前端出现 `Duplicate keys detected: '/index'` 警告的问题。
|
||||||
|
- 修复贷款定价敏感字段加解密服务只读取 `loan-pricing.sensitive.key`,未在专用配置缺失时回退到 `security.password-transfer.key`,导致首页列表接口返回 `loan-pricing.sensitive.key 未配置` 的问题。
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
|
||||||
|
- `ruoyi-ui/src/components/Breadcrumb/index.vue`
|
||||||
|
- `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/SensitiveFieldCryptoService.java`
|
||||||
|
- `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/SensitiveFieldCryptoServiceTest.java`
|
||||||
|
- `ruoyi-ui/tests/home-breadcrumb-dedup.test.js`
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 已执行 `node ruoyi-ui/tests/home-breadcrumb-dedup.test.js`,结果通过。
|
||||||
|
- 已尝试执行 `mvn -pl ruoyi-loan-pricing -am -Dtest=SensitiveFieldCryptoServiceTest -Dsurefire.failIfNoSpecifiedTests=false test`,但当前 `ruoyi-loan-pricing` 模块存在与本次改动无关的编译问题,导致无法完成该条 Maven 定向验证。
|
||||||
2
pom.xml
@@ -35,7 +35,7 @@
|
|||||||
<logback.version>1.2.13</logback.version>
|
<logback.version>1.2.13</logback.version>
|
||||||
<spring-security.version>5.7.14</spring-security.version>
|
<spring-security.version>5.7.14</spring-security.version>
|
||||||
<spring-framework.version>5.3.39</spring-framework.version>
|
<spring-framework.version>5.3.39</spring-framework.version>
|
||||||
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
<mybatis-plus.version>3.5.1</mybatis-plus.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<!-- 依赖声明 -->
|
<!-- 依赖声明 -->
|
||||||
|
|||||||
@@ -1,3 +1,22 @@
|
|||||||
|
# 开发环境配置
|
||||||
|
server:
|
||||||
|
# 服务器的HTTP端口,默认为63310
|
||||||
|
port: 63310
|
||||||
|
servlet:
|
||||||
|
# 应用的访问路径
|
||||||
|
context-path: /
|
||||||
|
tomcat:
|
||||||
|
# tomcat的URI编码
|
||||||
|
uri-encoding: UTF-8
|
||||||
|
# 连接数满后的排队数,默认为100
|
||||||
|
accept-count: 1000
|
||||||
|
threads:
|
||||||
|
# tomcat最大线程数,默认为200
|
||||||
|
max: 800
|
||||||
|
# Tomcat启动初始化的线程数,默认值10
|
||||||
|
min-spare: 100
|
||||||
|
|
||||||
|
|
||||||
# 数据源配置
|
# 数据源配置
|
||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
@@ -58,4 +77,7 @@ spring:
|
|||||||
merge-sql: true
|
merge-sql: true
|
||||||
wall:
|
wall:
|
||||||
config:
|
config:
|
||||||
multi-statement-allow: true
|
multi-statement-allow: true
|
||||||
|
|
||||||
|
model:
|
||||||
|
url: http://localhost:63310/rate/pricing/mock/invokeModel
|
||||||
|
|||||||
@@ -81,6 +81,4 @@ spring:
|
|||||||
model:
|
model:
|
||||||
url: http://64.202.32.40:8083/api/service/interface/invokeService/syllcs
|
url: http://64.202.32.40:8083/api/service/interface/invokeService/syllcs
|
||||||
|
|
||||||
security:
|
|
||||||
password-transfer:
|
|
||||||
key: "1234567890abcdef"
|
|
||||||
|
|||||||
@@ -81,6 +81,4 @@ spring:
|
|||||||
model:
|
model:
|
||||||
url: http://localhost:63310/rate/pricing/mock/invokeModel
|
url: http://localhost:63310/rate/pricing/mock/invokeModel
|
||||||
|
|
||||||
security:
|
|
||||||
password-transfer:
|
|
||||||
key: "1234567890abcdef"
|
|
||||||
|
|||||||
@@ -13,23 +13,6 @@ ruoyi:
|
|||||||
# 验证码类型 math 数字计算 char 字符验证
|
# 验证码类型 math 数字计算 char 字符验证
|
||||||
captchaType: math
|
captchaType: math
|
||||||
|
|
||||||
# 开发环境配置
|
|
||||||
server:
|
|
||||||
# 服务器的HTTP端口,默认为8080
|
|
||||||
port: 8080
|
|
||||||
servlet:
|
|
||||||
# 应用的访问路径
|
|
||||||
context-path: /
|
|
||||||
tomcat:
|
|
||||||
# tomcat的URI编码
|
|
||||||
uri-encoding: UTF-8
|
|
||||||
# 连接数满后的排队数,默认为100
|
|
||||||
accept-count: 1000
|
|
||||||
threads:
|
|
||||||
# tomcat最大线程数,默认为200
|
|
||||||
max: 800
|
|
||||||
# Tomcat启动初始化的线程数,默认值10
|
|
||||||
min-spare: 100
|
|
||||||
|
|
||||||
# 日志配置
|
# 日志配置
|
||||||
logging:
|
logging:
|
||||||
@@ -60,14 +43,14 @@ spring:
|
|||||||
max-file-size: 10MB
|
max-file-size: 10MB
|
||||||
# 设置总上传的文件大小
|
# 设置总上传的文件大小
|
||||||
max-request-size: 20MB
|
max-request-size: 20MB
|
||||||
# 服务模块
|
# 服务模块
|
||||||
devtools:
|
devtools:
|
||||||
restart:
|
restart:
|
||||||
# 热部署开关
|
# 热部署开关
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
# token配置
|
# token配置
|
||||||
token:
|
token:
|
||||||
# 令牌自定义标识
|
# 令牌自定义标识
|
||||||
header: Authorization
|
header: Authorization
|
||||||
# 令牌密钥
|
# 令牌密钥
|
||||||
@@ -75,8 +58,8 @@ token:
|
|||||||
# 令牌有效期(默认30分钟)
|
# 令牌有效期(默认30分钟)
|
||||||
expireTime: 30
|
expireTime: 30
|
||||||
|
|
||||||
# MyBatis配置
|
# MyBatis-Plus配置
|
||||||
mybatis:
|
mybatis-plus:
|
||||||
# 搜索指定包别名
|
# 搜索指定包别名
|
||||||
typeAliasesPackage: com.ruoyi.**.domain
|
typeAliasesPackage: com.ruoyi.**.domain
|
||||||
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
# 配置mapper的扫描,找到所有的mapper.xml映射文件
|
||||||
@@ -98,21 +81,21 @@ swagger:
|
|||||||
pathMapping: /dev-api
|
pathMapping: /dev-api
|
||||||
|
|
||||||
# 防盗链配置
|
# 防盗链配置
|
||||||
referer:
|
referer:
|
||||||
# 防盗链开关
|
# 防盗链开关
|
||||||
enabled: false
|
enabled: false
|
||||||
# 允许的域名列表
|
# 允许的域名列表
|
||||||
allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
|
allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
|
||||||
|
|
||||||
security:
|
# 防止XSS攻击
|
||||||
password-transfer:
|
xss:
|
||||||
key: "1234567890abcdef"
|
|
||||||
|
|
||||||
# 防止XSS攻击
|
|
||||||
xss:
|
|
||||||
# 过滤开关
|
# 过滤开关
|
||||||
enabled: true
|
enabled: true
|
||||||
# 排除链接(多个用逗号分隔)
|
# 排除链接(多个用逗号分隔)
|
||||||
excludes: /system/notice
|
excludes: /system/notice
|
||||||
# 匹配链接
|
# 匹配链接
|
||||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||||
|
|
||||||
|
security:
|
||||||
|
password-transfer:
|
||||||
|
key: "1234567890abcdef"
|
||||||
@@ -35,11 +35,16 @@
|
|||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- pagehelper 分页插件 -->
|
<!-- pagehelper 分页插件 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.pagehelper</groupId>
|
<groupId>com.github.pagehelper</groupId>
|
||||||
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 自定义验证注解 -->
|
<!-- 自定义验证注解 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -39,15 +39,20 @@ public class DictUtils
|
|||||||
* @param key 参数键
|
* @param key 参数键
|
||||||
* @return dictDatas 字典数据列表
|
* @return dictDatas 字典数据列表
|
||||||
*/
|
*/
|
||||||
public static List<SysDictData> getDictCache(String key)
|
public static List<SysDictData> getDictCache(String key)
|
||||||
{
|
{
|
||||||
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
Object cacheObject = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
|
||||||
if (StringUtils.isNotNull(arrayCache))
|
if (cacheObject instanceof List)
|
||||||
{
|
{
|
||||||
return arrayCache.toList(SysDictData.class);
|
return (List<SysDictData>) cacheObject;
|
||||||
}
|
}
|
||||||
return null;
|
if (cacheObject instanceof JSONArray)
|
||||||
}
|
{
|
||||||
|
JSONArray arrayCache = (JSONArray) cacheObject;
|
||||||
|
return arrayCache.toList(SysDictData.class);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据字典类型和字典值获取字典标签
|
* 根据字典类型和字典值获取字典标签
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||||
|
import com.ruoyi.common.core.domain.entity.SysDictData;
|
||||||
|
import com.ruoyi.common.core.redis.RedisCache;
|
||||||
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
class DictUtilsTest
|
||||||
|
{
|
||||||
|
@AfterEach
|
||||||
|
void tearDown()
|
||||||
|
{
|
||||||
|
ReflectionTestUtils.setField(SpringUtils.class, "beanFactory", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnDictListWhenCacheStoresArrayList()
|
||||||
|
{
|
||||||
|
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
|
beanFactory.registerSingleton("redisCache", new RedisCache(new InMemoryCacheStore()));
|
||||||
|
ReflectionTestUtils.setField(SpringUtils.class, "beanFactory", beanFactory);
|
||||||
|
|
||||||
|
SysDictData dictData = new SysDictData();
|
||||||
|
dictData.setDictType("sys_normal_disable");
|
||||||
|
dictData.setDictLabel("正常");
|
||||||
|
dictData.setDictValue("0");
|
||||||
|
|
||||||
|
DictUtils.setDictCache("sys_normal_disable", Collections.singletonList(dictData));
|
||||||
|
|
||||||
|
List<SysDictData> dictCache = DictUtils.getDictCache("sys_normal_disable");
|
||||||
|
|
||||||
|
assertNotNull(dictCache);
|
||||||
|
assertEquals(1, dictCache.size());
|
||||||
|
assertEquals("正常", dictCache.get(0).getDictLabel());
|
||||||
|
assertEquals("0", dictCache.get(0).getDictValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,15 +35,10 @@
|
|||||||
<artifactId>druid-spring-boot-starter</artifactId>
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 验证码 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>pro.fessional</groupId>
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>kaptcha</artifactId>
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 验证码 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>pro.fessional</groupId>
|
|
||||||
<artifactId>kaptcha</artifactId>
|
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<artifactId>servlet-api</artifactId>
|
<artifactId>servlet-api</artifactId>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.ruoyi.framework.config;
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import com.baomidou.mybatisplus.annotation.DbType;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -30,12 +27,4 @@ public class ApplicationConfig
|
|||||||
{
|
{
|
||||||
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
|
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public MybatisPlusInterceptor mybatisPlusInterceptor()
|
|
||||||
{
|
|
||||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
|
||||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
|
||||||
return interceptor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
package com.ruoyi.framework.config;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
|
||||||
import org.apache.ibatis.io.VFS;
|
|
||||||
import org.apache.ibatis.plugin.Interceptor;
|
|
||||||
import org.apache.ibatis.session.SqlSessionFactory;
|
|
||||||
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
|
||||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
|
||||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
|
||||||
import org.springframework.core.type.classreading.MetadataReader;
|
|
||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mybatis支持*匹配扫描包
|
|
||||||
*
|
|
||||||
* @author ruoyi
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class MyBatisConfig
|
|
||||||
{
|
|
||||||
@Autowired
|
|
||||||
private Environment env;
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private Interceptor[] interceptors;
|
|
||||||
|
|
||||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
|
||||||
|
|
||||||
public static String setTypeAliasesPackage(String typeAliasesPackage)
|
|
||||||
{
|
|
||||||
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
|
|
||||||
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
|
|
||||||
List<String> allResult = new ArrayList<String>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (String aliasesPackage : typeAliasesPackage.split(","))
|
|
||||||
{
|
|
||||||
List<String> result = new ArrayList<String>();
|
|
||||||
aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
|
|
||||||
+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
|
|
||||||
Resource[] resources = resolver.getResources(aliasesPackage);
|
|
||||||
if (resources != null && resources.length > 0)
|
|
||||||
{
|
|
||||||
MetadataReader metadataReader = null;
|
|
||||||
for (Resource resource : resources)
|
|
||||||
{
|
|
||||||
if (resource.isReadable())
|
|
||||||
{
|
|
||||||
metadataReader = metadataReaderFactory.getMetadataReader(resource);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result.size() > 0)
|
|
||||||
{
|
|
||||||
HashSet<String> hashResult = new HashSet<String>(result);
|
|
||||||
allResult.addAll(hashResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allResult.size() > 0)
|
|
||||||
{
|
|
||||||
typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return typeAliasesPackage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Resource[] resolveMapperLocations(String[] mapperLocations)
|
|
||||||
{
|
|
||||||
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
|
|
||||||
List<Resource> resources = new ArrayList<Resource>();
|
|
||||||
if (mapperLocations != null)
|
|
||||||
{
|
|
||||||
for (String mapperLocation : mapperLocations)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Resource[] mappers = resourceResolver.getResources(mapperLocation);
|
|
||||||
resources.addAll(Arrays.asList(mappers));
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resources.toArray(new Resource[resources.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
|
|
||||||
{
|
|
||||||
String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
|
|
||||||
String mapperLocations = env.getProperty("mybatis.mapperLocations");
|
|
||||||
String configLocation = env.getProperty("mybatis.configLocation");
|
|
||||||
typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
|
|
||||||
VFS.addImplClass(SpringBootVFS.class);
|
|
||||||
|
|
||||||
final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
|
|
||||||
sessionFactory.setDataSource(dataSource);
|
|
||||||
sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
|
|
||||||
sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
|
|
||||||
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
|
|
||||||
if (interceptors != null)
|
|
||||||
{
|
|
||||||
sessionFactory.setPlugins(interceptors);
|
|
||||||
}
|
|
||||||
return sessionFactory.getObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MybatisPlusConfig
|
||||||
|
{
|
||||||
|
@Bean
|
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor()
|
||||||
|
{
|
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
|
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,13 +14,13 @@ public class LoanPricingSensitiveDisplayService
|
|||||||
}
|
}
|
||||||
if (custName.contains("公司") && custName.length() > 4)
|
if (custName.contains("公司") && custName.length() > 4)
|
||||||
{
|
{
|
||||||
return custName.substring(0, 2) + "*".repeat(custName.length() - 4) + custName.substring(custName.length() - 2);
|
return custName.substring(0, 2) + repeatMask(custName.length() - 4) + custName.substring(custName.length() - 2);
|
||||||
}
|
}
|
||||||
if (custName.length() == 1)
|
if (custName.length() == 1)
|
||||||
{
|
{
|
||||||
return custName;
|
return custName;
|
||||||
}
|
}
|
||||||
return custName.substring(0, 1) + "*".repeat(custName.length() - 1);
|
return custName.substring(0, 1) + repeatMask(custName.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String maskIdNum(String idNum)
|
public String maskIdNum(String idNum)
|
||||||
@@ -31,16 +31,30 @@ public class LoanPricingSensitiveDisplayService
|
|||||||
}
|
}
|
||||||
if (idNum.startsWith("91") && idNum.length() == 18)
|
if (idNum.startsWith("91") && idNum.length() == 18)
|
||||||
{
|
{
|
||||||
return idNum.substring(0, 2) + "*".repeat(13) + idNum.substring(idNum.length() - 3);
|
return idNum.substring(0, 2) + repeatMask(13) + idNum.substring(idNum.length() - 3);
|
||||||
}
|
}
|
||||||
if (idNum.matches("\\d{17}[\\dXx]"))
|
if (idNum.matches("\\d{17}[\\dXx]"))
|
||||||
{
|
{
|
||||||
return idNum.substring(0, 4) + "*".repeat(8) + idNum.substring(idNum.length() - 4);
|
return idNum.substring(0, 4) + repeatMask(8) + idNum.substring(idNum.length() - 4);
|
||||||
}
|
}
|
||||||
if (idNum.length() > 5)
|
if (idNum.length() > 5)
|
||||||
{
|
{
|
||||||
return idNum.substring(0, 2) + "*".repeat(idNum.length() - 5) + idNum.substring(idNum.length() - 3);
|
return idNum.substring(0, 2) + repeatMask(idNum.length() - 5) + idNum.substring(idNum.length() - 3);
|
||||||
}
|
}
|
||||||
return "*".repeat(idNum.length());
|
return repeatMask(idNum.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String repeatMask(int count)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
StringBuilder builder = new StringBuilder(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
builder.append('*');
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class SensitiveFieldCryptoService
|
|||||||
{
|
{
|
||||||
private final String key;
|
private final String key;
|
||||||
|
|
||||||
public SensitiveFieldCryptoService(@Value("${loan-pricing.sensitive.key:}") String key)
|
public SensitiveFieldCryptoService(@Value("${security.password-transfer.key:}") String key)
|
||||||
{
|
{
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ public class SensitiveFieldCryptoService
|
|||||||
{
|
{
|
||||||
if (!StringUtils.hasText(key))
|
if (!StringUtils.hasText(key))
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("loan-pricing.sensitive.key 未配置");
|
throw new IllegalStateException("security.password-transfer.key 未配置");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class SensitiveFieldCryptoServiceTest
|
|||||||
{
|
{
|
||||||
SensitiveFieldCryptoService service = new SensitiveFieldCryptoService("");
|
SensitiveFieldCryptoService service = new SensitiveFieldCryptoService("");
|
||||||
|
|
||||||
assertThrows(IllegalStateException.class, () -> service.encrypt("张三"));
|
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> service.encrypt("张三"));
|
||||||
|
assertEquals("security.password-transfer.key 未配置", exception.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class LoanPricingWorkflowServiceImplTest
|
|||||||
row.setCalculateRate("6.15");
|
row.setCalculateRate("6.15");
|
||||||
|
|
||||||
Page<LoanPricingWorkflowListVO> pageResult = new Page<>(1, 10);
|
Page<LoanPricingWorkflowListVO> pageResult = new Page<>(1, 10);
|
||||||
pageResult.setRecords(java.util.List.of(row));
|
pageResult.setRecords(Collections.singletonList(row));
|
||||||
|
|
||||||
when(loanPricingWorkflowMapper.selectWorkflowPageWithRates(any(), any())).thenReturn(pageResult);
|
when(loanPricingWorkflowMapper.selectWorkflowPageWithRates(any(), any())).thenReturn(pageResult);
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
# 告诉EditorConfig插件,这是根文件,不用继续往上查找
|
# 告诉EditorConfig插件,这是根文件,不用继续往上查找
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
# 匹配全部文件
|
# 匹配全部文件
|
||||||
[*]
|
[*]
|
||||||
# 设置字符集
|
# 设置字符集
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
# 缩进风格,可选space、tab
|
# 缩进风格,可选space、tab
|
||||||
indent_style = space
|
indent_style = space
|
||||||
# 缩进的空格数
|
# 缩进的空格数
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
# 结尾换行符,可选lf、cr、crlf
|
# 结尾换行符,可选lf、cr、crlf
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
# 在文件结尾插入新行
|
# 在文件结尾插入新行
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
# 删除一行中的前后空格
|
# 删除一行中的前后空格
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
# 匹配md结尾的文件
|
# 匹配md结尾的文件
|
||||||
[*.md]
|
[*.md]
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = 若依管理系统
|
VUE_APP_TITLE = 上虞利率定价系统
|
||||||
|
|
||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
ENV = 'development'
|
ENV = 'development'
|
||||||
|
|
||||||
# 若依管理系统/开发环境
|
# 若依管理系统/开发环境
|
||||||
VUE_APP_BASE_API = '/dev-api'
|
VUE_APP_BASE_API = '/dev-api'
|
||||||
VUE_APP_PASSWORD_TRANSFER_KEY = '1234567890abcdef'
|
VUE_APP_PASSWORD_TRANSFER_KEY = '1234567890abcdef'
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = 若依管理系统
|
VUE_APP_TITLE = 上虞利率定价系统
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
ENV = 'production'
|
ENV = 'production'
|
||||||
|
|
||||||
# 若依管理系统/生产环境
|
# 若依管理系统/生产环境
|
||||||
VUE_APP_BASE_API = '/prod-api'
|
VUE_APP_BASE_API = '/prod-api'
|
||||||
VUE_APP_PASSWORD_TRANSFER_KEY = '1234567890abcdef'
|
VUE_APP_PASSWORD_TRANSFER_KEY = '1234567890abcdef'
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = 若依管理系统
|
VUE_APP_TITLE = 上虞利率定价系统
|
||||||
|
|
||||||
BABEL_ENV = production
|
BABEL_ENV = production
|
||||||
|
|
||||||
NODE_ENV = production
|
NODE_ENV = production
|
||||||
|
|
||||||
# 测试环境配置
|
# 测试环境配置
|
||||||
ENV = 'staging'
|
ENV = 'staging'
|
||||||
|
|
||||||
# 若依管理系统/测试环境
|
# 若依管理系统/测试环境
|
||||||
VUE_APP_BASE_API = '/stage-api'
|
VUE_APP_BASE_API = '/stage-api'
|
||||||
|
VUE_APP_PASSWORD_TRANSFER_KEY = '1234567890abcdef'
|
||||||
|
|||||||
46
ruoyi-ui/.gitignore
vendored
@@ -1,23 +1,23 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
**/*.log
|
**/*.log
|
||||||
|
|
||||||
tests/**/coverage/
|
tests/**/coverage/
|
||||||
tests/e2e/reports
|
tests/e2e/reports
|
||||||
selenium-debug.log
|
selenium-debug.log
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
*.suo
|
*.suo
|
||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 克隆项目
|
# 克隆项目
|
||||||
git clone https://gitee.com/y_project/RuoYi-Vue
|
git clone https://gitee.com/y_project/RuoYi-Vue
|
||||||
|
|
||||||
# 进入项目目录
|
# 进入项目目录
|
||||||
cd ruoyi-ui
|
cd ruoyi-ui
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
|
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
|
||||||
npm install --registry=https://registry.npmmirror.com
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
# 启动服务
|
# 启动服务
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
浏览器访问 http://localhost:80
|
浏览器访问 http://localhost:80
|
||||||
|
|
||||||
## 发布
|
## 发布
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 构建测试环境
|
# 构建测试环境
|
||||||
npm run build:stage
|
npm run build:stage
|
||||||
|
|
||||||
# 构建生产环境
|
# 构建生产环境
|
||||||
npm run build:prod
|
npm run build:prod
|
||||||
```
|
```
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [
|
||||||
// https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
|
// https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
|
||||||
'@vue/cli-plugin-babel/preset'
|
'@vue/cli-plugin-babel/preset'
|
||||||
],
|
],
|
||||||
'env': {
|
'env': {
|
||||||
'development': {
|
'development': {
|
||||||
// babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
|
// babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
|
||||||
// This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
|
// This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
|
||||||
'plugins': ['dynamic-import-node']
|
'plugins': ['dynamic-import-node']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo.
|
echo.
|
||||||
echo [信息] 安装Web工程,生成node_modules文件。
|
echo [信息] 安装Web工程,生成node_modules文件。
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
%~d0
|
%~d0
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
npm install --registry=https://registry.npmmirror.com
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
pause
|
pause
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo.
|
echo.
|
||||||
echo [信息] 使用 Vue CLI 命令运行 Web 工程。
|
echo [信息] 使用 Vue CLI 命令运行 Web 工程。
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
%~d0
|
%~d0
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
pause
|
pause
|
||||||
@@ -1,35 +1,35 @@
|
|||||||
const { run } = require('runjs')
|
const { run } = require('runjs')
|
||||||
const chalk = require('chalk')
|
const chalk = require('chalk')
|
||||||
const config = require('../vue.config.js')
|
const config = require('../vue.config.js')
|
||||||
const rawArgv = process.argv.slice(2)
|
const rawArgv = process.argv.slice(2)
|
||||||
const args = rawArgv.join(' ')
|
const args = rawArgv.join(' ')
|
||||||
|
|
||||||
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
|
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
|
||||||
const report = rawArgv.includes('--report')
|
const report = rawArgv.includes('--report')
|
||||||
|
|
||||||
run(`vue-cli-service build ${args}`)
|
run(`vue-cli-service build ${args}`)
|
||||||
|
|
||||||
const port = 9526
|
const port = 9526
|
||||||
const publicPath = config.publicPath
|
const publicPath = config.publicPath
|
||||||
|
|
||||||
var connect = require('connect')
|
var connect = require('connect')
|
||||||
var serveStatic = require('serve-static')
|
var serveStatic = require('serve-static')
|
||||||
const app = connect()
|
const app = connect()
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
publicPath,
|
publicPath,
|
||||||
serveStatic('./dist', {
|
serveStatic('./dist', {
|
||||||
index: ['index.html', '/']
|
index: ['index.html', '/']
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
app.listen(port, function () {
|
app.listen(port, function () {
|
||||||
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
|
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
|
||||||
if (report) {
|
if (report) {
|
||||||
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
|
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
run(`vue-cli-service build ${args}`)
|
run(`vue-cli-service build ${args}`)
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
ruoyi-ui/dist.zip
Normal file
14195
ruoyi-ui/package-lock.json
generated
Normal file
@@ -1,73 +1,77 @@
|
|||||||
{
|
{
|
||||||
"name": "ruoyi",
|
"name": "ruoyi",
|
||||||
"version": "3.9.2",
|
"version": "3.9.1",
|
||||||
"description": "若依管理系统",
|
"description": "若依管理系统",
|
||||||
"author": "若依",
|
"author": "若依",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vue-cli-service serve",
|
"dev": "vue-cli-service serve",
|
||||||
"build:prod": "vue-cli-service build",
|
"build:prod": "vue-cli-service build",
|
||||||
"build:stage": "vue-cli-service build --mode staging",
|
"build:stage": "vue-cli-service build --mode staging",
|
||||||
"preview": "node build/index.js --preview"
|
"preview": "node build/index.js --preview",
|
||||||
},
|
"test:password-transfer": "node tests/password-transfer-api.test.js",
|
||||||
"keywords": [
|
"test:retail-display-fields": "node tests/retail-display-fields.test.js",
|
||||||
"vue",
|
"test:personal-create-input-params": "node tests/personal-create-input-params.test.js",
|
||||||
"admin",
|
"test:id-number-validation-removal": "node tests/id-number-validation-removal.test.js"
|
||||||
"dashboard",
|
},
|
||||||
"element-ui",
|
"keywords": [
|
||||||
"boilerplate",
|
"vue",
|
||||||
"admin-template",
|
"admin",
|
||||||
"management-system"
|
"dashboard",
|
||||||
],
|
"element-ui",
|
||||||
"repository": {
|
"boilerplate",
|
||||||
"type": "git",
|
"admin-template",
|
||||||
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
|
"management-system"
|
||||||
},
|
],
|
||||||
"dependencies": {
|
"repository": {
|
||||||
"@riophae/vue-treeselect": "0.4.0",
|
"type": "git",
|
||||||
"axios": "0.30.3",
|
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
|
||||||
"clipboard": "2.0.8",
|
},
|
||||||
"core-js": "3.37.1",
|
"dependencies": {
|
||||||
"crypto-js": "4.2.0",
|
"@riophae/vue-treeselect": "0.4.0",
|
||||||
"echarts": "5.4.0",
|
"axios": "0.28.1",
|
||||||
"element-ui": "2.15.14",
|
"clipboard": "2.0.8",
|
||||||
"file-saver": "2.0.5",
|
"core-js": "3.37.1",
|
||||||
"fuse.js": "6.4.3",
|
"echarts": "5.4.0",
|
||||||
"highlight.js": "9.18.5",
|
"element-ui": "2.15.14",
|
||||||
"js-beautify": "1.13.0",
|
"file-saver": "2.0.5",
|
||||||
"js-cookie": "3.0.1",
|
"fuse.js": "6.4.3",
|
||||||
"jsencrypt": "3.0.0-rc.1",
|
"highlight.js": "9.18.5",
|
||||||
"nprogress": "0.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"quill": "2.0.2",
|
"js-beautify": "1.13.0",
|
||||||
"screenfull": "5.0.2",
|
"js-cookie": "3.0.1",
|
||||||
"sortablejs": "1.10.2",
|
"jsencrypt": "3.0.0-rc.1",
|
||||||
"vue": "2.6.12",
|
"nprogress": "0.2.0",
|
||||||
"vue-count-to": "1.0.13",
|
"quill": "2.0.2",
|
||||||
"vue-cropper": "0.5.5",
|
"screenfull": "5.0.2",
|
||||||
"vue-router": "3.4.9",
|
"sortablejs": "1.10.2",
|
||||||
"vuedraggable": "2.24.3",
|
"splitpanes": "2.4.1",
|
||||||
"vuex": "3.6.0"
|
"vue": "2.6.12",
|
||||||
},
|
"vue-count-to": "1.0.13",
|
||||||
"devDependencies": {
|
"vue-cropper": "0.5.5",
|
||||||
"@vue/cli-plugin-babel": "4.4.6",
|
"vue-router": "3.4.9",
|
||||||
"@vue/cli-service": "4.4.6",
|
"vuedraggable": "2.24.3",
|
||||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
"vuex": "3.6.0"
|
||||||
"chalk": "4.1.0",
|
},
|
||||||
"compression-webpack-plugin": "6.1.2",
|
"devDependencies": {
|
||||||
"connect": "3.6.6",
|
"@vue/cli-plugin-babel": "4.4.6",
|
||||||
"html-webpack-plugin": "3.2.0",
|
"@vue/cli-service": "4.4.6",
|
||||||
"sass": "1.32.13",
|
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||||
"sass-loader": "10.1.1",
|
"chalk": "4.1.0",
|
||||||
"script-ext-html-webpack-plugin": "2.1.5",
|
"compression-webpack-plugin": "6.1.2",
|
||||||
"svg-sprite-loader": "5.1.1",
|
"connect": "3.6.6",
|
||||||
"vue-template-compiler": "2.6.12"
|
"sass": "1.32.13",
|
||||||
},
|
"sass-loader": "10.1.1",
|
||||||
"engines": {
|
"script-ext-html-webpack-plugin": "2.1.5",
|
||||||
"node": ">=8.9",
|
"svg-sprite-loader": "5.1.1",
|
||||||
"npm": ">= 3.0.0"
|
"vue-template-compiler": "2.6.12"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"engines": {
|
||||||
"> 1%",
|
"node": ">=8.9",
|
||||||
"last 2 versions"
|
"npm": ">= 3.0.0"
|
||||||
]
|
},
|
||||||
}
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,208 +1,208 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="renderer" content="webkit">
|
<meta name="renderer" content="webkit">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<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" href="<%= BASE_URL %>favicon.ico">
|
||||||
<title><%= webpackConfig.name %></title>
|
<title><%= webpackConfig.name %></title>
|
||||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
#app {
|
#app {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
.chromeframe {
|
.chromeframe {
|
||||||
margin: 0.2em 0;
|
margin: 0.2em 0;
|
||||||
background: #ccc;
|
background: #ccc;
|
||||||
color: #000;
|
color: #000;
|
||||||
padding: 0.2em 0;
|
padding: 0.2em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader-wrapper {
|
#loader-wrapper {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 999999;
|
z-index: 999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader {
|
#loader {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
margin: -75px 0 0 -75px;
|
margin: -75px 0 0 -75px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
border-top-color: #FFF;
|
border-top-color: #FFF;
|
||||||
-webkit-animation: spin 2s linear infinite;
|
-webkit-animation: spin 2s linear infinite;
|
||||||
-ms-animation: spin 2s linear infinite;
|
-ms-animation: spin 2s linear infinite;
|
||||||
-moz-animation: spin 2s linear infinite;
|
-moz-animation: spin 2s linear infinite;
|
||||||
-o-animation: spin 2s linear infinite;
|
-o-animation: spin 2s linear infinite;
|
||||||
animation: spin 2s linear infinite;
|
animation: spin 2s linear infinite;
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader:before {
|
#loader:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
border-top-color: #FFF;
|
border-top-color: #FFF;
|
||||||
-webkit-animation: spin 3s linear infinite;
|
-webkit-animation: spin 3s linear infinite;
|
||||||
-moz-animation: spin 3s linear infinite;
|
-moz-animation: spin 3s linear infinite;
|
||||||
-o-animation: spin 3s linear infinite;
|
-o-animation: spin 3s linear infinite;
|
||||||
-ms-animation: spin 3s linear infinite;
|
-ms-animation: spin 3s linear infinite;
|
||||||
animation: spin 3s linear infinite;
|
animation: spin 3s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader:after {
|
#loader:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
left: 15px;
|
left: 15px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
border-top-color: #FFF;
|
border-top-color: #FFF;
|
||||||
-moz-animation: spin 1.5s linear infinite;
|
-moz-animation: spin 1.5s linear infinite;
|
||||||
-o-animation: spin 1.5s linear infinite;
|
-o-animation: spin 1.5s linear infinite;
|
||||||
-ms-animation: spin 1.5s linear infinite;
|
-ms-animation: spin 1.5s linear infinite;
|
||||||
-webkit-animation: spin 1.5s linear infinite;
|
-webkit-animation: spin 1.5s linear infinite;
|
||||||
animation: spin 1.5s linear infinite;
|
animation: spin 1.5s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@-webkit-keyframes spin {
|
@-webkit-keyframes spin {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: rotate(0deg);
|
-webkit-transform: rotate(0deg);
|
||||||
-ms-transform: rotate(0deg);
|
-ms-transform: rotate(0deg);
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: rotate(360deg);
|
-webkit-transform: rotate(360deg);
|
||||||
-ms-transform: rotate(360deg);
|
-ms-transform: rotate(360deg);
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% {
|
0% {
|
||||||
-webkit-transform: rotate(0deg);
|
-webkit-transform: rotate(0deg);
|
||||||
-ms-transform: rotate(0deg);
|
-ms-transform: rotate(0deg);
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: rotate(360deg);
|
-webkit-transform: rotate(360deg);
|
||||||
-ms-transform: rotate(360deg);
|
-ms-transform: rotate(360deg);
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#loader-wrapper .loader-section {
|
#loader-wrapper .loader-section {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 51%;
|
width: 51%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #7171C6;
|
background: #7171C6;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
-webkit-transform: translateX(0);
|
-webkit-transform: translateX(0);
|
||||||
-ms-transform: translateX(0);
|
-ms-transform: translateX(0);
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader-wrapper .loader-section.section-left {
|
#loader-wrapper .loader-section.section-left {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader-wrapper .loader-section.section-right {
|
#loader-wrapper .loader-section.section-right {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.loaded #loader-wrapper .loader-section.section-left {
|
.loaded #loader-wrapper .loader-section.section-left {
|
||||||
-webkit-transform: translateX(-100%);
|
-webkit-transform: translateX(-100%);
|
||||||
-ms-transform: translateX(-100%);
|
-ms-transform: translateX(-100%);
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
}
|
}
|
||||||
|
|
||||||
.loaded #loader-wrapper .loader-section.section-right {
|
.loaded #loader-wrapper .loader-section.section-right {
|
||||||
-webkit-transform: translateX(100%);
|
-webkit-transform: translateX(100%);
|
||||||
-ms-transform: translateX(100%);
|
-ms-transform: translateX(100%);
|
||||||
transform: translateX(100%);
|
transform: translateX(100%);
|
||||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||||
}
|
}
|
||||||
|
|
||||||
.loaded #loader {
|
.loaded #loader {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transition: all 0.3s ease-out;
|
-webkit-transition: all 0.3s ease-out;
|
||||||
transition: all 0.3s ease-out;
|
transition: all 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loaded #loader-wrapper {
|
.loaded #loader-wrapper {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
-webkit-transform: translateY(-100%);
|
-webkit-transform: translateY(-100%);
|
||||||
-ms-transform: translateY(-100%);
|
-ms-transform: translateY(-100%);
|
||||||
transform: translateY(-100%);
|
transform: translateY(-100%);
|
||||||
-webkit-transition: all 0.3s 1s ease-out;
|
-webkit-transition: all 0.3s 1s ease-out;
|
||||||
transition: all 0.3s 1s ease-out;
|
transition: all 0.3s 1s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-js #loader-wrapper {
|
.no-js #loader-wrapper {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-js h1 {
|
.no-js h1 {
|
||||||
color: #222222;
|
color: #222222;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader-wrapper .load_title {
|
#loader-wrapper .load_title {
|
||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
font-size: 19px;
|
font-size: 19px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 9999999999999;
|
z-index: 9999999999999;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 60%;
|
top: 60%;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loader-wrapper .load_title span {
|
#loader-wrapper .load_title span {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div id="loader-wrapper">
|
<div id="loader-wrapper">
|
||||||
<div id="loader"></div>
|
<div id="loader"></div>
|
||||||
<div class="loader-section section-left"></div>
|
<div class="loader-section section-left"></div>
|
||||||
<div class="loader-section section-right"></div>
|
<div class="loader-section section-right"></div>
|
||||||
<div class="load_title">正在加载系统资源,请耐心等待</div>
|
<div class="load_title">正在加载系统资源,请耐心等待</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow: /
|
Disallow: /
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<router-view />
|
<router-view />
|
||||||
<theme-picker />
|
<theme-picker />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ThemePicker from "@/components/ThemePicker"
|
import ThemePicker from "@/components/ThemePicker"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
components: { ThemePicker }
|
components: { ThemePicker }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#app .theme-picker {
|
#app .theme-picker {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ export function login(username, password, code, uuid) {
|
|||||||
uuid
|
uuid
|
||||||
}, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
}, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||||
return request({
|
return request({
|
||||||
url: '/login',
|
url: '/login',
|
||||||
headers: {
|
headers: {
|
||||||
isToken: false,
|
isToken: false,
|
||||||
repeatSubmit: false
|
repeatSubmit: false
|
||||||
},
|
},
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册方法
|
// 注册方法
|
||||||
@@ -25,47 +25,38 @@ export function register(data) {
|
|||||||
const payload = encryptPasswordFields(data, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
const payload = encryptPasswordFields(data, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||||
return request({
|
return request({
|
||||||
url: '/register',
|
url: '/register',
|
||||||
headers: {
|
headers: {
|
||||||
isToken: false
|
isToken: false
|
||||||
},
|
},
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: payload
|
data: payload
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户详细信息
|
// 获取用户详细信息
|
||||||
export function getInfo() {
|
export function getInfo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/getInfo',
|
url: '/getInfo',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解锁屏幕
|
// 退出方法
|
||||||
export function unlockScreen(password) {
|
export function logout() {
|
||||||
return request({
|
return request({
|
||||||
url: '/unlockscreen',
|
url: '/logout',
|
||||||
method: 'post',
|
method: 'post'
|
||||||
data: { password }
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
// 获取验证码
|
||||||
// 退出方法
|
export function getCodeImg() {
|
||||||
export function logout() {
|
return request({
|
||||||
return request({
|
url: '/captchaImage',
|
||||||
url: '/logout',
|
headers: {
|
||||||
method: 'post'
|
isToken: false
|
||||||
})
|
},
|
||||||
}
|
method: 'get',
|
||||||
|
timeout: 20000
|
||||||
// 获取验证码
|
})
|
||||||
export function getCodeImg() {
|
|
||||||
return request({
|
|
||||||
url: '/captchaImage',
|
|
||||||
headers: {
|
|
||||||
isToken: false
|
|
||||||
},
|
|
||||||
method: 'get',
|
|
||||||
timeout: 20000
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 获取路由
|
// 获取路由
|
||||||
export const getRouters = () => {
|
export const getRouters = () => {
|
||||||
return request({
|
return request({
|
||||||
url: '/getRouters',
|
url: '/getRouters',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,57 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询缓存详细
|
// 查询缓存概览
|
||||||
export function getCache() {
|
export function getCache() {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/cache',
|
url: '/monitor/cache',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询缓存名称列表
|
// 查询缓存名称列表
|
||||||
export function listCacheName() {
|
export function listCacheName() {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/cache/getNames',
|
url: '/monitor/cache/getNames',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询缓存键名列表
|
// 查询缓存键名列表
|
||||||
export function listCacheKey(cacheName) {
|
export function listCacheKey(cacheName) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/cache/getKeys/' + cacheName,
|
url: '/monitor/cache/getKeys/' + cacheName,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询缓存内容
|
// 查询缓存内容
|
||||||
export function getCacheValue(cacheName, cacheKey) {
|
export function getCacheValue(cacheName, cacheKey) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
|
url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理指定名称缓存
|
// 清理指定名称下的缓存
|
||||||
export function clearCacheName(cacheName) {
|
export function clearCacheName(cacheName) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/cache/clearCacheName/' + cacheName,
|
url: '/monitor/cache/clearCacheName/' + cacheName,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理指定键名缓存
|
// 清理指定键名缓存
|
||||||
export function clearCacheKey(cacheKey) {
|
export function clearCacheKey(cacheKey) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/cache/clearCacheKey/' + cacheKey,
|
url: '/monitor/cache/clearCacheKey/' + cacheKey,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理全部缓存
|
// 清理全部缓存
|
||||||
export function clearCacheAll() {
|
export function clearCacheAll() {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/cache/clearCacheAll',
|
url: '/monitor/cache/clearCacheAll',
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询登录日志列表
|
// 查询登录日志列表
|
||||||
export function list(query) {
|
export function list(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/logininfor/list',
|
url: '/monitor/logininfor/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除登录日志
|
// 删除登录日志
|
||||||
export function delLogininfor(infoId) {
|
export function delLogininfor(infoId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/logininfor/' + infoId,
|
url: '/monitor/logininfor/' + infoId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解锁用户登录状态
|
// 解锁用户登录状态
|
||||||
export function unlockLogininfor(userName) {
|
export function unlockLogininfor(userName) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/logininfor/unlock/' + userName,
|
url: '/monitor/logininfor/unlock/' + userName,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空登录日志
|
// 清空登录日志
|
||||||
export function cleanLogininfor() {
|
export function cleanLogininfor() {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/logininfor/clean',
|
url: '/monitor/logininfor/clean',
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询在线用户列表
|
// 查询在线用户列表
|
||||||
export function list(query) {
|
export function list(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/online/list',
|
url: '/monitor/online/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 强退用户
|
// 强退用户
|
||||||
export function forceLogout(tokenId) {
|
export function forceLogout(tokenId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/online/' + tokenId,
|
url: '/monitor/online/' + tokenId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询操作日志列表
|
// 查询操作日志列表
|
||||||
export function list(query) {
|
export function list(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/operlog/list',
|
url: '/monitor/operlog/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除操作日志
|
// 删除操作日志
|
||||||
export function delOperlog(operId) {
|
export function delOperlog(operId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/operlog/' + operId,
|
url: '/monitor/operlog/' + operId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空操作日志
|
// 清空操作日志
|
||||||
export function cleanOperlog() {
|
export function cleanOperlog() {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/operlog/clean',
|
url: '/monitor/operlog/clean',
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 获取服务信息
|
// 获取服务信息
|
||||||
export function getServer() {
|
export function getServer() {
|
||||||
return request({
|
return request({
|
||||||
url: '/monitor/server',
|
url: '/monitor/server',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1,60 +1,60 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询参数列表
|
// 查询参数列表
|
||||||
export function listConfig(query) {
|
export function listConfig(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/config/list',
|
url: '/system/config/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询参数详细
|
// 查询参数详细
|
||||||
export function getConfig(configId) {
|
export function getConfig(configId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/config/' + configId,
|
url: '/system/config/' + configId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据参数键名查询参数值
|
// 根据参数键名查询参数值
|
||||||
export function getConfigKey(configKey) {
|
export function getConfigKey(configKey) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/config/configKey/' + configKey,
|
url: '/system/config/configKey/' + configKey,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增参数配置
|
// 新增参数配置
|
||||||
export function addConfig(data) {
|
export function addConfig(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/config',
|
url: '/system/config',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改参数配置
|
// 修改参数配置
|
||||||
export function updateConfig(data) {
|
export function updateConfig(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/config',
|
url: '/system/config',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除参数配置
|
// 删除参数配置
|
||||||
export function delConfig(configId) {
|
export function delConfig(configId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/config/' + configId,
|
url: '/system/config/' + configId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新参数缓存
|
// 刷新参数缓存
|
||||||
export function refreshCache() {
|
export function refreshCache() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/config/refreshCache',
|
url: '/system/config/refreshCache',
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,52 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询部门列表
|
// 查询部门列表
|
||||||
export function listDept(query) {
|
export function listDept(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dept/list',
|
url: '/system/dept/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门列表(排除节点)
|
// 查询部门列表(排除节点)
|
||||||
export function listDeptExcludeChild(deptId) {
|
export function listDeptExcludeChild(deptId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dept/list/exclude/' + deptId,
|
url: '/system/dept/list/exclude/' + deptId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门详细
|
// 查询部门详细
|
||||||
export function getDept(deptId) {
|
export function getDept(deptId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dept/' + deptId,
|
url: '/system/dept/' + deptId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增部门
|
// 新增部门
|
||||||
export function addDept(data) {
|
export function addDept(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dept',
|
url: '/system/dept',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改部门
|
// 修改部门
|
||||||
export function updateDept(data) {
|
export function updateDept(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dept',
|
url: '/system/dept',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存部门排序
|
// 删除部门
|
||||||
export function updateDeptSort(data) {
|
export function delDept(deptId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dept/updateSort',
|
url: '/system/dept/' + deptId,
|
||||||
method: 'put',
|
method: 'delete'
|
||||||
data: data
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除部门
|
|
||||||
export function delDept(deptId) {
|
|
||||||
return request({
|
|
||||||
url: '/system/dept/' + deptId,
|
|
||||||
method: 'delete'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,52 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询字典数据列表
|
// 查询字典数据列表
|
||||||
export function listData(query) {
|
export function listData(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/list',
|
url: '/system/dict/data/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典数据详细
|
// 查询字典数据详细
|
||||||
export function getData(dictCode) {
|
export function getData(dictCode) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/' + dictCode,
|
url: '/system/dict/data/' + dictCode,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据字典类型查询字典数据信息
|
// 根据字典类型查询字典数据信息
|
||||||
export function getDicts(dictType) {
|
export function getDicts(dictType) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/type/' + dictType,
|
url: '/system/dict/data/type/' + dictType,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增字典数据
|
// 新增字典数据
|
||||||
export function addData(data) {
|
export function addData(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data',
|
url: '/system/dict/data',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改字典数据
|
// 修改字典数据
|
||||||
export function updateData(data) {
|
export function updateData(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data',
|
url: '/system/dict/data',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除字典数据
|
// 删除字典数据
|
||||||
export function delData(dictCode) {
|
export function delData(dictCode) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/' + dictCode,
|
url: '/system/dict/data/' + dictCode,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,60 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询字典类型列表
|
// 查询字典类型列表
|
||||||
export function listType(query) {
|
export function listType(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/type/list',
|
url: '/system/dict/type/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典类型详细
|
// 查询字典类型详细
|
||||||
export function getType(dictId) {
|
export function getType(dictId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/type/' + dictId,
|
url: '/system/dict/type/' + dictId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增字典类型
|
// 新增字典类型
|
||||||
export function addType(data) {
|
export function addType(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/type',
|
url: '/system/dict/type',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改字典类型
|
// 修改字典类型
|
||||||
export function updateType(data) {
|
export function updateType(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/type',
|
url: '/system/dict/type',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除字典类型
|
// 删除字典类型
|
||||||
export function delType(dictId) {
|
export function delType(dictId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/type/' + dictId,
|
url: '/system/dict/type/' + dictId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新字典缓存
|
// 刷新字典缓存
|
||||||
export function refreshCache() {
|
export function refreshCache() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/type/refreshCache',
|
url: '/system/dict/type/refreshCache',
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取字典选择框列表
|
// 获取字典选择框列表
|
||||||
export function optionselect() {
|
export function optionselect() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/type/optionselect',
|
url: '/system/dict/type/optionselect',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1,69 +1,60 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询菜单列表
|
// 查询菜单列表
|
||||||
export function listMenu(query) {
|
export function listMenu(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/menu/list',
|
url: '/system/menu/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单详细
|
// 查询菜单详细
|
||||||
export function getMenu(menuId) {
|
export function getMenu(menuId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/menu/' + menuId,
|
url: '/system/menu/' + menuId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单下拉树结构
|
// 查询菜单下拉树结构
|
||||||
export function treeselect() {
|
export function treeselect() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/menu/treeselect',
|
url: '/system/menu/treeselect',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据角色ID查询菜单下拉树结构
|
// 根据角色ID查询菜单下拉树结构
|
||||||
export function roleMenuTreeselect(roleId) {
|
export function roleMenuTreeselect(roleId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增菜单
|
// 新增菜单
|
||||||
export function addMenu(data) {
|
export function addMenu(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/menu',
|
url: '/system/menu',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改菜单
|
// 修改菜单
|
||||||
export function updateMenu(data) {
|
export function updateMenu(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/menu',
|
url: '/system/menu',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存菜单排序
|
// 删除菜单
|
||||||
export function updateMenuSort(data) {
|
export function delMenu(menuId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/menu/updateSort',
|
url: '/system/menu/' + menuId,
|
||||||
method: 'put',
|
method: 'delete'
|
||||||
data: data
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除菜单
|
|
||||||
export function delMenu(menuId) {
|
|
||||||
return request({
|
|
||||||
url: '/system/menu/' + menuId,
|
|
||||||
method: 'delete'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
@@ -1,79 +1,44 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询公告列表
|
// 查询公告列表
|
||||||
export function listNotice(query) {
|
export function listNotice(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/notice/list',
|
url: '/system/notice/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询公告详细
|
// 查询公告详细
|
||||||
export function getNotice(noticeId) {
|
export function getNotice(noticeId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/notice/' + noticeId,
|
url: '/system/notice/' + noticeId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增公告
|
// 新增公告
|
||||||
export function addNotice(data) {
|
export function addNotice(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/notice',
|
url: '/system/notice',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改公告
|
// 修改公告
|
||||||
export function updateNotice(data) {
|
export function updateNotice(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/notice',
|
url: '/system/notice',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除公告
|
// 删除公告
|
||||||
export function delNotice(noticeId) {
|
export function delNotice(noticeId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/notice/' + noticeId,
|
url: '/system/notice/' + noticeId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 首页顶部公告列表(带已读状态)
|
|
||||||
export function listNoticeTop() {
|
|
||||||
return request({
|
|
||||||
url: '/system/notice/listTop',
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记公告已读
|
|
||||||
export function markNoticeRead(noticeId) {
|
|
||||||
return request({
|
|
||||||
url: '/system/notice/markRead',
|
|
||||||
method: 'post',
|
|
||||||
params: { noticeId }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量标记已读
|
|
||||||
export function markNoticeReadAll(ids) {
|
|
||||||
return request({
|
|
||||||
url: '/system/notice/markReadAll',
|
|
||||||
method: 'post',
|
|
||||||
params: { ids }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询公告已读用户列表
|
|
||||||
export function listNoticeReadUsers(query) {
|
|
||||||
return request({
|
|
||||||
url: '/system/notice/readUsers/list',
|
|
||||||
method: 'get',
|
|
||||||
params: query
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,44 +1,44 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询岗位列表
|
// 查询岗位列表
|
||||||
export function listPost(query) {
|
export function listPost(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/post/list',
|
url: '/system/post/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询岗位详细
|
// 查询岗位详细
|
||||||
export function getPost(postId) {
|
export function getPost(postId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/post/' + postId,
|
url: '/system/post/' + postId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增岗位
|
// 新增岗位
|
||||||
export function addPost(data) {
|
export function addPost(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/post',
|
url: '/system/post',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改岗位
|
// 修改岗位
|
||||||
export function updatePost(data) {
|
export function updatePost(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/post',
|
url: '/system/post',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除岗位
|
// 删除岗位
|
||||||
export function delPost(postId) {
|
export function delPost(postId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/post/' + postId,
|
url: '/system/post/' + postId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +1,119 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询角色列表
|
// 查询角色列表
|
||||||
export function listRole(query) {
|
export function listRole(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/list',
|
url: '/system/role/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色详细
|
// 查询角色详细
|
||||||
export function getRole(roleId) {
|
export function getRole(roleId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/' + roleId,
|
url: '/system/role/' + roleId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增角色
|
// 新增角色
|
||||||
export function addRole(data) {
|
export function addRole(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role',
|
url: '/system/role',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改角色
|
// 修改角色
|
||||||
export function updateRole(data) {
|
export function updateRole(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role',
|
url: '/system/role',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色数据权限
|
// 角色数据权限
|
||||||
export function dataScope(data) {
|
export function dataScope(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/dataScope',
|
url: '/system/role/dataScope',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色状态修改
|
// 角色状态修改
|
||||||
export function changeRoleStatus(roleId, status) {
|
export function changeRoleStatus(roleId, status) {
|
||||||
const data = {
|
const data = {
|
||||||
roleId,
|
roleId,
|
||||||
status
|
status
|
||||||
}
|
}
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/changeStatus',
|
url: '/system/role/changeStatus',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除角色
|
// 删除角色
|
||||||
export function delRole(roleId) {
|
export function delRole(roleId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/' + roleId,
|
url: '/system/role/' + roleId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色已授权用户列表
|
// 查询角色已授权用户列表
|
||||||
export function allocatedUserList(query) {
|
export function allocatedUserList(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/authUser/allocatedList',
|
url: '/system/role/authUser/allocatedList',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色未授权用户列表
|
// 查询角色未授权用户列表
|
||||||
export function unallocatedUserList(query) {
|
export function unallocatedUserList(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/authUser/unallocatedList',
|
url: '/system/role/authUser/unallocatedList',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消用户授权角色
|
// 取消用户授权角色
|
||||||
export function authUserCancel(data) {
|
export function authUserCancel(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/authUser/cancel',
|
url: '/system/role/authUser/cancel',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量取消用户授权角色
|
// 批量取消用户授权角色
|
||||||
export function authUserCancelAll(data) {
|
export function authUserCancelAll(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/authUser/cancelAll',
|
url: '/system/role/authUser/cancelAll',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
params: data
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 授权用户选择
|
// 授权用户选择
|
||||||
export function authUserSelectAll(data) {
|
export function authUserSelectAll(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/authUser/selectAll',
|
url: '/system/role/authUser/selectAll',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
params: data
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据角色ID查询部门树结构
|
// 根据角色ID查询部门树结构
|
||||||
export function deptTreeSelect(roleId) {
|
export function deptTreeSelect(roleId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/role/deptTree/' + roleId,
|
url: '/system/role/deptTree/' + roleId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
import { parseStrEmpty } from "@/utils/ruoyi"
|
import { parseStrEmpty } from "@/utils/ruoyi";
|
||||||
import { encryptPasswordFields } from '@/utils/passwordTransfer'
|
import { encryptPasswordFields } from '@/utils/passwordTransfer'
|
||||||
|
|
||||||
// 查询用户列表
|
// 查询用户列表
|
||||||
export function listUser(query) {
|
export function listUser(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/list',
|
url: '/system/user/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户详细
|
// 查询用户详细
|
||||||
export function getUser(userId) {
|
export function getUser(userId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/' + parseStrEmpty(userId),
|
url: '/system/user/' + parseStrEmpty(userId),
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增用户
|
// 新增用户
|
||||||
export function addUser(data) {
|
export function addUser(data) {
|
||||||
const payload = encryptPasswordFields(data, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
const payload = encryptPasswordFields(data, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||||
return request({
|
return request({
|
||||||
@@ -28,111 +28,111 @@ export function addUser(data) {
|
|||||||
data: payload
|
data: payload
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改用户
|
// 修改用户
|
||||||
export function updateUser(data) {
|
export function updateUser(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user',
|
url: '/system/user',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除用户
|
// 删除用户
|
||||||
export function delUser(userId) {
|
export function delUser(userId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/' + userId,
|
url: '/system/user/' + userId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户密码重置
|
// 用户密码重置
|
||||||
export function resetUserPwd(userId, password) {
|
export function resetUserPwd(userId, password) {
|
||||||
const data = encryptPasswordFields({
|
const data = encryptPasswordFields({
|
||||||
userId,
|
userId,
|
||||||
password
|
password
|
||||||
}, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
}, ['password'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/resetPwd',
|
url: '/system/user/resetPwd',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户状态修改
|
// 用户状态修改
|
||||||
export function changeUserStatus(userId, status) {
|
export function changeUserStatus(userId, status) {
|
||||||
const data = {
|
const data = {
|
||||||
userId,
|
userId,
|
||||||
status
|
status
|
||||||
}
|
}
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/changeStatus',
|
url: '/system/user/changeStatus',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户个人信息
|
// 查询用户个人信息
|
||||||
export function getUserProfile() {
|
export function getUserProfile() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile',
|
url: '/system/user/profile',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改用户个人信息
|
// 修改用户个人信息
|
||||||
export function updateUserProfile(data) {
|
export function updateUserProfile(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile',
|
url: '/system/user/profile',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户密码重置
|
// 用户密码重置
|
||||||
export function updateUserPwd(oldPassword, newPassword) {
|
export function updateUserPwd(oldPassword, newPassword) {
|
||||||
const data = encryptPasswordFields({
|
const data = encryptPasswordFields({
|
||||||
oldPassword,
|
oldPassword,
|
||||||
newPassword
|
newPassword
|
||||||
}, ['oldPassword', 'newPassword'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
}, ['oldPassword', 'newPassword'], process.env.VUE_APP_PASSWORD_TRANSFER_KEY)
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile/updatePwd',
|
url: '/system/user/profile/updatePwd',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户头像上传
|
// 用户头像上传
|
||||||
export function uploadAvatar(data) {
|
export function uploadAvatar(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/profile/avatar',
|
url: '/system/user/profile/avatar',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询授权角色
|
// 查询授权角色
|
||||||
export function getAuthRole(userId) {
|
export function getAuthRole(userId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/authRole/' + userId,
|
url: '/system/user/authRole/' + userId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存授权角色
|
// 保存授权角色
|
||||||
export function updateAuthRole(data) {
|
export function updateAuthRole(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/authRole',
|
url: '/system/user/authRole',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
params: data
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门下拉树结构
|
// 查询部门下拉树结构
|
||||||
export function deptTreeSelect() {
|
export function deptTreeSelect() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/user/deptTree',
|
url: '/system/user/deptTree',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +1,85 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 查询生成表数据
|
// 查询生成表数据
|
||||||
export function listTable(query) {
|
export function listTable(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/list',
|
url: '/tool/gen/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 查询db数据库列表
|
// 查询db数据库列表
|
||||||
export function listDbTable(query) {
|
export function listDbTable(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/db/list',
|
url: '/tool/gen/db/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询表详细信息
|
// 查询表详细信息
|
||||||
export function getGenTable(tableId) {
|
export function getGenTable(tableId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/' + tableId,
|
url: '/tool/gen/' + tableId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改代码生成信息
|
// 修改代码生成信息
|
||||||
export function updateGenTable(data) {
|
export function updateGenTable(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen',
|
url: '/tool/gen',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导入表
|
// 导入表
|
||||||
export function importTable(data) {
|
export function importTable(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/importTable',
|
url: '/tool/gen/importTable',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: data
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建表
|
// 创建表
|
||||||
export function createTable(data) {
|
export function createTable(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/createTable',
|
url: '/tool/gen/createTable',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: data
|
params: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预览生成代码
|
// 预览生成代码
|
||||||
export function previewTable(tableId) {
|
export function previewTable(tableId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/preview/' + tableId,
|
url: '/tool/gen/preview/' + tableId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除表数据
|
// 删除表数据
|
||||||
export function delTable(tableId) {
|
export function delTable(tableId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/' + tableId,
|
url: '/tool/gen/' + tableId,
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成代码(自定义路径)
|
// 生成代码(自定义路径)
|
||||||
export function genCode(tableName) {
|
export function genCode(tableName) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/genCode/' + tableName,
|
url: '/tool/gen/genCode/' + tableName,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步数据库
|
// 同步数据库
|
||||||
export function synchDb(tableName) {
|
export function synchDb(tableName) {
|
||||||
return request({
|
return request({
|
||||||
url: '/tool/gen/synchDb/' + tableName,
|
url: '/tool/gen/synchDb/' + tableName,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import SvgIcon from '@/components/SvgIcon'// svg component
|
import SvgIcon from '@/components/SvgIcon'// svg component
|
||||||
|
|
||||||
// register globally
|
// register globally
|
||||||
Vue.component('svg-icon', SvgIcon)
|
Vue.component('svg-icon', SvgIcon)
|
||||||
|
|
||||||
const req = require.context('./svg', false, /\.svg$/)
|
const req = require.context('./svg', false, /\.svg$/)
|
||||||
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||||
requireAll(req)
|
requireAll(req)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1773923748724" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5930" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 212l48.8 12c101.6 24.8 176 117.6 176 220.8v254.4l18.4 18.4 24.8 25.6h-536l24.8-25.6 18.4-18.4V444.8c0-103.2 73.6-196.8 176-220.8l48.8-12M512 64c-36.8 0-64 30.4-64 68v30.4C320.8 192 223.2 307.2 223.2 444.8v228.8L136 763.2v44.8h752v-44.8l-87.2-89.6V444.8c0-137.6-97.6-252.8-224.8-283.2v-28.8c0-32-17.6-60.8-48-67.2-5.6-1.6-11.2-1.6-16-1.6z m88 808H424c0 49.6 38.4 88 88 88s88-38.4 88-88z" p-id="5931"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 750 B |
@@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827393750" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4695" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; }
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827393750" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4695" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; }
|
||||||
</style></defs><path d="M64 64V640H896V64H64zM0 0h960v704H0V0z" p-id="4696"></path><path d="M192 896H768v64H192zM448 640H512v256h-64z" p-id="4697"></path><path d="M479.232 561.604267l309.9904-348.330667-47.803733-42.5472-259.566934 291.669333L303.957333 240.008533 163.208533 438.6048l52.224 37.009067 91.6224-129.28z" p-id="4698"></path></svg>
|
</style></defs><path d="M64 64V640H896V64H64zM0 0h960v704H0V0z" p-id="4696"></path><path d="M192 896H768v64H192zM448 640H512v256h-64z" p-id="4697"></path><path d="M479.232 561.604267l309.9904-348.330667-47.803733-42.5472-259.566934 291.669333L303.957333 240.008533 163.208533 438.6048l52.224 37.009067 91.6224-129.28z" p-id="4698"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 884 B After Width: | Height: | Size: 883 B |
@@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827724451" class="icon" style="" viewBox="0 0 1084 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10233" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.71875" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; }
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827724451" class="icon" style="" viewBox="0 0 1084 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10233" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.71875" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; }
|
||||||
</style></defs><path d="M1080.09609 434.500756c-4.216302-23.731757-26.9241-47.945376-50.595623-53.185637l-17.648235-4.095836a175.940257 175.940257 0 0 1-101.612877-80.832531 177.807476 177.807476 0 0 1-18.732427-129.801867l5.541425-16.684509c7.10748-23.129428-2.108151-54.992624-20.599646-70.833873 0 0-16.624276-14.094495-63.244529-41.199293-46.800951-26.984332-66.858502-34.513443-66.858502-34.513443-22.76803-8.372371-54.631227-0.361397-71.255503 17.407304l-12.287509 13.251234a173.470708 173.470708 0 0 1-120.465769 48.065842A174.13327 174.13327 0 0 1 421.329029 33.590675L409.583617 20.761071C393.140039 2.99237 361.096144-4.898138 338.267881 3.353767c0 0-20.358715 7.529111-67.099434 34.513443-46.800951 27.34573-63.244529 41.440225-63.244529 41.440225-18.431263 15.66055-27.646894 47.222582-20.539413 70.592941l5.059562 16.865207a178.048407 178.048407 0 0 1-18.672194 129.621169 174.916297 174.916297 0 0 1-102.275439 81.073463l-17.045906 3.854904c-23.310126 5.42096-46.258856 29.333415-50.595623 53.185637 0 0-3.854905 21.382674-3.854905 75.712737 0 54.330062 3.854905 75.712736 3.854905 75.712736 4.216302 23.972688 26.9241 47.945376 50.595623 53.185637l16.624276 3.854905a174.253736 174.253736 0 0 1 102.395904 81.314394c23.310126 40.837896 28.911785 87.337683 18.732427 129.801867l-4.81863 16.443578c-7.10748 23.129428 2.108151 54.992624 20.599646 70.833872 0 0 16.624276 14.094495 63.244529 41.199293 46.800951 27.104798 66.918735 34.513443 66.918735 34.513443 22.707798 8.372371 54.631227 0.361397 71.255503-17.407303l11.624947-12.588673a175.096996 175.096996 0 0 1 242.256662 0.120465l11.624947 12.648906c16.383345 17.708468 48.427239 25.598976 71.255503 17.347071 0 0 20.358715-7.529111 67.159666-34.513443 46.740719-27.104798 63.124063-41.199293 63.124064-41.199293 18.491496-15.600317 27.707127-47.463513 20.599646-70.833873l-5.059562-17.106139a176.723284 176.723284 0 0 1 18.672194-129.139305 176.060722 176.060722 0 0 1 102.395904-81.314394l16.68451-3.854905c23.310126-5.42096 46.258856-29.333415 50.595623-53.185637 0 0 3.854905-21.382674 3.854904-75.712737-0.240932-54.330062-4.095836-75.833202-4.095836-75.833202z m-537.819428 293.334149c-119.261112 0-216.175824-97.336342-216.175824-217.621412a216.657687 216.657687 0 0 1 216.236057-217.320249c119.200879 0 216.115591 97.276109 216.11559 217.56118-0.240932 120.044139-96.974945 217.320248-216.175823 217.320249z" p-id="10234"></path></svg>
|
</style></defs><path d="M1080.09609 434.500756c-4.216302-23.731757-26.9241-47.945376-50.595623-53.185637l-17.648235-4.095836a175.940257 175.940257 0 0 1-101.612877-80.832531 177.807476 177.807476 0 0 1-18.732427-129.801867l5.541425-16.684509c7.10748-23.129428-2.108151-54.992624-20.599646-70.833873 0 0-16.624276-14.094495-63.244529-41.199293-46.800951-26.984332-66.858502-34.513443-66.858502-34.513443-22.76803-8.372371-54.631227-0.361397-71.255503 17.407304l-12.287509 13.251234a173.470708 173.470708 0 0 1-120.465769 48.065842A174.13327 174.13327 0 0 1 421.329029 33.590675L409.583617 20.761071C393.140039 2.99237 361.096144-4.898138 338.267881 3.353767c0 0-20.358715 7.529111-67.099434 34.513443-46.800951 27.34573-63.244529 41.440225-63.244529 41.440225-18.431263 15.66055-27.646894 47.222582-20.539413 70.592941l5.059562 16.865207a178.048407 178.048407 0 0 1-18.672194 129.621169 174.916297 174.916297 0 0 1-102.275439 81.073463l-17.045906 3.854904c-23.310126 5.42096-46.258856 29.333415-50.595623 53.185637 0 0-3.854905 21.382674-3.854905 75.712737 0 54.330062 3.854905 75.712736 3.854905 75.712736 4.216302 23.972688 26.9241 47.945376 50.595623 53.185637l16.624276 3.854905a174.253736 174.253736 0 0 1 102.395904 81.314394c23.310126 40.837896 28.911785 87.337683 18.732427 129.801867l-4.81863 16.443578c-7.10748 23.129428 2.108151 54.992624 20.599646 70.833872 0 0 16.624276 14.094495 63.244529 41.199293 46.800951 27.104798 66.918735 34.513443 66.918735 34.513443 22.707798 8.372371 54.631227 0.361397 71.255503-17.407303l11.624947-12.588673a175.096996 175.096996 0 0 1 242.256662 0.120465l11.624947 12.648906c16.383345 17.708468 48.427239 25.598976 71.255503 17.347071 0 0 20.358715-7.529111 67.159666-34.513443 46.740719-27.104798 63.124063-41.199293 63.124064-41.199293 18.491496-15.600317 27.707127-47.463513 20.599646-70.833873l-5.059562-17.106139a176.723284 176.723284 0 0 1 18.672194-129.139305 176.060722 176.060722 0 0 1 102.395904-81.314394l16.68451-3.854905c23.310126-5.42096 46.258856-29.333415 50.595623-53.185637 0 0 3.854905-21.382674 3.854904-75.712737-0.240932-54.330062-4.095836-75.833202-4.095836-75.833202z m-537.819428 293.334149c-119.261112 0-216.175824-97.336342-216.175824-217.621412a216.657687 216.657687 0 0 1 216.236057-217.320249c119.200879 0 216.115591 97.276109 216.11559 217.56118-0.240932 120.044139-96.974945 217.320248-216.175823 217.320249z" p-id="10234"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -1,22 +1,22 @@
|
|||||||
# replace default config
|
# replace default config
|
||||||
|
|
||||||
# multipass: true
|
# multipass: true
|
||||||
# full: true
|
# full: true
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
|
|
||||||
# - name
|
# - name
|
||||||
#
|
#
|
||||||
# or:
|
# or:
|
||||||
# - name: false
|
# - name: false
|
||||||
# - name: true
|
# - name: true
|
||||||
#
|
#
|
||||||
# or:
|
# or:
|
||||||
# - name:
|
# - name:
|
||||||
# param1: 1
|
# param1: 1
|
||||||
# param2: 2
|
# param2: 2
|
||||||
|
|
||||||
- removeAttrs:
|
- removeAttrs:
|
||||||
attrs:
|
attrs:
|
||||||
- 'fill'
|
- 'fill'
|
||||||
- 'fill-rule'
|
- 'fill-rule'
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
|
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<defs>
|
<defs>
|
||||||
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
|
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
|
||||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||||
<feMerge>
|
<feMerge>
|
||||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||||
<feMergeNode in="SourceGraphic"></feMergeNode>
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||||
</feMerge>
|
</feMerge>
|
||||||
</filter>
|
</filter>
|
||||||
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||||
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
|
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
|
||||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
||||||
</filter>
|
</filter>
|
||||||
</defs>
|
</defs>
|
||||||
<g id="配置面板" width="48" height="40" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g id="配置面板" width="48" height="40" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
<g id="setting-copy-2" width="48" height="40" transform="translate(-1190.000000, -136.000000)">
|
<g id="setting-copy-2" width="48" height="40" transform="translate(-1190.000000, -136.000000)">
|
||||||
<g id="Group-8" width="48" height="40" transform="translate(1167.000000, 0.000000)">
|
<g id="Group-8" width="48" height="40" transform="translate(1167.000000, 0.000000)">
|
||||||
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
|
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
|
||||||
<mask id="mask-3" fill="white">
|
<mask id="mask-3" fill="white">
|
||||||
<use xlink:href="#path-2"></use>
|
<use xlink:href="#path-2"></use>
|
||||||
</mask>
|
</mask>
|
||||||
<g id="Rectangle-18">
|
<g id="Rectangle-18">
|
||||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||||
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||||
</g>
|
</g>
|
||||||
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
|
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
|
||||||
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
|
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1,39 +1,39 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
|
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<defs>
|
<defs>
|
||||||
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
|
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
|
||||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||||
<feMerge>
|
<feMerge>
|
||||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||||
<feMergeNode in="SourceGraphic"></feMergeNode>
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||||
</feMerge>
|
</feMerge>
|
||||||
</filter>
|
</filter>
|
||||||
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||||
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
|
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
|
||||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||||
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
||||||
</filter>
|
</filter>
|
||||||
</defs>
|
</defs>
|
||||||
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
<g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
|
<g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
|
||||||
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||||
<g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
|
<g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
|
||||||
<mask id="mask-3" fill="white">
|
<mask id="mask-3" fill="white">
|
||||||
<use xlink:href="#path-2"></use>
|
<use xlink:href="#path-2"></use>
|
||||||
</mask>
|
</mask>
|
||||||
<g id="Rectangle-18">
|
<g id="Rectangle-18">
|
||||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||||
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||||
</g>
|
</g>
|
||||||
<rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
|
<rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
|
||||||
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
|
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1,99 +1,99 @@
|
|||||||
@import './variables.scss';
|
@import './variables.scss';
|
||||||
|
|
||||||
@mixin colorBtn($color) {
|
@mixin colorBtn($color) {
|
||||||
background: $color;
|
background: $color;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $color;
|
color: $color;
|
||||||
|
|
||||||
&:before,
|
&:before,
|
||||||
&:after {
|
&:after {
|
||||||
background: $color;
|
background: $color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.blue-btn {
|
.blue-btn {
|
||||||
@include colorBtn($blue)
|
@include colorBtn($blue)
|
||||||
}
|
}
|
||||||
|
|
||||||
.light-blue-btn {
|
.light-blue-btn {
|
||||||
@include colorBtn($light-blue)
|
@include colorBtn($light-blue)
|
||||||
}
|
}
|
||||||
|
|
||||||
.red-btn {
|
.red-btn {
|
||||||
@include colorBtn($red)
|
@include colorBtn($red)
|
||||||
}
|
}
|
||||||
|
|
||||||
.pink-btn {
|
.pink-btn {
|
||||||
@include colorBtn($pink)
|
@include colorBtn($pink)
|
||||||
}
|
}
|
||||||
|
|
||||||
.green-btn {
|
.green-btn {
|
||||||
@include colorBtn($green)
|
@include colorBtn($green)
|
||||||
}
|
}
|
||||||
|
|
||||||
.tiffany-btn {
|
.tiffany-btn {
|
||||||
@include colorBtn($tiffany)
|
@include colorBtn($tiffany)
|
||||||
}
|
}
|
||||||
|
|
||||||
.yellow-btn {
|
.yellow-btn {
|
||||||
@include colorBtn($yellow)
|
@include colorBtn($yellow)
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-btn {
|
.pan-btn {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 14px 36px;
|
padding: 14px 36px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: 600ms ease all;
|
transition: 600ms ease all;
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
||||||
&:before,
|
&:before,
|
||||||
&:after {
|
&:after {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: 600ms ease all;
|
transition: 600ms ease all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before,
|
&:before,
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
width: 0;
|
width: 0;
|
||||||
transition: 400ms ease all;
|
transition: 400ms ease all;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
right: inherit;
|
right: inherit;
|
||||||
top: inherit;
|
top: inherit;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-button {
|
.custom-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
/**
|
/**
|
||||||
* I think element-ui's default theme color is too light for long-term use.
|
* I think element-ui's default theme color is too light for long-term use.
|
||||||
* So I modified the default color and you can modify it to your liking.
|
* So I modified the default color and you can modify it to your liking.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
/* theme color */
|
/* theme color */
|
||||||
$--color-primary: #1890ff;
|
$--color-primary: #1890ff;
|
||||||
$--color-success: #13ce66;
|
$--color-success: #13ce66;
|
||||||
$--color-warning: #ffba00;
|
$--color-warning: #ffba00;
|
||||||
$--color-danger: #ff4949;
|
$--color-danger: #ff4949;
|
||||||
// $--color-info: #1E1E1E;
|
// $--color-info: #1E1E1E;
|
||||||
|
|
||||||
$--button-font-weight: 400;
|
$--button-font-weight: 400;
|
||||||
|
|
||||||
// $--color-text-regular: #1f2d3d;
|
// $--color-text-regular: #1f2d3d;
|
||||||
|
|
||||||
$--border-color-light: #dfe4ed;
|
$--border-color-light: #dfe4ed;
|
||||||
$--border-color-lighter: #e6ebf5;
|
$--border-color-lighter: #e6ebf5;
|
||||||
|
|
||||||
$--table-border: 1px solid #dfe6ec;
|
$--table-border: 1px solid #dfe6ec;
|
||||||
|
|
||||||
/* icon font path, required */
|
/* icon font path, required */
|
||||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
||||||
|
|
||||||
@import "~element-ui/packages/theme-chalk/src/index";
|
@import "~element-ui/packages/theme-chalk/src/index";
|
||||||
|
|
||||||
// the :export directive is the magic sauce for webpack
|
// the :export directive is the magic sauce for webpack
|
||||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||||
:export {
|
:export {
|
||||||
theme: $--color-primary;
|
theme: $--color-primary;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
@mixin clearfix {
|
@mixin clearfix {
|
||||||
&:after {
|
&:after {
|
||||||
content: "";
|
content: "";
|
||||||
display: table;
|
display: table;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin scrollBar {
|
@mixin scrollBar {
|
||||||
&::-webkit-scrollbar-track-piece {
|
&::-webkit-scrollbar-track-piece {
|
||||||
background: #d3dce6;
|
background: #d3dce6;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
background: #99a9bf;
|
background: #99a9bf;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin relative {
|
@mixin relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin pct($pct) {
|
@mixin pct($pct) {
|
||||||
width: #{$pct};
|
width: #{$pct};
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin triangle($width, $height, $color, $direction) {
|
@mixin triangle($width, $height, $color, $direction) {
|
||||||
$width: $width/2;
|
$width: $width/2;
|
||||||
$color-border-style: $height solid $color;
|
$color-border-style: $height solid $color;
|
||||||
$transparent-border-style: $width solid transparent;
|
$transparent-border-style: $width solid transparent;
|
||||||
height: 0;
|
height: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|
||||||
@if $direction==up {
|
@if $direction==up {
|
||||||
border-bottom: $color-border-style;
|
border-bottom: $color-border-style;
|
||||||
border-left: $transparent-border-style;
|
border-left: $transparent-border-style;
|
||||||
border-right: $transparent-border-style;
|
border-right: $transparent-border-style;
|
||||||
}
|
}
|
||||||
|
|
||||||
@else if $direction==right {
|
@else if $direction==right {
|
||||||
border-left: $color-border-style;
|
border-left: $color-border-style;
|
||||||
border-top: $transparent-border-style;
|
border-top: $transparent-border-style;
|
||||||
border-bottom: $transparent-border-style;
|
border-bottom: $transparent-border-style;
|
||||||
}
|
}
|
||||||
|
|
||||||
@else if $direction==down {
|
@else if $direction==down {
|
||||||
border-top: $color-border-style;
|
border-top: $color-border-style;
|
||||||
border-left: $transparent-border-style;
|
border-left: $transparent-border-style;
|
||||||
border-right: $transparent-border-style;
|
border-right: $transparent-border-style;
|
||||||
}
|
}
|
||||||
|
|
||||||
@else if $direction==left {
|
@else if $direction==left {
|
||||||
border-right: $color-border-style;
|
border-right: $color-border-style;
|
||||||
border-top: $transparent-border-style;
|
border-top: $transparent-border-style;
|
||||||
border-bottom: $transparent-border-style;
|
border-bottom: $transparent-border-style;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,10 +81,6 @@
|
|||||||
margin-top: 6vh !important;
|
margin-top: 6vh !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-dialog__body {
|
|
||||||
padding: 8px 20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body {
|
.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
@@ -232,191 +228,6 @@
|
|||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 详细卡片样式 */
|
|
||||||
.detail-drawer {
|
|
||||||
.el-drawer__header {
|
|
||||||
margin-bottom: 6px;
|
|
||||||
padding: 8px 12px 6px;
|
|
||||||
font-size: 15px;
|
|
||||||
color: #303133;
|
|
||||||
background: #f8f8f8;
|
|
||||||
}
|
|
||||||
.section-header {
|
|
||||||
font-size: 15px;
|
|
||||||
color: #6379bb;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
margin: 12px 0 16px 0;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
|
||||||
.drawer-content {
|
|
||||||
padding: 0 20px 20px 20px;
|
|
||||||
.info-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 8px 0;
|
|
||||||
min-height: 40px;
|
|
||||||
}
|
|
||||||
.info-label {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 200px;
|
|
||||||
color: #606266;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.6;
|
|
||||||
padding-top: 4px;
|
|
||||||
text-align: right;
|
|
||||||
margin-right: 14px;
|
|
||||||
}
|
|
||||||
.info-value {
|
|
||||||
flex: 1;
|
|
||||||
color: #303133;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.6;
|
|
||||||
word-break: break-all;
|
|
||||||
padding-top: 4px;
|
|
||||||
min-height: 1.6em;
|
|
||||||
&.plaintext {
|
|
||||||
border-bottom: 1px dashed #dde1e6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-wrap { padding: 0 4px; }
|
|
||||||
|
|
||||||
.detail-card {
|
|
||||||
border: 1px solid #ebeef5;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-card-title {
|
|
||||||
background: #f7f9fb;
|
|
||||||
padding: 8px 16px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
}
|
|
||||||
.detail-card-title i { margin-right: 5px; color: #409EFF; }
|
|
||||||
|
|
||||||
.detail-row { padding: 0 8px; }
|
|
||||||
|
|
||||||
.detail-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding: 10px 8px;
|
|
||||||
font-size: 13px;
|
|
||||||
border-bottom: 1px solid #f5f7fa;
|
|
||||||
}
|
|
||||||
.detail-item:last-child { border-bottom: none; }
|
|
||||||
|
|
||||||
.detail-label {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 72px;
|
|
||||||
color: #909399;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
.detail-value { color: #303133; flex: 1; word-break: break-all; }
|
|
||||||
.detail-location { color: #999; font-size: 12px; }
|
|
||||||
|
|
||||||
.method-tag {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 1px 7px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-right: 6px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.mono { font-family: Consolas, 'SFMono-Regular', monospace; font-size: 12px; }
|
|
||||||
.code-body { padding: 14px; }
|
|
||||||
.code-wrap {
|
|
||||||
background: #f7f9fb;
|
|
||||||
border: 1px solid #e8ecf0;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
max-height: 260px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.code-action {
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
right: 8px;
|
|
||||||
z-index: 10;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.code-action .el-button {
|
|
||||||
height: 24px;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
border: 1px solid #dcdcdc;
|
|
||||||
}
|
|
||||||
.code-action .el-button:hover {
|
|
||||||
background: #ffffff;
|
|
||||||
border-color: #409EFF;
|
|
||||||
}
|
|
||||||
.code-pre {
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 14px;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.6;
|
|
||||||
font-family: Consolas, 'SFMono-Regular', monospace;
|
|
||||||
color: #444;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
overflow: auto;
|
|
||||||
max-height: 240px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tree-sidebar content */
|
|
||||||
.tree-sidebar-manage-wrap {
|
|
||||||
display: flex;
|
|
||||||
gap: 0;
|
|
||||||
min-height: calc(100vh - 130px);
|
|
||||||
padding: 0 !important;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-sidebar-content {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
.content-inner {
|
|
||||||
padding: 12px 16px;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* error */
|
|
||||||
.error-title { color: #c0392b !important; }
|
|
||||||
.error-title i { color: #c0392b !important; }
|
|
||||||
.error-body { padding: 12px 16px; }
|
|
||||||
.error-msg {
|
|
||||||
background: #fff8f8;
|
|
||||||
border-left: 3px solid #e74c3c;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
color: #c0392b;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.7;
|
|
||||||
word-break: break-all;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* http method */
|
|
||||||
.method-GET { background: #e8f5e9; color: #27ae60; }
|
|
||||||
.method-POST { background: #e3f2fd; color: #1565c0; }
|
|
||||||
.method-PUT { background: #fff3e0; color: #e65100; }
|
|
||||||
.method-DELETE { background: #ffebee; color: #c62828; }
|
|
||||||
|
|
||||||
/* text color */
|
/* text color */
|
||||||
.text-navy {
|
.text-navy {
|
||||||
color: #1ab394;
|
color: #1ab394;
|
||||||
@@ -469,9 +280,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 拖拽列样式 */
|
/* 拖拽列样式 */
|
||||||
.allowDrag { cursor: grab; }
|
|
||||||
.allowDrag:active { cursor: grabbing; }
|
|
||||||
|
|
||||||
.sortable-ghost {
|
.sortable-ghost {
|
||||||
opacity: .8;
|
opacity: .8;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
@@ -482,3 +290,8 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 分割面板样式 */
|
||||||
|
.splitpanes.default-theme .splitpanes__pane {
|
||||||
|
background-color: #fff!important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,321 +1,229 @@
|
|||||||
#app {
|
#app {
|
||||||
|
|
||||||
.main-container {
|
.main-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
transition: margin-left .28s;
|
transition: margin-left .28s;
|
||||||
margin-left: $base-sidebar-width;
|
margin-left: $base-sidebar-width;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebarHide {
|
.sidebarHide {
|
||||||
margin-left: 0!important;
|
margin-left: 0!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-container {
|
.sidebar-container {
|
||||||
-webkit-transition: width .28s;
|
-webkit-transition: width .28s;
|
||||||
transition: width 0.28s;
|
transition: width 0.28s;
|
||||||
width: $base-sidebar-width !important;
|
width: $base-sidebar-width !important;
|
||||||
background-color: $base-menu-background;
|
background-color: $base-menu-background;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
font-size: 0px;
|
font-size: 0px;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);
|
-webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35);
|
||||||
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
// reset element-ui css
|
// reset element-ui css
|
||||||
.horizontal-collapse-transition {
|
.horizontal-collapse-transition {
|
||||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
|
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-wrapper {
|
.scrollbar-wrapper {
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-scrollbar__bar.is-vertical {
|
.el-scrollbar__bar.is-vertical {
|
||||||
right: 0px;
|
right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-scrollbar {
|
.el-scrollbar {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.has-logo {
|
&.has-logo {
|
||||||
.el-scrollbar {
|
.el-scrollbar {
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 50px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-horizontal {
|
.is-horizontal {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
margin-right: 10px !important;
|
margin-right: 10px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu {
|
.el-menu {
|
||||||
border: none;
|
border: none;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-item, .el-submenu__title {
|
.el-menu-item, .el-submenu__title {
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
text-overflow: ellipsis !important;
|
text-overflow: ellipsis !important;
|
||||||
white-space: nowrap !important;
|
white-space: nowrap !important;
|
||||||
height: 44px !important;
|
}
|
||||||
line-height: 44px !important;
|
|
||||||
}
|
// menu hover
|
||||||
|
.submenu-title-noDropdown,
|
||||||
// menu hover
|
.el-submenu__title {
|
||||||
.submenu-title-noDropdown,
|
&:hover {
|
||||||
.el-submenu__title {
|
background-color: rgba(0, 0, 0, 0.06) !important;
|
||||||
&:hover {
|
}
|
||||||
background-color: rgba(0, 0, 0, 0.06) !important;
|
}
|
||||||
}
|
|
||||||
}
|
& .theme-dark .is-active > .el-submenu__title {
|
||||||
|
color: $base-menu-color-active !important;
|
||||||
& .theme-dark .is-active > .el-submenu__title {
|
}
|
||||||
color: $base-menu-color-active !important;
|
|
||||||
}
|
& .nest-menu .el-submenu>.el-submenu__title,
|
||||||
|
& .el-submenu .el-menu-item {
|
||||||
& .nest-menu .el-submenu>.el-submenu__title,
|
min-width: $base-sidebar-width !important;
|
||||||
& .el-submenu .el-menu-item {
|
|
||||||
min-width: $base-sidebar-width !important;
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.06) !important;
|
||||||
&:hover {
|
}
|
||||||
background-color: rgba(0, 0, 0, 0.06) !important;
|
}
|
||||||
}
|
|
||||||
}
|
& .theme-dark .nest-menu .el-submenu>.el-submenu__title,
|
||||||
|
& .theme-dark .el-submenu .el-menu-item {
|
||||||
& .theme-dark .nest-menu .el-submenu>.el-submenu__title,
|
background-color: $base-sub-menu-background !important;
|
||||||
& .theme-dark .el-submenu .el-menu-item {
|
|
||||||
background-color: $base-sub-menu-background !important;
|
&:hover {
|
||||||
|
background-color: $base-sub-menu-hover !important;
|
||||||
&:hover {
|
}
|
||||||
background-color: $base-sub-menu-hover !important;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.hideSidebar {
|
||||||
// theme-dark 深色主题
|
.sidebar-container {
|
||||||
&.theme-dark {
|
width: 54px !important;
|
||||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.4);
|
}
|
||||||
border-right: none;
|
|
||||||
|
.main-container {
|
||||||
.el-menu-item.is-active {
|
margin-left: 54px;
|
||||||
position: relative;
|
}
|
||||||
|
|
||||||
&::before {
|
.el-menu:not(.el-menu--horizontal) {
|
||||||
content: '';
|
.submenu-title-noDropdown {
|
||||||
position: absolute;
|
padding: 0 !important;
|
||||||
inset: 0;
|
position: relative;
|
||||||
background-color: var(--current-color-dark-bg, rgba(64, 158, 255, 0.2));
|
|
||||||
border-right: 3px solid var(--current-color, #409eff);
|
.el-tooltip {
|
||||||
pointer-events: none;
|
padding: 0 !important;
|
||||||
z-index: 1;
|
|
||||||
}
|
.svg-icon {
|
||||||
}
|
margin-left: 20px;
|
||||||
|
}
|
||||||
.el-submenu.is-active > .el-submenu__title {
|
}
|
||||||
color: var(--current-color, #409eff) !important;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-item:not(.is-active),
|
.el-submenu {
|
||||||
.submenu-title-noDropdown,
|
overflow: hidden;
|
||||||
.el-submenu__title {
|
|
||||||
position: relative;
|
&>.el-submenu__title {
|
||||||
|
padding: 0 !important;
|
||||||
&::before {
|
|
||||||
content: '';
|
.svg-icon {
|
||||||
position: absolute;
|
margin-left: 20px;
|
||||||
inset: 0;
|
}
|
||||||
background-color: transparent;
|
|
||||||
pointer-events: none;
|
}
|
||||||
z-index: 1;
|
}
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
.el-menu--collapse {
|
||||||
|
.el-submenu {
|
||||||
&:hover::before {
|
&>.el-submenu__title {
|
||||||
background-color: var(--current-color-dark-bg, rgba(64, 158, 255, 0.2));
|
&>span {
|
||||||
}
|
height: 0;
|
||||||
}
|
width: 0;
|
||||||
}
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
// theme-light 浅色主题
|
display: inline-block;
|
||||||
&.theme-light {
|
}
|
||||||
border-right: 1px solid #e8eaf0;
|
}
|
||||||
box-shadow: none;
|
}
|
||||||
|
}
|
||||||
.el-menu-item,
|
}
|
||||||
.el-submenu__title {
|
|
||||||
color: rgba(0, 0, 0, 0.65);
|
.el-menu--collapse .el-menu .el-submenu {
|
||||||
}
|
min-width: $base-sidebar-width !important;
|
||||||
|
}
|
||||||
.el-menu-item.is-active {
|
|
||||||
color: var(--current-color, #409eff) !important;
|
// mobile responsive
|
||||||
position: relative;
|
.mobile {
|
||||||
|
.main-container {
|
||||||
&::before {
|
margin-left: 0px;
|
||||||
content: '';
|
}
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
.sidebar-container {
|
||||||
background-color: var(--current-color-light, #ecf5ff);
|
transition: transform .28s;
|
||||||
border-right: 3px solid var(--current-color, #409eff);
|
width: $base-sidebar-width !important;
|
||||||
pointer-events: none;
|
}
|
||||||
z-index: 1;
|
|
||||||
}
|
&.hideSidebar {
|
||||||
}
|
.sidebar-container {
|
||||||
|
pointer-events: none;
|
||||||
.el-submenu.is-active > .el-submenu__title {
|
transition-duration: 0.3s;
|
||||||
color: var(--current-color, #409eff) !important;
|
transform: translate3d(-$base-sidebar-width, 0, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.el-menu-item:not(.is-active):hover,
|
}
|
||||||
.submenu-title-noDropdown:hover,
|
|
||||||
.el-submenu__title:hover {
|
.withoutAnimation {
|
||||||
background-color: #f5f7fa !important;
|
|
||||||
color: rgba(0, 0, 0, 0.85) !important;
|
.main-container,
|
||||||
}
|
.sidebar-container {
|
||||||
|
transition: none;
|
||||||
.nest-menu .el-submenu > .el-submenu__title,
|
}
|
||||||
.el-submenu .el-menu-item {
|
}
|
||||||
background-color: #fafafa !important;
|
}
|
||||||
|
|
||||||
&:hover {
|
// when menu collapsed
|
||||||
background-color: #f0f5ff !important;
|
.el-menu--vertical {
|
||||||
}
|
&>.el-menu {
|
||||||
}
|
.svg-icon {
|
||||||
}
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.hideSidebar {
|
|
||||||
.sidebar-container {
|
.nest-menu .el-submenu>.el-submenu__title,
|
||||||
width: 54px !important;
|
.el-menu-item {
|
||||||
}
|
&:hover {
|
||||||
|
// you can use $subMenuHover
|
||||||
.main-container {
|
background-color: rgba(0, 0, 0, 0.06) !important;
|
||||||
margin-left: 54px;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu:not(.el-menu--horizontal) {
|
// the scroll bar appears when the subMenu is too long
|
||||||
.submenu-title-noDropdown {
|
>.el-menu--popup {
|
||||||
padding: 0 !important;
|
max-height: 100vh;
|
||||||
position: relative;
|
overflow-y: auto;
|
||||||
|
|
||||||
.el-tooltip {
|
&::-webkit-scrollbar-track-piece {
|
||||||
padding: 0 !important;
|
background: #d3dce6;
|
||||||
|
}
|
||||||
.svg-icon {
|
|
||||||
margin-left: 20px;
|
&::-webkit-scrollbar {
|
||||||
}
|
width: 6px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: #99a9bf;
|
||||||
.el-submenu {
|
border-radius: 20px;
|
||||||
overflow: hidden;
|
}
|
||||||
|
}
|
||||||
&>.el-submenu__title {
|
}
|
||||||
padding: 0 !important;
|
|
||||||
|
|
||||||
.svg-icon {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-menu--collapse {
|
|
||||||
.el-submenu {
|
|
||||||
&>.el-submenu__title {
|
|
||||||
&>span {
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
visibility: hidden;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-menu--collapse .el-menu .el-submenu {
|
|
||||||
min-width: $base-sidebar-width !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mobile responsive
|
|
||||||
.mobile {
|
|
||||||
.main-container {
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-container {
|
|
||||||
transition: transform .28s;
|
|
||||||
width: $base-sidebar-width !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.hideSidebar {
|
|
||||||
.sidebar-container {
|
|
||||||
pointer-events: none;
|
|
||||||
transition-duration: 0.3s;
|
|
||||||
transform: translate3d(-$base-sidebar-width, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.withoutAnimation {
|
|
||||||
|
|
||||||
.main-container,
|
|
||||||
.sidebar-container {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// when menu collapsed
|
|
||||||
.el-menu--vertical {
|
|
||||||
&>.el-menu {
|
|
||||||
.svg-icon {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nest-menu .el-submenu>.el-submenu__title,
|
|
||||||
.el-menu-item {
|
|
||||||
&:hover {
|
|
||||||
// you can use $subMenuHover
|
|
||||||
background-color: rgba(0, 0, 0, 0.06) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the scroll bar appears when the subMenu is too long
|
|
||||||
>.el-menu--popup {
|
|
||||||
max-height: 100vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track-piece {
|
|
||||||
background: #d3dce6;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: #99a9bf;
|
|
||||||
border-radius: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
// global transition css
|
// global transition css
|
||||||
|
|
||||||
/* fade */
|
/* fade */
|
||||||
.fade-enter-active,
|
.fade-enter-active,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.28s;
|
transition: opacity 0.28s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter,
|
.fade-enter,
|
||||||
.fade-leave-active {
|
.fade-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fade-transform */
|
/* fade-transform */
|
||||||
.fade-transform--move,
|
.fade-transform--move,
|
||||||
.fade-transform-leave-active,
|
.fade-transform-leave-active,
|
||||||
.fade-transform-enter-active {
|
.fade-transform-enter-active {
|
||||||
transition: all .5s;
|
transition: all .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-transform-enter {
|
.fade-transform-enter {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-30px);
|
transform: translateX(-30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-transform-leave-to {
|
.fade-transform-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(30px);
|
transform: translateX(30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* breadcrumb transition */
|
/* breadcrumb transition */
|
||||||
.breadcrumb-enter-active,
|
.breadcrumb-enter-active,
|
||||||
.breadcrumb-leave-active {
|
.breadcrumb-leave-active {
|
||||||
transition: all .5s;
|
transition: all .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-enter,
|
.breadcrumb-enter,
|
||||||
.breadcrumb-leave-active {
|
.breadcrumb-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(20px);
|
transform: translateX(20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-move {
|
.breadcrumb-move {
|
||||||
transition: all .5s;
|
transition: all .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-leave-active {
|
.breadcrumb-leave-active {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,54 @@
|
|||||||
// base color
|
// base color
|
||||||
$blue:#324157;
|
$blue:#324157;
|
||||||
$light-blue:#3A71A8;
|
$light-blue:#3A71A8;
|
||||||
$red:#C03639;
|
$red:#C03639;
|
||||||
$pink: #E65D6E;
|
$pink: #E65D6E;
|
||||||
$green: #30B08F;
|
$green: #30B08F;
|
||||||
$tiffany: #4AB7BD;
|
$tiffany: #4AB7BD;
|
||||||
$yellow:#FEC171;
|
$yellow:#FEC171;
|
||||||
$panGreen: #30B08F;
|
$panGreen: #30B08F;
|
||||||
|
|
||||||
// 默认菜单主题风格
|
// 默认菜单主题风格
|
||||||
$base-menu-color: #bfcbd9;
|
$base-menu-color:#bfcbd9;
|
||||||
$base-menu-color-active: #ffffff;
|
$base-menu-color-active:#f4f4f5;
|
||||||
$base-menu-background: #1a1f2e;
|
$base-menu-background:#304156;
|
||||||
$base-logo-title-color: #ffffff;
|
$base-logo-title-color: #ffffff;
|
||||||
|
|
||||||
$base-menu-light-color:rgba(0,0,0,.70);
|
$base-menu-light-color:rgba(0,0,0,.70);
|
||||||
$base-menu-light-background:#ffffff;
|
$base-menu-light-background:#ffffff;
|
||||||
$base-logo-light-title-color: #001529;
|
$base-logo-light-title-color: #001529;
|
||||||
|
|
||||||
$base-sub-menu-background: #141824;
|
$base-sub-menu-background:#1f2d3d;
|
||||||
$base-sub-menu-hover: rgba(255,255,255,.06);
|
$base-sub-menu-hover:#001528;
|
||||||
|
|
||||||
$base-sidebar-width: 200px;
|
// 自定义暗色菜单风格
|
||||||
|
/**
|
||||||
// the :export directive is the magic sauce for webpack
|
$base-menu-color:hsla(0,0%,100%,.65);
|
||||||
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
$base-menu-color-active:#fff;
|
||||||
:export {
|
$base-menu-background:#001529;
|
||||||
menuColor: $base-menu-color;
|
$base-logo-title-color: #ffffff;
|
||||||
menuLightColor: $base-menu-light-color;
|
|
||||||
menuColorActive: $base-menu-color-active;
|
$base-menu-light-color:rgba(0,0,0,.70);
|
||||||
menuBackground: $base-menu-background;
|
$base-menu-light-background:#ffffff;
|
||||||
menuLightBackground: $base-menu-light-background;
|
$base-logo-light-title-color: #001529;
|
||||||
subMenuBackground: $base-sub-menu-background;
|
|
||||||
subMenuHover: $base-sub-menu-hover;
|
$base-sub-menu-background:#000c17;
|
||||||
sideBarWidth: $base-sidebar-width;
|
$base-sub-menu-hover:#001528;
|
||||||
logoTitleColor: $base-logo-title-color;
|
*/
|
||||||
logoLightTitleColor: $base-logo-light-title-color
|
|
||||||
}
|
$base-sidebar-width: 200px;
|
||||||
|
|
||||||
|
// the :export directive is the magic sauce for webpack
|
||||||
|
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||||
|
:export {
|
||||||
|
menuColor: $base-menu-color;
|
||||||
|
menuLightColor: $base-menu-light-color;
|
||||||
|
menuColorActive: $base-menu-color-active;
|
||||||
|
menuBackground: $base-menu-background;
|
||||||
|
menuLightBackground: $base-menu-light-background;
|
||||||
|
subMenuBackground: $base-sub-menu-background;
|
||||||
|
subMenuHover: $base-sub-menu-hover;
|
||||||
|
sideBarWidth: $base-sidebar-width;
|
||||||
|
logoTitleColor: $base-logo-title-color;
|
||||||
|
logoLightTitleColor: $base-logo-light-title-color
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,116 +1,116 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form size="small">
|
<el-form size="small">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-radio v-model='radioValue' :label="1">
|
<el-radio v-model='radioValue' :label="1">
|
||||||
分钟,允许的通配符[, - * /]
|
分钟,允许的通配符[, - * /]
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-radio v-model='radioValue' :label="2">
|
<el-radio v-model='radioValue' :label="2">
|
||||||
周期从
|
周期从
|
||||||
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
||||||
<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 1" :max="59" /> 分钟
|
<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 1" :max="59" /> 分钟
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-radio v-model='radioValue' :label="3">
|
<el-radio v-model='radioValue' :label="3">
|
||||||
从
|
从
|
||||||
<el-input-number v-model='average01' :min="0" :max="58" /> 分钟开始,每
|
<el-input-number v-model='average01' :min="0" :max="58" /> 分钟开始,每
|
||||||
<el-input-number v-model='average02' :min="1" :max="59 - average01 || 0" /> 分钟执行一次
|
<el-input-number v-model='average02' :min="1" :max="59 - average01 || 0" /> 分钟执行一次
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-radio v-model='radioValue' :label="4">
|
<el-radio v-model='radioValue' :label="4">
|
||||||
指定
|
指定
|
||||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
||||||
<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
|
<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-radio>
|
</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
radioValue: 1,
|
radioValue: 1,
|
||||||
cycle01: 1,
|
cycle01: 1,
|
||||||
cycle02: 2,
|
cycle02: 2,
|
||||||
average01: 0,
|
average01: 0,
|
||||||
average02: 1,
|
average02: 1,
|
||||||
checkboxList: [],
|
checkboxList: [],
|
||||||
checkNum: this.$options.propsData.check
|
checkNum: this.$options.propsData.check
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
name: 'crontab-min',
|
name: 'crontab-min',
|
||||||
props: ['check', 'cron'],
|
props: ['check', 'cron'],
|
||||||
methods: {
|
methods: {
|
||||||
// 单选按钮值变化时
|
// 单选按钮值变化时
|
||||||
radioChange() {
|
radioChange() {
|
||||||
switch (this.radioValue) {
|
switch (this.radioValue) {
|
||||||
case 1:
|
case 1:
|
||||||
this.$emit('update', 'min', '*', 'min')
|
this.$emit('update', 'min', '*', 'min')
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
this.$emit('update', 'min', this.cycleTotal, 'min')
|
this.$emit('update', 'min', this.cycleTotal, 'min')
|
||||||
break
|
break
|
||||||
case 3:
|
case 3:
|
||||||
this.$emit('update', 'min', this.averageTotal, 'min')
|
this.$emit('update', 'min', this.averageTotal, 'min')
|
||||||
break
|
break
|
||||||
case 4:
|
case 4:
|
||||||
this.$emit('update', 'min', this.checkboxString, 'min')
|
this.$emit('update', 'min', this.checkboxString, 'min')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 周期两个值变化时
|
// 周期两个值变化时
|
||||||
cycleChange() {
|
cycleChange() {
|
||||||
if (this.radioValue == '2') {
|
if (this.radioValue == '2') {
|
||||||
this.$emit('update', 'min', this.cycleTotal, 'min')
|
this.$emit('update', 'min', this.cycleTotal, 'min')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 平均两个值变化时
|
// 平均两个值变化时
|
||||||
averageChange() {
|
averageChange() {
|
||||||
if (this.radioValue == '3') {
|
if (this.radioValue == '3') {
|
||||||
this.$emit('update', 'min', this.averageTotal, 'min')
|
this.$emit('update', 'min', this.averageTotal, 'min')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// checkbox值变化时
|
// checkbox值变化时
|
||||||
checkboxChange() {
|
checkboxChange() {
|
||||||
if (this.radioValue == '4') {
|
if (this.radioValue == '4') {
|
||||||
this.$emit('update', 'min', this.checkboxString, 'min')
|
this.$emit('update', 'min', this.checkboxString, 'min')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'radioValue': 'radioChange',
|
'radioValue': 'radioChange',
|
||||||
'cycleTotal': 'cycleChange',
|
'cycleTotal': 'cycleChange',
|
||||||
'averageTotal': 'averageChange',
|
'averageTotal': 'averageChange',
|
||||||
'checkboxString': 'checkboxChange',
|
'checkboxString': 'checkboxChange',
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// 计算两个周期值
|
// 计算两个周期值
|
||||||
cycleTotal: function () {
|
cycleTotal: function () {
|
||||||
const cycle01 = this.checkNum(this.cycle01, 0, 58)
|
const cycle01 = this.checkNum(this.cycle01, 0, 58)
|
||||||
const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)
|
const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)
|
||||||
return cycle01 + '-' + cycle02
|
return cycle01 + '-' + cycle02
|
||||||
},
|
},
|
||||||
// 计算平均用到的值
|
// 计算平均用到的值
|
||||||
averageTotal: function () {
|
averageTotal: function () {
|
||||||
const average01 = this.checkNum(this.average01, 0, 58)
|
const average01 = this.checkNum(this.average01, 0, 58)
|
||||||
const average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)
|
const average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)
|
||||||
return average01 + '/' + average02
|
return average01 + '/' + average02
|
||||||
},
|
},
|
||||||
// 计算勾选的checkbox值合集
|
// 计算勾选的checkbox值合集
|
||||||
checkboxString: function () {
|
checkboxString: function () {
|
||||||
let str = this.checkboxList.join()
|
let str = this.checkboxList.join()
|
||||||
return str == '' ? '*' : str
|
return str == '' ? '*' : str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,49 +1,49 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import DataDict from '@/utils/dict'
|
import DataDict from '@/utils/dict'
|
||||||
import { getDicts as getDicts } from '@/api/system/dict/data'
|
import { getDicts as getDicts } from '@/api/system/dict/data'
|
||||||
|
|
||||||
function searchDictByKey(dict, key) {
|
function searchDictByKey(dict, key) {
|
||||||
if (key == null && key == "") {
|
if (key == null && key == "") {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
for (let i = 0; i < dict.length; i++) {
|
for (let i = 0; i < dict.length; i++) {
|
||||||
if (dict[i].key == key) {
|
if (dict[i].key == key) {
|
||||||
return dict[i].value
|
return dict[i].value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function install() {
|
function install() {
|
||||||
Vue.use(DataDict, {
|
Vue.use(DataDict, {
|
||||||
metas: {
|
metas: {
|
||||||
'*': {
|
'*': {
|
||||||
labelField: 'dictLabel',
|
labelField: 'dictLabel',
|
||||||
valueField: 'dictValue',
|
valueField: 'dictValue',
|
||||||
request(dictMeta) {
|
request(dictMeta) {
|
||||||
const storeDict = searchDictByKey(store.getters.dict, dictMeta.type)
|
const storeDict = searchDictByKey(store.getters.dict, dictMeta.type)
|
||||||
if (storeDict) {
|
if (storeDict) {
|
||||||
return new Promise(resolve => { resolve(storeDict) })
|
return new Promise(resolve => { resolve(storeDict) })
|
||||||
} else {
|
} else {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getDicts(dictMeta.type).then(res => {
|
getDicts(dictMeta.type).then(res => {
|
||||||
store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data })
|
store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data })
|
||||||
resolve(res.data)
|
resolve(res.data)
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
install,
|
install,
|
||||||
}
|
}
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-dialog :title="title" :visible.sync="visible" :width="width" append-to-body @close="handleClose">
|
|
||||||
<el-upload ref="uploadRef" :limit="1" accept=".xlsx, .xls" :headers="headers" :action="uploadUrl" :disabled="isUploading" :on-progress="handleProgress" :on-success="handleSuccess" :auto-upload="false" drag>
|
|
||||||
<i class="el-icon-upload"></i>
|
|
||||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
||||||
<div class="el-upload__tip text-center" slot="tip">
|
|
||||||
<div class="el-upload__tip" slot="tip">
|
|
||||||
<el-checkbox v-model="updateSupport"> {{ updateSupportLabel }} </el-checkbox>
|
|
||||||
</div>
|
|
||||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
|
||||||
<el-link v-if="templateUrl" type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="handleDownloadTemplate">下载模板</el-link>
|
|
||||||
</div>
|
|
||||||
</el-upload>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
|
||||||
<el-button @click="visible = false">取 消</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { getToken } from '@/utils/auth'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
// 对话框标题
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: '数据导入'
|
|
||||||
},
|
|
||||||
// 对话框宽度
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: '400px'
|
|
||||||
},
|
|
||||||
// 上传接口地址(必传)
|
|
||||||
action: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
// 模板下载接口地址,不传则不显示下载模板链接
|
|
||||||
templateAction: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
// 模板文件名
|
|
||||||
templateFileName: {
|
|
||||||
type: String,
|
|
||||||
default: 'template'
|
|
||||||
},
|
|
||||||
// 覆盖更新勾选框的说明文字
|
|
||||||
updateSupportLabel: {
|
|
||||||
type: String,
|
|
||||||
default: '是否更新已经存在的数据'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
visible: false,
|
|
||||||
isUploading: false,
|
|
||||||
updateSupport: false,
|
|
||||||
headers: { Authorization: 'Bearer ' + getToken() }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
uploadUrl() {
|
|
||||||
return process.env.VUE_APP_BASE_API + this.action + '?updateSupport=' + (this.updateSupport ? 1 : 0)
|
|
||||||
},
|
|
||||||
templateUrl() {
|
|
||||||
return !!this.templateAction
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 打开对话框(供父组件通过 ref 调用)
|
|
||||||
open() {
|
|
||||||
this.updateSupport = false
|
|
||||||
this.isUploading = false
|
|
||||||
this.visible = true
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (this.$refs.uploadRef) {
|
|
||||||
this.$refs.uploadRef.clearFiles()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 关闭时清理
|
|
||||||
handleClose() {
|
|
||||||
this.isUploading = false
|
|
||||||
if (this.$refs.uploadRef) {
|
|
||||||
this.$refs.uploadRef.clearFiles()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 下载模板
|
|
||||||
handleDownloadTemplate() {
|
|
||||||
this.download(this.templateAction, {}, `${this.templateFileName}_${new Date().getTime()}.xlsx`)
|
|
||||||
},
|
|
||||||
// 上传进度
|
|
||||||
handleProgress() {
|
|
||||||
this.isUploading = true
|
|
||||||
},
|
|
||||||
// 上传成功
|
|
||||||
handleSuccess(response) {
|
|
||||||
this.visible = false
|
|
||||||
this.isUploading = false
|
|
||||||
if (this.$refs.uploadRef) {
|
|
||||||
this.$refs.uploadRef.clearFiles()
|
|
||||||
}
|
|
||||||
this.$alert("<div style='overflow:auto;overflow-x:hidden;max-height:70vh;padding:10px 20px 0;'>" + response.msg + '</div>', '导入结果', { dangerouslyUseHTMLString: true })
|
|
||||||
this.$emit('success')
|
|
||||||
},
|
|
||||||
// 提交上传
|
|
||||||
handleSubmit() {
|
|
||||||
const files = this.$refs.uploadRef.uploadFiles
|
|
||||||
if (!files || files.length === 0) {
|
|
||||||
this.$modal.msgError('请选择要上传的文件。')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const name = files[0].name.toLowerCase()
|
|
||||||
if (!name.endsWith('.xls') && !name.endsWith('.xlsx')) {
|
|
||||||
this.$modal.msgError('请选择后缀为 "xls" 或 "xlsx" 的文件。')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$refs.uploadRef.submit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,44 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="padding: 0 15px;" @click="toggleClick">
|
<div style="padding: 0 15px;" @click="toggleClick">
|
||||||
<svg
|
<svg
|
||||||
:class="{'is-active':isActive}"
|
:class="{'is-active':isActive}"
|
||||||
class="hamburger"
|
class="hamburger"
|
||||||
viewBox="0 0 1024 1024"
|
viewBox="0 0 1024 1024"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="64"
|
width="64"
|
||||||
height="64"
|
height="64"
|
||||||
>
|
>
|
||||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'Hamburger',
|
name: 'Hamburger',
|
||||||
props: {
|
props: {
|
||||||
isActive: {
|
isActive: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleClick() {
|
toggleClick() {
|
||||||
this.$emit('toggleClick')
|
this.$emit('toggleClick')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.hamburger {
|
.hamburger {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hamburger.is-active {
|
.hamburger.is-active {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,397 +1,264 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="header-search">
|
<div class="header-search">
|
||||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:visible.sync="show"
|
:visible.sync="show"
|
||||||
width="600px"
|
width="600px"
|
||||||
@close="close"
|
@close="close"
|
||||||
@opened="onDialogOpened"
|
:show-close="false"
|
||||||
:show-close="false"
|
append-to-body
|
||||||
append-to-body
|
>
|
||||||
>
|
<el-input
|
||||||
<el-input
|
v-model="search"
|
||||||
v-model="search"
|
ref="headerSearchSelectRef"
|
||||||
ref="headerSearchSelectRef"
|
size="large"
|
||||||
size="large"
|
@input="querySearch"
|
||||||
@input="querySearch"
|
prefix-icon="el-icon-search"
|
||||||
prefix-icon="el-icon-search"
|
placeholder="菜单搜索,支持标题、URL模糊查询"
|
||||||
placeholder="菜单搜索,支持标题、URL模糊查询"
|
clearable
|
||||||
clearable
|
@keyup.enter.native="selectActiveResult"
|
||||||
@keyup.enter.native="selectActiveResult"
|
@keydown.up.native="navigateResult('up')"
|
||||||
@keydown.up.native="navigateResult('up')"
|
@keydown.down.native="navigateResult('down')"
|
||||||
@keydown.down.native="navigateResult('down')"
|
>
|
||||||
>
|
</el-input>
|
||||||
</el-input>
|
<el-scrollbar wrap-class="right-scrollbar-wrapper">
|
||||||
|
<div class="result-wrap">
|
||||||
<div class="result-count" v-if="search && options.length > 0">
|
<div class="search-item" v-for="(item, index) in options" :key="item.path" :style="activeStyle(index)" @mouseenter="activeIndex = index" @mouseleave="activeIndex = -1">
|
||||||
找到 <strong>{{ options.length }}</strong> 个结果
|
<div class="left">
|
||||||
</div>
|
<svg-icon class="menu-icon" :icon-class="item.icon" />
|
||||||
|
</div>
|
||||||
<el-scrollbar wrap-class="right-scrollbar-wrapper">
|
<div class="search-info" @click="change(item)">
|
||||||
<div class="result-wrap">
|
<div class="menu-title">
|
||||||
<template v-if="options.length > 0">
|
{{ item.title.join(" / ") }}
|
||||||
<div
|
</div>
|
||||||
class="search-item"
|
<div class="menu-path">
|
||||||
v-for="(item, index) in options"
|
{{ item.path }}
|
||||||
:key="item.path"
|
</div>
|
||||||
:class="{ 'is-active': index === activeIndex }"
|
</div>
|
||||||
:style="activeStyle(index)"
|
<svg-icon icon-class="enter" v-show="index === activeIndex"/>
|
||||||
@mouseenter="activeIndex = index"
|
</div>
|
||||||
@mouseleave="activeIndex = -1"
|
</div>
|
||||||
>
|
</el-scrollbar>
|
||||||
<div class="left">
|
</el-dialog>
|
||||||
<svg-icon class="menu-icon" :icon-class="item.icon" />
|
</div>
|
||||||
</div>
|
</template>
|
||||||
<div class="search-info" @click="change(item)">
|
|
||||||
<div class="menu-title" v-html="highlightText(item.title.join(' / '))"></div>
|
<script>
|
||||||
<div class="menu-path" v-html="highlightText(item.path)"></div>
|
import Fuse from 'fuse.js/dist/fuse.min.js'
|
||||||
</div>
|
import path from 'path'
|
||||||
<svg-icon icon-class="enter" v-show="index === activeIndex" />
|
import { isHttp } from '@/utils/validate'
|
||||||
</div>
|
|
||||||
</template>
|
export default {
|
||||||
|
name: 'HeaderSearch',
|
||||||
<div class="empty-state" v-else-if="search && options.length === 0">
|
data() {
|
||||||
<i class="el-icon-search empty-icon"></i>
|
return {
|
||||||
<p class="empty-text">未找到 "<strong>{{ search }}</strong>" 相关菜单</p>
|
search: '',
|
||||||
<p class="empty-tip">试试其他关键词或路径</p>
|
options: [],
|
||||||
</div>
|
searchPool: [],
|
||||||
|
activeIndex: -1,
|
||||||
</div>
|
show: false,
|
||||||
</el-scrollbar>
|
fuse: undefined
|
||||||
|
}
|
||||||
<div class="search-footer">
|
},
|
||||||
<span class="shortcut-item">
|
computed: {
|
||||||
<kbd>↑</kbd><kbd>↓</kbd> 切换
|
theme() {
|
||||||
</span>
|
return this.$store.state.settings.theme
|
||||||
<span class="shortcut-item">
|
},
|
||||||
<kbd>↵</kbd> 选择
|
routes() {
|
||||||
</span>
|
return this.$store.getters.defaultRoutes
|
||||||
<span class="shortcut-item">
|
}
|
||||||
<kbd>Esc</kbd> 关闭
|
},
|
||||||
</span>
|
watch: {
|
||||||
</div>
|
routes() {
|
||||||
</el-dialog>
|
this.searchPool = this.generateRoutes(this.routes)
|
||||||
</div>
|
},
|
||||||
</template>
|
searchPool(list) {
|
||||||
|
this.initFuse(list)
|
||||||
<script>
|
}
|
||||||
import Fuse from 'fuse.js/dist/fuse.min.js'
|
},
|
||||||
import path from 'path'
|
mounted() {
|
||||||
import { isHttp } from '@/utils/validate'
|
this.searchPool = this.generateRoutes(this.routes)
|
||||||
|
},
|
||||||
export default {
|
methods: {
|
||||||
name: 'HeaderSearch',
|
click() {
|
||||||
data() {
|
this.show = !this.show
|
||||||
return {
|
if (this.show) {
|
||||||
search: '',
|
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
|
||||||
options: [],
|
this.options = this.searchPool
|
||||||
searchPool: [],
|
}
|
||||||
activeIndex: -1,
|
},
|
||||||
show: false,
|
close() {
|
||||||
fuse: undefined
|
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
|
||||||
}
|
this.search = ''
|
||||||
},
|
this.options = []
|
||||||
computed: {
|
this.show = false
|
||||||
theme() {
|
this.activeIndex = -1
|
||||||
return this.$store.state.settings.theme
|
},
|
||||||
},
|
change(val) {
|
||||||
routes() {
|
const path = val.path
|
||||||
return this.$store.getters.defaultRoutes
|
const query = val.query
|
||||||
}
|
if(isHttp(val.path)) {
|
||||||
},
|
// http(s):// 路径新窗口打开
|
||||||
watch: {
|
const pindex = path.indexOf("http")
|
||||||
routes() {
|
window.open(path.substr(pindex, path.length), "_blank")
|
||||||
this.searchPool = this.generateRoutes(this.routes)
|
} else {
|
||||||
},
|
if (query) {
|
||||||
searchPool(list) {
|
this.$router.push({ path: path, query: JSON.parse(query) })
|
||||||
this.initFuse(list)
|
} else {
|
||||||
}
|
this.$router.push(path)
|
||||||
},
|
}
|
||||||
mounted() {
|
}
|
||||||
this.searchPool = this.generateRoutes(this.routes)
|
this.search = ''
|
||||||
},
|
this.options = []
|
||||||
methods: {
|
this.$nextTick(() => {
|
||||||
click() {
|
this.show = false
|
||||||
this.show = !this.show
|
})
|
||||||
if (this.show) {
|
},
|
||||||
this.options = this.searchPool
|
initFuse(list) {
|
||||||
}
|
this.fuse = new Fuse(list, {
|
||||||
},
|
shouldSort: true,
|
||||||
onDialogOpened() {
|
threshold: 0.4,
|
||||||
this.$nextTick(() => {
|
location: 0,
|
||||||
this.$refs.headerSearchSelectRef && this.$refs.headerSearchSelectRef.focus()
|
distance: 100,
|
||||||
})
|
minMatchCharLength: 1,
|
||||||
},
|
keys: [{
|
||||||
close() {
|
name: 'title',
|
||||||
this.$refs.headerSearchSelectRef && this.$refs.headerSearchSelectRef.blur()
|
weight: 0.7
|
||||||
this.search = ''
|
}, {
|
||||||
this.options = this.searchPool
|
name: 'path',
|
||||||
this.show = false
|
weight: 0.3
|
||||||
this.activeIndex = -1
|
}]
|
||||||
},
|
})
|
||||||
change(val) {
|
},
|
||||||
const p = val.path
|
// Filter out the routes that can be displayed in the sidebar
|
||||||
const query = val.query
|
// And generate the internationalized title
|
||||||
if (isHttp(val.path)) {
|
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
||||||
// http(s):// 路径新窗口打开
|
let res = []
|
||||||
const pindex = p.indexOf('http')
|
|
||||||
window.open(p.substr(pindex, p.length), '_blank')
|
for (const router of routes) {
|
||||||
} else {
|
// skip hidden router
|
||||||
if (query) {
|
if (router.hidden) { continue }
|
||||||
this.$router.push({ path: p, query: JSON.parse(query) })
|
|
||||||
} else {
|
const data = {
|
||||||
this.$router.push(p)
|
path: !isHttp(router.path) ? path.resolve(basePath, router.path) : router.path,
|
||||||
}
|
title: [...prefixTitle],
|
||||||
}
|
icon: ''
|
||||||
this.search = ''
|
}
|
||||||
this.options = this.searchPool
|
|
||||||
this.$nextTick(() => {
|
if (router.meta && router.meta.title) {
|
||||||
this.show = false
|
data.title = [...data.title, router.meta.title]
|
||||||
})
|
data.icon = router.meta.icon
|
||||||
},
|
|
||||||
initFuse(list) {
|
if (router.redirect !== 'noRedirect') {
|
||||||
this.fuse = new Fuse(list, {
|
// only push the routes with title
|
||||||
shouldSort: true,
|
// special case: need to exclude parent router without redirect
|
||||||
threshold: 0.2,
|
res.push(data)
|
||||||
minMatchCharLength: 1,
|
}
|
||||||
keys: [{
|
}
|
||||||
name: 'title',
|
|
||||||
weight: 0.7
|
if (router.query) {
|
||||||
}, {
|
data.query = router.query
|
||||||
name: 'path',
|
}
|
||||||
weight: 0.3
|
|
||||||
}]
|
// recursive child routes
|
||||||
})
|
if (router.children) {
|
||||||
},
|
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
|
||||||
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
if (tempRoutes.length >= 1) {
|
||||||
let res = []
|
res = [...res, ...tempRoutes]
|
||||||
for (const router of routes) {
|
}
|
||||||
if (router.hidden) { continue }
|
}
|
||||||
const data = {
|
}
|
||||||
path: !isHttp(router.path) ? path.resolve(basePath, router.path) : router.path,
|
return res
|
||||||
title: [...prefixTitle],
|
},
|
||||||
icon: ''
|
querySearch(query) {
|
||||||
}
|
this.activeIndex = -1
|
||||||
if (router.meta && router.meta.title) {
|
if (query !== '') {
|
||||||
data.title = [...data.title, router.meta.title]
|
this.options = this.fuse.search(query).map((item) => item.item) ?? this.searchPool
|
||||||
data.icon = router.meta.icon
|
} else {
|
||||||
if (router.redirect !== 'noRedirect') {
|
this.options = this.searchPool
|
||||||
res.push(data)
|
}
|
||||||
}
|
},
|
||||||
}
|
activeStyle(index) {
|
||||||
if (router.query) {
|
if (index !== this.activeIndex) return {}
|
||||||
data.query = router.query
|
return {
|
||||||
}
|
"background-color": this.theme,
|
||||||
if (router.children) {
|
"color": "#fff"
|
||||||
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
|
}
|
||||||
if (tempRoutes.length >= 1) {
|
},
|
||||||
res = [...res, ...tempRoutes]
|
navigateResult(direction) {
|
||||||
}
|
if (direction === "up") {
|
||||||
}
|
this.activeIndex = this.activeIndex <= 0 ? this.options.length - 1 : this.activeIndex - 1
|
||||||
}
|
} else if (direction === "down") {
|
||||||
return res
|
this.activeIndex = this.activeIndex >= this.options.length - 1 ? 0 : this.activeIndex + 1
|
||||||
},
|
}
|
||||||
querySearch(query) {
|
},
|
||||||
this.activeIndex = -1
|
selectActiveResult() {
|
||||||
if (query !== '') {
|
if (this.options.length > 0 && this.activeIndex >= 0) {
|
||||||
const q = query.toLowerCase()
|
this.change(this.options[this.activeIndex])
|
||||||
const pathMatches = this.searchPool.filter(item =>
|
}
|
||||||
item.path.toLowerCase().includes(q)
|
}
|
||||||
)
|
}
|
||||||
const fuseMatches = this.fuse.search(query).map(item => item.item)
|
}
|
||||||
const merged = [...pathMatches]
|
</script>
|
||||||
fuseMatches.forEach(item => {
|
|
||||||
if (!merged.find(m => m.path === item.path)) {
|
<style lang='scss' scoped>
|
||||||
merged.push(item)
|
::v-deep {
|
||||||
}
|
.el-dialog__header {
|
||||||
})
|
padding: 0 !important;
|
||||||
this.options = merged
|
}
|
||||||
} else {
|
}
|
||||||
this.options = this.searchPool
|
|
||||||
}
|
.header-search {
|
||||||
},
|
.search-icon {
|
||||||
activeStyle(index) {
|
cursor: pointer;
|
||||||
if (index !== this.activeIndex) return {}
|
font-size: 18px;
|
||||||
return {
|
vertical-align: middle;
|
||||||
'background-color': this.theme,
|
}
|
||||||
'color': '#fff'
|
}
|
||||||
}
|
|
||||||
},
|
.result-wrap {
|
||||||
navigateResult(direction) {
|
height: 280px;
|
||||||
if (direction === 'up') {
|
margin: 6px 0;
|
||||||
this.activeIndex = this.activeIndex <= 0 ? this.options.length - 1 : this.activeIndex - 1
|
|
||||||
} else if (direction === 'down') {
|
.search-item {
|
||||||
this.activeIndex = this.activeIndex >= this.options.length - 1 ? 0 : this.activeIndex + 1
|
display: flex;
|
||||||
}
|
height: 48px;
|
||||||
},
|
align-items: center;
|
||||||
selectActiveResult() {
|
padding-right: 10px;
|
||||||
if (this.options.length > 0 && this.activeIndex >= 0) {
|
|
||||||
this.change(this.options[this.activeIndex])
|
.left {
|
||||||
}
|
width: 60px;
|
||||||
},
|
text-align: center;
|
||||||
highlightText(text) {
|
|
||||||
if (!text) return ''
|
.menu-icon {
|
||||||
if (!this.search) return text
|
width: 18px;
|
||||||
const keyword = this.escapeRegExp(this.search)
|
height: 18px;
|
||||||
const reg = new RegExp(`(${keyword})`, 'gi')
|
}
|
||||||
return text.replace(reg, '<span class="highlight">$1</span>')
|
}
|
||||||
},
|
|
||||||
escapeRegExp(str) {
|
.search-info {
|
||||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
padding-left: 5px;
|
||||||
}
|
margin-top: 10px;
|
||||||
}
|
width: 100%;
|
||||||
}
|
display: flex;
|
||||||
</script>
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
<style lang='scss' scoped>
|
flex: 1;
|
||||||
::v-deep {
|
|
||||||
.el-dialog__header {
|
.menu-title,
|
||||||
padding: 6px !important;
|
.menu-path {
|
||||||
}
|
height: 20px;
|
||||||
|
}
|
||||||
.highlight {
|
.menu-path {
|
||||||
color: red;
|
color: #ccc;
|
||||||
font-weight: 600;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.is-active .highlight {
|
}
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
font-weight: 600;
|
.search-item:hover {
|
||||||
}
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.header-search {
|
</style>
|
||||||
.search-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 18px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-count {
|
|
||||||
padding: 6px 16px 0;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #aaa;
|
|
||||||
|
|
||||||
strong {
|
|
||||||
color: red;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-wrap {
|
|
||||||
height: 280px;
|
|
||||||
margin: 4px 0;
|
|
||||||
|
|
||||||
.search-item {
|
|
||||||
display: flex;
|
|
||||||
height: 48px;
|
|
||||||
align-items: center;
|
|
||||||
padding-right: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: background 0.15s;
|
|
||||||
|
|
||||||
.left {
|
|
||||||
width: 60px;
|
|
||||||
text-align: center;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-info {
|
|
||||||
padding-left: 5px;
|
|
||||||
margin-top: 10px;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.menu-title,
|
|
||||||
.menu-path {
|
|
||||||
height: 20px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-path {
|
|
||||||
color: #ccc;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-item:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
font-size: 42px;
|
|
||||||
color: #e0e0e0;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #999;
|
|
||||||
margin: 0 0 6px;
|
|
||||||
|
|
||||||
strong {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-tip {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #bbb;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-footer {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 28px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
border-top: 1px solid #f0f0f0;
|
|
||||||
color: #999;
|
|
||||||
font-size: 12px;
|
|
||||||
|
|
||||||
.shortcut-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
kbd {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
padding: 0 5px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: #f7f7f7;
|
|
||||||
color: #555;
|
|
||||||
font-size: 11px;
|
|
||||||
font-family: inherit;
|
|
||||||
line-height: 1;
|
|
||||||
box-shadow: 0 1px 0 #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
const req = require.context('../../assets/icons/svg', false, /\.svg$/)
|
const req = require.context('../../assets/icons/svg', false, /\.svg$/)
|
||||||
const requireAll = requireContext => requireContext.keys()
|
const requireAll = requireContext => requireContext.keys()
|
||||||
|
|
||||||
const re = /\.\/(.*)\.svg/
|
const re = /\.\/(.*)\.svg/
|
||||||
|
|
||||||
const icons = requireAll(req).map(i => {
|
const icons = requireAll(req).map(i => {
|
||||||
return i.match(re)[1]
|
return i.match(re)[1]
|
||||||
})
|
})
|
||||||
|
|
||||||
export default icons
|
export default icons
|
||||||
|
|||||||
@@ -1,272 +1,272 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="component-upload-image">
|
<div class="component-upload-image">
|
||||||
<el-upload
|
<el-upload
|
||||||
multiple
|
multiple
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:action="uploadImgUrl"
|
:action="uploadImgUrl"
|
||||||
list-type="picture-card"
|
list-type="picture-card"
|
||||||
:on-success="handleUploadSuccess"
|
:on-success="handleUploadSuccess"
|
||||||
:before-upload="handleBeforeUpload"
|
:before-upload="handleBeforeUpload"
|
||||||
:data="data"
|
:data="data"
|
||||||
:limit="limit"
|
:limit="limit"
|
||||||
:on-error="handleUploadError"
|
:on-error="handleUploadError"
|
||||||
:on-exceed="handleExceed"
|
:on-exceed="handleExceed"
|
||||||
ref="imageUpload"
|
ref="imageUpload"
|
||||||
:on-remove="handleDelete"
|
:on-remove="handleDelete"
|
||||||
:show-file-list="true"
|
:show-file-list="true"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:file-list="fileList"
|
:file-list="fileList"
|
||||||
:on-preview="handlePictureCardPreview"
|
:on-preview="handlePictureCardPreview"
|
||||||
:class="{hide: this.fileList.length >= this.limit}"
|
:class="{hide: this.fileList.length >= this.limit}"
|
||||||
>
|
>
|
||||||
<i class="el-icon-plus"></i>
|
<i class="el-icon-plus"></i>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
|
||||||
<!-- 上传提示 -->
|
<!-- 上传提示 -->
|
||||||
<div class="el-upload__tip" slot="tip" v-if="showTip && !disabled">
|
<div class="el-upload__tip" slot="tip" v-if="showTip && !disabled">
|
||||||
请上传
|
请上传
|
||||||
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
||||||
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
||||||
的文件
|
的文件
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:visible.sync="dialogVisible"
|
:visible.sync="dialogVisible"
|
||||||
title="预览"
|
title="预览"
|
||||||
width="800"
|
width="800"
|
||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="dialogImageUrl"
|
:src="dialogImageUrl"
|
||||||
style="display: block; max-width: 100%; margin: 0 auto"
|
style="display: block; max-width: 100%; margin: 0 auto"
|
||||||
/>
|
/>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getToken } from "@/utils/auth"
|
import { getToken } from "@/utils/auth"
|
||||||
import { isExternal } from "@/utils/validate"
|
import { isExternal } from "@/utils/validate"
|
||||||
import Sortable from 'sortablejs'
|
import Sortable from 'sortablejs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: [String, Object, Array],
|
value: [String, Object, Array],
|
||||||
// 上传接口地址
|
// 上传接口地址
|
||||||
action: {
|
action: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "/common/upload"
|
default: "/common/upload"
|
||||||
},
|
},
|
||||||
// 上传携带的参数
|
// 上传携带的参数
|
||||||
data: {
|
data: {
|
||||||
type: Object
|
type: Object
|
||||||
},
|
},
|
||||||
// 图片数量限制
|
// 图片数量限制
|
||||||
limit: {
|
limit: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 5
|
default: 5
|
||||||
},
|
},
|
||||||
// 大小限制(MB)
|
// 大小限制(MB)
|
||||||
fileSize: {
|
fileSize: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 5
|
default: 5
|
||||||
},
|
},
|
||||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||||
fileType: {
|
fileType: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => ["png", "jpg", "jpeg"]
|
default: () => ["png", "jpg", "jpeg"]
|
||||||
},
|
},
|
||||||
// 是否显示提示
|
// 是否显示提示
|
||||||
isShowTip: {
|
isShowTip: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
// 禁用组件(仅查看图片)
|
// 禁用组件(仅查看图片)
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
// 拖动排序
|
// 拖动排序
|
||||||
drag: {
|
drag: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
number: 0,
|
number: 0,
|
||||||
uploadList: [],
|
uploadList: [],
|
||||||
dialogImageUrl: "",
|
dialogImageUrl: "",
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
hideUpload: false,
|
hideUpload: false,
|
||||||
baseUrl: process.env.VUE_APP_BASE_API,
|
baseUrl: process.env.VUE_APP_BASE_API,
|
||||||
uploadImgUrl: process.env.VUE_APP_BASE_API + this.action, // 上传的图片服务器地址
|
uploadImgUrl: process.env.VUE_APP_BASE_API + this.action, // 上传的图片服务器地址
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: "Bearer " + getToken(),
|
Authorization: "Bearer " + getToken(),
|
||||||
},
|
},
|
||||||
fileList: []
|
fileList: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.drag && !this.disabled) {
|
if (this.drag && !this.disabled) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const element = this.$refs.imageUpload?.$el?.querySelector('.el-upload-list')
|
const element = this.$refs.imageUpload?.$el?.querySelector('.el-upload-list')
|
||||||
Sortable.create(element, {
|
Sortable.create(element, {
|
||||||
onEnd: (evt) => {
|
onEnd: (evt) => {
|
||||||
const movedItem = this.fileList.splice(evt.oldIndex, 1)[0]
|
const movedItem = this.fileList.splice(evt.oldIndex, 1)[0]
|
||||||
this.fileList.splice(evt.newIndex, 0, movedItem)
|
this.fileList.splice(evt.newIndex, 0, movedItem)
|
||||||
this.$emit("input", this.listToString(this.fileList))
|
this.$emit("input", this.listToString(this.fileList))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value: {
|
value: {
|
||||||
handler(val) {
|
handler(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
// 首先将值转为数组
|
// 首先将值转为数组
|
||||||
const list = Array.isArray(val) ? val : this.value.split(',')
|
const list = Array.isArray(val) ? val : this.value.split(',')
|
||||||
// 然后将数组转为对象数组
|
// 然后将数组转为对象数组
|
||||||
this.fileList = list.map(item => {
|
this.fileList = list.map(item => {
|
||||||
if (typeof item === "string") {
|
if (typeof item === "string") {
|
||||||
if (item.indexOf(this.baseUrl) === -1 && !isExternal(item)) {
|
if (item.indexOf(this.baseUrl) === -1 && !isExternal(item)) {
|
||||||
item = { name: this.baseUrl + item, url: this.baseUrl + item }
|
item = { name: this.baseUrl + item, url: this.baseUrl + item }
|
||||||
} else {
|
} else {
|
||||||
item = { name: item, url: item }
|
item = { name: item, url: item }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.fileList = []
|
this.fileList = []
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// 是否显示提示
|
// 是否显示提示
|
||||||
showTip() {
|
showTip() {
|
||||||
return this.isShowTip && (this.fileType || this.fileSize)
|
return this.isShowTip && (this.fileType || this.fileSize)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 上传前loading加载
|
// 上传前loading加载
|
||||||
handleBeforeUpload(file) {
|
handleBeforeUpload(file) {
|
||||||
let isImg = false
|
let isImg = false
|
||||||
if (this.fileType.length) {
|
if (this.fileType.length) {
|
||||||
let fileExtension = ""
|
let fileExtension = ""
|
||||||
if (file.name.lastIndexOf(".") > -1) {
|
if (file.name.lastIndexOf(".") > -1) {
|
||||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1)
|
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1)
|
||||||
}
|
}
|
||||||
isImg = this.fileType.some(type => {
|
isImg = this.fileType.some(type => {
|
||||||
if (file.type.indexOf(type) > -1) return true
|
if (file.type.indexOf(type) > -1) return true
|
||||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true
|
if (fileExtension && fileExtension.indexOf(type) > -1) return true
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
isImg = file.type.indexOf("image") > -1
|
isImg = file.type.indexOf("image") > -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isImg) {
|
if (!isImg) {
|
||||||
this.$modal.msgError(`文件格式不正确,请上传${this.fileType.join("/")}图片格式文件!`)
|
this.$modal.msgError(`文件格式不正确,请上传${this.fileType.join("/")}图片格式文件!`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (file.name.includes(',')) {
|
if (file.name.includes(',')) {
|
||||||
this.$modal.msgError('文件名不正确,不能包含英文逗号!')
|
this.$modal.msgError('文件名不正确,不能包含英文逗号!')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (this.fileSize) {
|
if (this.fileSize) {
|
||||||
const isLt = file.size / 1024 / 1024 < this.fileSize
|
const isLt = file.size / 1024 / 1024 < this.fileSize
|
||||||
if (!isLt) {
|
if (!isLt) {
|
||||||
this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`)
|
this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$modal.loading("正在上传图片,请稍候...")
|
this.$modal.loading("正在上传图片,请稍候...")
|
||||||
this.number++
|
this.number++
|
||||||
},
|
},
|
||||||
// 文件个数超出
|
// 文件个数超出
|
||||||
handleExceed() {
|
handleExceed() {
|
||||||
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`)
|
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`)
|
||||||
},
|
},
|
||||||
// 上传成功回调
|
// 上传成功回调
|
||||||
handleUploadSuccess(res, file) {
|
handleUploadSuccess(res, file) {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.uploadList.push({ name: res.fileName, url: res.fileName })
|
this.uploadList.push({ name: res.fileName, url: res.fileName })
|
||||||
this.uploadedSuccessfully()
|
this.uploadedSuccessfully()
|
||||||
} else {
|
} else {
|
||||||
this.number--
|
this.number--
|
||||||
this.$modal.closeLoading()
|
this.$modal.closeLoading()
|
||||||
this.$modal.msgError(res.msg)
|
this.$modal.msgError(res.msg)
|
||||||
this.$refs.imageUpload.handleRemove(file)
|
this.$refs.imageUpload.handleRemove(file)
|
||||||
this.uploadedSuccessfully()
|
this.uploadedSuccessfully()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 删除图片
|
// 删除图片
|
||||||
handleDelete(file) {
|
handleDelete(file) {
|
||||||
const findex = this.fileList.map(f => f.name).indexOf(file.name)
|
const findex = this.fileList.map(f => f.name).indexOf(file.name)
|
||||||
if (findex > -1) {
|
if (findex > -1) {
|
||||||
this.fileList.splice(findex, 1)
|
this.fileList.splice(findex, 1)
|
||||||
this.$emit("input", this.listToString(this.fileList))
|
this.$emit("input", this.listToString(this.fileList))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 上传失败
|
// 上传失败
|
||||||
handleUploadError() {
|
handleUploadError() {
|
||||||
this.$modal.msgError("上传图片失败,请重试")
|
this.$modal.msgError("上传图片失败,请重试")
|
||||||
this.$modal.closeLoading()
|
this.$modal.closeLoading()
|
||||||
},
|
},
|
||||||
// 上传结束处理
|
// 上传结束处理
|
||||||
uploadedSuccessfully() {
|
uploadedSuccessfully() {
|
||||||
if (this.number > 0 && this.uploadList.length === this.number) {
|
if (this.number > 0 && this.uploadList.length === this.number) {
|
||||||
this.fileList = this.fileList.concat(this.uploadList)
|
this.fileList = this.fileList.concat(this.uploadList)
|
||||||
this.uploadList = []
|
this.uploadList = []
|
||||||
this.number = 0
|
this.number = 0
|
||||||
this.$emit("input", this.listToString(this.fileList))
|
this.$emit("input", this.listToString(this.fileList))
|
||||||
this.$modal.closeLoading()
|
this.$modal.closeLoading()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 预览
|
// 预览
|
||||||
handlePictureCardPreview(file) {
|
handlePictureCardPreview(file) {
|
||||||
this.dialogImageUrl = file.url
|
this.dialogImageUrl = file.url
|
||||||
this.dialogVisible = true
|
this.dialogVisible = true
|
||||||
},
|
},
|
||||||
// 对象转成指定字符串分隔
|
// 对象转成指定字符串分隔
|
||||||
listToString(list, separator) {
|
listToString(list, separator) {
|
||||||
let strs = ""
|
let strs = ""
|
||||||
separator = separator || ","
|
separator = separator || ","
|
||||||
for (let i in list) {
|
for (let i in list) {
|
||||||
if (list[i].url) {
|
if (list[i].url) {
|
||||||
strs += list[i].url.replace(this.baseUrl, "") + separator
|
strs += list[i].url.replace(this.baseUrl, "") + separator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strs != '' ? strs.substr(0, strs.length - 1) : ''
|
return strs != '' ? strs.substr(0, strs.length - 1) : ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
// .el-upload--picture-card 控制加号部分
|
// .el-upload--picture-card 控制加号部分
|
||||||
::v-deep.hide .el-upload--picture-card {
|
::v-deep.hide .el-upload--picture-card {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-upload-list--picture-card.is-disabled + .el-upload--picture-card {
|
::v-deep .el-upload-list--picture-card.is-disabled + .el-upload--picture-card {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去掉动画效果
|
// 去掉动画效果
|
||||||
::v-deep .el-list-enter-active,
|
::v-deep .el-list-enter-active,
|
||||||
::v-deep .el-list-leave-active {
|
::v-deep .el-list-leave-active {
|
||||||
transition: all 0s;
|
transition: all 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .el-list-enter, .el-list-leave-active {
|
::v-deep .el-list-enter, .el-list-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,113 +1,113 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{'hidden':hidden}" class="pagination-container">
|
<div :class="{'hidden':hidden}" class="pagination-container">
|
||||||
<el-pagination
|
<el-pagination
|
||||||
:background="background"
|
:background="background"
|
||||||
:current-page.sync="currentPage"
|
:current-page.sync="currentPage"
|
||||||
:page-size.sync="pageSize"
|
:page-size.sync="pageSize"
|
||||||
:layout="layout"
|
:layout="layout"
|
||||||
:page-sizes="pageSizes"
|
:page-sizes="pageSizes"
|
||||||
:pager-count="pagerCount"
|
:pager-count="pagerCount"
|
||||||
:total="total"
|
:total="total"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { scrollTo } from '@/utils/scroll-to'
|
import { scrollTo } from '@/utils/scroll-to'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Pagination',
|
name: 'Pagination',
|
||||||
props: {
|
props: {
|
||||||
total: {
|
total: {
|
||||||
required: true,
|
required: true,
|
||||||
type: Number
|
type: Number
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1
|
default: 1
|
||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 20
|
default: 20
|
||||||
},
|
},
|
||||||
pageSizes: {
|
pageSizes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default() {
|
default() {
|
||||||
return [10, 20, 30, 50]
|
return [10, 20, 30, 50]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 移动端页码按钮的数量端默认值5
|
// 移动端页码按钮的数量端默认值5
|
||||||
pagerCount: {
|
pagerCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: document.body.clientWidth < 992 ? 5 : 7
|
default: document.body.clientWidth < 992 ? 5 : 7
|
||||||
},
|
},
|
||||||
layout: {
|
layout: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'total, sizes, prev, pager, next, jumper'
|
default: 'total, sizes, prev, pager, next, jumper'
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
autoScroll: {
|
autoScroll: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
hidden: {
|
hidden: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
currentPage: {
|
currentPage: {
|
||||||
get() {
|
get() {
|
||||||
return this.page
|
return this.page
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
this.$emit('update:page', val)
|
this.$emit('update:page', val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageSize: {
|
pageSize: {
|
||||||
get() {
|
get() {
|
||||||
return this.limit
|
return this.limit
|
||||||
},
|
},
|
||||||
set(val) {
|
set(val) {
|
||||||
this.$emit('update:limit', val)
|
this.$emit('update:limit', val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleSizeChange(val) {
|
handleSizeChange(val) {
|
||||||
if (this.currentPage * val > this.total) {
|
if (this.currentPage * val > this.total) {
|
||||||
this.currentPage = 1
|
this.currentPage = 1
|
||||||
}
|
}
|
||||||
this.$emit('pagination', { page: this.currentPage, limit: val })
|
this.$emit('pagination', { page: this.currentPage, limit: val })
|
||||||
if (this.autoScroll) {
|
if (this.autoScroll) {
|
||||||
scrollTo(0, 800)
|
scrollTo(0, 800)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleCurrentChange(val) {
|
handleCurrentChange(val) {
|
||||||
this.$emit('pagination', { page: val, limit: this.pageSize })
|
this.$emit('pagination', { page: val, limit: this.pageSize })
|
||||||
if (this.autoScroll) {
|
if (this.autoScroll) {
|
||||||
scrollTo(0, 800)
|
scrollTo(0, 800)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
.pagination-container.hidden {
|
.pagination-container.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,141 +1,141 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
|
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
|
||||||
<div class="pan-info">
|
<div class="pan-info">
|
||||||
<div class="pan-info-roles-container">
|
<div class="pan-info-roles-container">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div>
|
<div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'PanThumb',
|
name: 'PanThumb',
|
||||||
props: {
|
props: {
|
||||||
image: {
|
image: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
zIndex: {
|
zIndex: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1
|
default: 1
|
||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '150px'
|
default: '150px'
|
||||||
},
|
},
|
||||||
height: {
|
height: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '150px'
|
default: '150px'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.pan-item {
|
.pan-item {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-info-roles-container {
|
.pan-info-roles-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-thumb {
|
.pan-thumb {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform-origin: 95% 40%;
|
transform-origin: 95% 40%;
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .pan-thumb:after {
|
/* .pan-thumb:after {
|
||||||
content: '';
|
content: '';
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
top: 40%;
|
top: 40%;
|
||||||
left: 95%;
|
left: 95%;
|
||||||
margin: -4px 0 0 -4px;
|
margin: -4px 0 0 -4px;
|
||||||
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
|
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
|
||||||
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
|
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
|
||||||
} */
|
} */
|
||||||
|
|
||||||
.pan-info {
|
.pan-info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: inherit;
|
width: inherit;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
|
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-info h3 {
|
.pan-info h3 {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
position: relative;
|
position: relative;
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
margin: 0 60px;
|
margin: 0 60px;
|
||||||
padding: 22px 0 0 0;
|
padding: 22px 0 0 0;
|
||||||
height: 85px;
|
height: 85px;
|
||||||
font-family: 'Open Sans', Arial, sans-serif;
|
font-family: 'Open Sans', Arial, sans-serif;
|
||||||
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
|
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-info p {
|
.pan-info p {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 10px 5px;
|
padding: 10px 5px;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
margin: 0 30px;
|
margin: 0 30px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.5);
|
border-top: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-info p a {
|
.pan-info p a {
|
||||||
display: block;
|
display: block;
|
||||||
color: #333;
|
color: #333;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
background: rgba(255, 255, 255, 0.3);
|
background: rgba(255, 255, 255, 0.3);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
margin: 7px auto 0;
|
margin: 7px auto 0;
|
||||||
font-family: 'Open Sans', Arial, sans-serif;
|
font-family: 'Open Sans', Arial, sans-serif;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
|
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
|
||||||
transform: translateX(60px) rotate(90deg);
|
transform: translateX(60px) rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-info p a:hover {
|
.pan-info p a:hover {
|
||||||
background: rgba(255, 255, 255, 0.5);
|
background: rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-item:hover .pan-thumb {
|
.pan-item:hover .pan-thumb {
|
||||||
transform: rotate(-110deg);
|
transform: rotate(-110deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pan-item:hover .pan-info p a {
|
.pan-item:hover .pan-info p a {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(0px) rotate(0deg);
|
transform: translateX(0px) rotate(0deg);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,253 +1,186 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="top-right-btn" :style="style">
|
<div class="top-right-btn" :style="style">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
|
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
|
||||||
<el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" />
|
<el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
||||||
<el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
|
<el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="Object.keys(columns).length > 0">
|
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="Object.keys(columns).length > 0">
|
||||||
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
|
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
|
||||||
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
|
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
|
||||||
<el-button size="mini" circle icon="el-icon-menu" />
|
<el-button size="mini" circle icon="el-icon-menu" />
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<!-- 全选/反选 按钮 -->
|
<!-- 全选/反选 按钮 -->
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> 列展示 </el-checkbox>
|
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> 列展示 </el-checkbox>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<div class="check-line"></div>
|
<div class="check-line"></div>
|
||||||
<template v-for="(item, key) in columns">
|
<template v-for="(item, key) in columns">
|
||||||
<el-dropdown-item :key="key">
|
<el-dropdown-item :key="key">
|
||||||
<el-checkbox v-model="item.visible" @change="checkboxChange($event, key)" :label="item.label" />
|
<el-checkbox v-model="item.visible" @change="checkboxChange($event, key)" :label="item.label" />
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-dialog :title="title" :visible.sync="open" append-to-body>
|
<el-dialog :title="title" :visible.sync="open" append-to-body>
|
||||||
<el-transfer
|
<el-transfer
|
||||||
:titles="['显示', '隐藏']"
|
:titles="['显示', '隐藏']"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
:data="transferData"
|
:data="transferData"
|
||||||
@change="dataChange"
|
@change="dataChange"
|
||||||
></el-transfer>
|
></el-transfer>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import cache from '@/plugins/cache'
|
export default {
|
||||||
|
name: "RightToolbar",
|
||||||
export default {
|
data() {
|
||||||
name: "RightToolbar",
|
return {
|
||||||
data() {
|
// 显隐数据
|
||||||
return {
|
value: [],
|
||||||
// 显隐数据
|
// 弹出层标题
|
||||||
value: [],
|
title: "显示/隐藏",
|
||||||
// 弹出层标题
|
// 是否显示弹出层
|
||||||
title: "显示/隐藏",
|
open: false
|
||||||
// 是否显示弹出层
|
}
|
||||||
open: false
|
},
|
||||||
}
|
props: {
|
||||||
},
|
/* 是否显示检索条件 */
|
||||||
props: {
|
showSearch: {
|
||||||
/* 是否显示检索条件 */
|
type: Boolean,
|
||||||
showSearch: {
|
default: true
|
||||||
type: Boolean,
|
},
|
||||||
default: true
|
/* 显隐列信息(数组格式、对象格式) */
|
||||||
},
|
columns: {
|
||||||
/* 显隐列信息(数组格式、对象格式) */
|
type: [Array, Object],
|
||||||
columns: {
|
default: () => ({})
|
||||||
type: [Array, Object],
|
},
|
||||||
default: () => ({})
|
/* 是否显示检索图标 */
|
||||||
},
|
search: {
|
||||||
/* 是否显示检索图标 */
|
type: Boolean,
|
||||||
search: {
|
default: true
|
||||||
type: Boolean,
|
},
|
||||||
default: true
|
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
|
||||||
},
|
showColumnsType: {
|
||||||
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
|
type: String,
|
||||||
showColumnsType: {
|
default: "checkbox"
|
||||||
type: String,
|
},
|
||||||
default: "checkbox"
|
/* 右外边距 */
|
||||||
},
|
gutter: {
|
||||||
/* 右外边距 */
|
type: Number,
|
||||||
gutter: {
|
default: 10
|
||||||
type: Number,
|
},
|
||||||
default: 10
|
},
|
||||||
},
|
computed: {
|
||||||
/* 列显隐状态记忆的 localStorage key(传入则启用记忆,不传则不记忆) */
|
style() {
|
||||||
storageKey: {
|
const ret = {}
|
||||||
type: String,
|
if (this.gutter) {
|
||||||
default: ""
|
ret.marginRight = `${this.gutter / 2}px`
|
||||||
}
|
}
|
||||||
},
|
return ret
|
||||||
computed: {
|
},
|
||||||
style() {
|
isChecked: {
|
||||||
const ret = {}
|
get() {
|
||||||
if (this.gutter) {
|
return Array.isArray(this.columns) ? this.columns.every((col) => col.visible) : Object.values(this.columns).every((col) => col.visible)
|
||||||
ret.marginRight = `${this.gutter / 2}px`
|
},
|
||||||
}
|
set() {}
|
||||||
return ret
|
},
|
||||||
},
|
isIndeterminate() {
|
||||||
isChecked: {
|
return Array.isArray(this.columns) ? this.columns.some((col) => col.visible) && !this.isChecked : Object.values(this.columns).some((col) => col.visible) && !this.isChecked
|
||||||
get() {
|
},
|
||||||
return Array.isArray(this.columns) ? this.columns.every((col) => col.visible) : Object.values(this.columns).every((col) => col.visible)
|
transferData() {
|
||||||
},
|
if (Array.isArray(this.columns)) {
|
||||||
set() {}
|
return this.columns.map((item, index) => ({ key: index, label: item.label }))
|
||||||
},
|
} else {
|
||||||
isIndeterminate() {
|
return Object.keys(this.columns).map((key, index) => ({ key: index, label: this.columns[key].label }))
|
||||||
return Array.isArray(this.columns) ? this.columns.some((col) => col.visible) && !this.isChecked : Object.values(this.columns).some((col) => col.visible) && !this.isChecked
|
}
|
||||||
},
|
}
|
||||||
transferData() {
|
},
|
||||||
if (Array.isArray(this.columns)) {
|
created() {
|
||||||
return this.columns.map((item, index) => ({ key: index, label: item.label }))
|
if (this.showColumnsType == 'transfer') {
|
||||||
} else {
|
// transfer穿梭显隐列初始默认隐藏列
|
||||||
return Object.keys(this.columns).map((key, index) => ({ key: index, label: this.columns[key].label }))
|
if (Array.isArray(this.columns)) {
|
||||||
}
|
for (let item in this.columns) {
|
||||||
}
|
if (this.columns[item].visible === false) {
|
||||||
},
|
this.value.push(parseInt(item))
|
||||||
created() {
|
}
|
||||||
// 如果传入了 storageKey,从 localStorage 恢复列显隐状态
|
}
|
||||||
if (this.storageKey) {
|
} else {
|
||||||
try {
|
Object.keys(this.columns).forEach((key, index) => {
|
||||||
const saved = cache.local.getJSON(this.storageKey)
|
if (this.columns[key].visible === false) {
|
||||||
if (saved && typeof saved === 'object') {
|
this.value.push(index)
|
||||||
if (Array.isArray(this.columns)) {
|
}
|
||||||
this.columns.forEach((col, index) => {
|
})
|
||||||
if (saved[index] !== undefined) col.visible = saved[index]
|
}
|
||||||
})
|
}
|
||||||
} else {
|
},
|
||||||
Object.keys(this.columns).forEach(key => {
|
methods: {
|
||||||
if (saved[key] !== undefined) this.columns[key].visible = saved[key]
|
// 搜索
|
||||||
})
|
toggleSearch() {
|
||||||
}
|
this.$emit("update:showSearch", !this.showSearch)
|
||||||
}
|
},
|
||||||
} catch (e) {}
|
// 刷新
|
||||||
}
|
refresh() {
|
||||||
if (this.showColumnsType == 'transfer') {
|
this.$emit("queryTable")
|
||||||
// transfer穿梭显隐列初始默认隐藏列
|
},
|
||||||
if (Array.isArray(this.columns)) {
|
// 右侧列表元素变化
|
||||||
for (let item in this.columns) {
|
dataChange(data) {
|
||||||
if (this.columns[item].visible === false) {
|
if (Array.isArray(this.columns)) {
|
||||||
this.value.push(parseInt(item))
|
for (let item in this.columns) {
|
||||||
}
|
const key = this.columns[item].key
|
||||||
}
|
this.columns[item].visible = !data.includes(key)
|
||||||
} else {
|
}
|
||||||
Object.keys(this.columns).forEach((key, index) => {
|
} else {
|
||||||
if (this.columns[key].visible === false) {
|
Object.keys(this.columns).forEach((key, index) => {
|
||||||
this.value.push(index)
|
this.columns[key].visible = !data.includes(index)
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
},
|
||||||
}
|
// 打开显隐列dialog
|
||||||
},
|
showColumn() {
|
||||||
methods: {
|
this.open = true
|
||||||
// 搜索
|
},
|
||||||
toggleSearch() {
|
// 单勾选
|
||||||
let el = this.$el
|
checkboxChange(event, key) {
|
||||||
let formEl = null
|
if (Array.isArray(this.columns)) {
|
||||||
while ((el = el.parentElement) && el !== document.body) {
|
this.columns.filter(item => item.key == key)[0].visible = event
|
||||||
if ((formEl = el.querySelector('.el-form'))) break
|
} else {
|
||||||
}
|
this.columns[key].visible = event
|
||||||
if (!formEl) return this.$emit('update:showSearch', !this.showSearch)
|
}
|
||||||
this._animateSearch(formEl, this.showSearch)
|
},
|
||||||
},
|
// 切换全选/反选
|
||||||
// 搜索栏动画
|
toggleCheckAll() {
|
||||||
_animateSearch(el, isHide) {
|
const newValue = !this.isChecked
|
||||||
const DURATION = 260
|
if (Array.isArray(this.columns)) {
|
||||||
const TRANSITION = 'max-height 0.25s ease, opacity 0.2s ease'
|
this.columns.forEach((col) => (col.visible = newValue))
|
||||||
const clear = () => Object.assign(el.style, { transition: '', maxHeight: '', opacity: '', overflow: '' })
|
} else {
|
||||||
Object.assign(el.style, { overflow: 'hidden', transition: '' })
|
Object.values(this.columns).forEach((col) => (col.visible = newValue))
|
||||||
if (isHide) {
|
}
|
||||||
Object.assign(el.style, { maxHeight: el.scrollHeight + 'px', opacity: '1', transition: TRANSITION })
|
}
|
||||||
requestAnimationFrame(() => Object.assign(el.style, { maxHeight: '0', opacity: '0' }))
|
},
|
||||||
setTimeout(() => { this.$emit('update:showSearch', false); clear() }, DURATION)
|
}
|
||||||
} else {
|
</script>
|
||||||
this.$emit('update:showSearch', true)
|
|
||||||
this.$nextTick(() => {
|
<style lang="scss" scoped>
|
||||||
Object.assign(el.style, { maxHeight: '0', opacity: '0' })
|
::v-deep .el-transfer__button {
|
||||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
border-radius: 50%;
|
||||||
Object.assign(el.style, { transition: TRANSITION, maxHeight: el.scrollHeight + 'px', opacity: '1' })
|
padding: 12px;
|
||||||
}))
|
display: block;
|
||||||
setTimeout(clear, DURATION)
|
margin-left: 0px;
|
||||||
})
|
}
|
||||||
}
|
::v-deep .el-transfer__button:first-child {
|
||||||
},
|
margin-bottom: 10px;
|
||||||
// 刷新
|
}
|
||||||
refresh() {
|
.check-line {
|
||||||
this.$emit("queryTable")
|
width: 90%;
|
||||||
},
|
height: 1px;
|
||||||
// 右侧列表元素变化
|
background-color: #ccc;
|
||||||
dataChange(data) {
|
margin: 3px auto;
|
||||||
if (Array.isArray(this.columns)) {
|
}
|
||||||
for (let item in this.columns) {
|
</style>
|
||||||
const key = this.columns[item].key
|
|
||||||
this.columns[item].visible = !data.includes(key)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Object.keys(this.columns).forEach((key, index) => {
|
|
||||||
this.columns[key].visible = !data.includes(index)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.saveStorage()
|
|
||||||
},
|
|
||||||
// 打开显隐列dialog
|
|
||||||
showColumn() {
|
|
||||||
this.open = true
|
|
||||||
},
|
|
||||||
// 单勾选
|
|
||||||
checkboxChange(event, key) {
|
|
||||||
if (Array.isArray(this.columns)) {
|
|
||||||
this.columns.filter(item => item.key == key)[0].visible = event
|
|
||||||
} else {
|
|
||||||
this.columns[key].visible = event
|
|
||||||
}
|
|
||||||
this.saveStorage()
|
|
||||||
},
|
|
||||||
// 切换全选/反选
|
|
||||||
toggleCheckAll() {
|
|
||||||
const newValue = !this.isChecked
|
|
||||||
if (Array.isArray(this.columns)) {
|
|
||||||
this.columns.forEach((col) => (col.visible = newValue))
|
|
||||||
} else {
|
|
||||||
Object.values(this.columns).forEach((col) => (col.visible = newValue))
|
|
||||||
}
|
|
||||||
this.saveStorage()
|
|
||||||
},
|
|
||||||
// 将当前列显隐状态持久化到 localStorage
|
|
||||||
saveStorage() {
|
|
||||||
if (!this.storageKey) return
|
|
||||||
try {
|
|
||||||
let state = {}
|
|
||||||
if (Array.isArray(this.columns)) {
|
|
||||||
this.columns.forEach((col, index) => { state[index] = col.visible })
|
|
||||||
} else {
|
|
||||||
Object.keys(this.columns).forEach(key => { state[key] = this.columns[key].visible })
|
|
||||||
}
|
|
||||||
cache.local.setJSON(this.storageKey, state)
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
::v-deep .el-transfer__button {
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 12px;
|
|
||||||
display: block;
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
::v-deep .el-transfer__button:first-child {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.check-line {
|
|
||||||
width: 90%;
|
|
||||||
height: 1px;
|
|
||||||
background-color: #ccc;
|
|
||||||
margin: 3px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<svg-icon icon-class="question" @click="goto" />
|
<svg-icon icon-class="question" @click="goto" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'RuoYiDoc',
|
name: 'RuoYiDoc',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
url: 'http://doc.ruoyi.vip/ruoyi-vue'
|
url: 'http://doc.ruoyi.vip/ruoyi-vue'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goto() {
|
goto() {
|
||||||
window.open(this.url)
|
window.open(this.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<svg-icon icon-class="github" @click="goto" />
|
<svg-icon icon-class="github" @click="goto" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'RuoYiGit',
|
name: 'RuoYiGit',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
url: 'https://gitee.com/y_project/RuoYi-Vue'
|
url: 'https://gitee.com/y_project/RuoYi-Vue'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goto() {
|
goto() {
|
||||||
window.open(this.url)
|
window.open(this.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -1,57 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
|
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import screenfull from 'screenfull'
|
import screenfull from 'screenfull'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Screenfull',
|
name: 'Screenfull',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isFullscreen: false
|
isFullscreen: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.init()
|
this.init()
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.destroy()
|
this.destroy()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
click() {
|
click() {
|
||||||
if (!screenfull.isEnabled) {
|
if (!screenfull.isEnabled) {
|
||||||
this.$message({ message: '你的浏览器不支持全屏', type: 'warning' })
|
this.$message({ message: '你的浏览器不支持全屏', type: 'warning' })
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
screenfull.toggle()
|
screenfull.toggle()
|
||||||
},
|
},
|
||||||
change() {
|
change() {
|
||||||
this.isFullscreen = screenfull.isFullscreen
|
this.isFullscreen = screenfull.isFullscreen
|
||||||
},
|
},
|
||||||
init() {
|
init() {
|
||||||
if (screenfull.isEnabled) {
|
if (screenfull.isEnabled) {
|
||||||
screenfull.on('change', this.change)
|
screenfull.on('change', this.change)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
if (screenfull.isEnabled) {
|
if (screenfull.isEnabled) {
|
||||||
screenfull.off('change', this.change)
|
screenfull.off('change', this.change)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.screenfull-svg {
|
.screenfull-svg {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
fill: #5a5e66;;
|
fill: #5a5e66;;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
vertical-align: 10px;
|
vertical-align: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dropdown trigger="click" @command="handleSetSize">
|
<el-dropdown trigger="click" @command="handleSetSize">
|
||||||
<div>
|
<div>
|
||||||
<svg-icon class-name="size-icon" icon-class="size" />
|
<svg-icon class-name="size-icon" icon-class="size" />
|
||||||
</div>
|
</div>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
|
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
sizeOptions: [
|
sizeOptions: [
|
||||||
{ label: 'Default', value: 'default' },
|
{ label: 'Default', value: 'default' },
|
||||||
{ label: 'Medium', value: 'medium' },
|
{ label: 'Medium', value: 'medium' },
|
||||||
{ label: 'Small', value: 'small' },
|
{ label: 'Small', value: 'small' },
|
||||||
{ label: 'Mini', value: 'mini' }
|
{ label: 'Mini', value: 'mini' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
size() {
|
size() {
|
||||||
return this.$store.getters.size
|
return this.$store.getters.size
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleSetSize(size) {
|
handleSetSize(size) {
|
||||||
this.$ELEMENT.size = size
|
this.$ELEMENT.size = size
|
||||||
this.$store.dispatch('app/setSize', size)
|
this.$store.dispatch('app/setSize', size)
|
||||||
this.refreshView()
|
this.refreshView()
|
||||||
this.$message({
|
this.$message({
|
||||||
message: 'Switch Size Success',
|
message: 'Switch Size Success',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
refreshView() {
|
refreshView() {
|
||||||
// In order to make the cached page re-rendered
|
// In order to make the cached page re-rendered
|
||||||
this.$store.dispatch('tagsView/delAllCachedViews', this.$route)
|
this.$store.dispatch('tagsView/delAllCachedViews', this.$route)
|
||||||
|
|
||||||
const { fullPath } = this.$route
|
const { fullPath } = this.$route
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$router.replace({
|
this.$router.replace({
|
||||||
path: '/redirect' + fullPath
|
path: '/redirect' + fullPath
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
|
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
|
||||||
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||||
<use :xlink:href="iconName" />
|
<use :xlink:href="iconName" />
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isExternal } from '@/utils/validate'
|
import { isExternal } from '@/utils/validate'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SvgIcon',
|
name: 'SvgIcon',
|
||||||
props: {
|
props: {
|
||||||
iconClass: {
|
iconClass: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
className: {
|
className: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isExternal() {
|
isExternal() {
|
||||||
return isExternal(this.iconClass)
|
return isExternal(this.iconClass)
|
||||||
},
|
},
|
||||||
iconName() {
|
iconName() {
|
||||||
return `#icon-${this.iconClass}`
|
return `#icon-${this.iconClass}`
|
||||||
},
|
},
|
||||||
svgClass() {
|
svgClass() {
|
||||||
if (this.className) {
|
if (this.className) {
|
||||||
return 'svg-icon ' + this.className
|
return 'svg-icon ' + this.className
|
||||||
} else {
|
} else {
|
||||||
return 'svg-icon'
|
return 'svg-icon'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
styleExternalIcon() {
|
styleExternalIcon() {
|
||||||
return {
|
return {
|
||||||
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
||||||
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
|
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
vertical-align: -0.15em;
|
vertical-align: -0.15em;
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.svg-external-icon {
|
.svg-external-icon {
|
||||||
background-color: currentColor;
|
background-color: currentColor;
|
||||||
mask-size: cover!important;
|
mask-size: cover!important;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,709 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="tree-sidebar" :class="{ collapsed: collapsed, resizing: isResizing, 'no-initial-transition': isLoadingFromStorage}" :style="{ width: sidebarWidth + 'px' }">
|
|
||||||
<!-- 右侧拖动条 -->
|
|
||||||
<div v-if="!collapsed" class="resize-handle" @mousedown="startResize" @touchstart="startResize" :class="{ active: isResizing }" />
|
|
||||||
<div class="tree-header">
|
|
||||||
<span class="tree-title" v-show="!collapsed">
|
|
||||||
<i :class="titleIconClass"></i> {{ title }}
|
|
||||||
</span>
|
|
||||||
<div class="tree-actions" v-show="!collapsed">
|
|
||||||
<el-tooltip :content="isExpandedAll ? '收起全部' : '展开全部'" placement="right">
|
|
||||||
<i class="tree-action-icon" :class="isExpandedAll ? 'el-icon-arrow-down' : 'el-icon-arrow-up'" @click="toggleExpandAll" />
|
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip content="刷新" placement="right">
|
|
||||||
<i class="tree-action-icon el-icon-refresh" @click="handleRefresh" />
|
|
||||||
</el-tooltip>
|
|
||||||
<slot name="actions"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 侧边栏展开/收起按钮 -->
|
|
||||||
<div class="collapse-button-container">
|
|
||||||
<el-tooltip :content="collapsed ? '展开' : '收起'" placement="right">
|
|
||||||
<i class="collapse-button" :class="collapsed ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'" @click="toggleCollapsed" />
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tree-search" v-show="!collapsed" v-if="showSearch">
|
|
||||||
<el-input v-model="searchKeyword" :placeholder="searchPlaceholder" clearable size="small" prefix-icon="el-icon-search" @input="onSearch" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tree-wrap" v-show="!collapsed">
|
|
||||||
<el-tree
|
|
||||||
ref="treeRef"
|
|
||||||
:data="treeData"
|
|
||||||
:props="treeProps"
|
|
||||||
:expand-on-click-node="expandOnClickNode"
|
|
||||||
:filter-node-method="filterNodeMethod"
|
|
||||||
:default-expand-all="defaultExpandAll"
|
|
||||||
:default-expanded-keys="defaultExpandedKeys"
|
|
||||||
:node-key="nodeKey"
|
|
||||||
:check-strictly="checkStrictly"
|
|
||||||
:show-checkbox="showCheckbox"
|
|
||||||
@node-click="onNodeClick"
|
|
||||||
@check="onCheck"
|
|
||||||
@node-expand="onNodeExpand"
|
|
||||||
@node-collapse="onNodeCollapse"
|
|
||||||
>
|
|
||||||
<span class="tree-node" slot-scope="{ node, data }">
|
|
||||||
<slot name="node" :node="node" :data="data">
|
|
||||||
<i :class="data.children && data.children.length ? 'el-icon-folder' : 'el-icon-document'" class="node-icon" />
|
|
||||||
<span class="node-label" :title="node.label">{{ node.label }}</span>
|
|
||||||
</slot>
|
|
||||||
</span>
|
|
||||||
</el-tree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "TreeSidebar",
|
|
||||||
props: {
|
|
||||||
// 树形数据
|
|
||||||
treeData: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
// 标题
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: '树形结构'
|
|
||||||
},
|
|
||||||
// 标题图标类名
|
|
||||||
titleIconClass: {
|
|
||||||
type: String,
|
|
||||||
default: 'el-icon-office-building'
|
|
||||||
},
|
|
||||||
// 是否显示搜索框
|
|
||||||
showSearch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
// 搜索框占位符
|
|
||||||
searchPlaceholder: {
|
|
||||||
type: String,
|
|
||||||
default: '请输入名称'
|
|
||||||
},
|
|
||||||
// 是否默认收起侧边栏
|
|
||||||
defaultCollapsed: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
// 树配置项
|
|
||||||
treeProps: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
children: "children",
|
|
||||||
label: "label"
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 节点唯一标识字段
|
|
||||||
nodeKey: {
|
|
||||||
type: String,
|
|
||||||
default: 'id'
|
|
||||||
},
|
|
||||||
// 是否在点击节点时展开或收起
|
|
||||||
expandOnClickNode: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
// 是否显示复选框
|
|
||||||
showCheckbox: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
// 是否严格的遵循父子不互相关联
|
|
||||||
checkStrictly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
// 是否默认展开所有节点
|
|
||||||
defaultExpandAll: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
// 默认展开的节点的key数组
|
|
||||||
defaultExpandedKeys: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
// 默认宽度
|
|
||||||
defaultWidth: {
|
|
||||||
type: Number,
|
|
||||||
default: 220
|
|
||||||
},
|
|
||||||
// 收起时的宽度
|
|
||||||
collapsedWidth: {
|
|
||||||
type: Number,
|
|
||||||
default: 20
|
|
||||||
},
|
|
||||||
// 最小宽度
|
|
||||||
minWidth: {
|
|
||||||
type: Number,
|
|
||||||
default: 180
|
|
||||||
},
|
|
||||||
// 最大宽度
|
|
||||||
maxWidth: {
|
|
||||||
type: Number,
|
|
||||||
default: 400
|
|
||||||
},
|
|
||||||
// 本地存储的宽度key
|
|
||||||
storageKey: {
|
|
||||||
type: String,
|
|
||||||
default: 'tree-sidebar-width'
|
|
||||||
},
|
|
||||||
// 是否启用本地存储宽度
|
|
||||||
enableStorage: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
// 自定义过滤方法
|
|
||||||
filterMethod: {
|
|
||||||
type: Function,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
searchKeyword: "",
|
|
||||||
collapsed: this.defaultCollapsed,
|
|
||||||
sidebarWidth: this.defaultCollapsed ? this.collapsedWidth : this.defaultWidth,
|
|
||||||
isResizing: false,
|
|
||||||
startX: 0,
|
|
||||||
startWidth: 0,
|
|
||||||
saveWidthTimer: null,
|
|
||||||
rafId: null,
|
|
||||||
isLoadingFromStorage: false,
|
|
||||||
expandedAll: this.defaultExpandAll
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// 计算当前是否全部展开
|
|
||||||
isExpandedAll: {
|
|
||||||
get() {
|
|
||||||
return this.expandedAll;
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
this.expandedAll = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
collapsed(newVal, oldVal) {
|
|
||||||
if (newVal !== oldVal) {
|
|
||||||
this.handleCollapseChange(newVal);
|
|
||||||
this.$emit("collapsed-change", newVal);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 监听内部展开状态变化,触发实际树的展开/收起
|
|
||||||
expandedAll(newVal) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
if (newVal) {
|
|
||||||
this.expandAllNodes();
|
|
||||||
} else {
|
|
||||||
this.collapseAllNodes();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.$emit("expanded-all-change", newVal);
|
|
||||||
},
|
|
||||||
// 监听搜索关键词
|
|
||||||
searchKeyword(val) {
|
|
||||||
if (this.$refs.treeRef) {
|
|
||||||
this.$refs.treeRef.filter(val);
|
|
||||||
this.$emit("search", val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.isLoadingFromStorage = true
|
|
||||||
if (!this.collapsed && this.enableStorage) {
|
|
||||||
const savedWidth = this.getSavedWidth();
|
|
||||||
if (savedWidth !== null) {
|
|
||||||
this.sidebarWidth = savedWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.isLoadingFromStorage = false
|
|
||||||
})
|
|
||||||
if (this.expandedAll) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.expandAllNodes();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.cleanup();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 节点过滤方法
|
|
||||||
filterNodeMethod(value, data) {
|
|
||||||
if (this.filterMethod) {
|
|
||||||
return this.filterMethod(value, data);
|
|
||||||
}
|
|
||||||
if (!value) return true;
|
|
||||||
return data.label && data.label.indexOf(value) !== -1;
|
|
||||||
},
|
|
||||||
// 清理定时器和动画帧
|
|
||||||
cleanup() {
|
|
||||||
if (this.rafId) {
|
|
||||||
cancelAnimationFrame(this.rafId);
|
|
||||||
this.rafId = null;
|
|
||||||
}
|
|
||||||
if (this.saveWidthTimer) {
|
|
||||||
clearTimeout(this.saveWidthTimer);
|
|
||||||
this.saveWidthTimer = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 处理收起/展开状态变化
|
|
||||||
handleCollapseChange(isCollapsed) {
|
|
||||||
if (isCollapsed) {
|
|
||||||
this.saveWidthToStorage();
|
|
||||||
this.sidebarWidth = this.collapsedWidth;
|
|
||||||
} else {
|
|
||||||
const savedWidth = this.getSavedWidth();
|
|
||||||
this.sidebarWidth = savedWidth !== null ? savedWidth : this.defaultWidth;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 获取保存的宽度
|
|
||||||
getSavedWidth() {
|
|
||||||
if (!this.enableStorage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const savedWidth = localStorage.getItem(this.storageKey);
|
|
||||||
if (savedWidth) {
|
|
||||||
const width = parseInt(savedWidth, 10);
|
|
||||||
if (!isNaN(width) && width >= this.minWidth && width <= this.maxWidth) {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Failed to load sidebar width from storage with key ${this.storageKey}:`, error);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
// 保存宽度到本地存储
|
|
||||||
saveWidthToStorage() {
|
|
||||||
if (this.collapsed || !this.enableStorage) return;
|
|
||||||
try {
|
|
||||||
localStorage.setItem(this.storageKey, this.sidebarWidth.toString());
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`Failed to save sidebar width to storage with key ${this.storageKey}:`, error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 切换侧边栏收起/展开状态
|
|
||||||
toggleCollapsed() {
|
|
||||||
this.collapsed = !this.collapsed;
|
|
||||||
},
|
|
||||||
// 切换展开/折叠所有节点
|
|
||||||
toggleExpandAll() {
|
|
||||||
this.isExpandedAll = !this.isExpandedAll;
|
|
||||||
},
|
|
||||||
// 展开所有节点
|
|
||||||
expandAllNodes() {
|
|
||||||
if (!this.$refs.treeRef) return;
|
|
||||||
const allNodes = this.getAllNodes(this.$refs.treeRef.root);
|
|
||||||
allNodes.forEach(node => {
|
|
||||||
if (node.expanded !== undefined && !node.expanded) {
|
|
||||||
node.expanded = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 获取所有节点
|
|
||||||
getAllNodes(rootNode) {
|
|
||||||
const nodes = [];
|
|
||||||
const traverse = (node) => {
|
|
||||||
if (!node) return;
|
|
||||||
nodes.push(node);
|
|
||||||
if (node.childNodes && node.childNodes.length) {
|
|
||||||
node.childNodes.forEach(child => traverse(child));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
traverse(rootNode);
|
|
||||||
return nodes;
|
|
||||||
},
|
|
||||||
// 收起所有节点
|
|
||||||
collapseAllNodes() {
|
|
||||||
if (!this.$refs.treeRef) return;
|
|
||||||
const allNodes = this.getAllNodes(this.$refs.treeRef.root);
|
|
||||||
allNodes.forEach(node => {
|
|
||||||
if (node.expanded !== undefined && node.expanded) {
|
|
||||||
node.expanded = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 处理刷新操作
|
|
||||||
handleRefresh() {
|
|
||||||
this.$emit("refresh");
|
|
||||||
},
|
|
||||||
// 节点点击事件
|
|
||||||
onNodeClick(data, node, e) {
|
|
||||||
this.$emit("node-click", data, node, e);
|
|
||||||
},
|
|
||||||
// 复选框选中事件
|
|
||||||
onCheck(data, checkedInfo) {
|
|
||||||
this.$emit("check", data, checkedInfo);
|
|
||||||
},
|
|
||||||
// 节点展开事件
|
|
||||||
onNodeExpand(data, node, e) {
|
|
||||||
this.$emit("node-expand", data, node, e);
|
|
||||||
},
|
|
||||||
// 节点折叠事件
|
|
||||||
onNodeCollapse(data, node, e) {
|
|
||||||
this.$emit("node-collapse", data, node, e);
|
|
||||||
},
|
|
||||||
// 搜索处理
|
|
||||||
onSearch() {
|
|
||||||
// 搜索逻辑已在 watch 中处理
|
|
||||||
},
|
|
||||||
// 设置当前选中的节点
|
|
||||||
setCurrentKey(key) {
|
|
||||||
if (this.$refs.treeRef) {
|
|
||||||
this.$refs.treeRef.setCurrentKey(key);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 获取当前选中的节点
|
|
||||||
getCurrentNode() {
|
|
||||||
if (this.$refs.treeRef) {
|
|
||||||
return this.$refs.treeRef.getCurrentNode();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
// 获取当前选中的节点的key
|
|
||||||
getCurrentKey() {
|
|
||||||
if (this.$refs.treeRef) {
|
|
||||||
return this.$refs.treeRef.getCurrentKey();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
// 设置选中的节点keys(复选框)
|
|
||||||
setCheckedKeys(keys) {
|
|
||||||
if (this.$refs.treeRef && this.showCheckbox) {
|
|
||||||
this.$refs.treeRef.setCheckedKeys(keys);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 获取选中的节点keys(复选框)
|
|
||||||
getCheckedKeys() {
|
|
||||||
if (this.$refs.treeRef && this.showCheckbox) {
|
|
||||||
return this.$refs.treeRef.getCheckedKeys();
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
// 获取选中的节点(复选框)
|
|
||||||
getCheckedNodes() {
|
|
||||||
if (this.$refs.treeRef && this.showCheckbox) {
|
|
||||||
return this.$refs.treeRef.getCheckedNodes();
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
// 清空搜索
|
|
||||||
clearSearch() {
|
|
||||||
this.searchKeyword = "";
|
|
||||||
if (this.$refs.treeRef) {
|
|
||||||
this.$refs.treeRef.filter("");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 过滤树
|
|
||||||
filter(value) {
|
|
||||||
this.searchKeyword = value;
|
|
||||||
},
|
|
||||||
// 开始调整大小
|
|
||||||
startResize(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
this.isResizing = true;
|
|
||||||
this.startX = e.type === 'mousedown' ? e.clientX : e.touches[0].clientX;
|
|
||||||
this.startWidth = this.sidebarWidth;
|
|
||||||
|
|
||||||
if (e.type === 'mousedown') {
|
|
||||||
document.addEventListener('mousemove', this.handleResizeMove);
|
|
||||||
document.addEventListener('mouseup', this.stopResize);
|
|
||||||
} else {
|
|
||||||
document.addEventListener('touchmove', this.handleResizeMove, { passive: false });
|
|
||||||
document.addEventListener('touchend', this.stopResize);
|
|
||||||
}
|
|
||||||
this.disableUserSelect();
|
|
||||||
},
|
|
||||||
// 处理调整大小移动
|
|
||||||
handleResizeMove(e) {
|
|
||||||
if (!this.isResizing) return;
|
|
||||||
if (this.rafId) {
|
|
||||||
cancelAnimationFrame(this.rafId);
|
|
||||||
}
|
|
||||||
this.rafId = requestAnimationFrame(() => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
const clientX = e.type === 'mousemove' ? e.clientX : e.touches[0].clientX;
|
|
||||||
const deltaX = clientX - this.startX;
|
|
||||||
const newWidth = this.startWidth + deltaX;
|
|
||||||
const clampedWidth = Math.max(this.minWidth, Math.min(this.maxWidth, newWidth));
|
|
||||||
if (Math.abs(clampedWidth - this.sidebarWidth) >= 1) {
|
|
||||||
this.sidebarWidth = clampedWidth;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 停止调整大小
|
|
||||||
stopResize() {
|
|
||||||
if (!this.isResizing) return;
|
|
||||||
this.isResizing = false;
|
|
||||||
if (this.rafId) {
|
|
||||||
cancelAnimationFrame(this.rafId);
|
|
||||||
this.rafId = null;
|
|
||||||
}
|
|
||||||
this.startX = 0;
|
|
||||||
this.startWidth = 0;
|
|
||||||
document.removeEventListener('mousemove', this.handleResizeMove);
|
|
||||||
document.removeEventListener('mouseup', this.stopResize);
|
|
||||||
document.removeEventListener('touchmove', this.handleResizeMove);
|
|
||||||
document.removeEventListener('touchend', this.stopResize);
|
|
||||||
this.enableUserSelect();
|
|
||||||
this.saveWidthToStorage();
|
|
||||||
},
|
|
||||||
// 禁用用户选择
|
|
||||||
disableUserSelect() {
|
|
||||||
document.body.style.userSelect = 'none';
|
|
||||||
document.body.style.webkitUserSelect = 'none';
|
|
||||||
document.body.style.mozUserSelect = 'none';
|
|
||||||
document.body.style.msUserSelect = 'none';
|
|
||||||
},
|
|
||||||
// 启用用户选择
|
|
||||||
enableUserSelect() {
|
|
||||||
document.body.style.userSelect = '';
|
|
||||||
document.body.style.webkitUserSelect = '';
|
|
||||||
document.body.style.mozUserSelect = '';
|
|
||||||
document.body.style.msUserSelect = '';
|
|
||||||
},
|
|
||||||
// 重置宽度到默认值
|
|
||||||
resetWidth() {
|
|
||||||
this.sidebarWidth = this.defaultWidth;
|
|
||||||
this.saveWidthToStorage();
|
|
||||||
},
|
|
||||||
// 获取当前宽度
|
|
||||||
getCurrentWidth() {
|
|
||||||
return this.sidebarWidth;
|
|
||||||
},
|
|
||||||
// 设置宽度
|
|
||||||
setWidth(width) {
|
|
||||||
if (typeof width === 'number' && width >= this.minWidth && width <= this.maxWidth) {
|
|
||||||
this.sidebarWidth = width;
|
|
||||||
if (!this.collapsed) {
|
|
||||||
this.saveWidthToStorage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.tree-sidebar {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 220px;
|
|
||||||
background: #fff;
|
|
||||||
border-right: 1px solid #e8eaed;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
transition: width 0.25s ease;
|
|
||||||
|
|
||||||
&.collapsed {
|
|
||||||
width: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.resizing {
|
|
||||||
transition: none;
|
|
||||||
will-change: width;
|
|
||||||
|
|
||||||
* {
|
|
||||||
pointer-events: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-initial-transition {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-handle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 6px;
|
|
||||||
height: 100%;
|
|
||||||
cursor: col-resize;
|
|
||||||
z-index: 20;
|
|
||||||
background: transparent;
|
|
||||||
transition: background 0.2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(64, 158, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background: rgba(64, 158, 255, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapse-button-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 0;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
z-index: 100;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 15px;
|
|
||||||
height: 20px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 0 4px 4px 0;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
.tree-sidebar.collapsed & {
|
|
||||||
right: 0;
|
|
||||||
background: #f7f8fa;
|
|
||||||
border-radius: 0 4px 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-sidebar.resizing & {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapse-button {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #909399;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #409eff;
|
|
||||||
background: #ecf5ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 10px;
|
|
||||||
height: 40px;
|
|
||||||
border-bottom: 1px solid #e8eaed;
|
|
||||||
background: #f7f8fa;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.tree-title {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #303133;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
i {
|
|
||||||
color: #409eff;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-action-icon {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #909399;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: all 0.2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #409eff;
|
|
||||||
background: #ecf5ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-search {
|
|
||||||
padding: 10px 10px 4px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-wrap {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 6px 6px 12px;
|
|
||||||
|
|
||||||
.tree-sidebar.resizing & {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: #dcdfe6;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #c0c4cc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-tree-node__content {
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 1px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #f0f7ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-tree-node.is-current > .el-tree-node__content {
|
|
||||||
background: #e6f0fd;
|
|
||||||
color: #409eff;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
.node-icon {
|
|
||||||
color: #409eff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-node {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.node-icon {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #f5a623;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-label {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-icon-document.node-icon {
|
|
||||||
color: #909399 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,36 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading" :style="'height:' + height">
|
<div v-loading="loading" :style="'height:' + height">
|
||||||
<iframe
|
<iframe
|
||||||
:src="src"
|
:src="src"
|
||||||
frameborder="no"
|
frameborder="no"
|
||||||
style="width: 100%; height: 100%"
|
style="width: 100%; height: 100%"
|
||||||
scrolling="auto"
|
scrolling="auto"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
src: {
|
src: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
height: document.documentElement.clientHeight - 94.5 + "px;",
|
height: document.documentElement.clientHeight - 94.5 + "px;",
|
||||||
loading: true,
|
loading: true,
|
||||||
url: this.src
|
url: this.src
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}, 300)
|
}, 300)
|
||||||
const that = this
|
const that = this
|
||||||
window.onresize = function temp() {
|
window.onresize = function temp() {
|
||||||
that.height = document.documentElement.clientHeight - 94.5 + "px;"
|
that.height = document.documentElement.clientHeight - 94.5 + "px;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,64 +1,64 @@
|
|||||||
/**
|
/**
|
||||||
* v-dialogDrag 弹窗拖拽
|
* v-dialogDrag 弹窗拖拽
|
||||||
* Copyright (c) 2019 ruoyi
|
* Copyright (c) 2019 ruoyi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
bind(el, binding, vnode, oldVnode) {
|
bind(el, binding, vnode, oldVnode) {
|
||||||
const value = binding.value
|
const value = binding.value
|
||||||
if (value == false) return
|
if (value == false) return
|
||||||
// 获取拖拽内容头部
|
// 获取拖拽内容头部
|
||||||
const dialogHeaderEl = el.querySelector('.el-dialog__header')
|
const dialogHeaderEl = el.querySelector('.el-dialog__header')
|
||||||
const dragDom = el.querySelector('.el-dialog')
|
const dragDom = el.querySelector('.el-dialog')
|
||||||
dialogHeaderEl.style.cursor = 'move'
|
dialogHeaderEl.style.cursor = 'move'
|
||||||
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null)
|
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null)
|
||||||
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
|
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)
|
||||||
dragDom.style.position = 'absolute'
|
dragDom.style.position = 'absolute'
|
||||||
dragDom.style.marginTop = 0
|
dragDom.style.marginTop = 0
|
||||||
let width = dragDom.style.width
|
let width = dragDom.style.width
|
||||||
if (width.includes('%')) {
|
if (width.includes('%')) {
|
||||||
width = +document.body.clientWidth * (+width.replace(/\%/g, '') / 100)
|
width = +document.body.clientWidth * (+width.replace(/\%/g, '') / 100)
|
||||||
} else {
|
} else {
|
||||||
width = +width.replace(/\px/g, '')
|
width = +width.replace(/\px/g, '')
|
||||||
}
|
}
|
||||||
dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`
|
dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`
|
||||||
// 鼠标按下事件
|
// 鼠标按下事件
|
||||||
dialogHeaderEl.onmousedown = (e) => {
|
dialogHeaderEl.onmousedown = (e) => {
|
||||||
// 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)
|
// 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)
|
||||||
const disX = e.clientX - dialogHeaderEl.offsetLeft
|
const disX = e.clientX - dialogHeaderEl.offsetLeft
|
||||||
const disY = e.clientY - dialogHeaderEl.offsetTop
|
const disY = e.clientY - dialogHeaderEl.offsetTop
|
||||||
|
|
||||||
// 获取到的值带px 正则匹配替换
|
// 获取到的值带px 正则匹配替换
|
||||||
let styL, styT
|
let styL, styT
|
||||||
|
|
||||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||||
if (sty.left.includes('%')) {
|
if (sty.left.includes('%')) {
|
||||||
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
|
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
|
||||||
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100)
|
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100)
|
||||||
} else {
|
} else {
|
||||||
styL = +sty.left.replace(/\px/g, '')
|
styL = +sty.left.replace(/\px/g, '')
|
||||||
styT = +sty.top.replace(/\px/g, '')
|
styT = +sty.top.replace(/\px/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 鼠标拖拽事件
|
// 鼠标拖拽事件
|
||||||
document.onmousemove = function (e) {
|
document.onmousemove = function (e) {
|
||||||
// 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离)
|
// 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离)
|
||||||
const l = e.clientX - disX
|
const l = e.clientX - disX
|
||||||
const t = e.clientY - disY
|
const t = e.clientY - disY
|
||||||
|
|
||||||
let finallyL = l + styL
|
let finallyL = l + styL
|
||||||
let finallyT = t + styT
|
let finallyT = t + styT
|
||||||
|
|
||||||
// 移动当前元素
|
// 移动当前元素
|
||||||
dragDom.style.left = `${finallyL}px`
|
dragDom.style.left = `${finallyL}px`
|
||||||
dragDom.style.top = `${finallyT}px`
|
dragDom.style.top = `${finallyT}px`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.onmouseup = function (e) {
|
document.onmouseup = function (e) {
|
||||||
document.onmousemove = null
|
document.onmousemove = null
|
||||||
document.onmouseup = null
|
document.onmouseup = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,34 +1,34 @@
|
|||||||
/**
|
/**
|
||||||
* v-dialogDragWidth 可拖动弹窗高度(右下角)
|
* v-dialogDragWidth 可拖动弹窗高度(右下角)
|
||||||
* Copyright (c) 2019 ruoyi
|
* Copyright (c) 2019 ruoyi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
bind(el) {
|
bind(el) {
|
||||||
const dragDom = el.querySelector('.el-dialog')
|
const dragDom = el.querySelector('.el-dialog')
|
||||||
const lineEl = document.createElement('div')
|
const lineEl = document.createElement('div')
|
||||||
lineEl.style = 'width: 6px; background: inherit; height: 10px; position: absolute; right: 0; bottom: 0; margin: auto; z-index: 1; cursor: nwse-resize;'
|
lineEl.style = 'width: 6px; background: inherit; height: 10px; position: absolute; right: 0; bottom: 0; margin: auto; z-index: 1; cursor: nwse-resize;'
|
||||||
lineEl.addEventListener('mousedown',
|
lineEl.addEventListener('mousedown',
|
||||||
function(e) {
|
function(e) {
|
||||||
// 鼠标按下,计算当前元素距离可视区的距离
|
// 鼠标按下,计算当前元素距离可视区的距离
|
||||||
const disX = e.clientX - el.offsetLeft
|
const disX = e.clientX - el.offsetLeft
|
||||||
const disY = e.clientY - el.offsetTop
|
const disY = e.clientY - el.offsetTop
|
||||||
// 当前宽度 高度
|
// 当前宽度 高度
|
||||||
const curWidth = dragDom.offsetWidth
|
const curWidth = dragDom.offsetWidth
|
||||||
const curHeight = dragDom.offsetHeight
|
const curHeight = dragDom.offsetHeight
|
||||||
document.onmousemove = function(e) {
|
document.onmousemove = function(e) {
|
||||||
e.preventDefault() // 移动时禁用默认事件
|
e.preventDefault() // 移动时禁用默认事件
|
||||||
// 通过事件委托,计算移动的距离
|
// 通过事件委托,计算移动的距离
|
||||||
const xl = e.clientX - disX
|
const xl = e.clientX - disX
|
||||||
const yl = e.clientY - disY
|
const yl = e.clientY - disY
|
||||||
dragDom.style.width = `${curWidth + xl}px`
|
dragDom.style.width = `${curWidth + xl}px`
|
||||||
dragDom.style.height = `${curHeight + yl}px`
|
dragDom.style.height = `${curHeight + yl}px`
|
||||||
}
|
}
|
||||||
document.onmouseup = function(e) {
|
document.onmouseup = function(e) {
|
||||||
document.onmousemove = null
|
document.onmousemove = null
|
||||||
document.onmouseup = null
|
document.onmouseup = null
|
||||||
}
|
}
|
||||||
}, false)
|
}, false)
|
||||||
dragDom.appendChild(lineEl)
|
dragDom.appendChild(lineEl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
/**
|
/**
|
||||||
* v-dialogDragWidth 可拖动弹窗宽度(右侧边)
|
* v-dialogDragWidth 可拖动弹窗宽度(右侧边)
|
||||||
* Copyright (c) 2019 ruoyi
|
* Copyright (c) 2019 ruoyi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
bind(el) {
|
bind(el) {
|
||||||
const dragDom = el.querySelector('.el-dialog')
|
const dragDom = el.querySelector('.el-dialog')
|
||||||
const lineEl = document.createElement('div')
|
const lineEl = document.createElement('div')
|
||||||
lineEl.style = 'width: 5px; background: inherit; height: 80%; position: absolute; right: 0; top: 0; bottom: 0; margin: auto; z-index: 1; cursor: w-resize;'
|
lineEl.style = 'width: 5px; background: inherit; height: 80%; position: absolute; right: 0; top: 0; bottom: 0; margin: auto; z-index: 1; cursor: w-resize;'
|
||||||
lineEl.addEventListener('mousedown',
|
lineEl.addEventListener('mousedown',
|
||||||
function (e) {
|
function (e) {
|
||||||
// 鼠标按下,计算当前元素距离可视区的距离
|
// 鼠标按下,计算当前元素距离可视区的距离
|
||||||
const disX = e.clientX - el.offsetLeft
|
const disX = e.clientX - el.offsetLeft
|
||||||
// 当前宽度
|
// 当前宽度
|
||||||
const curWidth = dragDom.offsetWidth
|
const curWidth = dragDom.offsetWidth
|
||||||
document.onmousemove = function (e) {
|
document.onmousemove = function (e) {
|
||||||
e.preventDefault() // 移动时禁用默认事件
|
e.preventDefault() // 移动时禁用默认事件
|
||||||
// 通过事件委托,计算移动的距离
|
// 通过事件委托,计算移动的距离
|
||||||
const l = e.clientX - disX
|
const l = e.clientX - disX
|
||||||
dragDom.style.width = `${curWidth + l}px`
|
dragDom.style.width = `${curWidth + l}px`
|
||||||
}
|
}
|
||||||
document.onmouseup = function (e) {
|
document.onmouseup = function (e) {
|
||||||
document.onmousemove = null
|
document.onmousemove = null
|
||||||
document.onmouseup = null
|
document.onmouseup = null
|
||||||
}
|
}
|
||||||
}, false)
|
}, false)
|
||||||
dragDom.appendChild(lineEl)
|
dragDom.appendChild(lineEl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import hasRole from './permission/hasRole'
|
import hasRole from './permission/hasRole'
|
||||||
import hasPermi from './permission/hasPermi'
|
import hasPermi from './permission/hasPermi'
|
||||||
import dialogDrag from './dialog/drag'
|
import dialogDrag from './dialog/drag'
|
||||||
import dialogDragWidth from './dialog/dragWidth'
|
import dialogDragWidth from './dialog/dragWidth'
|
||||||
import dialogDragHeight from './dialog/dragHeight'
|
import dialogDragHeight from './dialog/dragHeight'
|
||||||
import clipboard from './module/clipboard'
|
import clipboard from './module/clipboard'
|
||||||
|
|
||||||
const install = function(Vue) {
|
const install = function(Vue) {
|
||||||
Vue.directive('hasRole', hasRole)
|
Vue.directive('hasRole', hasRole)
|
||||||
Vue.directive('hasPermi', hasPermi)
|
Vue.directive('hasPermi', hasPermi)
|
||||||
Vue.directive('clipboard', clipboard)
|
Vue.directive('clipboard', clipboard)
|
||||||
Vue.directive('dialogDrag', dialogDrag)
|
Vue.directive('dialogDrag', dialogDrag)
|
||||||
Vue.directive('dialogDragWidth', dialogDragWidth)
|
Vue.directive('dialogDragWidth', dialogDragWidth)
|
||||||
Vue.directive('dialogDragHeight', dialogDragHeight)
|
Vue.directive('dialogDragHeight', dialogDragHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.Vue) {
|
if (window.Vue) {
|
||||||
window['hasRole'] = hasRole
|
window['hasRole'] = hasRole
|
||||||
window['hasPermi'] = hasPermi
|
window['hasPermi'] = hasPermi
|
||||||
Vue.use(install)
|
Vue.use(install)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default install
|
export default install
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
/**
|
/**
|
||||||
* v-hasPermi 操作权限处理
|
* v-hasPermi 操作权限处理
|
||||||
* Copyright (c) 2019 ruoyi
|
* Copyright (c) 2019 ruoyi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inserted(el, binding, vnode) {
|
inserted(el, binding, vnode) {
|
||||||
const { value } = binding
|
const { value } = binding
|
||||||
const all_permission = "*:*:*"
|
const all_permission = "*:*:*"
|
||||||
const permissions = store.getters && store.getters.permissions
|
const permissions = store.getters && store.getters.permissions
|
||||||
|
|
||||||
if (value && value instanceof Array && value.length > 0) {
|
if (value && value instanceof Array && value.length > 0) {
|
||||||
const permissionFlag = value
|
const permissionFlag = value
|
||||||
|
|
||||||
const hasPermissions = permissions.some(permission => {
|
const hasPermissions = permissions.some(permission => {
|
||||||
return all_permission === permission || permissionFlag.includes(permission)
|
return all_permission === permission || permissionFlag.includes(permission)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!hasPermissions) {
|
if (!hasPermissions) {
|
||||||
el.parentNode && el.parentNode.removeChild(el)
|
el.parentNode && el.parentNode.removeChild(el)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`请设置操作权限标签值`)
|
throw new Error(`请设置操作权限标签值`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
/**
|
/**
|
||||||
* v-hasRole 角色权限处理
|
* v-hasRole 角色权限处理
|
||||||
* Copyright (c) 2019 ruoyi
|
* Copyright (c) 2019 ruoyi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inserted(el, binding, vnode) {
|
inserted(el, binding, vnode) {
|
||||||
const { value } = binding
|
const { value } = binding
|
||||||
const super_admin = "admin"
|
const super_admin = "admin"
|
||||||
const roles = store.getters && store.getters.roles
|
const roles = store.getters && store.getters.roles
|
||||||
|
|
||||||
if (value && value instanceof Array && value.length > 0) {
|
if (value && value instanceof Array && value.length > 0) {
|
||||||
const roleFlag = value
|
const roleFlag = value
|
||||||
|
|
||||||
const hasRole = roles.some(role => {
|
const hasRole = roles.some(role => {
|
||||||
return super_admin === role || roleFlag.includes(role)
|
return super_admin === role || roleFlag.includes(role)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!hasRole) {
|
if (!hasRole) {
|
||||||
el.parentNode && el.parentNode.removeChild(el)
|
el.parentNode && el.parentNode.removeChild(el)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`请设置角色权限标签值"`)
|
throw new Error(`请设置角色权限标签值"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,14 +51,6 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:fullscreen,
|
|
||||||
&:-webkit-full-screen,
|
|
||||||
&:-moz-full-screen,
|
|
||||||
&:-ms-fullscreen {
|
|
||||||
background: #fff;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-header + .app-main {
|
.fixed-header + .app-main {
|
||||||
@@ -137,4 +129,4 @@ export default {
|
|||||||
background-color: #c0c0c0;
|
background-color: #c0c0c0;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,362 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-drawer title="公告详情" :visible.sync="visible" direction="rtl" size="50%" append-to-body :before-close="handleClose" custom-class="notice-detail-drawer">
|
|
||||||
<div v-loading="loading" class="notice-detail-drawer__body">
|
|
||||||
<div v-if="!detail" class="notice-empty">
|
|
||||||
<i class="el-icon-document"></i>
|
|
||||||
<span>暂无数据</span>
|
|
||||||
</div>
|
|
||||||
<div v-else class="notice-page">
|
|
||||||
<div class="notice-type-wrap">
|
|
||||||
<span v-if="detail.noticeType === '1'" class="notice-type-tag type-notify">
|
|
||||||
<i class="el-icon-bell"></i> 通知
|
|
||||||
</span>
|
|
||||||
<span v-else-if="detail.noticeType === '2'" class="notice-type-tag type-announce">
|
|
||||||
<i class="el-icon-message"></i> 公告
|
|
||||||
</span>
|
|
||||||
<span v-else class="notice-type-tag type-notify">
|
|
||||||
<i class="el-icon-document"></i> 消息
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 class="notice-title">{{ detail.noticeTitle }}</h1>
|
|
||||||
|
|
||||||
<div class="notice-meta">
|
|
||||||
<span class="meta-item">
|
|
||||||
<i class="el-icon-user"></i>
|
|
||||||
<span>{{ detail.createBy || '—' }}</span>
|
|
||||||
</span>
|
|
||||||
<span class="meta-item">
|
|
||||||
<i class="el-icon-time"></i>
|
|
||||||
<span>{{ detail.createTime || '—' }}</span>
|
|
||||||
</span>
|
|
||||||
<span class="meta-item">
|
|
||||||
<span :class="['status-dot', isStatusNormal ? 'status-ok' : 'status-off']"></span>
|
|
||||||
<span>{{ isStatusNormal ? '正常' : '已关闭' }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="notice-divider">
|
|
||||||
<span class="notice-divider-dot"></span>
|
|
||||||
<span class="notice-divider-dot"></span>
|
|
||||||
<span class="notice-divider-dot"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="notice-body">
|
|
||||||
<div v-if="hasContent" class="notice-content" v-html="detail.noticeContent" />
|
|
||||||
<div v-else class="notice-empty notice-empty--inner">
|
|
||||||
<i class="el-icon-document"></i> 暂无内容
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-drawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { getNotice } from '@/api/system/notice'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'NoticeDetailView',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
visible: false,
|
|
||||||
loading: false,
|
|
||||||
detail: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isStatusNormal() {
|
|
||||||
const s = this.detail && this.detail.status
|
|
||||||
return s === '0' || s === 0
|
|
||||||
},
|
|
||||||
hasContent() {
|
|
||||||
const c = this.detail && this.detail.noticeContent
|
|
||||||
return c != null && String(c).trim() !== ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
open(payload) {
|
|
||||||
let id = null
|
|
||||||
let preset = null
|
|
||||||
if (payload != null && typeof payload === 'object') {
|
|
||||||
id = payload.noticeId
|
|
||||||
if (payload.noticeContent != null) {
|
|
||||||
preset = payload
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
id = payload
|
|
||||||
}
|
|
||||||
this.visible = true
|
|
||||||
if (preset) {
|
|
||||||
this.detail = preset
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (id == null || id === '') {
|
|
||||||
this.detail = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.loading = true
|
|
||||||
this.detail = null
|
|
||||||
getNotice(id).then(res => {
|
|
||||||
this.detail = res.data
|
|
||||||
}).catch(() => {
|
|
||||||
this.detail = null
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleClose() {
|
|
||||||
this.visible = false
|
|
||||||
this.detail = null
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.notice-page {
|
|
||||||
max-width: 760px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 8px 8px 20px;
|
|
||||||
animation: notice-fade-up 0.28s ease both;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes notice-fade-up {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(14px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-type-tag {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
padding: 3px 12px;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-notify {
|
|
||||||
background: #fff8e6;
|
|
||||||
color: #b7791f;
|
|
||||||
border-left: 3px solid #d97706;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-announce {
|
|
||||||
background: #e8f5e9;
|
|
||||||
color: #276749;
|
|
||||||
border-left: 3px solid #38a169;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-title {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #1a202c;
|
|
||||||
line-height: 1.45;
|
|
||||||
margin: 0 0 16px;
|
|
||||||
letter-spacing: -0.2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-meta {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 12px 0;
|
|
||||||
border-top: 1px solid #e9ecef;
|
|
||||||
border-bottom: 1px solid #e9ecef;
|
|
||||||
margin-bottom: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #718096;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meta-item i {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #a0aec0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-dot {
|
|
||||||
display: inline-block;
|
|
||||||
width: 7px;
|
|
||||||
height: 7px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-ok {
|
|
||||||
background: #38a169;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-off {
|
|
||||||
background: #e53e3e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-divider {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-divider::before,
|
|
||||||
.notice-divider::after {
|
|
||||||
content: '';
|
|
||||||
flex: 1;
|
|
||||||
height: 1px;
|
|
||||||
background: linear-gradient(to right, transparent, #dee2e6, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-divider-dot {
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #cbd5e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-body {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 28px 32px;
|
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.04);
|
|
||||||
min-height: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.85;
|
|
||||||
color: #2d3748;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep p {
|
|
||||||
margin: 0 0 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep h1,
|
|
||||||
.notice-content ::v-deep h2,
|
|
||||||
.notice-content ::v-deep h3 {
|
|
||||||
font-weight: 700;
|
|
||||||
color: #1a202c;
|
|
||||||
margin: 1.4em 0 0.6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep h1 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep h2 {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep h3 {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep a {
|
|
||||||
color: #3182ce;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep a:hover {
|
|
||||||
color: #2b6cb0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep img {
|
|
||||||
max-width: 100%;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep ul,
|
|
||||||
.notice-content ::v-deep ol {
|
|
||||||
padding-left: 20px;
|
|
||||||
margin: 0 0 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep li {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep blockquote {
|
|
||||||
border-left: 3px solid #cbd5e0;
|
|
||||||
margin: 1em 0;
|
|
||||||
padding: 6px 16px;
|
|
||||||
color: #718096;
|
|
||||||
background: #f7fafc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
margin: 1em 0;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep table th,
|
|
||||||
.notice-content ::v-deep table td {
|
|
||||||
border: 1px solid #e2e8f0;
|
|
||||||
padding: 7px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-content ::v-deep table th {
|
|
||||||
background: #f7fafc;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-empty {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px 0;
|
|
||||||
color: #a0aec0;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-empty i {
|
|
||||||
font-size: 28px;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-empty--inner {
|
|
||||||
padding: 32px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-empty--inner i {
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .notice-detail-drawer {
|
|
||||||
.el-drawer__header {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding: 16px 20px;
|
|
||||||
border-bottom: 1px solid #ebeef5;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
.el-drawer__body {
|
|
||||||
background: #f5f6f8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notice-detail-drawer__body {
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
padding: 10px 16px 22px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-popover ref="noticePopover" placement="bottom-end" width="320" trigger="manual" :value="noticeVisible" popper-class="notice-popover">
|
|
||||||
<div class="notice-header">
|
|
||||||
<span class="notice-title">通知公告</span>
|
|
||||||
<span class="notice-mark-all" @click="markAllRead">全部已读</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="noticeLoading" class="notice-loading"><i class="el-icon-loading"></i> 加载中...</div>
|
|
||||||
<div v-else-if="noticeList.length === 0" class="notice-empty"><i class="el-icon-inbox"></i><br>暂无公告</div>
|
|
||||||
<div v-else>
|
|
||||||
<div v-for="item in noticeList" :key="item.noticeId" class="notice-item" :class="{ 'is-read': item.isRead }" @click="previewNotice(item)">
|
|
||||||
<el-tag size="mini" :type="item.noticeType === '1' ? 'warning' : 'success'" class="notice-tag">
|
|
||||||
{{ item.noticeType === '1' ? '通知' : '公告' }}
|
|
||||||
</el-tag>
|
|
||||||
<span class="notice-item-title">{{ item.noticeTitle }}</span>
|
|
||||||
<span class="notice-item-date">{{ item.createTime }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-popover>
|
|
||||||
|
|
||||||
<div v-popover:noticePopover class="right-menu-item hover-effect notice-trigger" @mouseenter="onNoticeEnter" @mouseleave="onNoticeLeave">
|
|
||||||
<svg-icon icon-class="bell" />
|
|
||||||
<span v-if="unreadCount > 0" class="notice-badge">{{ unreadCount }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<notice-detail-view ref="noticeViewRef" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import NoticeDetailView from './DetailView'
|
|
||||||
import { listNoticeTop, markNoticeRead, markNoticeReadAll } from '@/api/system/notice'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'HeaderNotice',
|
|
||||||
components: { NoticeDetailView },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
noticeList: [], // 通知列表
|
|
||||||
unreadCount: 0, // 未读数量
|
|
||||||
noticeLoading: false, // 加载状态
|
|
||||||
noticeVisible: false, // 弹出层显示状态
|
|
||||||
noticeLeaveTimer: null // 鼠标离开计时器
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.loadNoticeTop()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 鼠标移入铃铛区域
|
|
||||||
onNoticeEnter() {
|
|
||||||
clearTimeout(this.noticeLeaveTimer)
|
|
||||||
this.noticeVisible = true
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const popper = this.$refs.noticePopover.$refs.popper
|
|
||||||
if (popper && !popper._noticeBound) {
|
|
||||||
popper._noticeBound = true
|
|
||||||
popper.addEventListener('mouseenter', () => clearTimeout(this.noticeLeaveTimer))
|
|
||||||
popper.addEventListener('mouseleave', () => {
|
|
||||||
this.noticeLeaveTimer = setTimeout(() => { this.noticeVisible = false }, 100)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 鼠标离开铃铛区域
|
|
||||||
onNoticeLeave() {
|
|
||||||
this.noticeLeaveTimer = setTimeout(() => { this.noticeVisible = false }, 150)
|
|
||||||
},
|
|
||||||
// 加载顶部公告列表
|
|
||||||
loadNoticeTop() {
|
|
||||||
this.noticeLoading = true
|
|
||||||
listNoticeTop().then(res => {
|
|
||||||
this.noticeList = res.data || []
|
|
||||||
this.unreadCount = res.unreadCount !== undefined ? res.unreadCount : this.noticeList.filter(n => !n.isRead).length
|
|
||||||
}).finally(() => {
|
|
||||||
this.noticeLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 预览公告详情
|
|
||||||
previewNotice(item) {
|
|
||||||
if (!item.isRead) {
|
|
||||||
markNoticeRead(item.noticeId).catch(() => {})
|
|
||||||
item.isRead = true
|
|
||||||
const idx = this.noticeList.indexOf(item)
|
|
||||||
if (idx !== -1) this.$set(this.noticeList, idx, { ...item, isRead: true })
|
|
||||||
this.unreadCount = Math.max(0, this.unreadCount - 1)
|
|
||||||
}
|
|
||||||
this.$refs.noticeViewRef.open(item.noticeId)
|
|
||||||
},
|
|
||||||
// 全部已读
|
|
||||||
markAllRead() {
|
|
||||||
const ids = this.noticeList.map(n => n.noticeId).join(',')
|
|
||||||
if (!ids) return
|
|
||||||
markNoticeReadAll(ids).catch(() => {})
|
|
||||||
this.noticeList = this.noticeList.map(n => ({ ...n, isRead: true }))
|
|
||||||
this.unreadCount = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.notice-trigger {
|
|
||||||
position: relative;
|
|
||||||
transform: translateX(-6px);
|
|
||||||
.svg-icon { width: 1.2em; height: 1.2em; vertical-align: -0.2em; }
|
|
||||||
.notice-badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 7px;
|
|
||||||
right: -3px;
|
|
||||||
background: #f56c6c;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 10px;
|
|
||||||
height: 16px;
|
|
||||||
line-height: 16px;
|
|
||||||
padding: 0 4px;
|
|
||||||
min-width: 16px;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.notice-popover {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
.notice-popover .notice-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 10px 14px;
|
|
||||||
background: #f7f9fb;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.notice-popover .notice-mark-all {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #409EFF;
|
|
||||||
font-weight: normal;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.notice-popover .notice-mark-all:hover { color: #2b7cc1; }
|
|
||||||
.notice-popover .notice-loading,
|
|
||||||
.notice-popover .notice-empty {
|
|
||||||
padding: 24px;
|
|
||||||
text-align: center;
|
|
||||||
color: #bbb;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
.notice-popover .notice-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 10px 14px;
|
|
||||||
border-bottom: 1px solid #f5f5f5;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background 0.15s;
|
|
||||||
}
|
|
||||||
.notice-popover .notice-item:last-child { border-bottom: none; }
|
|
||||||
.notice-popover .notice-item:hover { background: #f7f9fb; }
|
|
||||||
.notice-popover .notice-item.is-read .notice-tag,
|
|
||||||
.notice-popover .notice-item.is-read .notice-item-title,
|
|
||||||
.notice-popover .notice-item.is-read .notice-item-date { opacity: 0.45; filter: grayscale(1); color: #999; }
|
|
||||||
.notice-popover .notice-tag { flex-shrink: 0; }
|
|
||||||
.notice-popover .notice-item-title {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #333;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.notice-popover .notice-item-date {
|
|
||||||
flex-shrink: 0;
|
|
||||||
font-size: 11px;
|
|
||||||
color: #bbb;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||