【升级】全新V3.0发布

pull/205/head v3.0
俞宝山 2024-05-07 04:29:18 +08:00
parent 3805aead41
commit f06cd305f4
584 changed files with 8188 additions and 9092 deletions

View File

@ -11,7 +11,7 @@ SnowySnowyAdmin是国内首个国密前后端分离快速开发平台
技术框架与密码结合,让更多的人认识密码,使用密码;更是让前后分离“密”不可分。
采用SpringBoot+MybatisPlus+AntDesignVue+Vite 等更多优秀组件及前沿技术开发,注释丰富,代码简洁,开箱即用!
采用SpringBoot+MybatisPlus+AntDesignVue+Vite 等更多组件及前沿技术开发,注释丰富,代码简洁,开箱即用!
Snowy谐音“小诺”恰应小诺团队名称意思为”下雪的、纯洁的“寓意框架追求简洁至上大道至简。
@ -24,19 +24,19 @@ Snowy谐音“小诺”恰应小诺团队名称意思为”下雪的、纯
<img src="https://gitee.com/xiaonuobase/snowy/badge/fork.svg?theme=dark" alt="Gitee fork">
</a>
<a href="https://www.antdv.com/docs/vue/introduce-cn/">
<img src="https://img.shields.io/badge/vue-3.2-blue.svg" alt="bootstrap">
<img src="https://img.shields.io/badge/vue-3-blue.svg" alt="bootstrap">
</a>
<a href="http://spring.io/projects/spring-boot">
<img src="https://img.shields.io/badge/vite-2.8-green.svg" alt="spring-boot">
<img src="https://img.shields.io/badge/vite-5-green.svg" alt="spring-boot">
</a>
<a href="https://www.antdv.com/docs/vue/introduce-cn/">
<img src="https://img.shields.io/badge/vue--ant--design-3.2-blue.svg" alt="bootstrap">
<img src="https://img.shields.io/badge/vue--ant--design-4-blue.svg" alt="bootstrap">
</a>
<a href="http://spring.io/projects/spring-boot">
<img src="https://img.shields.io/badge/spring--boot-2.5-green.svg" alt="spring-boot">
<img src="https://img.shields.io/badge/spring--boot-3-green.svg" alt="spring-boot">
</a>
<a href="http://mp.baomidou.com">
<img src="https://img.shields.io/badge/mybatis--plus-3.5-blue.svg" alt="mybatis-plus">
<img src="https://img.shields.io/badge/mybatis--plus-3-blue.svg" alt="mybatis-plus">
</a>
<a href="./LICENSE">
<img src="https://img.shields.io/badge/license-Apache%202-red" alt="license Apache 2.0">
@ -62,9 +62,9 @@ github下载地址镜像[https://github.com/xiaonuobase/Snowy](https://
全栈工程师推荐idea
### 前端支撑
| 插件 | 版本 | 用途 |
|--- | ----- | ----- |
| node.js | ≥16 | JavaScript运行环境 |
| 插件 | 版本 | 用途 |
|--- |-----| ----- |
| node.js | ≥18 | JavaScript运行环境 |
### 启动前端
@ -75,12 +75,12 @@ npm install
npm run dev
```
### 后端支撑
| 插件 | 版本 | 用途 |
| --- | ----- | ----- |
| jdk | 11 / 1.8 |java环境 |
| lombok | idea内 |代码简化插件 |
| maven | 最新版 |包管理工具 |
| redis | 最新版 | 缓存库 |
| 插件 | 版本 | 用途 |
| --- |-----------| ----- |
| jdk | 17 |java环境 |
| lombok | idea内 |代码简化插件 |
| maven | 最新版 |包管理工具 |
| redis | 最新版 | 缓存库 |
| mysql | 8.0 / 5.7 | 数据库 |
### 启动后端
@ -88,7 +88,7 @@ npm run dev
## 代码结构
Snowy2.0框架对代码以插件化的模式进行分包使得包层级结构更加清晰合理同时降低了耦合度关于插件模块化开发的规范请查阅文档【SNOWY开源文档——前端手册or后端手册——开发规范】板块。
Snowy3.0框架对代码以插件化的模式进行分包使得包层级结构更加清晰合理同时降低了耦合度关于插件模块化开发的规范请查阅文档【SNOWY开源文档——前端手册or后端手册——开发规范】板块。
```
snowy
@ -141,6 +141,10 @@ snowy
1.x分支目前已停止新增功能只限于bug的维护推荐使用2x版本
- snowy2.5
2.x分支目前已停止新增功能只限于bug的维护可以平滑过渡至3x版本
## 视频教程
教程地址(免费开放):[https://space.bilibili.com/50101698/channel/collectiondetail?sid=739071](https://space.bilibili.com/50101698/channel/collectiondetail?sid=739071)

421
pom.xml
View File

@ -6,83 +6,23 @@
<groupId>vip.xiaonuo</groupId>
<artifactId>snowy</artifactId>
<name>snowy</name>
<version>2.0.0</version>
<version>3.0.0</version>
<description>snowy快速开发平台</description>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.12</version>
<version>3.2.1</version>
</parent>
<properties>
<java.version>1.8</java.version>
<snowy.version>2.0.0</snowy.version>
<spring-framework.version>5.3.26</spring-framework.version>
<java.version>17</java.version>
<snowy.version>3.0.0</snowy.version>
<spring-boot.version>3.2.1</spring-boot.version>
<spring-framework.version>6.1.2</spring-framework.version>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 锁定依赖版本号 -->
<ali.oss.version>3.14.0</ali.oss.version>
<aliyun.sdk.dm.version>3.3.1</aliyun.sdk.dm.version>
<aliyun.sdk.dysmsapi.version>2.0.9</aliyun.sdk.dysmsapi.version>
<aliyun.sdk.ecs.version>3.1.0</aliyun.sdk.ecs.version>
<bcprov.jdk15on.version>1.70</bcprov.jdk15on.version>
<beetl.version>1.2.40.Beetl.RELEASE</beetl.version>
<checker.qual.version>3.31.0</checker.qual.version>
<commons.beanutils.version>1.9.4</commons.beanutils.version>
<commons.compress.version>1.22</commons.compress.version>
<commons.pool2.version>2.11.1</commons.pool2.version>
<druid.version>1.2.9</druid.version>
<dynamic.datasource.version>3.5.1</dynamic.datasource.version>
<easy.trans.version>2.1.7</easy.trans.version>
<easyexcel.version>3.2.1</easyexcel.version>
<easypoi.version>4.3.0</easypoi.version>
<fastjson.version>2.0.24</fastjson.version>
<gson.version>2.8.9</gson.version>
<guava.version>31.1-jre</guava.version>
<hutool.version>5.8.12</hutool.version>
<ip2region.version>2.6.3</ip2region.version>
<jackson.annotations.version>2.14.2</jackson.annotations.version>
<jackson.core.version>2.14.2</jackson.core.version>
<jackson.databind.version>2.14.2</jackson.databind.version>
<jackson.datatype.jdk8.version>2.14.2</jackson.datatype.jdk8.version>
<jackson.datatype.jsr310.version>2.14.2</jackson.datatype.jsr310.version>
<jackson.module.parameter.names.version>2.14.2</jackson.module.parameter.names.version>
<javax.mail.version>1.6.2</javax.mail.version>
<jettison.version>1.5.4</jettison.version>
<junit.version>4.13.2</junit.version>
<just.auth.version>1.16.5</just.auth.version>
<knife4j.version>2.0.9</knife4j.version>
<logback.classic.version>1.2.0</logback.classic.version>
<lombok.versin>1.18.22</lombok.versin>
<minio.version>8.5.2</minio.version>
<mssql.connector.java.version>9.2.1.jre8</mssql.connector.java.version>
<mybatis.plus.version>3.5.3.1</mybatis.plus.version>
<mybatis.version>3.5.10</mybatis.version>
<mysql.connector.java.version>8.0.28</mysql.connector.java.version>
<netty.common.version>4.1.89.Final</netty.common.version>
<netty.handler.version>4.1.89.Final</netty.handler.version>
<okhttp3.version>4.10.0</okhttp3.version>
<okio.version>3.3.0</okio.version>
<dm.connector.java.version>8.1.2.192</dm.connector.java.version>
<kingbase.connector.java.version>8.6.0</kingbase.connector.java.version>
<oracle.connector.java.version>21.5.0.0</oracle.connector.java.version>
<oracle.nls.orai18n.version>19.7.0.0</oracle.nls.orai18n.version>
<oshi.core.version>6.2.2</oshi.core.version>
<pinyin.version>2.5.1</pinyin.version>
<postgres.connector.java.version>42.2.25</postgres.connector.java.version>
<protobuf.java.version>3.21.12</protobuf.java.version>
<sa.token.version>1.31.0</sa.token.version>
<smcrypto.version>0.3.2</smcrypto.version>
<snakeyaml.version>2.0</snakeyaml.version>
<spring.context.version>5.3.19</spring.context.version>
<spring.security.crypto.version>5.8.9</spring.security.crypto.version>
<springfox.swagger2.version>2.10.5</springfox.swagger2.version>
<ten.cos.version>5.6.68</ten.cos.version>
<ten.sdk.ses.version>3.1.455</ten.sdk.ses.version>
<ten.sdk.sms.version>3.1.455</ten.sdk.sms.version>
<tomcat.embed.core.version>9.0.72</tomcat.embed.core.version>
</properties>
<modules>
@ -207,458 +147,282 @@
<version>${snowy.version}</version>
</dependency>
<!-- nashorn-core -->
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.4</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.versin}</version>
<version>1.18.30</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis-plus-core -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>${mybatis.plus.version}</version>
<version>1.2.21</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<!-- easy-trans -->
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-spring-boot-starter</artifactId>
<version>${easy.trans.version}</version>
<version>3.0.0</version>
</dependency>
<!-- easy-trans-mybatis-plus-extend -->
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
<version>${easy.trans.version}</version>
<version>3.0.0</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons.pool2.version}</version>
</dependency>
<!-- okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<!-- okio -->
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>${okio.version}</version>
<version>2.12.0</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
<version>5.8.25</version>
</dependency>
<!-- pinyin4j -->
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>${pinyin.version}</version>
<version>2.5.1</version>
</dependency>
<!-- ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>${ip2region.version}</version>
<version>2.7.0</version>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<!-- easy-poi -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>${easypoi.version}</version>
<version>4.4.0</version>
</dependency>
<!-- sm-crypto -->
<dependency>
<groupId>com.antherd</groupId>
<artifactId>sm-crypto</artifactId>
<version>${smcrypto.version}</version>
<version>0.3.2</version>
</dependency>
<!-- easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
<version>3.3.3</version>
</dependency>
<!-- sa-token-core -->
<!-- Sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
<version>${sa.token.version}</version>
<version>1.37.0</version>
</dependency>
<!-- sa-token -->
<!-- Sa-token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa.token.version}</version>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>1.37.0</version>
</dependency>
<!-- sa-token 整合 redis 使用jackson序列化方式 -->
<!-- Sa-token 整合 redis 使用jackson序列化方式 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>${sa.token.version}</version>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.37.0</version>
</dependency>
<!-- Sa-Token插件权限缓存与业务缓存分离 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis</artifactId>
<version>${sa.token.version}</version>
<version>1.37.0</version>
</dependency>
<!-- Sa-Token 插件整合SSO -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-sso</artifactId>
<version>${sa.token.version}</version>
<version>1.37.0</version>
</dependency>
<!-- JustAuth 第三方登录 -->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${just.auth.version}</version>
<version>1.16.6</version>
</dependency>
<!-- beetl模板引擎 -->
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl-framework-starter</artifactId>
<version>${beetl.version}</version>
<version>1.2.40.Beetl.RELEASE</version>
</dependency>
<!--腾讯云上传文件客户端-->
<!--x-file-storage文件sdk-->
<dependency>
<groupId>org.dromara.x-file-storage</groupId>
<artifactId>x-file-storage-spring</artifactId>
<version>2.1.0</version>
</dependency>
<!--腾讯云文件sdk-->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>${ten.cos.version}</version>
<version>5.6.199</version>
</dependency>
<!--阿里云上传文件客户端-->
<!--阿里云文件sdk-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${ali.oss.version}</version>
<version>3.15.1</version>
</dependency>
<!--minio上传文件客户端-->
<!--minio文件sdk-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>${minio.version}</version>
<version>8.5.2</version>
</dependency>
<!--java邮件发送-->
<!--java邮件sdk-->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>${javax.mail.version}</version>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version>
</dependency>
<!--阿里云邮件发送-->
<!--阿里云邮件sdk-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dm</artifactId>
<version>${aliyun.sdk.dm.version}</version>
<artifactId>dm20151123</artifactId>
<version>1.0.6</version>
</dependency>
<!-- 腾讯云邮件发送 -->
<!-- 腾讯云邮件sdk -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-ses</artifactId>
<version>${ten.sdk.ses.version}</version>
<version>3.1.944</version>
</dependency>
<!--阿里云短信发送-->
<!--阿里云短信sdk-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${aliyun.sdk.dysmsapi.version}</version>
<version>2.0.24</version>
</dependency>
<!--腾讯云短信发送-->
<!--腾讯云短信sdk-->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
<version>${ten.sdk.sms.version}</version>
<version>3.1.893</version>
</dependency>
<!-- sms4j短信sdk -->
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-javase-plugin</artifactId>
<version>3.1.1</version>
</dependency>
<!--系统硬件信息-->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>${oshi.core.version}</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.classic.version}</version>
</dependency>
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!-- guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- netty-common -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
<version>${netty.common.version}</version>
</dependency>
<!-- netty-common -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
<version>${netty.handler.version}</version>
</dependency>
<!-- jettison -->
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>${jettison.version}</version>
</dependency>
<!-- snakeyaml -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.context.version}</version>
</dependency>
<!-- spring-security-crypto -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>${spring.security.crypto.version}</version>
</dependency>
<!-- springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.swagger2.version}</version>
</dependency>
<!-- tomcat-embed-core -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.embed.core.version}</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.annotations.version}</version>
</dependency>
<!-- jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.core.version}</version>
</dependency>
<!-- jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.databind.version}</version>
</dependency>
<!-- jackson-datatype -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson.datatype.jdk8.version}</version>
</dependency>
<!-- jackson-jsr310 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.datatype.jsr310.version}</version>
</dependency>
<!-- jackson-module-parameter-names -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>${jackson.module.parameter.names.version}</version>
</dependency>
<!-- commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons.beanutils.version}</version>
</dependency>
<!-- commons-compress -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons.compress.version}</version>
</dependency>
<!-- protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.java.version}</version>
</dependency>
<!-- checker-qual -->
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>${checker.qual.version}</version>
</dependency>
<!-- bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bcprov.jdk15on.version}</version>
<version>6.4.11</version>
</dependency>
<!-- dynamic-datasource -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic.datasource.version}</version>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>4.3.0</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.java.version}</version>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
<!-- postgresql -->
<!--<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgres.connector.java.version}</version>
<version>42.7.1</version>
</dependency>-->
<!-- 达梦数据库 -->
<!--<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>${dm.connector.java.version}</version>
<version>8.1.3.62</version>
</dependency>-->
<!-- 人大金仓数据库 -->
<!--<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
<version>${kingbase.connector.java.version}</version>
<version>8.6.0</version>
</dependency>-->
<!-- oracle -->
<!--<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>${oracle.connector.java.version}</version>
<artifactId>ojdbc10</artifactId>
<version>19.21.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.database.nls</groupId>
<artifactId>orai18n</artifactId>
<version>${oracle.nls.orai18n.version}</version>
<version>23.3.0.23.09</version>
</dependency>-->
<!-- mssql -->
<!--<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>${mssql.connector.java.version}</version>
<version>12.4.2.jre11</version>
</dependency>-->
</dependencies>
</dependencyManagement>
@ -668,16 +432,19 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<version>3.12.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<version>3.3.0</version>
<configuration>
<attach>true</attach>
</configuration>
@ -694,10 +461,6 @@
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>_sql/*</exclude>
<exclude>*.md</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/java</directory>

View File

@ -7,7 +7,7 @@
<link rel="icon" href="/favicon.ico">
<title>Snowy</title>
<style>
.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}
.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1677FF;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}
.app-loading {position: absolute;top:0px;left:0px;right:0px;bottom:0px;display: flex;justify-content: center;align-items: center;flex-direction: column;background: #fff;}
.app-loading__logo {margin-bottom: 30px;}
.app-loading__logo img {width: 90px;vertical-align: bottom;}

View File

@ -1,6 +1,6 @@
{
"name": "snowy-admin-web",
"version": "1.0.0",
"version": "3.0.0",
"private": true,
"description": "小诺团队旗下Snowy前端基于Antdv3.2+Vue3.2+Vite2.8",
"repository": {
@ -24,10 +24,10 @@
"@chenfengyuan/vue-qrcode": "2.0.0",
"@highlightjs/vue-plugin": "2.1.0",
"@tinymce/tinymce-vue": "5.1.1",
"@vue-office/docx": "1.3.2",
"@vue-office/excel": "1.4.7",
"@vue-office/pdf": "1.5.5",
"ant-design-vue": "3.2.14",
"@vue-office/docx": "1.6.0",
"@vue-office/excel": "1.7.1",
"@vue-office/pdf": "1.6.4",
"ant-design-vue": "4.1.2",
"axios": "1.6.2",
"cropperjs": "1.6.1",
"dayjs": "1.11.10",
@ -48,12 +48,12 @@
"snowflake-id": "1.1.0",
"sortablejs": "1.15.1",
"tinymce": "6.8.1",
"vue": "3.3.10",
"vue": "3.4.21",
"vue-cropper": "1.1.1",
"vue-demi": "0.13.11",
"vue-demi": "0.14.7",
"vue-i18n": "9.8.0",
"vue-router": "4.2.5",
"vue3-colorpicker": "2.2.3",
"vue-router": "4.3.0",
"vue3-colorpicker": "2.3.0",
"vue3-tree-org": "4.2.2",
"vuedraggable-es": "4.1.1"
},
@ -79,7 +79,7 @@
"typescript": "5.3.3",
"unplugin-auto-import": "0.17.2",
"unplugin-vue-components": "0.26.0",
"vite": "5.1.4",
"vite": "5.1.6",
"vite-plugin-compression": "0.5.1",
"vite-plugin-vue-setup-extend": "0.4.0",
"vue-eslint-parser": "9.3.2"

View File

@ -1,14 +1,40 @@
<template>
<a-config-provider :locale="locale">
<router-view />
<a-config-provider
:locale="locale"
:theme="{
algorithm: store.theme === 'realDark' ? theme.darkAlgorithm : theme.defaultAlgorithm,
token: {
colorPrimary: `${store.themeColor}`,
borderRadius: roundedCornerStyleOpen ? 6 : 2
}
}"
>
<a-watermark
:content="loginUserWatermarkOpen && userInfo ? [userInfo.name, userInfo.account] : undefined"
class="admin-ui-main"
>
<router-view />
</a-watermark>
</a-config-provider>
</template>
<script setup name="App">
import i18n from '@/locales'
import { globalStore } from '@/store'
import { theme } from 'ant-design-vue'
const store = globalStore()
store.initTheme()
const locale = i18n.global.messages.value[i18n.global.locale.value].lang
//
const userInfo = computed(() => {
return store.userInfo
})
//
const loginUserWatermarkOpen = computed(() => {
return store.loginUserWatermarkOpen
})
//
const roundedCornerStyleOpen = computed(() => {
return store.roundedCornerStyleOpen
})
</script>

View File

@ -30,6 +30,10 @@ export default {
smsSendTencent(data) {
return request('sendTencent', data)
},
// 发送短信——小诺短信
smsSendXiaonuo(data) {
return request('sendXiaonuo', data)
},
// 删除短信
smsDelete(data) {
return request('delete', data)

View File

@ -97,5 +97,9 @@ export default {
// 根据id集合获取角色集合
userCenterGetRoleListByIdList(data) {
return request('getRoleListByIdList', data)
},
// 根据id获取头像
userCenterGtAvatarById(data) {
return request('getAvatarById', data)
}
}

View File

@ -20,7 +20,7 @@
angleField: 'sold',
colorField: 'sex',
radius: 0.66,
color: ['#1890ff', '#f04864'],
color: ['#1677FF', '#f04864'],
label: {
content: (obj) => {
const group = new G.Group({})
@ -46,7 +46,7 @@
text: obj.sex,
textAlign: 'center',
textBaseline: 'top',
fill: obj.sex === '男' ? '#1890ff' : '#f04864'
fill: obj.sex === '男' ? '#1677FF' : '#f04864'
}
})
return group

View File

@ -20,7 +20,7 @@
},
areaStyle: () => {
return {
fill: 'l(270) 0:#ffffff 0.5:#7ec2f3 1:#1890ff'
fill: 'l(270) 0:#ffffff 0.5:#7ec2f3 1:#1677FF'
}
}
})

View File

@ -13,7 +13,7 @@
const props = defineProps({
value: {
type: String,
default: '#1890ff'
default: '#1677FF'
}
})
@ -22,11 +22,17 @@
}
const update = (val) => {
showTxt(val)
emit('update:value', val)
}
onMounted(() => {
showTxt(props.value)
})
const showTxt = (val) => {
const currentColor = document.querySelector('.current-color')
if (currentColor) {
currentColor.textContent = val
}
emit('update:value', val)
}
</script>

View File

@ -71,31 +71,31 @@ export const data = {
month: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
week: [
{
value: '1',
value: '0',
label: '周日'
},
{
value: '2',
value: '1',
label: '周一'
},
{
value: '3',
value: '2',
label: '周二'
},
{
value: '4',
value: '3',
label: '周三'
},
{
value: '5',
value: '4',
label: '周四'
},
{
value: '6',
value: '5',
label: '周五'
},
{
value: '7',
value: '6',
label: '周六'
}
],

View File

@ -22,7 +22,7 @@
</a-input>
<a-modal
title="CRON规则生成器"
v-model:visible="modalVisible"
v-model:open="modalVisible"
:width="580"
@cancel="modalVisible = false"
@ok="submit"
@ -35,9 +35,7 @@
<template #tab>
<div class="cron-num">
<h2></h2>
<a-tag :color="activeKey === '1' ? `var(--primary-color)` : ''" style="margin-right: 0px">{{
value_second
}}</a-tag>
<a-tag :color="activeKey === '1' ? `var(--primary-color)` : ''" class="xn-mr">{{ value_second }}</a-tag>
</div>
</template>
<a-form>
@ -56,7 +54,7 @@
:max="59"
controls-position="right"
></a-input-number>
<span style="padding: 0 15px">-</span>
<span class="xn-pd">-</span>
<a-input-number
v-model:value="dateValue.second.range.end"
:min="0"
@ -79,7 +77,7 @@
<a-select
v-model:value="dateValue.second.appoint"
multiple
style="width: 100%"
class="xn-wd"
mode="multiple"
placeholder="请选择"
>
@ -92,9 +90,7 @@
<template #tab>
<div class="cron-num">
<h2>分钟</h2>
<a-tag :color="activeKey === '2' ? `var(--primary-color)` : ''" style="margin-right: 0px">{{
value_minute
}}</a-tag>
<a-tag :color="activeKey === '2' ? `var(--primary-color)` : ''" class="xn-mr">{{ value_minute }}</a-tag>
</div>
</template>
<a-form>
@ -113,7 +109,7 @@
:max="59"
controls-position="right"
/>
<span style="padding: 0 15px">-</span>
<span class="xn-pd">-</span>
<a-input-number v-model:value="dateValue.minute.range.end" :min="0" :max="59" controls-position="right" />
</a-form-item>
<a-form-item label="间隔" v-if="dateValue.minute.type === '2'">
@ -131,7 +127,7 @@
<a-select
v-model:value="dateValue.minute.appoint"
multiple
style="width: 100%"
class="xn-wd"
mode="multiple"
placeholder="请选择"
>
@ -144,9 +140,7 @@
<template #tab>
<div class="cron-num">
<h2>小时</h2>
<a-tag :color="activeKey === '3' ? `var(--primary-color)` : ''" style="margin-right: 0px">{{
value_hour
}}</a-tag>
<a-tag :color="activeKey === '3' ? `var(--primary-color)` : ''" class="xn-mr">{{ value_hour }}</a-tag>
</div>
</template>
<a-form>
@ -160,7 +154,7 @@
</a-form-item>
<a-form-item label="范围" v-if="dateValue.hour.type === '1'">
<a-input-number v-model:value="dateValue.hour.range.start" :min="0" :max="23" controls-position="right" />
<span style="padding: 0 15px">-</span>
<span class="xn-pd">-</span>
<a-input-number v-model:value="dateValue.hour.range.end" :min="0" :max="23" controls-position="right" />
</a-form-item>
<a-form-item label="间隔" v-if="dateValue.hour.type === '2'">
@ -173,7 +167,7 @@
<a-select
v-model:value="dateValue.hour.appoint"
multiple
style="width: 100%"
class="xn-wd"
mode="multiple"
placeholder="请选择"
>
@ -186,9 +180,7 @@
<template #tab>
<div class="cron-num">
<h2></h2>
<a-tag :color="activeKey === '4' ? `var(--primary-color)` : ''" style="margin-right: 0px">{{
value_day
}}</a-tag>
<a-tag :color="activeKey === '4' ? `var(--primary-color)` : ''" class="xn-mr">{{ value_day }}</a-tag>
</div>
</template>
<a-form>
@ -204,7 +196,7 @@
</a-form-item>
<a-form-item label="范围" v-if="dateValue.day.type === '1'">
<a-input-number v-model:value="dateValue.day.range.start" :min="1" :max="31" controls-position="right" />
<span style="padding: 0 15px">-</span>
<span class="xn-pd">-</span>
<a-input-number v-model:value="dateValue.day.range.end" :min="1" :max="31" controls-position="right" />
</a-form-item>
<a-form-item label="间隔" v-if="dateValue.day.type === '2'">
@ -217,7 +209,7 @@
<a-select
v-model:value="dateValue.day.appoint"
multiple
style="width: 100%"
class="xn-wd"
mode="multiple"
placeholder="请选择"
>
@ -230,9 +222,7 @@
<template #tab>
<div class="cron-num">
<h2></h2>
<a-tag :color="activeKey === '5' ? `var(--primary-color)` : ''" style="margin-right: 0px">{{
value_month
}}</a-tag>
<a-tag :color="activeKey === '5' ? `var(--primary-color)` : ''" class="xn-mr">{{ value_month }}</a-tag>
</div>
</template>
<a-form>
@ -251,7 +241,7 @@
:max="12"
controls-position="right"
/>
<span style="padding: 0 15px">-</span>
<span class="xn-pd">-</span>
<a-input-number v-model:value="dateValue.month.range.end" :min="1" :max="12" controls-position="right" />
</a-form-item>
<a-form-item label="间隔" v-if="dateValue.month.type === '2'">
@ -264,7 +254,7 @@
<a-select
v-model:value="dateValue.month.appoint"
multiple
style="width: 100%"
class="xn-wd"
mode="multiple"
placeholder="请选择"
>
@ -277,9 +267,7 @@
<template #tab>
<div class="cron-num">
<h2></h2>
<a-tag :color="activeKey === '6' ? `var(--primary-color)` : ''" style="margin-right: 0px">{{
value_week
}}</a-tag>
<a-tag :color="activeKey === '6' ? `var(--primary-color)` : ''" class="xn-mr">{{ value_week }}</a-tag>
</div>
</template>
<a-form>
@ -303,7 +291,7 @@
:value="item.value"
/>
</a-select>
<span style="padding: 0 15px">-</span>
<span class="xn-pd">-</span>
<a-select v-model:value="dateValue.week.range.end" placeholder="请选择">
<a-select-option
v-for="(item, index) in data.week"
@ -331,7 +319,7 @@
<a-select
v-model:value="dateValue.week.appoint"
multiple
style="width: 100%"
class="xn-wd"
mode="multiple"
placeholder="请选择"
>
@ -360,9 +348,7 @@
<template #tab>
<div class="cron-num">
<h2></h2>
<a-tag :color="activeKey === '7' ? `var(--primary-color)` : ''" style="margin-right: 0px">{{
value_year
}}</a-tag>
<a-tag :color="activeKey === '7' ? `var(--primary-color)` : ''" class="xn-mr">{{ value_year }}</a-tag>
</div>
</template>
<a-form>
@ -377,7 +363,7 @@
</a-form-item>
<a-form-item label="范围" v-if="dateValue.year.type === '1'">
<a-input-number v-model:value="dateValue.year.range.start" controls-position="right" />
<span style="padding: 0 15px">-</span>
<span class="xn-pd">-</span>
<a-input-number v-model:value="dateValue.year.range.end" controls-position="right" />
</a-form-item>
<a-form-item label="间隔" v-if="dateValue.year.type === '2'">
@ -390,7 +376,7 @@
<a-select
v-model:value="dateValue.year.appoint"
multiple
style="width: 100%"
class="xn-wd"
mode="multiple"
placeholder="请选择"
>
@ -580,6 +566,9 @@
}
})
watch(props, (newValue) => {
if (newValue.modelValue === '') {
defaultValue.value = ''
}
if (newValue.modelValue) {
defaultValue.value = newValue.modelValue
}
@ -758,4 +747,10 @@
font-size: 12px;
font-weight: normal;
}
.xn-mr {
margin-right: 0px;
}
.xn-pd {
padding: 0 15px;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<a-modal ref="cropmodal" v-model:visible="visible" :width="700" title="头像裁剪" @cancel="handleClear" @ok="cropOk">
<a-modal v-model:open="visible" :width="700" title="头像裁剪" @cancel="handleClear" @ok="cropOk">
<a-row :gutter="10">
<!-- 裁剪区 -->
<a-col :span="17">
@ -26,27 +26,27 @@
/>
</a-col>
<a-col :span="7">
<div style="width: 165px; height: 165px; border: 1px solid #e9e9e9; border-radius: 2px">
<div class="xn-cj">
<a-image :src="previewUrl" />
</div>
<div style="padding-top: 10px; display: flex">
<div style="height: 100px; width: 100px; border: 1px solid #e9e9e9; border-radius: 2px">
<div class="xn-cj-two">
<div>
<a-image :src="previewUrl" />
</div>
<div style="height: 60px; width: 60px; border: 1px solid #e9e9e9; margin-left: 5px; border-radius: 2px">
<div>
<a-image :src="previewUrl" />
</div>
</div>
</a-col>
</a-row>
<div style="text-align: center; padding-top: 10px">
<div class="xn-tl">
<a-space>
<a-button @click="cropper.changeScale(1)"></a-button>
<a-button @click="cropper.changeScale(-1)"></a-button>
<a-button @click="cropper.rotateLeft()"></a-button>
<a-button @click="cropper.rotateRight()"></a-button>
</a-space>
<div style="padding-top: 10px">
<div class="xn-pt">
<a-upload
name="file"
:show-upload-list="false"
@ -60,7 +60,7 @@
</a-button>
</a-upload>
</div>
<div style="padding-top: 10px">请上传图片文件建议不超过2M</div>
<div class="xn-pt">请上传图片文件建议不超过2M</div>
</div>
</a-modal>
</template>
@ -152,4 +152,35 @@
.cropper {
height: 280px;
}
.xn-cj {
width: 165px;
height: 165px;
border: 1px solid #e9e9e9;
border-radius: 2px
}
.xn-pt {
padding-top: 10px;
}
.xn-tl {
text-align: center;
padding-top: 10px
}
.xn-cj-two {
padding-top: 10px;
display: flex
}
.xn-cj-two > div:first-child {
height: 100px;
width: 100px;
border: 1px solid #e9e9e9;
border-radius: 2px
}
.xn-cj-two > div:nth-child(2) {
height: 60px;
width: 60px;
border: 1px solid #e9e9e9;
margin-left: 5px;
border-radius: 2px
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<a-modal
:class="['my-modal', modalClass, simpleClass]"
:visible="visible"
:open="visible"
v-bind="props"
:width="modalWidth"
:wrap-class-name="wrapClassName + fullscreenClass"

View File

@ -1,6 +1,6 @@
<template>
<div class="baiduMap" :style="{ height: `${height}px` }">
<div :id="`container-${mid}`" style="width: 100%; height: 100%">地图资源加载中...</div>
<div :id="`container-${mid}`" class="xn-wh">地图资源加载中...</div>
</div>
</template>
<!--BMapGL官网https://lbsyun.baidu.com/index.php?title=jspopularGL-->
@ -332,6 +332,10 @@
</script>
<style lang="less">
.xn-wh {
width: 100%;
height: 100%
}
.baiduMap {
padding: 0;
margin: 0;

View File

@ -1,6 +1,6 @@
<template>
<div class="gaodeMap" :style="{ height: `${height}px` }">
<div :id="`container-${mid}`" style="width: 100%; height: 100%">地图资源加载中...</div>
<div :id="`container-${mid}`" class="xn-wh">地图资源加载中...</div>
</div>
</template>
<!--AMap官网https://lbs.amap.com/api/javascript-api-v2/summary-->
@ -353,6 +353,10 @@
</script>
<style lang="less">
.xn-wh {
width: 100%;
height: 100%;
}
.gaodeMap {
padding: 0;
margin: 0;

View File

@ -44,5 +44,43 @@
</script>
<style lang="less" scoped>
@import './index.less';
.ant-pro-number-info-subtitle {
color: @text-color-secondary;
font-size: @font-size-base;
height: 22px;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
}
.number-info-value {
margin-top: 4px;
font-size: 0;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
& > span {
color: @heading-color;
display: inline-block;
line-height: 32px;
height: 32px;
font-size: 24px;
margin-right: 32px;
}
.sub-total {
color: @text-color-secondary;
font-size: @font-size-lg;
vertical-align: top;
margin-right: 0;
i {
font-size: 12px;
transform: scale(0.82);
margin-left: 4px;
}
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<a-modal
v-model:visible="visible"
v-model:open="visible"
title="移动端图标选择"
:mask-closable="false"
:width="800"

View File

@ -1,6 +1,6 @@
<template>
<a-modal
v-model:visible="visible"
v-model:open="visible"
title="图标选择"
:mask-closable="false"
:width="800"

View File

@ -1,6 +1,6 @@
<template>
<a-modal
v-model:visible="visible"
v-model:open="visible"
title="机构选择"
:width="1000"
:mask-closable="false"
@ -22,7 +22,7 @@
</a-card>
</a-col>
<a-col :span="11">
<div class="table-operator" style="margin-bottom: 10px">
<div class="table-operator xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="12">
@ -32,7 +32,7 @@
</a-col>
<a-col :span="12">
<a-button type="primary" class="primarySele" @click="loadData()"> </a-button>
<a-button class="snowy-buttom-left" @click="() => reset()"> 重置 </a-button>
<a-button class="snowy-button-left" @click="() => reset()"> 重置 </a-button>
</a-col>
</a-row>
</a-form>
@ -50,7 +50,7 @@
>
<template #title>
<span>待选择列表 {{ tableRecordNum }} </span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fd">
<a-button type="dashed" size="small" @click="addAllPageRecord"></a-button>
</div>
</template>
@ -59,7 +59,7 @@
{{ $TOOL.dictTypeData('ORG_CATEGORY', record.category) }}
</template>
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" size="small" @click="addRecord(record)"></a-button>
<a-button type="dashed" size="small" @click="addRecord(record)"><PlusOutlined /></a-button>
</template>
</template>
</a-table>
@ -89,13 +89,13 @@
>
<template #title>
<span>已选择: {{ selectedData.length }}</span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fd">
<a-button type="dashed" danger size="small" @click="delAllRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" danger size="small" @click="delRecord(record)"></a-button>
<a-button type="dashed" danger size="small" @click="delRecord(record)"><MinusOutlined /></a-button>
</template>
</template>
</a-table>
@ -116,7 +116,7 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '机构名',
@ -134,7 +134,7 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '机构名',
@ -250,10 +250,7 @@
loadData()
}
const judge = () => {
if (radioModel && selectedData.value.length > 0) {
return false
}
return true
return !(radioModel && selectedData.value.length > 0)
}
//
const addRecord = (record) => {
@ -377,6 +374,12 @@
</script>
<style lang="less" scoped>
.xn-mb10 {
margin-bottom: 10px;
}
.xn-fd {
float: right;
}
.selectorTreeDiv {
max-height: 500px;
overflow: auto;

View File

@ -1,6 +1,6 @@
<template>
<a-modal
v-model:visible="visible"
v-model:open="visible"
title="职位选择"
:width="1000"
:mask-closable="false"
@ -22,7 +22,7 @@
</a-card>
</a-col>
<a-col :span="11">
<div class="table-operator" style="margin-bottom: 10px">
<div class="table-operator xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="12">
@ -32,7 +32,7 @@
</a-col>
<a-col :span="12">
<a-button type="primary" class="primarySele" @click="loadData()"> </a-button>
<a-button class="snowy-buttom-left" @click="() => reset()"> 重置 </a-button>
<a-button class="snowy-button-left" @click="() => reset()"> 重置 </a-button>
</a-col>
</a-row>
</a-form>
@ -50,13 +50,13 @@
>
<template #title>
<span>待选择列表 {{ tableRecordNum }} </span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" size="small" @click="addAllPageRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" size="small" @click="addRecord(record)"></a-button>
<a-button type="dashed" size="small" @click="addRecord(record)"><PlusOutlined /></a-button>
</template>
<template v-if="column.dataIndex === 'category'">
{{ $TOOL.dictTypeData('POSITION_CATEGORY', record.category) }}
@ -89,13 +89,13 @@
>
<template #title>
<span>已选择: {{ selectedData.length }}</span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" danger size="small" @click="delAllRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" danger size="small" @click="delRecord(record)"></a-button>
<a-button type="dashed" danger size="small" @click="delRecord(record)"><MinusOutlined /></a-button>
</template>
</template>
</a-table>
@ -116,7 +116,7 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '职位名',
@ -134,7 +134,7 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '职位名',
@ -251,10 +251,7 @@
loadData()
}
const judge = () => {
if (radioModel && selectedData.value.length > 0) {
return false
}
return true
return !(radioModel && selectedData.value.length > 0)
}
//
const addRecord = (record) => {

View File

@ -1,6 +1,6 @@
<template>
<a-modal
v-model:visible="visible"
v-model:open="visible"
title="角色选择"
:width="1000"
:mask-closable="false"
@ -22,7 +22,7 @@
</a-card>
</a-col>
<a-col :span="11">
<div class="table-operator" style="margin-bottom: 10px">
<div class="table-operator xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="12">
@ -32,7 +32,7 @@
</a-col>
<a-col :span="12">
<a-button type="primary" class="primarySele" @click="loadData()"> </a-button>
<a-button class="snowy-buttom-left" @click="() => reset()"> 重置 </a-button>
<a-button class="snowy-button-left" @click="() => reset()"> 重置 </a-button>
</a-col>
</a-row>
</a-form>
@ -50,13 +50,13 @@
>
<template #title>
<span>待选择列表 {{ tableRecordNum }} </span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" size="small" @click="addAllPageRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" size="small" @click="addRecord(record)"></a-button>
<a-button type="dashed" size="small" @click="addRecord(record)"><PlusOutlined /></a-button>
</template>
<template v-if="column.dataIndex === 'category'">
{{ $TOOL.dictTypeData('ROLE_CATEGORY', record.category) }}
@ -89,13 +89,13 @@
>
<template #title>
<span>已选择: {{ selectedData.length }}</span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" danger size="small" @click="delAllRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" danger size="small" @click="delRecord(record)"></a-button>
<a-button type="dashed" danger size="small" @click="delRecord(record)"><MinusOutlined /></a-button>
</template>
</template>
</a-table>
@ -116,7 +116,7 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '角色名',
@ -134,7 +134,7 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '角色名',
@ -298,10 +298,7 @@
loadData()
}
const judge = () => {
if (radioModel && selectedData.value.length > 0) {
return false
}
return true
return !(radioModel && selectedData.value.length > 0)
}
//
const addRecord = (record) => {

View File

@ -1,6 +1,6 @@
<template>
<a-modal
v-model:visible="visible"
v-model:open="visible"
title="用户选择"
:width="1000"
:mask-closable="false"
@ -22,7 +22,7 @@
</a-card>
</a-col>
<a-col :span="11">
<div class="table-operator" style="margin-bottom: 10px">
<div class="table-operator xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="12">
@ -32,7 +32,7 @@
</a-col>
<a-col :span="12">
<a-button type="primary" class="primarySele" @click="loadData()"> </a-button>
<a-button class="snowy-buttom-left" @click="reset()"> </a-button>
<a-button class="snowy-button-left" @click="reset()"> </a-button>
</a-col>
</a-row>
</a-form>
@ -50,13 +50,16 @@
>
<template #title>
<span>待选择列表 {{ tableRecordNum }} </span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" size="small" @click="addAllPageRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'avatar'">
<a-avatar :src="record.avatar" style="margin-bottom: -5px; margin-top: -5px" />
</template>
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" size="small" @click="addRecord(record)"></a-button>
<a-button type="dashed" size="small" @click="addRecord(record)"><PlusOutlined /></a-button>
</template>
<template v-if="column.dataIndex === 'category'">
{{ $TOOL.dictTypeData('ROLE_CATEGORY', record.category) }}
@ -89,13 +92,13 @@
>
<template #title>
<span>已选择: {{ selectedData.length }}</span>
<div v-if="!radioModel" style="float: right">
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" danger size="small" @click="delAllRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" danger size="small" @click="delRecord(record)"></a-button>
<a-button type="dashed" danger size="small" @click="delRecord(record)"><MinusOutlined /></a-button>
</template>
</template>
</a-table>
@ -116,7 +119,12 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '头像',
dataIndex: 'avatar',
width: 50
},
{
title: '用户名',
@ -134,7 +142,7 @@
title: '操作',
dataIndex: 'action',
align: 'center',
width: 80
width: 50
},
{
title: '用户名',
@ -252,10 +260,7 @@
loadData()
}
const judge = () => {
if (radioModel && selectedData.value.length > 0) {
return false
}
return true
return !(radioModel && selectedData.value.length > 0)
}
//
const addRecord = (record) => {

View File

@ -26,7 +26,7 @@
}
})
//
const colorList = ['#7265E6', '#FFBF00', '#00A2AE', '#F56A00', '#1890FF', '#606D80']
const colorList = ['#7265E6', '#FFBF00', '#00A2AE', '#F56A00', '#1677FF', '#606D80']
//
const randomColor = () => {
if (props.color) {

View File

@ -112,7 +112,7 @@ export default {
<!-- #bodyCell 放入column表格列需要显示的数据可以通过判断进行一个自定义显示 -->
<template #bodyCell="{ column, record }">
<template >
<a-avatar style="width: 25px; height: 25px" />
<a-avatar class="xn-wh25" />
</template>
<template v-if="column.dataIndex === 'status'">
<!-- 进行自定义显示内容 -->
@ -199,7 +199,8 @@ const edit = (row) => {
| -------------- | ----------------------------------------------- | ----------------- | ------ |
| alert | 设置是否显示表格信息栏 | [object, boolean] | null |
| showPagination | 显示分页选择器,可传 'auto' \| boolean | [string, boolean] | 'auto' |
| data | 加载数据方法 必须为 `Promise` 对象 **必须绑定** | Promise | - |
| data | 加载数据方法 必须为 `Promise` 对象 **必须绑定** | Promise | - |
| lineSelection | 是否开启点击行高亮显示并选中 | Boolean | 'false' |
`alert` 属性对象:
@ -245,6 +246,7 @@ result.then((r) => {
data.localLoading = false
return
}
// 获取分页数据及分页的显示内容
data.localPagination =
(props.showPagination &&
Object.assign({}, data.localPagination, {
@ -270,31 +272,35 @@ result.then((r) => {
loadData()
return
}
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
try {
/*
if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.size))) {
data.localPagination.hideOnSinglePage = true
}
*/
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
// 没有数据或只有一页数据时隐藏分页栏
// if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.pageSize))) {
// data.localPagination.hideOnSinglePage = true
// }
if (!props.showPagination) {
data.localPagination.hideOnSinglePage = true
}
} catch (e) {
data.localPagination = false
}
// if (props.showPagination === false) {
// // 既然配置了不分页,那么我们这里接收到肯定是数组
// console.log(r);
// data.localDataSource = []
// if (r instanceof Array) {
// data.localDataSource = r
// }
// } else {
// data.localDataSource = r.records
// }
// 返回结果中的数组数据
if (props.showPagination === false) {
// 既然配置了不分页,那么我们这里接收到肯定是数组
data.localDataSource = []
if (r instanceof Array) {
data.localDataSource = r
}
} else {
data.localDataSource = r.records
}
data.localDataSource = r.records
data.localLoading = false
getTableProps() // 获取到后端返回的数据后需要调用一下获取table的props的方法去刷新table
getTableProps()
})
```
返回 JSON 例子:

View File

@ -89,3 +89,36 @@
emit('columnChange', columnsSetting.value)
}
</script>
<style lang="less" scoped>
.s-tool-column-item {
display: flex;
align-items: center;
padding: 4px 16px 4px 4px;
.ant-checkbox-wrapper {
flex: 1;
}
.s-tool-column-handle {
opacity: 0.8;
cursor: move;
.anticon-more {
font-size: 12px;
& + .anticon-more {
margin: 0px 4px 0 -8px;
}
}
}
}
.s-tool-column-header {
padding: 5px 16px 10px 24px;
min-width: 180px;
}
.s-tool-column {
.ant-divider {
margin: 0;
}
.ant-checkbox-group {
padding: 4px 0;
display: block;
}
}
</style>

View File

@ -1,57 +0,0 @@
.table-wrapper{
}
.table-striped td {
background-color: var(--table-row-hover-bg);
}
.s-table-tool{
display: flex;
margin-bottom: 16px;
.s-table-tool-left{
flex: 1;
}
.s-table-tool-right{
.s-tool-item{
font-size: 16px;
@apply ml-4;
cursor: pointer;
}
}
}
.s-tool-column-item{
display: flex;
align-items: center;
padding: 4px 16px 4px 4px;
.ant-checkbox-wrapper{
flex: 1;
}
.s-tool-column-handle{
opacity: .8;
cursor: move;
.anticon-more{
font-size: 12px;
& + .anticon-more{
margin: 0px 4px 0 -8px;
}
}
}
}
.s-tool-column-header{
padding: 5px 16px 10px 24px;
min-width: 180px;
}
.s-tool-column{
.ant-divider{
margin: 0;
}
.ant-checkbox-group{
padding: 4px 0;
display: block;
}
}
.s-table-column-settings .ant-popover-inner-content{
padding: 0;
}

View File

@ -10,7 +10,7 @@
<div className="layout-items-center ml-4" v-show="props.toolConfig.striped">
<a-checkbox :checked="data.localSettings.rowClassNameSwitch" @change="changeRowClass"> </a-checkbox>
</div>
<span v-for="item in tool">
<span v-for="item in tool" :key="item.name">
<!-- 刷新 -->
<a-tooltip
v-if="item.name === 'refresh' && props.toolConfig.refresh"
@ -37,7 +37,7 @@
</a-tooltip>
</a-popover>
<!-- 密度 -->
<a-dropdown trigger="click" v-if="item.isDropdown && item.name == 'height' && props.toolConfig.height">
<a-dropdown trigger="click" v-if="item.isDropdown && item.name === 'height' && props.toolConfig.height">
<template #overlay>
<a-menu selectable :selectedKeys="[data.customSize]" @click="changeHeight">
<a-menu-item key="default">默认</a-menu-item>
@ -93,29 +93,35 @@
<!-- 表格 -->
<a-table
v-bind="{ ...renderTableProps, ...data.localSettings }"
@change="loadData"
v-bind="{ ...renderTableProps }"
:loading="data.localLoading"
:row-key="(record) => record.id"
@change="loadData"
@expand="
(expanded, record) => {
emit('expand', expanded, record)
}
"
:rowClassName="
(record, index) => (data.localSettings.rowClassNameSwitch ? ((index + 1) % 2 == 0 ? 'odd' : '') : null)
"
>
<template #[item]="scope" v-for="item in renderSlots">
<slot v-if="item && renderTableProps.columns.length > 0" :name="item" :scope="scope" v-bind="scope || {}"></slot>
<slot
v-if="item && renderTableProps.columns && renderTableProps.columns.length > 0"
:name="item"
:scope="scope"
v-bind="scope || {}"
/>
</template>
</a-table>
</div>
</template>
<script setup>
import './index.less'
import { tableProps } from 'ant-design-vue/es/table/Table.js'
import columnSetting from './columnSetting.vue'
import { get } from 'lodash-es'
import { useSlots } from 'vue'
import { useRoute } from 'vue-router'
import { get } from 'lodash-es'
const slots = useSlots()
const route = useRoute()
const emit = defineEmits(['expand'])
@ -191,10 +197,8 @@
}
})
)
const data = reactive({
needTotalList: [],
localLoading: false,
localDataSource: [],
localPagination: Object.assign({}, props.pagination),
isFullscreen: false,
@ -275,11 +279,9 @@
getTableProps()
}
//
const changeRowClass = (value) => {
const val = value.target.checked
data.localSettings.rowClassNameSwitch = val
const evenClass = val ? (_record, index) => (index % 2 === 1 ? 'table-striped' : null) : props.rowClassName
data.localSettings.rowClassName = evenClass
const changeRowClass = (v) => {
data.localSettings.rowClassNameSwitch = v.target.checked
getTableProps()
}
//
const changeHeight = (v) => {
@ -287,13 +289,12 @@
getTableProps()
}
//
const columnChange = (val) => {
data.columnsSetting = val
const columnChange = (v) => {
data.columnsSetting = v
getTableProps()
}
//
const rowClear = (callback) => {
callback
clearSelected()
}
//
@ -317,6 +318,7 @@
data.columnsSetting = props.columns
loadData()
}
const initTotalList = (columns) => {
const totalList = []
columns &&
@ -331,9 +333,12 @@
})
return totalList
}
//
const loadData = (pagination, filters, sorter) => {
// loading
data.localLoading = true
//
const parameter = Object.assign(
{
current:
@ -359,6 +364,7 @@
...filters
}
)
//
const result = props.data(parameter)
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then((r) => {
@ -366,6 +372,7 @@
data.localLoading = false
return
}
//
data.localPagination =
(props.showPagination &&
Object.assign({}, data.localPagination, {
@ -379,38 +386,32 @@
pageSize: (pagination && pagination.pageSize) || data.localPagination.pageSize
})) ||
false
// recordsnull
if (r.records == null) {
r.records = []
}
// 0 ,
if (r.records.length === 0 && props.showPagination && data.localPagination.current > 1) {
data.localPagination.current--
loadData()
return
}
// table
try {
/*
if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.size))) {
data.localPagination.hideOnSinglePage = true
}
*/
// table
//
// if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.pageSize))) {
// data.localPagination.hideOnSinglePage = true
// }
if (!props.showPagination) {
data.localPagination.hideOnSinglePage = true
}
} catch (e) {
data.localPagination = false
}
//
if (props.showPagination === false) {
//
data.localDataSource = []
if (r instanceof Array) {
data.localDataSource = r
}
data.localDataSource = r instanceof Array ? r : r.records
} else {
data.localDataSource = r.records
}
@ -419,33 +420,37 @@
})
}
}
// props
// tableprops
const getTableProps = () => {
let renderProps = {}
const localKeys = Object.keys(data)
Object.keys(tableProps()).forEach((k) => {
// antdAPI
Object.keys(Object.assign(tableProps(), props)).forEach((k) => {
// localdataAPI
const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
// table props
if (localKeys.includes(localKey)) {
renderProps[k] = data[localKey]
return renderProps[k]
return
}
// alert rowSelection
// rowSelection
if (k === 'rowSelection') {
if (props.rowSelection) {
// 使alert rowSelection
renderProps[k] = {
...props.rowSelection,
onChange: (selectedRowKeys, selectedRows) => {
updateSelect(selectedRowKeys, selectedRows)
typeof props[k].onChange !== 'undefined' && props[k].onChange(selectedRowKeys, selectedRows)
}
}
return renderProps[k]
} else if (!props.rowSelection) {
// rowSelection
renderProps[k] = null
return renderProps[k]
}
renderProps[k] = props.rowSelection
? {
...props.rowSelection,
onChange: (selectedRowKeys, selectedRows) => {
updateSelect(selectedRowKeys, selectedRows)
typeof props[k].onChange !== 'undefined' && props[k].onChange(selectedRowKeys, selectedRows)
}
}
: null
return
}
// ,
if (k === 'customRow') {
if (props.lineSelection && props.rowSelection) {
// customRow
@ -488,19 +493,18 @@
return renderProps[k]
}
}
data[k] && (renderProps[k] = data[k])
//
renderProps = {
...renderProps,
scroll: props.scroll,
bordered: props.bordered,
size: data.customSize, // sizea-tablecompSize
columns: data.columnsSetting.filter((value) => value.checked === undefined || value.checked)
}
return renderProps[k]
renderProps[k] = props[k]
})
renderTableProps.value = renderProps
renderProps = {
...renderProps,
size: data.customSize, // sizea-tablecompSize
columns: data.columnsSetting.filter((value) => value.checked === undefined || value.checked),
...data.localSettings
}
// undefined null tableprops
renderTableProps.value = Object.entries(renderProps).reduce((x, [y, z]) => (z == null ? x : ((x[y] = z), x)), {})
}
// total
const updateSelect = (selectedRowKeys, selectedRows) => {
if (props.rowSelection) {
@ -544,3 +548,19 @@
init()
})
</script>
<style lang="less" scoped>
.s-table-tool {
display: flex;
margin-bottom: 16px;
.s-table-tool-left {
flex: 1;
}
.s-table-tool-right {
.s-tool-item {
font-size: 16px;
@apply ml-4;
cursor: pointer;
}
}
}
</style>

View File

@ -2,7 +2,7 @@
<a-tree-select
v-model:value="defaultSelectKeys"
show-search
style="width: 100%"
class="xn-wd"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择菜单"
:field-names="treeFieldNames"

View File

@ -32,5 +32,33 @@
</script>
<style lang="less" scoped>
@import './index.less';
.up,
.down {
margin-left: 4px;
position: relative;
top: 1px;
i {
font-size: 12px;
transform: scale(0.83);
}
}
.item-text {
display: inline-block;
margin-left: 8px;
color: rgba(0, 0, 0, 0.85);
}
.up {
color: @red-6;
}
.down {
color: @green-6;
top: -1px;
}
&.reverse-color .up {
color: @green-6;
}
&.reverse-color .down {
color: @red-6;
}
</style>

View File

@ -1,10 +1,5 @@
<template>
<a-popconfirm
title="批量处理此信息?"
:visible="batchVisible"
@visibleChange="batchVisibleChange"
@confirm="deleteBatch"
>
<a-popconfirm title="批量处理此信息?" :open="batchVisible" @openChange="batchVisibleChange" @confirm="deleteBatch">
<a-button :type="props.buttonType" :danger="props.buttonDanger" :size="props.size" :loading="buttonLoading">
<template #icon v-if="props.icon">
<component :is="props.icon" :style="{ color: props.color }" />
@ -30,11 +25,11 @@
},
buttonType: {
type: String,
default: () => ''
default: () => undefined
},
icon: {
type: String,
default: () => ''
default: () => undefined
},
size: {
type: String,
@ -74,7 +69,7 @@
emit('batchCallBack', params)
}
// loading
const loading = () => {
const openLoading = () => {
buttonLoading.value = true
}
// loading

View File

@ -1,8 +1,8 @@
<template>
<a-popconfirm
title="删除此信息?"
:visible="deleteVisible"
@visibleChange="deleteVisibleChange"
:open="deleteVisible"
@openChange="deleteVisibleChange"
@confirm="deleteBatch"
>
<a-button danger>

View File

@ -1,47 +1,49 @@
<template>
<a-space class="go-back-button">
<a-button type="primary" :href="props.src" target="_blank">
<a-button :href="props.src" size="small" target="_blank">
<template #icon><download-outlined /></template>
</a-button>
<a-button type="primary" @click="emit('goBack')">
<a-button type="primary" size="small" @click="emit('goBack')">
<template #icon><rollback-outlined /></template>
返回
</a-button>
</a-space>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<vue-office-docx
v-if="props.fileType.toLowerCase() === 'doc' || props.fileType.toLowerCase() === 'docx'"
:src="props.src"
style="height: 82vh"
@rendered="renderedHandler"
/>
<vue-office-excel
v-else-if="props.fileType.toLowerCase() === 'xls' || props.fileType.toLowerCase() === 'xlsx'"
:src="props.src"
style="height: 82vh"
@rendered="renderedHandler"
@error="errorHandler"
/>
<vue-office-pdf
v-else-if="props.fileType.toLowerCase() === 'pdf'"
:src="props.src"
@rendered="renderedHandler"
@error="errorHandler"
/>
<img
v-else-if="
props.fileType.toLowerCase() === 'png' ||
props.fileType.toLowerCase() === 'jpg' ||
props.fileType.toLowerCase() === 'gif' ||
props.fileType.toLowerCase() === 'bmp' ||
props.fileType.toLowerCase() === 'jpeg' ||
props.fileType.toLowerCase() === 'ico' ||
props.fileType.toLowerCase() === 'svg'
"
:src="props.src"
style="max-width: 100%"
/>
<a-result v-else status="warning" title="不支持预览的文件类型" />
<a-spin :spinning="loading">
<vue-office-docx
v-if="fileType === 'doc' || fileType === 'docx'"
:src="props.src"
class="xn-ht82"
@rendered="renderedHandler"
/>
<vue-office-excel
v-else-if="fileType === 'xls' || fileType === 'xlsx'"
:src="props.src"
class="xn-ht82"
@rendered="renderedHandler"
@error="errorHandler"
/>
<vue-office-pdf
v-else-if="fileType === 'pdf'"
:src="props.src"
@rendered="renderedHandler"
@error="errorHandler"
/>
<img
v-else-if="
fileType === 'png' ||
fileType === 'jpg' ||
fileType === 'gif' ||
fileType === 'bmp' ||
fileType === 'jpeg' ||
fileType === 'ico' ||
fileType === 'svg'
"
:src="props.src"
class="xn-mwh"
/>
<a-result v-else status="warning" title="不支持预览的文件类型" />
</a-spin>
</a-card>
</template>
@ -58,6 +60,7 @@
//VueOfficePdf
import VueOfficePdf from '@vue-office/pdf'
const loading = ref(false)
const emit = defineEmits({ goBack: null })
const props = defineProps({
src: {
@ -72,8 +75,31 @@
required: false
}
})
const fileType = ref()
watch(
() => props.src,
() => {
fileType.value = props.fileType.toLowerCase()
}
)
watch(
() => props.src,
() => {
if (
fileType.value === 'doc' ||
fileType.value === 'docx' ||
fileType.value === 'xls' ||
fileType.value === 'xlsx' ||
fileType.value === 'pdf'
) {
loading.value = true
}
}
)
//
const renderedHandler = () => {}
const renderedHandler = () => {
loading.value = false
}
//
const errorHandler = () => {
message.warning('渲染失败,请尝试重新打开!')
@ -81,6 +107,12 @@
</script>
<style lang="less" scoped>
.xn-mwh {
max-width: 100%;
}
.xn-ht82 {
height: 82vh;
}
.go-back-button {
position: absolute;
float: right;

View File

@ -1,10 +1,10 @@
<template>
<a-modal v-if="isModal" :visible="visible" @cancel="cancel" v-bind="$attrs">
<a-modal v-if="isModal" :open="visible" @cancel="cancel" v-bind="$attrs">
<template v-for="slotKey in slotKeys" #[slotKey]>
<slot :name="slotKey" />
</template>
</a-modal>
<a-drawer v-else :visible="visible" v-bind="$attrs" @close="cancel" :footer-style="{ textAlign: 'right' }">
<a-drawer v-else :open="visible" v-bind="$attrs" @close="cancel" :footer-style="{ textAlign: 'right' }">
<template v-for="slotKey in slotKeys" #[slotKey]>
<slot :name="slotKey" />
</template>
@ -13,23 +13,20 @@
<script setup>
import { useSlots } from 'vue'
const slots = useSlots()
import { globalStore } from '@/store'
const slots = useSlots()
const store = globalStore()
const props = defineProps({
visible: {
type: Boolean,
default: false,
required: true
required: false
}
})
const FormContainerTypeEnum = {
DRAWER: 'drawer',
MODAL: 'modal'
}
const formStyle = computed(() => {
return store.formStyle
})
@ -44,7 +41,6 @@
emit('close')
}
</script>
<script>
//
export default {

View File

@ -1,15 +1,16 @@
<template>
<!-- 本组件这兄弟写的很好 请参照https://blog.csdn.net/weixin_41897680/article/details/124925222-->
<div class="hljs-container" :codetype="props.language">
<a-button v-if="props.copy" size="small" type="primary" class="hljs-copy" @click="codeCopy">
<CopyOutlined />
拷贝
</a-button>
<highlightjs :language="props.language" :autodetect="!props.language" :code="props.code" />
</div>
</template>
<script setup name="XnHighlightjs">
/*import 'highlight.js/styles/atom-one-dark.css'
import 'highlight.js/lib/common'
import hljsVuePlugin from '@highlightjs/vue-plugin'*/
import { message } from 'ant-design-vue'
const props = defineProps({
language: {
type: String,
@ -18,41 +19,27 @@
code: {
type: String,
default: () => '无'
},
copy: {
type: Boolean,
default: () => false
}
})
const codeCopy = () => {
copyTextToClipboard(props.code).then(() => {
message.success('拷贝成功')
})
}
const copyTextToClipboard = async (text) => {
try {
await navigator.clipboard.writeText(text)
} catch (err) {
message.warning('拷贝失败')
}
}
</script>
<style scoped lang="less">
/* 语法高亮 */
/*.hljs-container {
position: relative;
display: block;
padding: 30px 5px 2px;
overflow-x: hidden;
line-height: 20px;
text-align: left;
background: #21252b;
box-shadow: 0 10px 30px 0 rgb(0 0 0 / 40%);
}*/
/** 3个点 */
/*.hljs-container::before {
position: absolute;
top: 10px;
left: 15px;
width: 12px;
height: 12px;
overflow: visible;
font-weight: 700;
font-size: 16px;
line-height: 12px;
white-space: nowrap;
text-indent: 75px;
background-color: #fc625d;
border-radius: 16px;
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
content: attr(codetype);
}*/
/** 滚动条 */
:deep(.hljs, .hljs-container) {
max-height: 300px !important;
@ -88,4 +75,12 @@
::-webkit-scrollbar-button {
display: none;
}
/** 复制样式 */
.hljs-copy {
float: right;
top: 10px;
right: 10px;
position: absolute;
z-index: 9;
}
</style>

View File

@ -4,7 +4,7 @@
v-model:value="modelValue"
:options="options"
:field-names="{ label: 'name', value: 'id' }"
style="width: 100%"
class="xn-wd"
:placeholder="props.placeholder"
:allow-clear="props.allowClear"
:disabled="props.disabled"

View File

@ -1,17 +1,10 @@
<template>
<xn-form-container
ref="signModel"
v-model:visible="visible"
:width="700"
title="电子签名"
@close="handleClear"
@ok="handleOk"
>
<xn-form-container :visible="visible" :width="700" title="电子签名" @close="handleClear" @ok="handleOk">
<a-row :gutter="5">
<a-col :span="15">
<div style="border: 1px solid rgb(236 236 236)">
<div class="xn-bdr236">
<vue-esign
ref="esign"
ref="esignRef"
v-model:bgColor="bgColor"
:width="800"
:height="400"
@ -23,12 +16,12 @@
</div>
</a-col>
<a-col :span="9">
<div style="height: 90px; width: auto">
<img :src="resultImg" style="height: 90px; width: 100%; border: 1px solid rgb(236 236 236)" />
<div class="xn-h90wat">
<img :src="resultImg" class="xn-bdr236 xn-h90w100" />
</div>
</a-col>
</a-row>
<div style="margin-top: 10px">
<div class="xn-mt10">
<a-space>
<a-form>
<a-row :gutter="16">
@ -39,7 +32,7 @@
</a-col>
<a-col :span="12">
<a-form-item>
<div style="padding-right: 50px">是否裁剪<a-checkbox v-model:checked="isCrop"></a-checkbox></div>
<div class="xn-pr50">是否裁剪<a-checkbox v-model:checked="isCrop"></a-checkbox></div>
</a-form-item>
</a-col>
</a-row>
@ -49,7 +42,7 @@
</a-space>
</div>
<template #footer>
<a-button style="margin-right: 8px" @click="handleClear"></a-button>
<a-button class="xn-mr8" @click="handleClear"></a-button>
<a-button type="primary" @click="handleOk"></a-button>
</template>
</xn-form-container>
@ -57,13 +50,12 @@
<script setup>
import { message } from 'ant-design-vue'
import vueEsign from './vueEsign.vue'
const signModel = ref(false)
import VueEsign from './vueEsign.vue'
const visible = ref(false)
const esign = ref(false)
const esignRef = ref(false)
const resultImg = ref('')
const isCrop = ref(false)
const lineWidth = ref(6)
const lineWidth = ref(10)
const lineColor = ref('#000000')
const bgColor = ref('')
const props = defineProps(['image'])
@ -74,11 +66,11 @@
visible.value = true
}
const handleReset = () => {
esign.value.reset()
esignRef.value.reset()
resultImg.value = ''
}
const handleGenerate = () => {
esign.value
esignRef.value
.generate()
.then((res) => {
resultImg.value = res
@ -91,7 +83,7 @@
visible.value = false
}
const handleOk = () => {
esign.value
esignRef.value
.generate()
.then((res) => {
emit('successful', res)
@ -107,7 +99,24 @@
</script>
<style scoped>
.xn-h90w100 {
height: 90px;
width: 100%;
}
.xn-mt10 {
margin-top: 10px;
}
.xn-h90wat {
height: 90px;
width: auto;
}
.xn-bdr236 {
border: 1px solid rgb(236 236 236);
}
.ant-form-item {
margin-bottom: 0px !important;
}
.xn-pr50 {
padding-right: 50px;
}
</style>

View File

@ -87,7 +87,9 @@
canvas.value.height = props.height
canvas.value.width = props.width
canvas.value.style.background = myBg.value
$_resizeHandler()
setTimeout(() => {
$_resizeHandler()
})
//
document.onmouseup = () => {
isDrawing.value = false

View File

@ -0,0 +1,36 @@
## 小诺人员选择器
### 说明
改组件为小诺人员选择器可返回id用逗号隔离的字符串或id数组类型的数据格式
@author yubaoshan
@data 2024年4月13日23:59:23
### props定义
| 序号 | 编码 | 类型 | 说明 | 默认 |
|-----|---------------------|---------------|------------------------------|--------|
| 1 | radioModel | Boolean | 是否单选与addShow隐藏同时可用 | false |
| 2 | dataIsConverterFlw | Boolean | 是否为工作流格式 | false |
| 3 | orgTreeApi | function | 机构树接口 | - |
| 4 | userPageApi | function | 用户分页接口 | - |
| 5 | userListByIdListApi | function | 通过id数组查询list数据接口 | - |
| 6 | value | object或string | 通过v-model:value绑定数据 | - |
| 7 | dataType | string | 数据类型object或string | string |
| 8 | userShow | Boolean | 是否显示已选择用户(非表单内、单纯的选择用户需要隐藏) | true |
| 9 | addShow | Boolean | 是否默认的增加人员按钮与radioModel为或的关系 | true |
### emits定义
| 序号 | 方法名 | 参数类型 | 说明 |
|----|--------|----------------|---------------------------------|
| 1 | value | 根据 dataType 而定 | 当选择用户后通过v-model:value绑定到组件上 |
| 2 | onBack | 根据 dataType 而定 | 通过@onBack 方法返回选中的数据,触发点为选中或删除用户 |
### slot定义
| 序号 | 插槽名 | 用途 | 用途 |
|----|--------|-------------------|-------------------|
| 1 | button | 在人员新增按钮后可以插入自定义按钮 | 不满足新增人员按钮样式,可以自定义 |

View File

@ -0,0 +1,622 @@
<template>
<!-- 这是引入后展示的样式 -->
<div style="display: flex" v-if="props.userShow">
<div
class="user-container"
v-for="(user, index) in userObj"
:key="user.id"
@mouseover="onMouseEnter(index)"
@mouseleave="onMouseLeave(index)"
>
<span class="user-delete">
<CloseCircleFilled
:class="index === deleteShow ? 'show-delete-icon' : ''"
class="delete-icon"
@click="deleteUser(user)"
/>
<a-avatar :src="user.avatar" />
</span>
<span class="user-name">{{ user.name }}</span>
</div>
<a-button shape="circle" @click="openModal" v-if="(props.radioModel ? userObj.length !== 1 : true) && addShow">
<PlusOutlined />
</a-button>
<slot name="button"></slot>
</div>
<!-- 以下是弹窗内容 -->
<a-modal
v-model:open="visible"
title="用户选择"
:width="1000"
:mask-closable="false"
:destroy-on-close="true"
@ok="handleOk"
@cancel="handleClose"
>
<a-row :gutter="10">
<a-col :span="7">
<a-card size="small" :loading="cardLoading" class="selectorTreeDiv">
<a-tree
v-if="treeData"
v-model:expandedKeys="defaultExpandedKeys"
:tree-data="treeData"
:field-names="treeFieldNames"
@select="treeSelect"
>
</a-tree>
</a-card>
</a-col>
<a-col :span="11">
<div class="table-operator xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="12">
<a-form-item name="searchKey">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入用户名" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-button type="primary" class="xn-mr-10" @click="loadData()"> </a-button>
<a-button @click="reset()"> </a-button>
</a-col>
</a-row>
</a-form>
</div>
<div class="user-table">
<a-table
ref="tableRef"
size="small"
:columns="commons"
:data-source="tableData"
:expand-row-by-click="true"
:loading="pageLoading"
bordered
:pagination="false"
>
<template #title>
<span>待选择列表 {{ tableRecordNum }} </span>
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" size="small" @click="addAllPageRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'avatar'">
<a-avatar :src="record.avatar" style="margin-bottom: -5px; margin-top: -5px" />
</template>
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" size="small" @click="addRecord(record)"><PlusOutlined /></a-button>
</template>
<template v-if="column.dataIndex === 'category'">
{{ $TOOL.dictTypeData('ROLE_CATEGORY', record.category) }}
</template>
</template>
</a-table>
<div class="mt-2">
<a-pagination
v-if="!isEmpty(tableData)"
v-model:current="current"
v-model:page-size="pageSize"
:total="total"
size="small"
showSizeChanger
@change="paginationChange"
/>
</div>
</div>
</a-col>
<a-col :span="6">
<div class="user-table">
<a-table
ref="selectedTable"
size="small"
:columns="selectedCommons"
:data-source="selectedData"
:expand-row-by-click="true"
:loading="selectedTableListLoading"
bordered
>
<template #title>
<span>已选择: {{ selectedData.length }}</span>
<div v-if="!radioModel" class="xn-fdr">
<a-button type="dashed" danger size="small" @click="delAllRecord"></a-button>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-button type="dashed" danger size="small" @click="delRecord(record)"><MinusOutlined /></a-button>
</template>
</template>
</a-table>
</div>
</a-col>
</a-row>
</a-modal>
</template>
<script setup name="userSelector">
import { message } from 'ant-design-vue'
import { remove, isEmpty, cloneDeep } from 'lodash-es'
//
const visible = ref(false)
const deleteShow = ref('')
// common
const commons = [
{
title: '操作',
dataIndex: 'action',
align: 'center',
width: 50
},
{
title: '头像',
dataIndex: 'avatar',
width: 50
},
{
title: '用户名',
dataIndex: 'name',
ellipsis: true
},
{
title: '账号',
dataIndex: 'account'
}
]
// common
const selectedCommons = [
{
title: '操作',
dataIndex: 'action',
align: 'center',
width: 50
},
{
title: '用户名',
dataIndex: 'name',
ellipsis: true
}
]
const props = defineProps({
radioModel: {
type: Boolean,
default: () => false
},
dataIsConverterFlw: {
type: Boolean,
default: () => false
},
orgTreeApi: {
type: Function,
default: () => undefined
},
userPageApi: {
type: Function,
default: () => undefined
},
userListByIdListApi: {
type: Function,
default: () => undefined
},
value: {
default: () => ''
},
dataType: {
type: String,
default: () => 'string'
},
userShow: {
type: Boolean,
default: () => true
},
addShow: {
type: Boolean,
default: () => true
}
})
// ref
const tableRef = ref()
// ref
const selectedTable = ref()
const tableRecordNum = ref()
const searchFormState = ref({})
const searchFormRef = ref()
const cardLoading = ref(true)
const pageLoading = ref(false)
const selectedTableListLoading = ref(false)
// treeNode title,key,children
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
//
const treeData = ref()
// id
const defaultExpandedKeys = ref([])
const emit = defineEmits(['update:value', 'onBack'])
const tableData = ref([])
const selectedData = ref([])
const recordIds = ref([])
//
const current = ref(0) //
const pageSize = ref(20) //
const total = ref(0) //
//
const showUserPlusModal = (ids = []) => {
const data = goDataConverter(ids)
recordIds.value = data
getUserAvatarById(data)
openModal()
}
const onMouseEnter = (index) => {
deleteShow.value = index
}
const onMouseLeave = (index) => {
deleteShow.value = ''
}
const openModal = () => {
if (typeof props.orgTreeApi !== 'function') {
message.warning('未配置选择器需要的orgTreeApi接口')
return
}
if (typeof props.userPageApi !== 'function') {
message.warning('未配置选择器需要的userPageApi接口')
return
}
if (typeof props.userListByIdListApi !== 'function') {
message.warning('未配置选择器需要的userListByIdListApi接口')
return
}
visible.value = true
//
props
.orgTreeApi()
.then((data) => {
if (data !== null) {
treeData.value = data
// 2
treeData.value.forEach((item) => {
// 0
if (item.parentId === '0') {
defaultExpandedKeys.value.push(item.id)
// ID
if (item.children) {
item.children.forEach((items) => {
defaultExpandedKeys.value.push(items.id)
})
}
}
})
}
})
.finally(() => {
cardLoading.value = false
})
searchFormState.value.size = pageSize.value
loadData()
if (props.userListByIdListApi) {
if (isEmpty(recordIds.value)) {
return
}
const param = {
idList: recordIds.value
}
selectedTableListLoading.value = true
props
.userListByIdListApi(param)
.then((data) => {
selectedData.value = data
})
.finally(() => {
selectedTableListLoading.value = false
})
}
}
//
const deleteUser = (user) => {
//
remove(userObj.value, (item) => item.id === user.id)
//
remove(recordIds.value, (item) => item === user.id)
const value = []
const showUser = []
userObj.value.forEach((item) => {
const obj = {
id: item.id,
name: item.name
}
value.push(item.id)
// obj
const objClone = cloneDeep(obj)
objClone.avatar = item.avatar
showUser.push(objClone)
})
userObj.value = showUser
//
const resultData = outDataConverter(value)
emit('update:value', resultData)
emit('onBack', resultData)
}
//
const loadData = () => {
pageLoading.value = true
props
.userPageApi(searchFormState.value)
.then((data) => {
current.value = data.current
// pageSize.value = data.size
total.value = data.total
//
tableData.value = []
tableRecordNum.value = 0
tableData.value = data.records
if (data.records) {
tableRecordNum.value = data.records.length
} else {
tableRecordNum.value = 0
}
})
.finally(() => {
pageLoading.value = false
})
}
// pageSize
const paginationChange = (page, pageSize) => {
searchFormState.value.current = page
searchFormState.value.size = pageSize
loadData()
}
const judge = () => {
return !(props.radioModel && selectedData.value.length > 0)
}
//
const addRecord = (record) => {
if (!judge()) {
message.warning('只可选择一条')
return
}
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
if (selectedRecord.length === 0) {
selectedData.value.push(record)
} else {
message.warning('该记录已存在')
}
}
//
const addAllPageRecord = () => {
let newArray = selectedData.value.concat(tableData.value)
let list = []
for (let item1 of newArray) {
let flag = true
for (let item2 of list) {
if (item1.id === item2.id) {
flag = false
}
}
if (flag) {
list.push(item1)
}
}
selectedData.value = list
}
//
const delRecord = (record) => {
remove(selectedData.value, (item) => item.id === record.id)
}
//
const delAllRecord = () => {
selectedData.value = []
}
//
const treeSelect = (selectedKeys) => {
searchFormState.value.current = 0
if (selectedKeys.length > 0) {
searchFormState.value.orgId = selectedKeys.toString()
} else {
delete searchFormState.value.orgId
}
loadData()
}
const userObj = ref([])
//
const handleOk = () => {
userObj.value = []
const value = []
const showUser = []
selectedData.value.forEach((item) => {
const obj = {
id: item.id,
name: item.name
}
value.push(item.id)
// obj
const objClone = cloneDeep(obj)
objClone.avatar = item.avatar
showUser.push(objClone)
})
userObj.value = showUser
//
const resultData = outDataConverter(value)
emit('update:value', resultData)
emit('onBack', resultData)
handleClose()
}
//
const reset = () => {
delete searchFormState.value.searchKey
loadData()
}
const handleClose = () => {
searchFormState.value = {}
tableRecordNum.value = 0
tableData.value = []
current.value = 0
pageSize.value = 20
total.value = 0
selectedData.value = []
// userObj.value = []
visible.value = false
}
//
const goDataConverter = (data) => {
if (props.dataIsConverterFlw) {
const resultData = []
//
if (!isEmpty(data.value)) {
const values = data.value.split(',')
if (values.length > 0) {
values.forEach((id) => {
resultData.push(id)
})
} else {
resultData.push(data.value)
}
} else {
//
if (!isEmpty(data) && !isEmpty(data[0]) && !isEmpty(data[0].value)) {
const values = data[0].value.split(',')
for (let i = 0; i < values.length; i++) {
resultData.push(values[i])
}
}
}
return resultData
} else {
if (getValueType() !== 'string') {
return data
}
if (data.length > 1) {
const resultData = []
data.split(',').forEach((id) => {
resultData.push(id)
})
return resultData
} else {
return data
}
}
}
//
const outDataConverter = (data) => {
if (props.dataIsConverterFlw) {
data = userObj.value
const obj = {}
let label = ''
let value = ''
for (let i = 0; i < data.length; i++) {
if (data.length === i + 1) {
label = label + data[i].name
value = value + data[i].id
} else {
label = label + data[i].name + ','
value = value + data[i].id + ','
}
}
obj.key = 'USER'
obj.label = label
obj.value = value
obj.extJson = ''
return obj
} else {
if (getValueType() !== 'string') {
return data
}
let resultData = ''
data.forEach((id) => {
resultData = resultData + ',' + id
})
resultData = resultData.substring(1, resultData.length)
return resultData
}
}
//
const getValueType = () => {
if (props.dataType) {
return props.dataType
} else {
if (props.radioModel) {
return 'string'
}
return typeof typeof props.value
}
}
const getUserAvatarById = (ids) => {
if (isEmpty(userObj.value) && !isEmpty(ids)) {
const param = {
idList: recordIds.value
}
//
props.userListByIdListApi(param).then((data) => {
userObj.value = data
})
}
}
watch(
() => props.value,
(newValue) => {
if (!isEmpty(props.value)) {
const ids = goDataConverter(newValue)
recordIds.value = ids
getUserAvatarById(ids)
}
},
{
immediate: true //
}
)
defineExpose({
showUserPlusModal
})
</script>
<style lang="less" scoped>
.xn-mr-5 {
margin-right: 5px;
}
.xn-mr-10 {
margin-right: 10px;
}
.selectorTreeDiv {
max-height: 500px;
overflow: auto;
}
.ant-form-item {
margin-bottom: 0 !important;
}
.user-table {
overflow: auto;
max-height: 450px;
}
.user-container {
display: flex;
align-items: center; /* 垂直居中 */
flex-direction: column;
margin-right: 10px;
text-align: center;
}
.user-avatar {
width: 30px;
border-radius: 50%; /* 设置为50%以创建圆形头像 */
}
.user-name {
font-size: 12px;
max-width: 50px;
white-space: nowrap;
overflow: hidden;
}
.user-delete {
z-index: 99;
color: rgba(0, 0, 0, 0.25);
position: relative;
display: flex;
flex-direction: column;
}
.delete-icon {
position: absolute;
right: -2px;
z-index: 5;
top: -3px;
cursor: pointer;
visibility: hidden;
}
.show-delete-icon {
visibility: visible;
}
</style>

View File

@ -0,0 +1,62 @@
<template>
<a-card>
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
表单的值{{ formData }}
<a-form-item name="userIdList">
<xn-user-selector
ref="userSelectorPlusProRef"
:org-tree-api="selectorApiFunction.orgTreeApi"
:user-page-api="selectorApiFunction.userPageApi"
:user-list-by-id-list-api="selectorApiFunction.userListByIdListApi"
v-model:value="formData.userIdList"
@onBack="userSelectorOnBack"
/>
</a-form-item>
</a-form>
<a-button type="primary" @click="onSubmit"></a-button>
</a-card>
</template>
<script setup name="userTest">
import bizOrgApi from '@/api/biz/bizOrgApi'
import userCenterApi from '@/api/sys/userCenterApi'
import { required } from '@/utils/formRules'
const formRef = ref()
const formData = ref({
userIdList: '1543837863788879871,1543837863788879873'
// userIdList: ['1543837863788879871', '1543837863788879873']
})
const formRules = {
userIdList: [required('请选择用户')]
}
const onSubmit = () => {
formRef.value
.validate()
.then((result) => {
console.log('最终表单数据:' + JSON.stringify(result))
})
.catch(() => {})
}
// API
const selectorApiFunction = {
orgTreeApi: (param) => {
return bizOrgApi.orgTreeSelector(param).then((data) => {
return Promise.resolve(data)
})
},
userPageApi: (param) => {
return bizOrgApi.orgUserSelector(param).then((data) => {
return Promise.resolve(data)
})
},
userListByIdListApi: (param) => {
return userCenterApi.userCenterGetUserListByIdList(param).then((data) => {
return Promise.resolve(data)
})
}
}
// v-model:value
const userSelectorOnBack = (data) => {
console.log('返回的:' + JSON.stringify(data))
}
</script>

View File

@ -30,7 +30,7 @@ const DEFAULT_CONFIG = {
// 请求是否开启缓存
REQUEST_CACHE: false,
// 布局 经典classical双排菜单doublerow
// 布局 经典classical双排菜单doublerow, 顶栏菜单top
SNOWY_LAYOUT: 'doublerow',
// 菜单是否折叠
@ -54,11 +54,20 @@ const DEFAULT_CONFIG = {
// 侧边菜单是否排他展开
SNOWY_SIDE_UNIQUE_OPEN: true,
// 登录用户水印
SNOWY_LOGIN_USER_WATERMARK_OPEN: false,
// 页脚版权信息
SNOWY_FOOTER_COPYRIGHT_OPEN: true,
// 圆角风格
SNOWY_ROUNDED_CORNER_STYLE_OPEN: true,
// 语言
LANG: 'zh-cn',
// 主题颜色
COLOR: '#1890FF',
COLOR: '#1677FF',
// 默认整体主题
SNOWY_THEME: 'dark',
@ -66,15 +75,6 @@ const DEFAULT_CONFIG = {
// 整体表单风格
SNOWY_FORM_STYLE: 'drawer',
// 成功色
success: '#52c41a',
// 警告色
warning: '#faad14',
// 错误色
error: '#f5222f',
// 系统基础配置,这些是数据库中保存起来的
SYS_BASE_CONFIG: {
// 默认logo

View File

@ -39,7 +39,7 @@ const colorList = [
},
{
key: '拂晓蓝(默认)',
color: '#1890FF'
color: '#1677FF'
},
{
key: '极客蓝',

View File

@ -1,5 +1,5 @@
<template>
<div v-if="navMenus.length <= 0" style="padding: 20px">
<div v-if="navMenus.length <= 0" class="xn-pd20">
<a-alert message="无任何菜单" type="info" :closable="false" />
</div>
<template v-for="navMenu in navMenus" :key="navMenu">
@ -26,6 +26,8 @@
</template>
<script setup>
import { globalStore } from '@/store'
const store = globalStore()
const props = defineProps({
navMenus: {
type: Array,
@ -44,3 +46,20 @@
return false
}
</script>
<style>
.ant-menu-light.ant-menu-horizontal > .ant-menu-submenu-selected {
background-color: var(--primary-1);
}
.ant-menu-dark.ant-menu-horizontal > .ant-menu-submenu-selected {
background-color: var(--primary-5);
}
.ant-menu-light.ant-menu-horizontal > .ant-menu-item-selected {
background-color: none;
}
.ant-menu-dark.ant-menu-horizontal > .ant-menu-item-selected {
background-color: var(--primary-5);
}
.xn-pd20 {
padding: 20px;
}
</style>

View File

@ -0,0 +1,53 @@
<template>
<div class="admin-ui-breadcrumb">
<div class="left-panel">
<a-breadcrumb>
<template v-for="item in breadList" :key="item.title">
<a-breadcrumb-item v-if="item.path !== '/' && !item.meta.hiddenBreadcrumb" :key="item.meta.title">{{
item.meta.title
}}</a-breadcrumb-item>
</template>
</a-breadcrumb>
</div>
<div class="center-panel"></div>
<div class="right-panel">
<slot></slot>
</div>
</div>
</template>
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const breadList = ref([])
watch(route, () => {
getBreadcrumb()
})
onBeforeMount(() => {
getBreadcrumb()
})
const getBreadcrumb = () => {
breadList.value = route.meta.breadcrumb
}
</script>
<style scoped>
.admin-ui-breadcrumb {
padding-left: 15px;
background: var(--breadcrumb-background);
min-height: 40px;
display: flex;
border-bottom: 1px solid var(--header-bottom);
}
.admin-ui-breadcrumb .left-panel {
display: flex;
align-items: center;
}
.admin-ui-breadcrumb .right-panel {
display: flex;
align-items: center;
}
</style>

View File

@ -3,7 +3,7 @@
<a-badge :count="unreadMessageNum" class="badge">
<comment-outlined />
</a-badge>
<a-drawer v-model:visible="msgVisible" title="新消息" placement="right" :width="500">
<a-drawer v-model:open="msgVisible" title="新消息" placement="right" :width="500">
<a-list :data-source="messageList" size="small" class="mb-3" :loading="miniMessageLoading">
<template #renderItem="{ item }">
<a-list-item>
@ -15,12 +15,12 @@
</a-list-item>
</template>
</a-list>
<a-space style="float: right">
<a-space class="xn-fdr">
<a-button v-if="unreadMessageNum > 0" @click="markRead"></a-button>
<a-button type="primary" @click="leaveFor('/usercenter')"></a-button>
</a-space>
</a-drawer>
<xn-form-container title="详情" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
<xn-form-container title="详情" :width="700" :open="visible" :destroy-on-close="true" @close="onClose">
<a-form ref="formRef" :model="formData" layout="vertical">
<a-form-item label="主题:" name="subject">
<span>{{ formData.subject }}</span>
@ -43,8 +43,8 @@
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'read'">
<span v-if="record.read" style="color: #d9d9d9"></span>
<span v-else style="color: #ff4d4f">未读</span>
<span v-if="record.read" class="xn-color-d9d9d9"></span>
<span v-else class="xn-color-ff4d4f">未读</span>
</template>
</template>
</s-table>

View File

@ -1,5 +1,5 @@
<template>
<div class="layout-items-center" v-if="moduleUnfoldOpen">
<div class="layout-items-center" v-if="moduleUnfoldOpen && layout !== layoutEnum.TOP">
<a-menu
v-model:selectedKeys="selectedKeys"
mode="horizontal"
@ -10,14 +10,14 @@
<a-menu-item
v-for="item in menu"
:key="item.id"
class="!px-3"
style="position: relative"
class="xn-pxn-r"
@click="moduleClick(item.id)"
:class="{ 'ant-menu-item-select': item.id === module }"
>
<template #icon>
<component :is="item.meta.icon" />
</template>
<span style="margin-left: -5px">{{ item.meta.title }}</span>
<span class="xn-ml-5">{{ item.meta.title }}</span>
</a-menu-item>
</a-menu>
</div>
@ -27,7 +27,12 @@
<a-row :gutter="[0, 5]" class="module-row">
<div v-for="item in menu" :key="item.id">
<a-col :span="6">
<a-tag class="module-card" :color="item.color" @click="moduleClick(item.id)">
<a-tag
class="module-card"
:class="roundedCornerStyleOpen ? 'module-card-radius-round' : 'module-card-radius-default'"
:color="item.color"
@click="moduleClick(item.id)"
>
<component :is="item.meta.icon" class="module-card-icon" />
<div class="module-card-font">{{ item.meta.title }}</div>
</a-tag>
@ -48,33 +53,69 @@
import { globalStore } from '@/store'
import { watch } from 'vue'
import { storeToRefs } from 'pinia'
import { layoutEnum } from '@/layout/enum/layoutEnum'
const store = globalStore()
const { moduleUnfoldOpen, topHeaderThemeColorOpen } = storeToRefs(store)
const moduleBackColor = ref(topHeaderThemeColorOpen)
const layout = ref()
const module = computed(() => {
return store.module
})
const isMobile = computed(() => {
return store.isMobile
})
const themeColor = computed(() => {
return store.themeColor
})
const theme = computed(() => {
return store.theme
})
//
const roundedCornerStyleOpen = computed(() => {
return store.roundedCornerStyleOpen
})
//
watch(moduleUnfoldOpen, (newValue) => {
watch(moduleUnfoldOpen, () => {
nextTick(() => {
setModuleBackColor()
})
})
//
watch(module, (newValue) => {
selectedKeys.value = [newValue]
setSelectedKeys()
})
//
watch(themeColor, () => {
nextTick(() => {
setModuleBackColor()
})
})
//
watch(theme, () => {
nextTick(() => {
setModuleBackColor()
})
})
//
watch(isMobile, (newValue) => {
if (!newValue) {
nextTick(() => {
setModuleBackColor()
})
}
})
//
watch(topHeaderThemeColorOpen, (newValue) => {
moduleBackColor.value = newValue
setModuleBackColor()
})
//
watch(roundedCornerStyleOpen, () => {
nextTick(() => {
setModuleBackColor()
})
})
const emit = defineEmits({ switchModule: null })
const menu = router.getMenu()
const selectedKeys = ref([module.value])
@ -85,9 +126,9 @@
setSelectedKeys()
})
}
onMounted(() => {
setModuleBackColor()
layout.value = tool.data.get('SNOWY_LAYOUT')
})
//
const setModuleBackColor = () => {
@ -112,26 +153,33 @@
</script>
<style lang="less">
.xn-pxn-r {
position: relative;
}
.module-row {
max-width: 357px;
}
.module-card {
width: 80px;
height: 80px;
width: 70px;
height: 70px;
background-color: #0d84ff;
text-align: center;
align-items: center;
cursor: pointer;
}
.module-card-radius-default {
border-radius: 2px;
}
.module-card-radius-round {
border-radius: 6px;
}
.module-card-icon {
color: white;
font-size: 20px;
margin-top: 20px;
font-size: 16px;
margin-top: 15px;
}
.module-card-font {
color: white;
font-size: 8px;
}
.ant-menu-horizontal > .ant-menu-item::after,
.ant-menu-horizontal > .ant-menu-submenu::after {
@ -139,7 +187,7 @@
}
.module-menu {
line-height: 50px;
border-bottom: 0px;
border-bottom: 0;
width: 105%;
flex: 0 0 auto;
}
@ -153,4 +201,15 @@
.module-card-scope {
height: 49px;
}
.ant-menu-item-select {
color: #ccc;
background-color: var(--primary-7);
}
.xn-ml-5 {
margin-left: -5px;
}
.ant-menu-horizontal > .ant-menu-item::after,
.ant-menu-horizontal > .ant-menu-submenu::after {
display: none;
}
</style>

View File

@ -22,8 +22,7 @@
<a-input
ref="inputRef"
v-model="searchText"
class="search-box"
style="width: 100%"
class="search-box xn-wd"
allowClear
placeholder="搜索页面(支持拼音检索)"
@change="querySearch"
@ -39,7 +38,8 @@
@mouseleave="onCardOut"
@keypress.up="handleKeyUp"
@keypress.down="handleKeyDown"
style="margin: 10px 0"
class="xn-mn10p0"
>
<div ref="cardListRef" class="search-card beauty-scroll">
<a-list size="small" :data-source="resultsList">
@ -47,8 +47,7 @@
<a-list-item
@click="handleSelect(item.fullPath)"
@mouseover="onCardItemHover(index)"
:class="{ active: index === cardIndex }"
style="padding-right: 10px"
:class="{ active: index === cardIndex },'xn-pr10'"
>
<template #actions>
<a>
@ -279,6 +278,12 @@
:deep(.ant-list-item.active) {
background-color: var(--primary-1);
}
.xn-mn10p0 {
margin: 10px 0;
}
.xn-pr10 {
padding-right: 10px;
}
.search-box {
width: 100%;
}
@ -321,7 +326,10 @@
padding-bottom: 2px;
margin: 0px 4px;
border-radius: 2px;
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
box-shadow:
inset 0 -2px #cdcde6,
inset 0 0 1px 1px #fff,
0 1px 2px 1px #1e235a66;
font-weight: bold;
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="setting-drawer-index-content">
<div class="scrollbar">
<h3>整体风格设置</h3>
<h3 class="setting-item-title">整体风格设置</h3>
<div class="snowy-setting-checkbox">
<a-tooltip v-for="(a, i) in sideStyleList" :key="i" placement="top">
<template #title>
@ -12,7 +12,7 @@
</div>
</a-tooltip>
</div>
<h3>整体界面布局</h3>
<h3 class="setting-item-title">整体界面布局</h3>
<div class="snowy-setting-checkbox">
<a-tooltip v-for="(a, i) in layoutList" :key="i" placement="top">
<template #title>
@ -27,7 +27,7 @@
</div>
<a-divider />
<div class="mb-4">
<h3>主题色</h3>
<h3 class="setting-item-title">主题色</h3>
<div class="h-[50px]">
<a-tooltip v-for="(item, index) in colorList" :key="index" class="snowy-setting-theme-color-colorBlock">
<template #title>
@ -39,23 +39,35 @@
</a-tooltip>
</div>
</div>
<div class="mb-4 layout-slide">
<h4 class="">顶栏应用主题色</h4>
<a-switch :checked="topHeaderThemeColorOpen" @change="changeTopHanderThemeColorOpen" />
</div>
<div class="mb-4 layout-slide">
<h4>顶栏主题色通栏</h4>
<div class="mb-4 layout-slide" v-if="!topHeaderThemeColorOpenDisabled" style="padding-top: 10px">
<h4 class="setting-item-title">顶栏应用主题色</h4>
<a-switch
style="float: right"
:checked="topHeaderThemeColorSpread"
:disabled="!topHeaderThemeColorOpen"
@change="changeTopHanderThemeColorSpread"
:checked="topHeaderThemeColorOpen"
@change="changeTopHeaderThemeColorOpen"
:disabled="topHeaderThemeColorOpenDisabled"
/>
</div>
<div class="mb-4 layout-slide" v-if="!topHeaderThemeColorSpreadDisabled">
<h4 class="setting-item-title">顶栏主题色通栏</h4>
<a-switch
class="xn-fdr"
:checked="topHeaderThemeColorSpread"
:disabled="!topHeaderThemeColorOpen || topHeaderThemeColorSpreadDisabled"
@change="changeTopHeaderThemeColorSpread"
/>
</div>
<a-divider />
<a-form ref="formRef" class="text-right">
<a-form-item label="模块坞">
<a-switch :checked="moduleUnfoldOpen" @change="toggleState('moduleUnfoldOpen')" />
<a-form-item label="模块坞" v-if="!moduleUnfoldDisabled">
<a-switch
:checked="moduleUnfoldOpen"
@change="toggleState('moduleUnfoldOpen')"
:disabled="moduleUnfoldDisabled"
/>
</a-form-item>
<a-form-item label="固定宽度" v-if="layout == layoutEnum.TOP">
<a-switch :checked="fixedWidth" @change="toggleState('fixedWidth')" />
</a-form-item>
<a-form-item label="面包屑">
<a-switch :checked="breadcrumbOpen" @change="toggleState('breadcrumbOpen')" />
@ -63,11 +75,28 @@
<a-form-item label="多标签">
<a-switch :checked="layoutTagsOpen" @change="toggleState('layoutTagsOpen')" />
</a-form-item>
<a-form-item label="折叠菜单">
<a-switch :checked="menuIsCollapse" @change="toggleState('menuIsCollapse')" />
<a-form-item label="折叠菜单" v-if="!menuIsCollapseDisabled">
<a-switch
:checked="menuIsCollapse"
@change="toggleState('menuIsCollapse')"
:disabled="menuIsCollapseDisabled"
/>
</a-form-item>
<a-form-item label="菜单排他展开">
<a-switch :checked="sideUniqueOpen" @change="toggleState('sideUniqueOpen')" />
<a-form-item label="菜单排他展开" v-if="!sideUniqueOpenDisabled">
<a-switch
:checked="sideUniqueOpen"
@change="toggleState('sideUniqueOpen')"
:disabled="sideUniqueOpenDisabled"
/>
</a-form-item>
<a-form-item label="登录用户水印">
<a-switch :checked="loginUserWatermarkOpen" @change="toggleState('loginUserWatermarkOpen')" />
</a-form-item>
<a-form-item label="页脚版权信息">
<a-switch :checked="footerCopyrightOpen" @change="toggleState('footerCopyrightOpen')" />
</a-form-item>
<a-form-item label="圆角风格">
<a-switch :checked="roundedCornerStyleOpen" @change="toggleState('roundedCornerStyleOpen')" />
</a-form-item>
<a-form-item label="表单风格">
<a-select
@ -88,50 +117,63 @@
</template>
<script setup>
import { colorList } from '@/config/settingConfig'
import { ThemeModeEnum } from '@/utils/enum'
import { themeEnum } from '@/layout/enum/themeEnum'
import { layoutEnum } from '@/layout/enum/layoutEnum'
import { globalStore } from '@/store'
import tool from '@/utils/tool'
const store = globalStore()
const topHeaderThemeColorOpenDisabled = ref(false)
const topHeaderThemeColorSpreadDisabled = ref(false)
const moduleUnfoldDisabled = ref(false)
const menuIsCollapseDisabled = ref(false)
const sideUniqueOpenDisabled = ref(false)
const toolDataNameMap = {
menuIsCollapse: 'MENU_COLLAPSE',
sideUniqueOpen: 'SIDE_UNIQUE_OPEN',
layoutTagsOpen: 'LAYOUT_TAGS_OPEN',
breadcrumbOpen: 'BREADCRUMD_OPEN',
fixedWidth: 'FIXEDWIDTH_OPEN',
topHeaderThemeColorOpen: 'TOP_HEADER_THEME_COLOR_OPEN',
topHeaderThemeColorSpread: 'TOP_HEADER_THEME_COLOR_SPREAD',
loginUserWatermarkOpen: 'LOGIN_USER_WATERMARK_OPEN',
footerCopyrightOpen: 'FOOTER_COPYRIGHT_OPEN',
roundedCornerStyleOpen: 'ROUNDED_CORNER_STYLE_OPEN',
moduleUnfoldOpen: 'MODULE_UNFOLD_OPEN'
}
const sideStyleList = ref([
{
tips: '暗色主题风格',
value: ThemeModeEnum.DARK,
value: themeEnum.DARK,
style: 'snowy-setting-checkbox-item-dark'
},
{
tips: '亮色主题风格',
value: ThemeModeEnum.LIGHT,
value: themeEnum.LIGHT,
style: 'snowy-setting-checkbox-item-light'
},
{
tips: '暗黑模式',
value: ThemeModeEnum.REAL_DARK,
value: themeEnum.REAL_DARK,
style: 'snowy-setting-checkbox-item-realdark'
}
])
const layoutList = ref([
{
tips: '经典',
value: 'classical',
value: layoutEnum.CLASSICAL,
style: 'snowy-setting-layout-menu-classical'
},
{
tips: '双排菜单',
value: 'doublerow',
value: layoutEnum.DOUBLEROW,
style: 'snowy-setting-layout-menu-doublerow'
},
{
tips: '顶部菜单',
value: layoutEnum.TOP,
style: 'snowy-setting-layout-menu-top'
}
])
const xnFormStyleOptions = ref([
{
label: '抽屉',
@ -142,7 +184,6 @@
value: 'modal'
}
])
const theme = computed(() => {
return store.theme
})
@ -158,12 +199,24 @@
const sideUniqueOpen = computed(() => {
return store.sideUniqueOpen
})
const loginUserWatermarkOpen = computed(() => {
return store.loginUserWatermarkOpen
})
const footerCopyrightOpen = computed(() => {
return store.footerCopyrightOpen
})
const roundedCornerStyleOpen = computed(() => {
return store.roundedCornerStyleOpen
})
const layoutTagsOpen = computed(() => {
return store.layoutTagsOpen
})
const breadcrumbOpen = computed(() => {
return store.breadcrumbOpen
})
const fixedWidth = computed(() => {
return store.fixedWidth
})
const moduleUnfoldOpen = computed(() => {
return store.moduleUnfoldOpen
})
@ -176,16 +229,14 @@
const formStyle = computed(() => {
return store.formStyle
})
const changeTopHanderThemeColorOpen = () => {
const changeTopHeaderThemeColorOpen = () => {
toggleState('topHeaderThemeColorOpen')
if (!topHeaderThemeColorOpen) {
if (!topHeaderThemeColorOpen.value) {
store.topHeaderThemeColorSpread = false
tool.data.set('SNOWY_TOP_HEADER_THEME_COLOR_SPREAD', false)
}
}
const changeTopHanderThemeColorSpread = () => {
const changeTopHeaderThemeColorSpread = () => {
toggleState('topHeaderThemeColorSpread')
}
const toggleState = (stateName) => {
@ -197,11 +248,13 @@
const setSideStyle = (value) => {
store.setTheme(value)
tool.data.set('SNOWY_THEME', value)
layoutChange(layout.value)
}
//
const layoutStyle = (value) => {
store.setLayout(value)
tool.data.set('SNOWY_LAYOUT', value)
layoutChange(value)
}
//
const tagColor = (value) => {
@ -213,6 +266,48 @@
tool.data.set('SNOWY_FORM_STYLE', value)
store.setFormStyle(value)
}
//
const layoutChange = (layout) => {
//
if (theme.value === themeEnum.REAL_DARK) {
topHeaderThemeColorOpenDisabled.value = true
topHeaderThemeColorSpreadDisabled.value = true
//
if (topHeaderThemeColorOpen.value) {
toggleState('topHeaderThemeColorOpen')
tool.data.set('SNOWY_TOP_HEADER_THEME_COLOR_SPREAD', false)
}
} else {
if (layout !== layoutEnum.TOP) {
topHeaderThemeColorSpreadDisabled.value = false
topHeaderThemeColorOpenDisabled.value = false
} else {
topHeaderThemeColorOpenDisabled.value = false
}
}
//
if (layout === layoutEnum.TOP) {
//
moduleUnfoldDisabled.value = true
//
menuIsCollapseDisabled.value = true
//
if (sideUniqueOpen.value) {
toggleState('sideUniqueOpen')
}
sideUniqueOpenDisabled.value = true
topHeaderThemeColorSpreadDisabled.value = true
} else {
moduleUnfoldDisabled.value = false
menuIsCollapseDisabled.value = false
sideUniqueOpenDisabled.value = false
}
}
onMounted(() => {
const layout = tool.data.get('SNOWY_LAYOUT')
// dom
layoutChange(layout)
})
</script>
<style lang="less" scoped>
@ -294,12 +389,11 @@
position: absolute;
right: 8px;
bottom: 8px;
color: #1890ff;
color: #1677FF;
font-weight: 700;
font-size: 14px;
pointer-events: none;
}
.snowy-setting-theme-color-colorBlock {
margin-top: 8px;
width: 20px;
@ -314,7 +408,6 @@
color: #fff;
font-weight: 700;
}
.snowy-setting-layout-menu-doublerow {
z-index: 1;
background-color: #ebeef1;
@ -364,8 +457,33 @@
background-color: #fff;
content: '';
}
.snowy-setting-layout-menu-top {
z-index: 1;
background-color: #ebeef1;
content: '';
}
.snowy-setting-layout-menu-top::before {
z-index: 1;
background-color: #ebeef1;
content: '';
}
.snowy-setting-layout-menu-top::after {
z-index: 2;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 25%;
background-color: #001529;
content: '';
}
.scrollbar {
margin: 0 auto;
}
.setting-item-title {
color: var(--font-color);
}
:deep(.ant-form-item) {
margin-bottom: 12px !important;
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<div v-drag class="mobile-nav-button" draggable="false" @click="showMobileNav($event)">
<appstore-outlined style="font-size: 20px; color: white" />
<appstore-outlined class="xn-appout-line" />
</div>
<a-drawer v-model:visible="visible" :width="210" :closable="false" placement="left">
<a-drawer v-model:open="visible" :width="210" :closable="false" placement="left">
<header class="snowy-header-logo mobile-nav">
<div class="snowy-header-left">
<div class="logo-bar">
@ -11,7 +11,7 @@
</div>
</div>
</header>
<a-menu style="width: 208px; margin-left: -24px" mode="inline" @select="onSelect">
<a-menu class="xn-inline-line" mode="inline" @select="onSelect">
<NavMenu :nav-menus="menu"></NavMenu>
</a-menu>
</a-drawer>
@ -113,6 +113,14 @@
</script>
<style lang="less" scoped>
.xn-appout-line {
font-size: 20px;
color: white;
}
.xn-inline-line {
width: 208px;
margin-left: -24px;
}
.mobile-nav {
margin-top: -24px;
margin-left: -24px;

View File

@ -9,32 +9,33 @@
>
<div class="right-menu-item" @click="refreshTab">
<reload-outlined class="snowy-header-tags-right" />
<div class="pl-3">刷新</div>
<div class="pl-3 snowy-header-tags-right-font">刷新</div>
</div>
<div class="right-menu-item" @click="closeTabs">
<close-outlined class="snowy-header-tags-right" />
<div class="pl-3">关闭</div>
<div class="pl-3 snowy-header-tags-right-font">关闭</div>
</div>
<div class="right-menu-item" @click="closeOtherTabs">
<close-outlined class="snowy-header-tags-right" />
<div class="pl-3">关闭其他标签</div>
<div class="pl-3 snowy-header-tags-right-font">关闭其他标签</div>
</div>
<div class="right-menu-item" @click="maximize">
<expand-outlined class="snowy-header-tags-right" />
<div class="pl-3">最大化</div>
<div class="pl-3 snowy-header-tags-right-font">最大化</div>
</div>
<div class="right-menu-item" @click="openWindow">
<select-outlined class="snowy-header-tags-right" />
<div class="pl-3">新窗口打开</div>
<div class="pl-3 snowy-header-tags-right-font">新窗口打开</div>
</div>
</xn-context-menu>
<a-tabs
v-model:activeKey="activeKey"
type="editable-card"
class="snowy-admin-tabs"
:class="[{ 'snowy-radius': roundedCornerStyleOpen }, 'snowy-admin-tabs']"
:animated="!roundedCornerStyleOpen"
hide-add
ref="tabs"
@edit="onTabRemove"
@ -85,11 +86,13 @@
const layoutTagsOpen = computed(() => {
return store.layoutTagsOpen
})
const roundedCornerStyleOpen = computed(() => {
return store.roundedCornerStyleOpen
})
const tagList = computed(() => {
return viewTags.value
})
watch(route, (to) => {
addViewTags(to)
activeKey.value = to.fullPath
@ -97,7 +100,6 @@
watch(layoutTagsOpen, () => {
// closeOtherCacheTabs()
})
onMounted(() => {
const tabNavList = document.querySelector('.ant-tabs-nav-list')
if (tabNavList) {
@ -299,9 +301,8 @@
</script>
<style lang="less">
.snowy-admin-tabs {
overflow: hidden; //
&.ant-tabs {
background: var(--component-background);
box-shadow: var(--header-light-shadow);
z-index: 99;
.ant-tabs-nav {
margin-bottom: 0;
@ -336,7 +337,6 @@
}
}
}
.snowy-admin-tabs-drop,
.snowy-admin-tabs-arrow,
.ant-tabs-nav-operations .ant-tabs-nav-more {
@ -355,7 +355,7 @@
}
.right-menu {
position: fixed;
background: #fff;
background: var(--tag-background);
z-index: 999;
border: 1px solid #eee;
box-shadow: 0 0.5em 1em 0 rgb(0 0 0 / 10%);
@ -374,4 +374,101 @@
}
}
}
.snowy-tags {
height: 40px;
background: var(--snowy-background-color);
}
.snowy-tags ul {
display: flex;
overflow: hidden;
padding-left: 0;
}
.snowy-tags li {
cursor: pointer;
display: inline-block;
float: left;
line-height: 39.5px;
position: relative;
flex-shrink: 0;
}
.snowy-tags li::after {
content: ' ';
width: 1px;
height: 100%;
position: absolute;
right: 0px;
background-image: linear-gradient(#fff, #e6e6e6);
}
.snowy-tags li a {
padding: 0 10px;
width: 100%;
height: 100%;
text-decoration: none;
display: flex;
align-items: center;
}
.snowy-tags li i {
margin-left: 10px;
border-radius: 3px;
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.snowy-tags li i:hover {
background: rgba(0, 0, 0, 0.2);
color: @body-background;
}
.snowy-tags li:hover {
background: @body-background;
}
.snowy-tags li.active {
background: @primary-color;
}
.snowy-tags li.active a {
color: var(--font-color);
}
.snowy-tags li.sortable-ghost {
opacity: 0;
}
.snowy-header-tags-right {
margin-right: 10px;
color: var(--font-color);
}
.snowy-header-tags-right-font {
color: var(--font-color);
}
.snowy-radius .ant-tabs-tab-active {
position: relative;
z-index: 1;
border-radius: 10px 10px 0 0 !important;
box-shadow:
12px 15px 0 0 var(--primary-1),
-12px 15px 0 0 var(--primary-1);
}
.snowy-radius .ant-tabs-tab-active::before {
content: '';
position: absolute;
left: -13px;
bottom: 1px;
width: 13px;
height: 40px;
background: var(--primary-radius);
border-radius: 0 0 20px 0;
}
.snowy-radius .ant-tabs-tab-active::after {
content: '';
position: absolute;
right: -13px;
bottom: 1px;
width: 13px;
height: 40px;
background: var(--primary-radius);
border-radius: 0 0 0 20px;
}
.snowy-radius .ant-tabs-ink-bar {
visibility: hidden !important;
}
</style>

View File

@ -8,22 +8,22 @@
<!-- <dev-user-message />-->
<a-dropdown class="user panel-item">
<div class="user-avatar">
<a-avatar :src="userInfo.avatar" />
<a-avatar :src="userInfo ? userInfo.avatar : undefined" />
<label>{{ userName }}</label>
</div>
<template #overlay>
<a-menu>
<a-menu-item key="uc" @click="handleUser('uc')">
<UserOutlined style="margin-right: 8px" />
<UserOutlined class="xn-mr8" />
<span>个人中心</span>
</a-menu-item>
<a-menu-item key="clearCache" @click="handleUser('clearCache')">
<loading3-quarters-outlined style="margin-right: 8px" />
<loading3-quarters-outlined class="xn-mr8" />
<span>清理缓存</span>
</a-menu-item>
<a-menu-divider />
<a-menu-item key="outLogin" @click="handleUser('outLogin')">
<export-outlined style="margin-right: 8px" />
<export-outlined class="xn-mr8" />
<span>退出登录</span>
</a-menu-item>
</a-menu>
@ -48,7 +48,7 @@
</div>
<!-- 整体风格设置抽屉 -->
<a-drawer v-model:visible="settingDialog" :closable="false" width="300">
<a-drawer v-model:open="settingDialog" :closable="false" width="300">
<setting />
</a-drawer>
</template>
@ -128,6 +128,10 @@
tool.data.remove('MENU')
tool.data.remove('PERMISSIONS')
router.replace({ path: '/login' })
nextTick(() => {
//
store.userInfo = undefined
})
})
.catch(() => {
tool.data.clear()

View File

@ -0,0 +1,15 @@
/**
* Copyright [2022] [https://www.xiaonuo.vip]
* Snowy采用APACHE LICENSE 2.0开源协议您在使用过程中需要注意以下几点
* 1.请不要删除和修改根目录下的LICENSE文件
* 2.请不要删除和修改Snowy源码头部的版权声明
* 3.本项目代码可免费商业使用商业使用请保留源码和相关描述文件的项目出处作者声明等
* 4.分发源码时候请注明软件出处 https://www.xiaonuo.vip
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
export const layoutEnum = {
CLASSICAL: 'classical',
DOUBLEROW: 'doublerow',
TOP: 'top'
}

View File

@ -0,0 +1,15 @@
/**
* Copyright [2022] [https://www.xiaonuo.vip]
* Snowy采用APACHE LICENSE 2.0开源协议您在使用过程中需要注意以下几点
* 1.请不要删除和修改根目录下的LICENSE文件
* 2.请不要删除和修改Snowy源码头部的版权声明
* 3.本项目代码可免费商业使用商业使用请保留源码和相关描述文件的项目出处作者声明等
* 4.分发源码时候请注明软件出处 https://www.xiaonuo.vip
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
export const themeEnum = {
LIGHT: 'light',
DARK: 'dark',
REAL_DARK: 'realDark'
}

View File

@ -1,191 +1,86 @@
<template>
<!-- 经典布局 -->
<a-layout v-if="layout === 'classical'">
<a-layout-sider
v-if="!isMobile"
v-model:collapsed="menuIsCollapse"
:trigger="null"
collapsible
:theme="sideTheme"
width="210"
>
<header id="snowyHeaderLogo" class="snowy-header-logo">
<div class="snowy-header-left">
<div class="logo-bar">
<img class="logo" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
<span>{{ sysBaseConfig.SNOWY_SYS_NAME }}</span>
</div>
</div>
</header>
<div :class="menuIsCollapse ? 'admin-ui-side isCollapse' : 'admin-ui-side'">
<div class="admin-ui-side-scroll">
<a-menu
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
:theme="sideTheme"
mode="inline"
@select="onSelect"
@openChange="onOpenChange"
>
<NavMenu :nav-menus="menu" />
</a-menu>
</div>
</div>
</a-layout-sider>
<!-- 手机端情况下的左侧菜单 -->
<Side-m v-if="isMobile" />
<!-- 右侧布局 -->
<a-layout>
<div id="snowyHeader" class="snowy-header">
<div class="snowy-header-left" style="padding-left: 0px">
<div v-if="!isMobile" class="panel-item hidden-sm-and-down" @click="menuIsCollapseClick">
<MenuUnfoldOutlined v-if="menuIsCollapse" />
<MenuFoldOutlined v-else />
</div>
<moduleMenu v-if="moduleMenuShow" @switchModule="switchModule" />
<top-bar v-if="!isMobile && breadcrumbOpen" />
</div>
<div class="snowy-header-right">
<user-bar />
</div>
</div>
<!-- 多标签 -->
<Tags v-if="!isMobile && layoutTagsOpen" />
<a-layout-content class="main-content-wrapper">
<div id="admin-ui-main" class="admin-ui-main">
<router-view v-slot="{ Component }">
<keep-alive :include="kStore.keepLiveRoute">
<component :is="Component" v-if="kStore.routeShow" :key="route.name" />
</keep-alive>
</router-view>
<iframe-view />
<div class="main-bottom-wrapper">
<a style="color: #a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a>
</div>
</div>
</a-layout-content>
</a-layout>
</a-layout>
<ClassicalMenu
v-if="layout === layoutEnum.CLASSICAL"
:layout="layout"
:isMobile="isMobile"
:menuIsCollapse="menuIsCollapse"
:sideTheme="sideTheme"
:sysBaseConfig="sysBaseConfig"
:openKeys="openKeys"
:selectedKeys="selectedKeys"
:menu="menu"
:breadcrumbOpen="breadcrumbOpen"
:layoutTagsOpen="layoutTagsOpen"
:kStore="kStore"
:footerCopyrightOpen="footerCopyrightOpen"
:moduleMenuShow="moduleMenuShow"
@onSelect="onSelect"
@onOpenChange="onOpenChange"
@switchModule="switchModule"
@menuIsCollapseClick="menuIsCollapseClick"
/>
<!-- 双排菜单布局 -->
<a-layout v-else-if="layout === 'doublerow'">
<a-layout-sider v-if="!isMobile" width="80" :theme="sideTheme" :trigger="null" collapsible>
<header id="snowyHeaderLogo" class="snowy-header-logo">
<div class="snowy-header-left">
<div class="logo-bar">
<router-link to="/">
<img class="logo" :title="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
</router-link>
</div>
</div>
</header>
<a-menu
v-model:selectedKeys="doublerowSelectedKey"
:theme="sideTheme"
class="snowy-doublerow-layout-menu"
v-for="item in menu"
:key="item.path"
>
<a-menu-item
:key="item.path"
style="
text-align: center;
border-radius: 2px;
height: auto;
line-height: 20px;
flex: none;
display: block;
padding: 12px 0 !important;
"
@click="showMenu(item)"
v-if="!item.meta.hidden"
>
<a v-if="item.meta && item.meta.type === 'link'" :href="item.path" target="_blank" @click.stop="() => {}" />
<template #icon>
<component :is="item.meta.icon" style="padding-left: 10px" />
</template>
<div class="snowy-doublerow-layout-menu-item-fort-div">
<span class="snowy-doublerow-layout-menu-item-fort-div-span">
{{ item.meta.title }}
</span>
</div>
</a-menu-item>
</a-menu>
</a-layout-sider>
<a-layout-sider
v-if="!isMobile"
v-show="layoutSiderDowbleMenu"
v-model:collapsed="menuIsCollapse"
:trigger="null"
width="170"
collapsible
:theme="secondMenuSideTheme"
>
<div v-if="!menuIsCollapse" id="snowyDoublerowSideTop" class="snowy-doublerow-side-top">
<h2 class="snowy-title">{{ pMenu.meta.title }}</h2>
</div>
<a-menu
v-model:collapsed="menuIsCollapse"
v-model:openKeys="openKeys"
v-model:selectedKeys="selectedKeys"
mode="inline"
:theme="secondMenuSideTheme"
@select="onSelect"
>
<NavMenu :nav-menus="nextMenu" />
</a-menu>
</a-layout-sider>
<!-- 手机端情况下的左侧菜单 -->
<Side-m v-if="isMobile" />
<a-layout>
<div id="snowyHeader" class="snowy-header">
<div class="snowy-header-left" style="padding-left: 0px">
<moduleMenu v-if="moduleMenuShow" @switchModule="switchModule" />
<top-bar v-if="!isMobile && breadcrumbOpen" />
</div>
<div class="snowy-header-right">
<user-bar />
</div>
</div>
<!-- 多标签 -->
<Tags v-if="!isMobile && layoutTagsOpen" />
<a-layout-content class="main-content-wrapper">
<div id="admin-ui-main" class="admin-ui-main">
<router-view v-slot="{ Component }">
<keep-alive :include="kStore.keepLiveRoute">
<component :is="Component" v-if="kStore.routeShow" :key="route.name" />
</keep-alive>
</router-view>
<iframe-view />
<div class="main-bottom-wrapper">
<a style="color: #a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a>
</div>
</div>
</a-layout-content>
</a-layout>
</a-layout>
<DoubleRowMenu
v-else-if="layout === layoutEnum.DOUBLEROW"
:layout="layout"
:isMobile="isMobile"
:sideTheme="sideTheme"
:secondMenuSideTheme="secondMenuSideTheme"
:sysBaseConfig="sysBaseConfig"
:openKeys="openKeys"
:selectedKeys="selectedKeys"
:menuIsCollapse="menuIsCollapse"
:doublerowSelectedKey="doublerowSelectedKey"
:menu="menu"
:nextMenu="nextMenu"
:breadcrumbOpen="breadcrumbOpen"
:layoutTagsOpen="layoutTagsOpen"
:layoutSiderDowbleMenu="layoutSiderDowbleMenu"
:kStore="kStore"
:footerCopyrightOpen="footerCopyrightOpen"
:moduleMenuShow="moduleMenuShow"
@onSelect="onSelect"
@switchModule="switchModule"
@showMenu="showMenu"
/>
<!-- 顶部菜单布局 -->
<TopMenu
v-else-if="layout === layoutEnum.TOP"
:layout="layout"
:menuList="menuList"
:menu="menu"
:sysBaseConfig="sysBaseConfig"
:moduleMenuShow="moduleMenuShow"
:openKeys="openKeys"
:selectedKeys="selectedKeys"
:breadcrumbOpen="breadcrumbOpen"
:footerCopyrightOpen="footerCopyrightOpen"
:sideTheme="sideTheme"
:isMobile="isMobile"
:kStore="kStore"
:layoutTagsOpen="layoutTagsOpen"
@switchModule="switchModule"
@onOpenChange="onOpenChange"
@onSelect="onSelect"
/>
<!-- 退出最大化 -->
<div class="main-maximize-exit" @click="exitMaximize">
<fullscreen-exit-outlined style="color: #fff" />
<fullscreen-exit-outlined class="xn-color-fff" />
</div>
</template>
<script setup>
import UserBar from '@/layout/components/userbar.vue'
import Tags from '@/layout/components/tags.vue'
import SideM from '@/layout/components/sideM.vue'
import NavMenu from '@/layout/components/NavMenu.vue'
import ModuleMenu from '@/layout/components/moduleMenu.vue'
import IframeView from '@/layout/components/iframeView.vue'
import TopBar from '@/layout/components/topbar.vue'
import { globalStore, keepAliveStore } from '@/store'
import { ThemeModeEnum } from '@/utils/enum'
import { themeEnum } from '@/layout/enum/themeEnum'
import { layoutEnum } from '@/layout/enum/layoutEnum'
import { useRoute, useRouter } from 'vue-router'
import tool from '@/utils/tool'
import { message } from 'ant-design-vue'
import ClassicalMenu from '@/layout/menu/classicalMenu.vue'
import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue'
import TopMenu from '@/layout/menu/topMenu.vue'
const store = globalStore()
const kStore = keepAliveStore()
@ -197,10 +92,11 @@
const selectedKeys = ref([])
const openKeys = ref([])
const onSelectTag = ref(false)
const moduleMenuData = ref([])
const moduleMenu = ref([])
const moduleMenuShow = ref(true)
const doublerowSelectedKey = ref([])
const layoutSiderDowbleMenu = ref(true)
const menuList = ref([])
// computed - start
const layout = computed(() => {
return store.layout
@ -214,6 +110,9 @@
const theme = computed(() => {
return store.theme
})
const themeColor = computed(() => {
return store.themeColor
})
const layoutTagsOpen = computed(() => {
// keepAlive
if (!store.layoutTagsOpen) {
@ -224,6 +123,9 @@
const breadcrumbOpen = computed(() => {
return store.breadcrumbOpen
})
const fixedWidth = computed(() => {
return store.fixedWidth
})
const topHeaderThemeColorOpen = computed(() => {
return store.topHeaderThemeColorOpen
})
@ -233,6 +135,9 @@
const sideUniqueOpen = computed(() => {
return store.sideUniqueOpen
})
const footerCopyrightOpen = computed(() => {
return store.footerCopyrightOpen
})
const sysBaseConfig = computed(() => {
return store.sysBaseConfig
})
@ -240,10 +145,13 @@
return store.module
})
const sideTheme = computed(() => {
return theme.value === ThemeModeEnum.REAL_DARK ? ThemeModeEnum.DARK : theme.value
return theme.value === themeEnum.REAL_DARK ? themeEnum.DARK : theme.value
})
const secondMenuSideTheme = computed(() => {
return theme.value === ThemeModeEnum.REAL_DARK ? ThemeModeEnum.DARK : ThemeModeEnum.LIGHT
return theme.value === themeEnum.REAL_DARK ? themeEnum.DARK : themeEnum.LIGHT
})
const roundedCornerStyleOpen = computed(() => {
return store.roundedCornerStyleOpen
})
//
const showThis = () => {
@ -266,14 +174,14 @@
}
const nextTickMenu = pMenu.value.children
if (pidKey) {
const modelPidKey = getParentKeys(moduleMenuData.value, route.path)
moduleMenuData.value.forEach((item) => {
const modelPidKey = getParentKeys(moduleMenu.value, route.path)
moduleMenu.value.forEach((item) => {
if (modelPidKey.includes(item.path)) {
tagSwitchModule(item.id)
}
})
const parentPath = pidKey[pidKey.length - 1]
if (layout.value === 'doublerow') {
if (layout.value === layoutEnum.DOUBLEROW) {
//
const nextMenuTemp = nextTickMenu.filter((item) => item.path === parentPath)[0].children
if (nextMenuTemp) {
@ -285,14 +193,14 @@
openKeys.value = pidKey
}
//
if (layout.value === 'doublerow') {
if (layout.value === layoutEnum.DOUBLEROW) {
setDoubleRowSelectedKey()
}
})
}
// -start
moduleMenuData.value = router.getMenu()
moduleMenu.value = router.getMenu()
//
const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID')
if (menuModuleId) {
@ -307,13 +215,86 @@
menu.value = router.getMenu()[0].children
}
showThis()
onMounted(() => {
onLayoutResize()
window.addEventListener('resize', onLayoutResize)
window.addEventListener('resize', getNav)
switchoverTopHeaderThemeColor()
settingTopHeaderThemeOrColor(theme.value, layout.value)
settingFixedWidth()
nextTick(() => {
getNav(menu.value)
})
})
watch(route, (newValue) => {
//
const getNav = (items) => {
const item = menu.value
//
if (layout.value !== 'top') return
const menuNavList = menu.value
menuList.value = menuNavList
nextTick(() => {
//
let liArr = document.querySelector('#topHeaderMenu').querySelectorAll('li')
let allWidth = document.querySelector('#xn-line-nav').offsetWidth //
//
let num = 0
let startIndex = 0
for (const [index, item] of liArr.entries()) {
num += item.offsetWidth
if (num > allWidth) {
startIndex = index - 1
break
}
}
//
if (num < allWidth) {
menuList.value = menuNavList
return
}
//
const showNav = menuNavList.slice(0, startIndex)
const hiddenNav = menuNavList.slice(startIndex, menuNavList.length)
menuList.value = showNav
menuList.value.push({
meta: {
icon: 'rightCircle-outlined',
title: '更多',
type: 'catalog'
},
children: hiddenNav
})
})
}
//
const handleMouseWheel = (event) => {
let element = document.querySelector('#xn-line-nav')
let element2 = document.querySelector('#topHeaderMenu')
//
let delta = event.deltaY
//
const num = 20
//
let leftMove = Number(element2.style.left.slice(0, -2))
//
let remove = element.offsetWidth - element2.scrollWidth
//
//
if (delta < 0 && leftMove > remove) {
element2.style.left = leftMove - num + 'px'
} else if (delta > 0 && leftMove < 0) {
//
// 0
element2.style.left = leftMove + num + 'px'
}
}
watch(route, () => {
//
selectedKeys.value = []
showThis()
@ -321,50 +302,128 @@
//
watch(layout, (newValue) => {
document.body.setAttribute('data-layout', newValue)
if (newValue.includes('doublerow')) {
if (newValue.includes(layoutEnum.DOUBLEROW)) {
showThis()
setDoubleRowSelectedKey()
}
nextTick(() => {
//
switchoverTopHeaderThemeColor()
// top
settingTopHeaderThemeOrColor(theme.value, newValue)
getNav(menu.value)
settingFixedWidth()
let element = document.querySelector('#xn-line-nav')
if (element) {
element.addEventListener('mousewheel', handleMouseWheel, false)
}
})
})
watch(topHeaderThemeColorOpen, () => {
switchoverTopHeaderThemeColor()
})
watch(fixedWidth, () => {
settingFixedWidth()
})
watch(layoutTagsOpen, () => {
settingFixedWidth()
})
watch(breadcrumbOpen, () => {
settingFixedWidth()
})
watch(topHeaderThemeColorSpread, () => {
switchoverTopHeaderThemeColor()
})
watch(theme, (newValue) => {
settingTopHeaderThemeOrColor(newValue, layout.value)
})
watch(themeColor, () => {
settingTopHeaderThemeOrColor(theme.value, layout.value)
})
watch(topHeaderThemeColorOpen, (newValue) => {
switchoverTopHeaderThemeColor()
const header = document.getElementById('snowyHeader')
const topHeaderMenu = document.getElementById('topHeaderMenu')
if (layout.value === layoutEnum.TOP) {
if (newValue) {
header.classList.add('top-snowy-header-layout')
topHeaderMenu.classList.add('top-snowy-header-layout')
} else {
header.classList.remove('top-snowy-header-layout')
topHeaderMenu.classList.remove('top-snowy-header-layout')
}
}
})
watch(topHeaderThemeColorSpread, (newValue) => {
switchoverTopHeaderThemeColor()
watch(roundedCornerStyleOpen, () => {
settingTopHeaderThemeOrColor(theme.value, layout.value)
})
//
const settingFixedWidth = () => {
nextTick(() => {
const breadcrumbWidth = document.querySelector('.admin-ui-breadcrumb')
const showWidth = document.querySelector('.snowy-tags')
const mainWidth = document.querySelector('.ant-layout-content')
if (fixedWidth.value && layout.value === layoutEnum.TOP) {
breadcrumbWidth?.classList.add('xn-mg050')
showWidth?.classList.add('xn-mg050')
mainWidth?.classList.add('xn-pd1180')
} else {
breadcrumbWidth?.classList.remove('xn-mg050')
showWidth?.classList.remove('xn-mg050')
mainWidth?.classList.remove('xn-pd1180')
}
})
}
//
const settingTopHeaderThemeOrColor = (theme, layout) => {
const header = document.getElementById('snowyHeader')
const topHeaderMenu = document.getElementById('topHeaderMenu')
if (topHeaderThemeColorOpen.value && layout === layoutEnum.TOP) {
nextTick(() => {
topHeaderMenu.classList.add('top-snowy-header-layout')
header.classList.add('top-snowy-header-layout')
})
} else if (!topHeaderThemeColorOpen.value && layout === layoutEnum.TOP) {
nextTick(() => {
topHeaderMenu.classList.remove('top-snowy-header-layout')
header.classList.remove('top-snowy-header-layout')
})
}
if (theme === themeEnum.LIGHT && layout === layoutEnum.TOP) {
header.classList.remove('top-snowy-header')
header.classList.add('top-snowy-header-light')
} else {
header.classList.remove('top-snowy-header-light')
if (layout === layoutEnum.TOP) {
header.classList.add('top-snowy-header')
} else {
if (theme === themeEnum.REAL_DARK) {
header.classList.add('top-snowy-header')
} else {
header.classList.remove('top-snowy-header')
}
}
}
}
const menuIsCollapseClick = () => {
store.toggleConfig('menuIsCollapse')
}
//
const switchoverTopHeaderThemeColor = () => {
//
const header = document.getElementById('snowyHeader')
topHeaderThemeColorOpen.value
? header.classList.add('snowy-header-primary-color')
: header.classList.remove('snowy-header-primary-color')
//
const headerLogin = document.getElementById('snowyHeaderLogo')
try {
//
const header = document.getElementById('snowyHeader')
topHeaderThemeColorOpen.value
? header.classList.add('snowy-header-primary-color')
: header.classList.remove('snowy-header-primary-color')
//
const headerLogin = document.getElementById('snowyHeaderLogo')
topHeaderThemeColorSpread.value
? headerLogin.classList.add('snowy-header-logo-primary-color')
: headerLogin.classList.remove('snowy-header-logo-primary-color')
// eslint-disable-next-line no-empty
} catch (e) {}
//
if (layout.value === 'doublerow') {
const snowyDoublerowSideTop = document.getElementById('snowyDoublerowSideTop')
try {
topHeaderThemeColorSpread.value
? snowyDoublerowSideTop.classList.add('snowy-doublerow-side-top-primary-color')
: snowyDoublerowSideTop.classList.remove('snowy-doublerow-side-top-primary-color')
// eslint-disable-next-line no-empty
} catch (e) {}
}
}
//
@ -447,7 +506,7 @@
layoutSiderDowbleMenu.value = false
}
}
if (layout.value === 'doublerow') {
if (layout.value === layoutEnum.DOUBLEROW) {
doublerowSelectedKey.value = [route.path]
}
}
@ -466,9 +525,9 @@
}
//
const switchModule = (id) => {
if (moduleMenuData.value.length > 0) {
if (moduleMenu.value.length > 0) {
showThis()
const menus = moduleMenuData.value.filter((item) => item.id === id)[0].children
const menus = moduleMenu.value.filter((item) => item.id === id)[0].children
if (menus.length > 0) {
//
menu.value = menus
@ -487,6 +546,7 @@
message.warning('该模块下无任何菜单')
}
}
getNav(menu.value)
}
//
const tagSwitchModule = (id) => {
@ -494,7 +554,7 @@
tool.data.set('SNOWY_MENU_MODULE_ID', id)
store.setModule(id)
//
menu.value = moduleMenuData.value.filter((item) => item.id === id)[0].children
menu.value = moduleMenu.value.filter((item) => item.id === id)[0].children
}
//
const traverseChild = (menu) => {
@ -519,3 +579,46 @@
})
}
</script>
<style lang="less" scoped>
.xn-color-fff {
color: #fff;
}
.xn-pdl25 {
padding-left: 11px;
}
.xn-menu-line {
text-align: center;
height: auto;
line-height: 20px;
flex: none;
display: block;
padding: 12px 0 !important;
}
.xn-navmenu-line {
min-width: 0;
flex: 1 1 0%;
// padding: 0 20px;
overflow: hidden;
}
.xn-bb0 {
border-bottom: none;
position: relative;
}
.ant-layout-content {
display: flex;
flex-direction: column;
}
.xn-pd1180 {
padding: 10px 150px 0 150px;
}
.xn-pd050 {
padding: 0 50px;
}
.xn-pl10 {
padding-left: 10px;
}
.xn-mg050 {
margin: 0px 150px;
}
</style>

View File

@ -0,0 +1,154 @@
<template>
<a-layout>
<a-layout-sider
v-if="!isMobile"
:collapsed="menuIsCollapse"
:trigger="null"
collapsible
:theme="sideTheme"
width="210"
>
<header id="snowyHeaderLogo" class="snowy-header-logo">
<div class="snowy-header-left">
<div class="logo-bar">
<img class="logo" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
<span>{{ sysBaseConfig.SNOWY_SYS_NAME }}</span>
</div>
</div>
</header>
<div :class="menuIsCollapse ? 'admin-ui-side isCollapse' : 'admin-ui-side'">
<div class="admin-ui-side-scroll">
<a-menu
v-bind:openKeys="openKeys"
v-bind:selectedKeys="selectedKeys"
:theme="sideTheme"
mode="inline"
@select="onSelect"
@openChange="onOpenChange"
>
<NavMenu :nav-menus="menu" />
</a-menu>
</div>
</div>
</a-layout-sider>
<!-- 手机端情况下的左侧菜单 -->
<Side-m v-if="isMobile" />
<!-- 右侧布局 -->
<a-layout>
<div id="snowyHeader" class="snowy-header">
<div class="snowy-header-left xn-pl0">
<div v-if="!isMobile" class="panel-item hidden-sm-and-down" @click="menuIsCollapseClick">
<MenuUnfoldOutlined v-if="menuIsCollapse" />
<MenuFoldOutlined v-else />
</div>
<moduleMenu v-if="moduleMenuShow" @switchModule="switchModule" />
</div>
<div class="snowy-header-right">
<user-bar />
</div>
</div>
<Breadcrumb v-if="!isMobile && breadcrumbOpen" />
<!-- 多标签 -->
<Tags v-if="!isMobile && layoutTagsOpen" />
<a-layout-content class="main-content-wrapper">
<div id="admin-ui-main" class="admin-ui-main">
<router-view v-slot="{ Component }">
<keep-alive :include="kStore.keepLiveRoute">
<component :is="Component" v-if="kStore.routeShow" :key="route.name" />
</keep-alive>
</router-view>
<iframe-view />
<div v-if="footerCopyrightOpen" class="main-bottom-wrapper">
<a class="xn-color-a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a>
</div>
</div>
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
import UserBar from '@/layout/components/userbar.vue'
import Tags from '@/layout/components/tags.vue'
import SideM from '@/layout/components/sideM.vue'
import NavMenu from '@/layout/components/NavMenu.vue'
import ModuleMenu from '@/layout/components/moduleMenu.vue'
import IframeView from '@/layout/components/iframeView.vue'
import Breadcrumb from '@/layout/components/breadcrumb.vue'
const props = defineProps({
layout: { type: String }, //
isMobile: { type: Boolean }, //
menuIsCollapse: { type: Boolean }, //
sideTheme: { type: String },
sysBaseConfig: { type: Object },
openKeys: { type: Array },
selectedKeys: { type: Array },
menu: { type: Array }, //
breadcrumbOpen: { type: Boolean }, //
layoutTagsOpen: { type: Boolean },
kStore: { type: Object }, //
footerCopyrightOpen: { type: Boolean }, //
moduleMenuShow: { type: Boolean }
})
const emit = defineEmits(['onSelect', 'onOpenChange', 'switchModule', 'menuIsCollapseClick'])
const onSelect = (obj) => {
emit('onSelect', obj)
}
const onOpenChange = (keys) => {
emit('onOpenChange', keys)
}
const switchModule = (id) => {
emit('switchModule', id)
}
const menuIsCollapseClick = () => {
emit('menuIsCollapseClick')
}
</script>
<style lang="less" scoped>
.xn-color-fff {
color: #fff;
}
.xn-pdl25 {
padding-left: 11px;
}
.xn-menu-line {
text-align: center;
height: auto;
line-height: 20px;
flex: none;
display: block;
padding: 12px 0 !important;
}
.xn-navmenu-line {
min-width: 0;
flex: 1 1 0%;
overflow: hidden;
}
.xn-bb0 {
border-bottom: none;
position: relative;
}
.ant-layout-content {
display: flex;
flex-direction: column;
}
.xn-pd1180 {
padding: 10px 150px 0 150px;
}
.xn-pd050 {
padding: 0 50px;
}
.xn-pl10 {
padding-left: 10px;
}
.xn-mg050 {
margin: 0px 150px;
}
</style>

View File

@ -0,0 +1,185 @@
<template>
<a-layout>
<a-layout-sider v-if="!isMobile" width="80" :theme="sideTheme" :trigger="null" collapsible>
<header id="snowyHeaderLogo" class="snowy-header-logo">
<div class="snowy-header-left">
<div class="logo-bar">
<router-link to="/">
<img class="logo" :title="sysBaseConfig.SNOWY_SYS_NAME" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
</router-link>
</div>
</div>
</header>
<a-menu
:selectedKeys="doublerowSelectedKey"
:theme="sideTheme"
class="snowy-doublerow-layout-menu"
v-for="item in menu"
:key="item.path"
>
<a-menu-item
:key="item.path"
style="
text-align: center;
height: auto;
line-height: 20px;
flex: none;
display: block;
padding: 12px 0 !important;
"
@click="showMenu(item)"
v-if="!item.meta.hidden"
>
<a v-if="item.meta && item.meta.type === 'link'" :href="item.path" target="_blank" @click.stop="() => {}" />
<template #icon>
<component :is="item.meta.icon" class="xn-pl10" />
</template>
<div class="snowy-doublerow-layout-menu-item-fort-div">
<span class="snowy-doublerow-layout-menu-item-fort-div-span">
{{ item.meta.title }}
</span>
</div>
</a-menu-item>
</a-menu>
</a-layout-sider>
<!-- 手机端情况下的左侧菜单 -->
<Side-m v-if="isMobile" />
<a-layout>
<div id="snowyHeader" class="snowy-header">
<div class="snowy-header-left xn-pl0">
<moduleMenu v-if="moduleMenuShow" @switchModule="switchModule" />
</div>
<div class="snowy-header-right">
<user-bar />
</div>
</div>
<a-layout>
<a-layout-sider
v-if="!isMobile"
v-show="layoutSiderDowbleMenu"
:collapsed="menuIsCollapse"
:trigger="null"
width="170"
collapsible
:theme="secondMenuSideTheme"
>
<a-menu
:collapsed="menuIsCollapse"
:openKeys="openKeys"
:selectedKeys="selectedKeys"
mode="inline"
:theme="secondMenuSideTheme"
@select="onSelect"
>
<NavMenu :nav-menus="nextMenu" />
</a-menu>
</a-layout-sider>
<a-layout-content>
<breadcrumb v-if="!isMobile && breadcrumbOpen" />
<!-- 多标签 -->
<Tags v-if="!isMobile && layoutTagsOpen" />
<div class="main-content-wrapper">
<div id="admin-ui-main" class="admin-ui-main">
<router-view v-slot="{ Component }">
<keep-alive :include="kStore.keepLiveRoute">
<component :is="Component" v-if="kStore.routeShow" :key="route.name" />
</keep-alive>
</router-view>
<iframe-view />
<div v-if="footerCopyrightOpen" class="main-bottom-wrapper">
<a class="xn-color-a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a>
</div>
</div>
</div>
</a-layout-content>
</a-layout>
</a-layout>
</a-layout>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
import UserBar from '@/layout/components/userbar.vue'
import Tags from '@/layout/components/tags.vue'
import SideM from '@/layout/components/sideM.vue'
import NavMenu from '@/layout/components/NavMenu.vue'
import ModuleMenu from '@/layout/components/moduleMenu.vue'
import IframeView from '@/layout/components/iframeView.vue'
import Breadcrumb from '@/layout/components/breadcrumb.vue'
const props = defineProps({
layout: { type: String }, //
isMobile: { type: Boolean }, //
sideTheme: { type: String },
menuIsCollapse: {},
sysBaseConfig: { type: Object },
openKeys: { type: Array },
selectedKeys: { type: Array },
doublerowSelectedKey: { type: Array },
nextMenu: { type: Array },
menu: { type: Array }, //
breadcrumbOpen: { type: Boolean }, //
layoutTagsOpen: { type: Boolean },
layoutSiderDowbleMenu: { type: Boolean },
kStore: { type: Object }, //
footerCopyrightOpen: { type: Boolean }, //
moduleMenuShow: { type: Boolean },
secondMenuSideTheme: {}
})
const emit = defineEmits(['onSelect', 'switchModule', 'showMenu'])
const onSelect = (obj) => {
emit('onSelect', obj)
}
const switchModule = (id) => {
emit('switchModule', id)
}
const showMenu = (route) => {
emit('showMenu', route)
}
</script>
<style lang="less" scoped>
.xn-color-fff {
color: #fff;
}
.xn-pdl25 {
padding-left: 11px;
}
.xn-menu-line {
text-align: center;
height: auto;
line-height: 20px;
flex: none;
display: block;
padding: 12px 0 !important;
}
.xn-navmenu-line {
min-width: 0;
flex: 1 1 0%;
overflow: hidden;
}
.xn-bb0 {
border-bottom: none;
position: relative;
}
.ant-layout-content {
display: flex;
flex-direction: column;
}
.xn-pd1180 {
padding: 10px 150px 0 150px;
}
.xn-pd050 {
padding: 0 50px;
}
.xn-pl10 {
padding-left: 10px;
}
.xn-mg050 {
margin: 0px 150px;
}
</style>

View File

@ -0,0 +1,138 @@
<template>
<a-layout>
<a-layout class="layout">
<div id="snowyHeader" class="snowy-header top-snowy-header xn-pd050">
<div class="snowy-header-left xn-pl0">
<header id="snowyHeaderLogo" class="snowy-header-logo">
<div class="snowy-header-left">
<div class="logo-bar">
<img class="logo" :src="sysBaseConfig.SNOWY_SYS_LOGO" />
<span>{{ sysBaseConfig.SNOWY_SYS_NAME }}</span>
</div>
</div>
</header>
</div>
<moduleMenu v-if="moduleMenuShow" @switchModule="switchModule" class="xn-pdl25" />
<div class="xn-navmenu-line" id="xn-line-nav">
<a-menu
class="xn-bb0"
id="topHeaderMenu"
:selectedKeys="selectedKeys"
:theme="sideTheme"
mode="horizontal"
@select="onSelect"
@openChange="onOpenChange"
collapsed="true"
>
<NavMenu :nav-menus="menuList" />
</a-menu>
</div>
<div class="snowy-header-right">
<user-bar />
</div>
</div>
<!-- 手机端情况下的左侧菜单 -->
<Side-m v-if="isMobile" />
<breadcrumb v-if="!isMobile && breadcrumbOpen" />
<!-- 多标签 -->
<Tags v-if="!isMobile && layoutTagsOpen" />
<a-layout-content class="main-content-wrapper">
<div id="admin-ui-main" class="admin-ui-main">
<router-view v-slot="{ Component }">
<keep-alive :include="kStore.keepLiveRoute">
<component :is="Component" v-if="kStore.routeShow" :key="route.name" />
</keep-alive>
</router-view>
<iframe-view />
<div v-if="footerCopyrightOpen" class="main-bottom-wrapper">
<a class="xn-color-a0a0a0" :href="sysBaseConfig.SNOWY_SYS_COPYRIGHT_URL" target="_blank">{{
sysBaseConfig.SNOWY_SYS_COPYRIGHT
}}</a>
</div>
</div>
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
import UserBar from '@/layout/components/userbar.vue'
import Tags from '@/layout/components/tags.vue'
import SideM from '@/layout/components/sideM.vue'
import NavMenu from '@/layout/components/NavMenu.vue'
import ModuleMenu from '@/layout/components/moduleMenu.vue'
import IframeView from '@/layout/components/iframeView.vue'
import Breadcrumb from '@/layout/components/breadcrumb.vue'
const props = defineProps({
layout: {},
menu: { type: Array }, //
menuList: { type: Array }, //
sysBaseConfig: { type: Object },
moduleMenuShow: { type: Boolean },
selectedKeys: { type: Array },
openKeys: { type: Array },
sideTheme: { type: String },
isMobile: { type: Boolean }, //
breadcrumbOpen: { type: Boolean }, //
layoutTagsOpen: { type: Boolean },
layoutSiderDowbleMenu: { type: Boolean },
kStore: { type: Object }, //
footerCopyrightOpen: { type: Boolean } //
})
const emit = defineEmits(['onSelect', 'switchModule', 'onOpenChange'])
const onSelect = (obj) => {
emit('onSelect', obj)
}
const switchModule = (id) => {
emit('switchModule', id)
}
const onOpenChange = (keys) => {
emit('onOpenChange', keys)
}
</script>
<style lang="less" scoped>
.xn-color-fff {
color: #fff;
}
.xn-pdl25 {
padding-left: 11px;
}
.xn-menu-line {
text-align: center;
height: auto;
line-height: 20px;
flex: none;
display: block;
padding: 12px 0 !important;
}
.xn-navmenu-line {
min-width: 0;
flex: 1 1 0%;
overflow: hidden;
}
.xn-bb0 {
border-bottom: none;
position: relative;
}
.ant-layout-content {
display: flex;
flex-direction: column;
}
.xn-pd1180 {
padding: 10px 150px 0 150px;
}
.xn-pd050 {
padding: 0 50px;
}
.xn-pl10 {
padding-left: 10px;
}
.xn-mg050 {
margin: 0px 150px;
}
</style>

View File

@ -27,20 +27,12 @@ const getCacheConfig = (value) => {
return data
}
/**
* deprecated 请使用 useGlobalStore
*/
// deprecated 请使用 useGlobalStore
export const globalStore = defineStore('global', () => {
// 利用Vue3组合式APIref()定义state的属性
// function() 定义actions
// computed 定义getters
// 定义state
// 移动端布局
const isMobile = ref(false)
// 布局
const layout = ref(getCacheConfig('SNOWY_LAYOUT'))
// 菜单是否折叠 toggle
const menuIsCollapse = ref(getCacheConfig('SNOWY_MENU_COLLAPSE'))
// 侧边菜单是否排他展开
@ -49,17 +41,24 @@ export const globalStore = defineStore('global', () => {
const layoutTagsOpen = ref(getCacheConfig('SNOWY_LAYOUT_TAGS_OPEN'))
// 是否展示面包屑
const breadcrumbOpen = ref(getCacheConfig('SNOWY_BREADCRUMD_OPEN'))
// 是否开启固定宽度(顶栏菜单)
const fixedWidth = ref(getCacheConfig('SNOWY_FIXEDWIDTH_OPEN'))
// 顶栏是否应用主题色
const topHeaderThemeColorOpen = ref(getCacheConfig('SNOWY_TOP_HEADER_THEME_COLOR_OPEN'))
// 顶栏主题色通栏
const topHeaderThemeColorSpread = ref(getCacheConfig('SNOWY_TOP_HEADER_THEME_COLOR_SPREAD'))
// 登录用户水印
const loginUserWatermarkOpen = ref(getCacheConfig('SNOWY_LOGIN_USER_WATERMARK_OPEN'))
// 页脚版权信息
const footerCopyrightOpen = ref(getCacheConfig('SNOWY_FOOTER_COPYRIGHT_OPEN'))
// 模块坞
const moduleUnfoldOpen = ref(getCacheConfig('SNOWY_MODULE_UNFOLD_OPEN'))
// 主题
const theme = ref(getCacheConfig('SNOWY_THEME'))
// 主题颜色
const themeColor = ref(toolDataGet('SNOWY_THEME_COLOR') || config.COLOR)
// 圆角分格
const roundedCornerStyleOpen = ref(getCacheConfig('SNOWY_ROUNDED_CORNER_STYLE_OPEN'))
// 整体表单风格
const formStyle = ref(getCacheConfig('SNOWY_FORM_STYLE'))
// 用户信息
@ -107,12 +106,24 @@ export const globalStore = defineStore('global', () => {
case 'breadcrumbOpen':
breadcrumbOpen.value = !breadcrumbOpen.value
break
case 'fixedWidth':
fixedWidth.value = !fixedWidth.value
break
case 'topHeaderThemeColorOpen':
topHeaderThemeColorOpen.value = !topHeaderThemeColorOpen.value
topHeaderThemeColorSpread.value = topHeaderThemeColorOpen.value
? topHeaderThemeColorSpread.value
: topHeaderThemeColorOpen.value
break
case 'loginUserWatermarkOpen':
loginUserWatermarkOpen.value = !loginUserWatermarkOpen.value
break
case 'footerCopyrightOpen':
footerCopyrightOpen.value = !footerCopyrightOpen.value
break
case 'roundedCornerStyleOpen':
roundedCornerStyleOpen.value = !roundedCornerStyleOpen.value
break
case 'moduleUnfoldOpen':
moduleUnfoldOpen.value = !moduleUnfoldOpen.value
break
@ -137,11 +148,15 @@ export const globalStore = defineStore('global', () => {
sideUniqueOpen,
layoutTagsOpen,
breadcrumbOpen,
fixedWidth,
topHeaderThemeColorOpen,
topHeaderThemeColorSpread,
loginUserWatermarkOpen,
footerCopyrightOpen,
moduleUnfoldOpen,
theme,
themeColor,
roundedCornerStyleOpen,
formStyle,
userInfo,
sysBaseConfig,

View File

@ -1,398 +1,399 @@
@import 'ant-design-vue/es/style/themes/default.less';
:root {
--blue-1: #e6f7ff;
--blue-2: #bae7ff;
--blue-3: #91d5ff;
--blue-4: #69c0ff;
--blue-5: #40a9ff;
--blue-6: #1890ff;
--blue-7: #096dd9;
--blue-8: #0050b3;
--blue-9: #003a8c;
--blue-10: #002766;
--green-1: #f6ffed;
--green-2: #d9f7be;
--green-3: #b7eb8f;
--green-4: #95de64;
--green-5: #73d13d;
--green-6: #52c41a;
--green-7: #389e0d;
--green-8: #237804;
--green-9: #135200;
--green-10: #092b00;
--red-1: #fff1f0;
--red-2: #ffccc7;
--red-3: #ffa39e;
--red-4: #ff7875;
--red-5: #ff4d4f;
--red-6: #f5222d;
--red-7: #cf1322;
--red-8: #a8071a;
--red-9: #820014;
--red-10: #5c0011;
--gold-1: #fffbe6;
--gold-2: #fff1b8;
--gold-3: #ffe58f;
--gold-4: #ffd666;
--gold-5: #ffc53d;
--gold-6: #faad14;
--gold-7: #d48806;
--gold-8: #ad6800;
--gold-9: #874d00;
--gold-10: #613400;
--purple-1: #f9f0ff;
--purple-2: #efdbff;
--purple-3: #d3adf7;
--purple-4: #b37feb;
--purple-5: #9254de;
--purple-6: #722ed1;
--purple-7: #531dab;
--purple-8: #391085;
--purple-9: #22075e;
--purple-10: #120338;
--cyan-1: #e6fffb;
--cyan-2: #b5f5ec;
--cyan-3: #87e8de;
--cyan-4: #5cdbd3;
--cyan-5: #36cfc9;
--cyan-6: #13c2c2;
--cyan-7: #08979c;
--cyan-8: #006d75;
--cyan-9: #00474f;
--cyan-10: #002329;
--pink-1: #fff0f6;
--pink-2: #ffd6e7;
--pink-3: #ffadd2;
--pink-4: #ff85c0;
--pink-5: #f759ab;
--pink-6: #eb2f96;
--pink-7: #c41d7f;
--pink-8: #9e1068;
--pink-9: #780650;
--pink-10: #520339;
--orange-1: #fff7e6;
--orange-2: #ffe7ba;
--orange-3: #ffd591;
--orange-4: #ffc069;
--orange-5: #ffa940;
--orange-6: #fa8c16;
--orange-7: #d46b08;
--orange-8: #ad4e00;
--orange-9: #873800;
--orange-10: #612500;
--primary-1: var(--blue-1);
--primary-2: var(--blue-2);
--primary-3: var(--blue-3);
--primary-4: var(--blue-4);
--primary-5: var(--blue-5);
--primary-6: var(--blue-6);
--primary-7: var(--blue-7);
--primary-8: var(--blue-8);
--primary-9: var(--blue-9);
--primary-10: var(--blue-10);
--primary-color: var(--primary-6);
--primary-color-hover: var(--primary-5);
--primary-color-active: var(--primary-7);
--primary-color-outline: var(--primary-2);
--info-color: var(--primary-color);
--success-color: var(--green-6);
--processing-color: var(--blue-6);
--highlight-color: var(--red-5);
--warning-color: var(--gold-6);
--warning-color-hover: var(--gold-5);
--warning-color-active: var(--gold-7);
--warning-color-outline: var(--gold-2);
--error-color: var(--red-5);
--error-color-hover: var(--red-4);
--error-color-active: var(--red-7);
--error-color-outline: var(--red-2);
--body-background: #fff;
--component-background: #fff;
--popover-background: @component-background;
--popover-customize-border-color: @border-color-split;
--text-color: fade(@black, 85%);
--text-color-secondary: fade(@black, 45%);
--text-color-inverse: @white;
--icon-color-hover: fade(@black, 75%);
--heading-color: fade(@black, 85%);
--item-hover-bg: #f5f5f5;
// Border color
--border-color-base: hsv(0, 0, 85%);
--border-color-split: hsv(0, 0, 94%);
//--border-color-inverse: @white;
//
--background-color-light: hsv(0, 0, 98%);
--background-color-base: hsv(0, 0, 96%);
// Disabled states
--disabled-color: fade(#000, 25%);
--disabled-bg: @background-color-base;
--disabled-color-dark: fade(#fff, 35%);
// Shadow
--shadow-color: rgba(0, 0, 0, 0.15);
--shadow-color-inverse: @component-background;
--box-shadow-base: @shadow-1-down;
--shadow-1-up: 0 -2px 8px @shadow-color;
--shadow-1-down: 0 2px 8px @shadow-color;
--shadow-1-left: -2px 0 8px @shadow-color;
--shadow-1-right: 2px 0 8px @shadow-color;
--shadow-2: 0 4px 12px @shadow-color;
// Buttons
--btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
--btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
--btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
--btn-default-bg: @component-background;
--btn-default-ghost-color: @component-background;
--btn-default-ghost-border: @component-background;
--btn-text-hover-bg: rgba(0, 0, 0, 0.018);
--btn-text-active-bg: rgba(0, 0, 0, 0.028);
// Checkbox
--checkbox-check-bg: @checkbox-check-color;
// Descriptions
--descriptions-bg: #fafafa;
// Divider
--divider-color: rgba(0, 0, 0, 6%);
// Dropdown 有两个
--dropdown-menu-submenu-disabled-bg: @component-background;
// Radio
--radio-dot-disabled-color: fade(@black, 20%);
--radio-solid-checked-color: @component-background;
// Radio buttons
--radio-disabled-button-checked-bg: coverTintMixin(@black, 90%);
--radio-disabled-button-checked-color: @disabled-color;
// Layout
--layout-body-background: #f0f2f5;
--layout-header-background: #001529;
--layout-trigger-background: #002140;
//--layout-sider-background-1: coverTintMixin(#001529, 10%);
// Dropdown 有两个
--dropdown-menu-bg: @component-background;
// Input
--input-placeholder-color: hsv(0, 0, 75%);
--input-icon-color: @input-color;
--input-bg: @component-background;
--input-number-handler-active-bg: #f4f4f4;
--input-icon-hover-color: fade(@black, 85%);
// Mentions
--mentions-dropdown-bg: @component-background;
// Select
--select-dropdown-bg: @component-background;
--select-background: @component-background;
--select-clear-background: @select-background;
--select-selection-item-bg: @background-color-base;
--select-selection-item-border-color: @border-color-split;
--select-multiple-disabled-background: @input-disabled-bg;
--select-multiple-item-disabled-color: #bfbfbf;
--select-multiple-item-disabled-border-color: @select-border-color;
// Cascader
--cascader-bg: @component-background;
--cascader-menu-bg: @component-background;
--cascader-menu-border-color-split: @border-color-split;
// Tooltip
--tooltip-bg: rgba(0, 0, 0, 0.75);
// Popover
--popover-bg: @component-background;
// Modal
--modal-header-bg: @component-background;
--modal-header-border-color-split: @border-color-split;
--modal-content-bg: @component-background;
--modal-footer-border-color-split: @border-color-split;
// Progress
--progress-steps-item-bg: #f3f3f3;
// Menu
--menu-popup-bg: @component-background;
--menu-dark-bg: @layout-header-background;
--menu-dark-inline-submenu-bg: #000c17;
// Table
--table-header-bg: @background-color-light;
--table-header-sort-bg: @background-color-base;
--table-body-sort-bg: #fafafa;
--table-row-hover-bg: @background-color-light;
--table-expanded-row-bg: #fbfbfb;
--table-header-cell-split-color: rgba(0, 0, 0, 0.06);
--table-header-sort-active-bg: rgba(0, 0, 0, 0.04);
--table-header-filter-active-bg: rgba(0, 0, 0, 0.04);
--table-filter-btns-bg: inherit;
--table-filter-dropdown-bg: @component-background;
--table-expand-icon-bg: @component-background;
// TimePicker
--picker-bg: @component-background;
--picker-basic-cell-disabled-bg: @disabled-bg;
--picker-border-color: @border-color-split;
// Calendar
--calendar-bg: @component-background;
--calendar-input-bg: @input-bg;
--calendar-border-color: @border-color-inverse;
--calendar-full-bg: @calendar-bg;
// Badge
--badge-text-color: @component-background;
// Rate
--rate-star-bg: @border-color-split;
// Card
--card-actions-background: @component-background;
--card-skeleton-bg: #cfd8dc;
--card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16),
0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09);
// Comment
--comment-bg: inherit;
--comment-author-time-color: #ccc;
--comment-action-hover-color: #595959;
// BackTop
--back-top-bg: @text-color-secondary;
--back-top-hover-bg: @text-color;
// Avatar
--avatar-bg: #ccc;
// Switch
--switch-bg: @component-background;
// Pagination
--pagination-item-bg: @component-background;
--pagination-item-bg-active: @component-background;
--pagination-item-link-bg: @component-background;
--pagination-item-disabled-color-active: @white;
--pagination-item-disabled-bg-active: darken(hsv(0, 0, 96%), 10%);
--pagination-item-input-bg: @component-background;
// PageHeader
--page-header-back-color: #000;
--page-header-ghost-bg: inherit;
// Slider
--slider-rail-background-color: @background-color-base;
--slider-rail-background-color-hover: #e1e1e1;
--slider-dot-border-color: @border-color-split;
--slider-dot-border-color-active: @primary-4;
// Tree
--tree-bg: @component-background;
// Skeleton
--skeleton-to-color: coverShadeMixin(@skeleton-color, 5%);
// Transfer
--transfer-item-hover-bg: @item-hover-bg;
// Message
--message-notice-content-bg: @component-background;
// List
--list-customize-card-bg: @component-background;
// Drawer
--drawer-bg: @component-background;
// Timeline
--timeline-color: @border-color-split;
--timeline-dot-color: @primary-color;
// Image
--image-preview-operation-disabled-color: rgba(255, 255, 255, 0.45);
// Steps
--steps-nav-arrow-color: fade(@black, 25%);
--steps-background: @component-background;
// Notification
--notification-bg: @component-background;
// 侧边栏
--sidebar-light-shadow: 1px 3px 3px rgba(0, 21, 41, 0.08);
--sidebar-dark-shadow: 0 4px 4px rgba(0, 0, 0, 0.35);
// 顶栏
--header-light-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
--header-dark-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
--header-tool-hover-bg: rgba(0, 0, 0, 0.025);
--header-dark-tool-hover-bg: rgba(255, 255, 255, 0.05);
--header-color-split: rgba(0, 0, 0, 0.08);
// logo
--logo-light-shadow: 1px 2px 3px rgba(0, 21, 41, 0.08);
--logo-dark-shadow: 0 3px 4px rgba(0, 0, 0, 0.35);
//
--gradient-min: fade(#cfd8dc, 20%);
--gradient-max: fade(#cfd8dc, 40%);
//
--success-fade-20: fade(#52c41a, 20%);
--error-fade-20: fade(#ff4d4f, 20%);
--warning-fade-20: fade(#faad14, 20%);
//--primary-fade-20: fade(#1890ff, 20%);
--primary-fade-20: var(--primary-2);
//--primary-fade-8: fade(#1890ff, 8%);
--white--fade--65: rgba(255,255,255,.65);
--menu-dark-highlight-color: #fff;
--btn-primary-color: #fff;
--tooltip-color: #fff;
// workfolw design
--node-wrap-box-color: rgb(255, 255, 255);
--node-wrap-box-before-color: #FFFFFF;
--node-wrap-box-before-borde-color: rgb(202, 202, 202);
--auto-judge-before-color: @component-background;
}
#app .form-designer-container-9136076486841527{
--form-designer-primary-color: var(--primary-6);
--primary-background-color: @component-background;
--layout-background-color: fade(#9867f7, 12%);
--layout-hover-bg-color: fade(#9867f7, 24%);
--title-text-color: fade(@white, 85%);
--border-color: var(--border-color-split);
--blue-1: #e6f7ff;
--blue-2: #bae7ff;
--blue-3: #91d5ff;
--blue-4: #69c0ff;
--blue-5: #40a9ff;
--blue-6: #1677FF;
--blue-7: #096dd9;
--blue-8: #0050b3;
--blue-9: #003a8c;
--blue-10: #002766;
--green-1: #f6ffed;
--green-2: #d9f7be;
--green-3: #b7eb8f;
--green-4: #95de64;
--green-5: #73d13d;
--green-6: #52c41a;
--green-7: #389e0d;
--green-8: #237804;
--green-9: #135200;
--green-10: #092b00;
--red-1: #fff1f0;
--red-2: #ffccc7;
--red-3: #ffa39e;
--red-4: #ff7875;
--red-5: #ff4d4f;
--red-6: #f5222d;
--red-7: #cf1322;
--red-8: #a8071a;
--red-9: #820014;
--red-10: #5c0011;
--gold-1: #fffbe6;
--gold-2: #fff1b8;
--gold-3: #ffe58f;
--gold-4: #ffd666;
--gold-5: #ffc53d;
--gold-6: #faad14;
--gold-7: #d48806;
--gold-8: #ad6800;
--gold-9: #874d00;
--gold-10: #613400;
--purple-1: #f9f0ff;
--purple-2: #efdbff;
--purple-3: #d3adf7;
--purple-4: #b37feb;
--purple-5: #9254de;
--purple-6: #722ed1;
--purple-7: #531dab;
--purple-8: #391085;
--purple-9: #22075e;
--purple-10: #120338;
--cyan-1: #e6fffb;
--cyan-2: #b5f5ec;
--cyan-3: #87e8de;
--cyan-4: #5cdbd3;
--cyan-5: #36cfc9;
--cyan-6: #13c2c2;
--cyan-7: #08979c;
--cyan-8: #006d75;
--cyan-9: #00474f;
--cyan-10: #002329;
--pink-1: #fff0f6;
--pink-2: #ffd6e7;
--pink-3: #ffadd2;
--pink-4: #ff85c0;
--pink-5: #f759ab;
--pink-6: #eb2f96;
--pink-7: #c41d7f;
--pink-8: #9e1068;
--pink-9: #780650;
--pink-10: #520339;
--orange-1: #fff7e6;
--orange-2: #ffe7ba;
--orange-3: #ffd591;
--orange-4: #ffc069;
--orange-5: #ffa940;
--orange-6: #fa8c16;
--orange-7: #d46b08;
--orange-8: #ad4e00;
--orange-9: #873800;
--orange-10: #612500;
--primary-radius: #fff;
--primary-1: var(--blue-1);
--primary-2: var(--blue-2);
--primary-3: var(--blue-3);
--primary-4: var(--blue-4);
--primary-5: var(--blue-5);
--primary-6: var(--blue-6);
--primary-7: var(--blue-7);
--primary-8: var(--blue-8);
--primary-9: var(--blue-9);
--primary-10: var(--blue-10);
--primary-color: var(--primary-6);
--primary-color-hover: var(--primary-5);
--primary-color-active: var(--primary-7);
--primary-color-outline: var(--primary-2);
--info-color: var(--primary-color);
--success-color: var(--green-6);
--processing-color: var(--blue-6);
--highlight-color: var(--red-5);
--warning-color: var(--gold-6);
--warning-color-hover: var(--gold-5);
--warning-color-active: var(--gold-7);
--warning-color-outline: var(--gold-2);
--error-color: var(--red-5);
--error-color-hover: var(--red-4);
--error-color-active: var(--red-7);
--error-color-outline: var(--red-2);
--body-background: #fff;
--component-background: #fff;
--popover-background: @component-background;
--popover-customize-border-color: @border-color-split;
--text-color: fade(@black, 85%);
--text-color-secondary: fade(@black, 45%);
--text-color-inverse: @white;
--icon-color-hover: fade(@black, 75%);
--heading-color: fade(@black, 85%);
--item-hover-bg: #f5f5f5;
// Border color
--border-color-base: hsv(0, 0, 85%);
--border-color-split: hsv(0, 0, 94%);
//--border-color-inverse: @white;
//
--background-color-light: hsv(0, 0, 98%);
--background-color-base: hsv(0, 0, 96%);
// Disabled states
--disabled-color: fade(#000, 25%);
--disabled-bg: @background-color-base;
--disabled-color-dark: fade(#fff, 35%);
// Shadow
--shadow-color: rgba(195, 62, 62, 0.15);
--shadow-color-inverse: @component-background;
--box-shadow-base: @shadow-1-down;
--shadow-1-up: 0 -2px 8px @shadow-color;
--shadow-1-down: 0 2px 8px @shadow-color;
--shadow-1-left: -2px 0 8px @shadow-color;
--shadow-1-right: 2px 0 8px @shadow-color;
--shadow-2: 0 4px 12px @shadow-color;
// Buttons
--btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
--btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
--btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
--btn-default-bg: @component-background;
--btn-default-ghost-color: @component-background;
--btn-default-ghost-border: @component-background;
--btn-text-hover-bg: rgba(0, 0, 0, 0.018);
--btn-text-active-bg: rgba(0, 0, 0, 0.028);
// Checkbox
--checkbox-check-bg: @checkbox-check-color;
// Descriptions
--descriptions-bg: #fafafa;
// Divider
--divider-color: rgba(0, 0, 0, 6%);
// Dropdown 有两个
--dropdown-menu-submenu-disabled-bg: @component-background;
// Radio
--radio-dot-disabled-color: fade(@black, 20%);
--radio-solid-checked-color: @component-background;
// Radio buttons
--radio-disabled-button-checked-bg: coverTintMixin(@black, 90%);
--radio-disabled-button-checked-color: @disabled-color;
// Layout
--layout-body-background: #f0f2f5;
--layout-header-background: #001529;
--layout-trigger-background: #002140;
//--layout-sider-background-1: coverTintMixin(#001529, 10%);
// Dropdown 有两个
--dropdown-menu-bg: @component-background;
// Input
--input-placeholder-color: hsv(0, 0, 75%);
--input-icon-color: @input-color;
--input-bg: @component-background;
--input-number-handler-active-bg: #f4f4f4;
--input-icon-hover-color: fade(@black, 85%);
// Mentions
--mentions-dropdown-bg: @component-background;
// Select
--select-dropdown-bg: @component-background;
--select-background: @component-background;
--select-clear-background: @select-background;
--select-selection-item-bg: @background-color-base;
--select-selection-item-border-color: @border-color-split;
--select-multiple-disabled-background: @input-disabled-bg;
--select-multiple-item-disabled-color: #bfbfbf;
--select-multiple-item-disabled-border-color: @select-border-color;
// Cascader
--cascader-bg: @component-background;
--cascader-menu-bg: @component-background;
--cascader-menu-border-color-split: @border-color-split;
// Tooltip
--tooltip-bg: rgba(0, 0, 0, 0.75);
// Popover
--popover-bg: @component-background;
// Modal
--modal-header-bg: @component-background;
--modal-header-border-color-split: @border-color-split;
--modal-content-bg: @component-background;
--modal-footer-border-color-split: @border-color-split;
// Progress
--progress-steps-item-bg: #f3f3f3;
// Menu
--menu-popup-bg: @component-background;
--menu-dark-bg: @layout-header-background;
--menu-dark-inline-submenu-bg: #000c17;
// Table
--table-header-bg: @background-color-light;
--table-header-sort-bg: @background-color-base;
--table-body-sort-bg: #fafafa;
--table-row-hover-bg: @background-color-light;
--table-expanded-row-bg: #fbfbfb;
--table-header-cell-split-color: rgba(0, 0, 0, 0.06);
--table-header-sort-active-bg: rgba(0, 0, 0, 0.04);
--table-header-filter-active-bg: rgba(0, 0, 0, 0.04);
--table-filter-btns-bg: inherit;
--table-filter-dropdown-bg: @component-background;
--table-expand-icon-bg: @component-background;
// TimePicker
--picker-bg: @component-background;
--picker-basic-cell-disabled-bg: @disabled-bg;
--picker-border-color: @border-color-split;
// Calendar
--calendar-bg: @component-background;
--calendar-input-bg: @input-bg;
--calendar-border-color: @border-color-inverse;
--calendar-full-bg: @calendar-bg;
// Badge
--badge-text-color: @component-background;
// Rate
--rate-star-bg: @border-color-split;
// Card
--card-actions-background: @component-background;
--card-skeleton-bg: #cfd8dc;
--card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16),
0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09);
// Comment
--comment-bg: inherit;
--comment-author-time-color: #ccc;
--comment-action-hover-color: #595959;
// BackTop
--back-top-bg: @text-color-secondary;
--back-top-hover-bg: @text-color;
// Avatar
--avatar-bg: #ccc;
// Switch
--switch-bg: @component-background;
// Pagination
--pagination-item-bg: @component-background;
--pagination-item-bg-active: @component-background;
--pagination-item-link-bg: @component-background;
--pagination-item-disabled-color-active: @white;
--pagination-item-disabled-bg-active: darken(hsv(0, 0, 96%), 10%);
--pagination-item-input-bg: @component-background;
// PageHeader
--page-header-back-color: #000;
--page-header-ghost-bg: inherit;
// Slider
--slider-rail-background-color: @background-color-base;
--slider-rail-background-color-hover: #e1e1e1;
--slider-dot-border-color: @border-color-split;
--slider-dot-border-color-active: @primary-4;
// Tree
--tree-bg: @component-background;
// Skeleton
--skeleton-to-color: coverShadeMixin(@skeleton-color, 5%);
// Transfer
--transfer-item-hover-bg: @item-hover-bg;
// Message
--message-notice-content-bg: @component-background;
// List
--list-customize-card-bg: @component-background;
// Drawer
--drawer-bg: @component-background;
// Timeline
--timeline-color: @border-color-split;
--timeline-dot-color: @primary-color;
// Image
--image-preview-operation-disabled-color: rgba(255, 255, 255, 0.45);
// Steps
--steps-nav-arrow-color: fade(@black, 25%);
--steps-background: @component-background;
// Notification
--notification-bg: @component-background;
// 侧边栏
--sidebar-light-shadow: 1px 3px 3px rgba(0, 21, 41, 0.08);
--sidebar-dark-shadow: 0 4px 4px rgba(0, 0, 0, 0.35);
// 顶栏
--header-light-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
--header-dark-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
--header-tool-hover-bg: rgba(0, 0, 0, 0.025);
--header-dark-tool-hover-bg: rgba(255, 255, 255, 0.05);
--header-color-split: rgba(0, 0, 0, 0.08);
// logo
--logo-light-shadow: 1px 2px 3px rgba(0, 21, 41, 0.08);
--logo-dark-shadow: 0 3px 4px rgba(0, 0, 0, 0.35);
//
--gradient-min: fade(#cfd8dc, 20%);
--gradient-max: fade(#cfd8dc, 40%);
// font
--font-color: rgba(0, 0, 0, 0.88);
// header-bottom
--header-bottom: rgba(246, 246, 246, 0.85);
// breadcrumb-background
--breadcrumb-background: rgba(253, 253, 253, 0.85);
// background-color
--snowy-background-color: #FFFFFF;
// tag-background
--tag-background: rgba(253, 253, 253);
//
--success-fade-20: fade(#52c41a, 20%);
--error-fade-20: fade(#ff4d4f, 20%);
--warning-fade-20: fade(#faad14, 20%);
//--primary-fade-20: fade(#1890ff, 20%);
--primary-fade-20: var(--primary-2);
//--primary-fade-8: fade(#1890ff, 8%);
--white--fade--65: rgba(255,255,255,.65);
--menu-dark-highlight-color: #fff;
--btn-primary-color: #fff;
--tooltip-color: #fff;
--card-above-color: #F0F0F0;
--card-above-border-color: #CCCCCC;
// workfolw design
--node-wrap-box-color: rgb(255, 255, 255);
--node-wrap-box-before-color: #FFFFFF;
--node-wrap-box-before-borde-color: rgb(202, 202, 202);
--auto-judge-before-color: #FFF;
--cover-line-before-color: #FFF;
}

View File

@ -1,17 +1,6 @@
@import 'ant-design-vue/dist/antd';
@import 'ant-design-vue/es/style/themes/default.less';
@import './realdark';
@import './default';
/* 全局 */
/*
#app, body, html {
width: 100%;
height: 100%;
background-color: #f6f8f9;
}
*/
.body, html {
width: 100%;
height: 100%;
@ -56,6 +45,7 @@ a, button, input, textarea {
padding: 11px 11px 0px;
overflow-y: auto;
overflow-x: hidden;
flex: auto;
}
.main-bottom-wrapper {
@ -71,7 +61,6 @@ a, button, input, textarea {
/* 双排菜单布局 */
.snowy-doublerow-layout-menu {
padding-left: 5px;
padding-right: 5px;
line-height: 0;
align-items: center;
@ -86,26 +75,10 @@ a, button, input, textarea {
}
.snowy-doublerow-layout-menu-item-fort-div-span {
font-size: 12px;
font-size: 13px;
text-overflow: ellipsis;
}
.snowy-doublerow-side-top {
border-bottom: 1px solid var(--border-color-split);
height: 50px;
line-height: 50px;
padding-left: 20px;
font-size: 12px
}
// 应用主题色
.snowy-doublerow-side-top-primary-color {
background-color: var(--primary-color);
.snowy-title {
color: white;
}
}
.snowy-title{
color: var(--text-color);
}
@ -120,7 +93,6 @@ a, button, input, textarea {
}
}
/* 设置抽屉样式 */
.layout-setting {
position: fixed;
@ -147,14 +119,13 @@ a, button, input, textarea {
height: 50px;
display: flex;
justify-content: space-between;
border-bottom: 1px solid var(--border-color-split);
box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
background-color: var(--body-background);
border-bottom: 1px solid var(--header-bottom);
box-shadow: 0 0.4px 0.5px rgb(0 21 41 / 12%);
.ant-menu-item{
height: 48px;
line-height: 48px;
}
background: var(--snowy-background-color);
}
// 应用主题色
.snowy-header-primary-color {
@ -197,7 +168,7 @@ a, button, input, textarea {
}
.snowy-header-logo {
height: 50px;
height: 49px;
display: flex;
justify-content: space-between;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
@ -221,105 +192,29 @@ a, button, input, textarea {
height: 35px;
}
/* 面包屑 */
.admin-ui-topbar {
padding-left: 15px
.top-snowy-header {
background: #001529;
color: white;
}
.admin-ui-topbar .left-panel {
display: flex;
align-items: center;
.top-snowy-header-light {
background: #ffffff;
color: #000000;
}
.admin-ui-topbar .right-panel {
display: flex;
align-items: center;
.top-snowy-header-layout {
background: var(--primary-color);
color: #ffffff;
}
.panel-item {
padding: 0 10px;
cursor: pointer;
height: 100%;
display: flex;
align-items: center;
/*color: var(--font-color);*/
}
.panel-item:hover {
background: var(--header-color-split);
}
/* 多标签 */
.snowy-tags {
height: 40px;
background: var(--component-background);
}
.snowy-tags ul {
display: flex;
overflow: hidden;
padding-left: 0;
}
.snowy-tags li {
cursor: pointer;
display: inline-block;
float: left;
line-height: 39.5px;
position: relative;
flex-shrink: 0;
}
.snowy-tags li::after {
content: " ";
width: 1px;
height: 100%;
position: absolute;
right: 0px;
background-image: linear-gradient(#fff, #e6e6e6);
}
.snowy-tags li a {
padding: 0 10px;
width: 100%;
height: 100%;
text-decoration: none;
display: flex;
align-items: center;
}
.snowy-tags li i {
margin-left: 10px;
border-radius: 3px;
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.snowy-tags li i:hover {
background: rgba(0, 0, 0, .2);
color: @body-background;
}
.snowy-tags li:hover {
background: @body-background;
}
.snowy-tags li.active {
background: @primary-color;
}
.snowy-tags li.active a {
color: #fff;
}
.snowy-tags li.sortable-ghost {
opacity: 0;
}
.snowy-header-tags-right {
margin-right: 10px;
}
.contextmenu {
position: fixed;
width: 200px;
@ -407,6 +302,10 @@ a, button, input, textarea {
.ant-card-extra {
padding: 12px 0!important;
}
.ant-card-head {
border-bottom: 0px !important;
min-height: 50px !important;
}
/* 重写antdv的表格滚动条 */
.ant-table-body, .ant-table-content{
@ -427,9 +326,9 @@ a, button, input, textarea {
}
.left-span-label {
border-left: 4px solid @primary-color;
border-left: 4px solid var(--primary-color);
font-size: 15px;
color: #212121;
color: var(--font-color);
font-weight: 600;
padding-left: 8px;
}
@ -464,8 +363,8 @@ body,
.admin-ui-main{
&::-webkit-scrollbar {
/*滚动条整体样式*/
width : 0px; /*高宽分别对应横竖滚动条的尺寸*/
height: 0px;
width : 0; /*高宽分别对应横竖滚动条的尺寸*/
height: 0;
}
&::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
@ -498,3 +397,115 @@ body,
display: none!important;
}
}
.ant-modal-close-x .anticon {
padding: 2px !important;
}
.xn-mb10 {
margin-bottom: 10px;
}
.xn-mt4 {
margin-top: 4px;
}
.xn-mg08 {
margin: 0 8px;
}
.xn-fdr {
float: right;
}
.xn-wd {
width: 100%;
}
.xn-wd90 {
width: 90px;
}
.xn-wdcalc-70 {
width: calc(100% - 70px);
}
.xn-mr8 {
margin-right: 8px;
}
.xn-ht400 {
height: 400px;
}
.xn-wh25 {
height: 25px;
width: 25px;
}
.xn-ml10 {
margin-left: 10px;
}
.xn-pl0 {
padding-left: 0px;
}
.xn-pd8 {
padding: 8px;
}
.xn-pb10 {
padding-bottom: 10px;
}
.xn-color-a0a0a0 {
color: #a0a0a0;
}
.xn-color-d9d9d9 {
color: #d9d9d9;
}
.xn-color-ff4d4f {
color: #ff4d4f;
}
.xn-color-00025 {
color: rgba(0, 0, 0, 0.25);
}
.xn-jk-line {
width: 188px;
margin-bottom: 8px;
display: block;
}
.xn-findform-line {
border: 1px solid var(--border-color-split);
cursor: pointer;
width: 100%;
height: 40px;
}
.odd {
background-color: var(--table-row-hover-bg);
}
.snowy-theme-dark .odd {
background-color: #1d1d1d
}
// 以下是重写表单设计器的样式
.list-main {
background: var(--auto-judge-before-color) !important;
}
.drag-move-box:before {
background: var(--primary-color) !important;
}
.drag-move-box>.delete {
background: var(--primary-color) !important;
}
.drag-move-box>.copy {
background: var(--primary-color) !important;
}
.drag-move-box .show-key-box {
color: var(--primary-color) !important;
}
.left-ul-item:hover {
color: var(--primary-color) !important;
border: 1px solid var(--primary-color) !important;
-webkit-box-shadow: 0 2px 6px var(--primary-color) !important;
box-shadow: 0 2px 6px var(--primary-color) !important;
}
.list-main>.moving:before {
background: var(--primary-color) !important;
}
.operating-area a:hover {
color: var(--primary-color) !important;
}
.batch-box>.delete {
background: var(--primary-color) !important;
}
.batch-box>.copy {
background: var(--primary-color) !important;
}
.batch-box.active:before {
background: var(--primary-color) !important;
}

View File

@ -1,126 +0,0 @@
@media (max-width: 992px) {
// 移动端样式覆盖
.el-form-item {
display: block;
}
.el-form-item__label {
display: block;
text-align: left;
padding: 0 0 10px;
}
.el-dialog {
width: 90% !important;
}
.el-dialog.is-fullscreen {
width: 100% !important;
}
.el-drawer.rtl {
width: 90% !important;
}
.el-form-item__content {
margin-left: 0px !important;
}
.admin-ui-main {
> .el-container {
display: block;
height: auto;
}
> .el-container > .el-aside {
width: 100% !important;
border: 0
}
}
.scTable {
.el-table,
.el-table__body-wrapper {
display: block !important;
height: auto !important;
}
.scTable-page {
padding: 0 5px !important;
}
.el-pagination__total,
.el-pagination__jump,
.scTable-do {
display: none !important;
}
}
.headerPublic {
height: auto !important;
display: block;
.left-panel {
overflow: auto;
}
.left-panel::-webkit-scrollbar {
display: none;
}
.right-panel {
display: block;
margin-top: 15px;
}
.right-panel .right-panel-search {
display: block;
}
.right-panel .right-panel-search > * {
width: 100%;
margin: 0;
margin-top: 15px;
}
}
.admin-ui-main > .el-container > *:first-child:not(.el-aside):not(.el-header) {
border: 0;
margin-top: 0;
}
.admin-ui-main > .el-container > *:first-child:not(.el-aside):not(.el-header) + .el-aside {
margin-top: 0;
}
.admin-ui-main > .el-container > .el-aside {
border-bottom: 1px solid #ebeef5 !important;
}
.admin-ui-main > .el-container > .el-container {
border-top: 1px solid #ebeef5;
border-bottom: 1px solid #ebeef5;
margin-top: 15px;
}
.admin-ui-main > .el-container > .el-header {
@extend . headerPublic;
border-bottom: 1px solid #ebeef5;
}
.admin-ui-main > .el-container > .el-main {
border-top: 1px solid #ebeef5;
border-bottom: 1px solid #ebeef5;
margin-top: 15px;
}
.admin-ui-main > .el-container > .el-main + .el-aside {
border-left: 0 !important;
border-top: 1px solid #ebeef5;
margin-top: 15px;
}
.admin-ui-main > .el-container > .el-container > .el-header {
@extend . headerPublic
}
}

View File

@ -1,92 +0,0 @@
/* USERCENTER */
.user-info {
padding: 20px 40px;
}
.user-info-top {
text-align: center;
}
.user-info-top h2 {
margin-top: 10px;
font-size: 24px;
}
.user-info-top p {
color: #999;
margin-top: 5px;
}
.user-info-top button {
margin-top: 10px;
}
.user-info-main {
padding: 20px 0;
}
.user-info-main li {
list-style-type: none;
line-height: 2;
font-size: 14px;
}
.user-info-main li i {
margin-right: 10px;
}
.user-info-bottom {
border-top: 1px solid #e6e6e6;
}
.user-info-bottom h2 {
font-size: 14px;
margin: 15px 0;
}
/*static-table*/
.static-table {
border-collapse: collapse;
width: 100%;
font-size: 14px;
margin-bottom: 45px;
line-height: 1.5em;
}
.static-table th {
text-align: left;
white-space: nowrap;
color: #909399;
font-weight: 400;
border-bottom: 1px solid #dcdfe6;
padding: 15px;
max-width: 250px;
}
.static-table td {
border-bottom: 1px solid #dcdfe6;
padding: 15px;
max-width: 250px;
color: #606266;
}
/*header-tabs*/
.header-tabs {
padding: 0;
display: block;
border: 0 !important;
height: auto;
}
.header-tabs .el-tabs {
border: 0;
box-shadow: none;
}
.header-tabs .el-tabs__content {
display: none;
}
.header-tabs .el-tabs__item {
font-size: 12px;
}

View File

@ -3,5 +3,5 @@
直接 var(--primary-color)
例如:新建个变量,黑的白的在不同的less中设定好这个时候就会跟着颜色的主题变化
例如:新建个变量,在default.less跟realdark.less中设定好这个时候就会跟着颜色的主题变化

View File

@ -11,16 +11,10 @@
@functions: ~`(function() {
this.fade = function(color, amount) {
if (String(color).indexOf('var(') === 0) {
/*
(var(--primary-color), 7) -> var(--primary-7)
*/
if (color.indexOf('--primary-color') !== -1 ) {
var m = amount > 10 ? amount/10 :amount
return color.replace('-color)', '-' + m + ')')
}
/*
(var(--error-color), 70%) ===> var(--error-color--fade-7)
*/
return color.replace(')', '--fade--' + parseInt(amount) + ')')
}
return color
@ -29,403 +23,395 @@
}
.fade();
@import 'ant-design-vue/es/style/themes/default.less';
//@import '../themes/default.less';
@import 'ant-design-vue/dist/reset.css';
.snowy-theme-dark {
--blue-1: #111d2c;
--blue-2: #112a45;
--blue-3: #15395b;
--blue-4: #164c7e;
--blue-5: #1765ad;
--blue-6: #177ddc;
--blue-7: #3c9ae8;
--blue-8: #65b7f3;
--blue-9: #8dcff8;
--blue-10: #b7e3fa;
--green-1: #162312;
--green-2: #1d3712;
--green-3: #274916;
--green-4: #306317;
--green-5: #3c8618;
--green-6: #49aa19;
--green-7: #6abe39;
--green-8: #8fd460;
--green-9: #b2e58b;
--green-10: #d5f2bb;
--red-1: #2a1215;
--red-2: #431418;
--red-3: #58181c;
--red-4: #791a1f;
--red-5: #a61d24;
--red-6: #f5222d;
--red-7: #e84749;
--red-8: #f37370;
--red-9: #f89f9a;
--red-10: #fac8c3;
--gold-1: #2b2111;
--gold-2: #443111;
--gold-3: #594214;
--gold-4: #7c5914;
--gold-5: #aa7714;
--gold-6: #d89614;
--gold-7: #e8b339;
--gold-8: #f3cc62;
--gold-9: #f8df8b;
--gold-10: #faedb5;
--purple-1: #1a1325;
--purple-2: #24163a;
--purple-3: #301c4d;
--purple-4: #3e2069;
--purple-5: #51258f;
--purple-6: #642ab5;
--purple-7: #854eca;
--purple-8: #ab7ae0;
--purple-9: #cda8f0;
--purple-10: #ebd7fa;
--cyan-1: #112123;
--cyan-2: #113536;
--cyan-3: #144848;
--cyan-4: #146262;
--cyan-5: #138585;
--cyan-6: #13a8a8;
--cyan-7: #33bcb7;
--cyan-8: #58d1c9;
--cyan-9: #84e2d8;
--cyan-10: #b2f1e8;
--pink-1: #291321;
--pink-2: #40162f;
--pink-3: #551c3b;
--pink-4: #75204f;
--pink-5: #a02669;
--pink-6: #cb2b83;
--pink-7: #e0529c;
--pink-8: #f37fb7;
--pink-9: #f8a8cc;
--pink-10: #fad2e3;
--orange-1: #2b1d11;
--orange-2: #442a11;
--orange-3: #593815;
--orange-4: #7c4a15;
--orange-5: #aa6215;
--orange-6: #d87a16;
--orange-7: #e89a3c;
--orange-8: #f3b765;
--orange-9: #f8cf8d;
--orange-10: #fae3b7;
--primary-1: var(--blue-1);
--primary-2: var(--blue-2);
--primary-3: var(--blue-3);
--primary-4: var(--blue-4);
--primary-5: var(--blue-5);
--primary-6: var(--blue-6);
--primary-7: var(--blue-7);
--primary-8: var(--blue-8);
--primary-9: var(--blue-9);
--primary-10: var(--blue-10);
--primary-color: var(--primary-6);
--primary-color-hover: var(--primary-5);
--primary-color-active: var(--primary-7);
--primary-color-outline: var(--primary-2);
--info-color: var(--primary-color);
--success-color: var(--green-6);
--processing-color: var(--blue-6);
--highlight-color: var(--red-5);
--warning-color: var(--gold-6);
--warning-color-hover: var(--gold-5);
--warning-color-active: var(--gold-7);
--warning-color-outline: var(--gold-2);
--error-color: var(--red-5);
--error-color-hover: var(--red-4);
--error-color-active: var(--red-7);
--error-color-outline: var(--red-2);
--body-background: @black;
--component-background: #141414;
--popover-background: #1f1f1f;
--popover-customize-border-color: #3a3a3a;
--text-color: fade(@white, 85%);
--text-color-secondary: fade(@white, 45%);
--text-color-inverse: @white;
--icon-color-hover: fade(@white, 75%);
--heading-color: fade(@white, 85%);
--item-hover-bg: fade(@white, 8%);
// Border color
--border-color-base: #434343;
--border-color-split: #303030;
//--border-color-inverse: @black;
//
--background-color-light: fade(@white, 4%);
--background-color-base: fade(@white, 8%);
// Disabled states
--disabled-color: fade(@white, 30%);
--disabled-bg: @background-color-base;
--disabled-color-dark: fade(@white, 30%);
// Shadow
--shadow-color: rgba(0, 0, 0, 0.45);
--shadow-color-inverse: @component-background;
--box-shadow-base: @shadow-2;
--shadow-1-up: 0 -6px 16px -8px rgba(0, 0, 0, 0.32),
0 -9px 28px 0 rgba(0, 0, 0, 0.2), 0 -12px 48px 16px rgba(0, 0, 0, 0.12);
--shadow-1-down: 0 6px 16px -8px rgba(0, 0, 0, 0.32),
0 9px 28px 0 rgba(0, 0, 0, 0.2), 0 12px 48px 16px rgba(0, 0, 0, 0.12);
--shadow-1-right: 6px 0 16px -8px rgba(0, 0, 0, 0.32),
9px 0 28px 0 rgba(0, 0, 0, 0.2), 12px 0 48px 16px rgba(0, 0, 0, 0.12);
--shadow-2: 0 3px 6px -4px rgba(0, 0, 0, 0.48),
0 6px 16px 0 rgba(0, 0, 0, 0.32), 0 9px 28px 8px rgba(0, 0, 0, 0.2);
// Buttons
--btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
--btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
--btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
--btn-default-bg: transparent;
--btn-default-ghost-color: @text-color;
--btn-default-ghost-border: fade(@white, 25%);
--btn-text-hover-bg: rgba(255, 255, 255, 0.03);
--btn-text-active-bg: rgba(255, 255, 255, 0.04);
// Checkbox
--checkbox-check-bg: transparent;
// Descriptions
--descriptions-bg: @background-color-light;
// Divider
--divider-color: rgba(255, 255, 255, 12%);
// Dropdown 有两个
--dropdown-menu-submenu-disabled-bg: transparent;
// Radio
--radio-dot-disabled-color: fade(@white, 20%);
--radio-solid-checked-color: @white;
// Radio buttons
--radio-disabled-button-checked-bg: fade(@white, 20%);
--radio-disabled-button-checked-color: @disabled-color;
// Layout
--layout-body-background: @body-background;
--layout-header-background: @popover-background;
--layout-trigger-background: #262626;
//--layout-sider-background-1: tint(#1f1f1f, 10%);
// Dropdown 有两个
--dropdown-menu-bg: @popover-background;
// Input
--input-placeholder-color: fade(@white, 30%);
--input-icon-color: fade(@white, 30%);
--input-bg: transparent;
--input-number-handler-active-bg: @item-hover-bg;
--input-icon-hover-color: fade(@white, 85%);
// Mentions
--mentions-dropdown-bg: @popover-background;
// Select
--select-dropdown-bg: @popover-background;
--select-background: transparent;
--select-clear-background: @component-background;
--select-selection-item-bg: fade(@white, 8);
--select-selection-item-border-color: @border-color-split;
--select-multiple-disabled-background: @component-background;
--select-multiple-item-disabled-color: #595959;
--select-multiple-item-disabled-border-color: @popover-background;
// Cascader
--cascader-bg: transparent;
--cascader-menu-bg: @popover-background;
--cascader-menu-border-color-split: @border-color-split;
// Tooltip
--tooltip-bg: #434343;
// Popover
--popover-bg: @popover-background;
// Modal
--modal-header-bg: @popover-background;
--modal-header-border-color-split: @border-color-split;
--modal-content-bg: @popover-background;
--modal-footer-border-color-split: @border-color-split;
// Progress
--progress-steps-item-bg: fade(@white, 8%);
// Menu
--menu-popup-bg: @popover-background;
--menu-dark-bg: @popover-background;
--menu-dark-inline-submenu-bg: @component-background;
// Table
--table-header-bg: #1d1d1d;
--table-header-sort-bg: #262626;
--table-body-sort-bg: fade(@white, 1%);
--table-row-hover-bg: #262626;
--table-expanded-row-bg: @table-header-bg;
--table-header-cell-split-color: fade(@white, 8%);
--table-header-sort-active-bg: #303030;
--table-header-filter-active-bg: #434343;
--table-filter-btns-bg: @popover-background;
--table-filter-dropdown-bg: @popover-background;
--table-expand-icon-bg: transparent;
// TimePicker
--picker-bg: transparent;
--picker-basic-cell-disabled-bg: #303030;
--picker-border-color: @border-color-split;
// Calendar
--calendar-bg: @popover-background;
--calendar-input-bg: @calendar-bg;
--calendar-border-color: transparent;
--calendar-full-bg: @component-background;
// Badge
--badge-text-color: @white;
// Rate
--rate-star-bg: fade(@white, 12%);
// Card
--card-actions-background: @component-background;
--card-skeleton-bg: #303030;
--card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.64),
0 3px 6px 0 rgba(0, 0, 0, 0.48), 0 5px 12px 4px rgba(0, 0, 0, 0.36);
// Comment
--comment-bg: transparent;
--comment-author-time-color: fade(@white, 30%);
--comment-action-hover-color: fade(@white, 65%);
// BackTop
--back-top-bg: var(--tooltip-bg);
--back-top-hover-bg: var(--border-color-split);
// Avatar
--avatar-bg: fade(@white, 30%);
// Switch
--switch-bg: @white;
// Pagination
--pagination-item-bg: transparent;
--pagination-item-bg-active: transparent;
--pagination-item-link-bg: transparent;
--pagination-item-disabled-color-active: @black;
--pagination-item-disabled-bg-active: fade(@white, 25%);
--pagination-item-input-bg: @pagination-item-bg;
// PageHeader
--page-header-back-color: @icon-color;
--page-header-ghost-bg: transparent;
// Slider
--slider-rail-background-color: #262626;
--slider-rail-background-color-hover: @border-color-base;
--slider-dot-border-color: @border-color-split;
--slider-dot-border-color-active: @primary-4;
// Tree
--tree-bg: transparent;
// Skeleton
--skeleton-to-color: fade(@white, 16%);
// Transfer
--transfer-item-hover-bg: #262626;
// Message
--message-notice-content-bg: @popover-background;
// List
--list-customize-card-bg: transparent;
// Drawer
--drawer-bg: @popover-background;
// Timeline
--timeline-color: @border-color-split;
--timeline-dot-color: @primary-color;
// Steps
--steps-nav-arrow-color: fade(@white, 20%);
--steps-background: transparent;
// Notification
--notification-bg: @popover-background;
// 侧边栏
--sidebar-light-shadow: 0 4px 4px rgba(0, 0, 0, 0.6);
--sidebar-dark-shadow: 0 4px 4px rgba(0, 0, 0, 0.6);
// 顶栏
--header-light-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
--header-dark-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
--header-tool-hover-bg: rgba(255, 255, 255, 0.05);
--header-dark-tool-hover-bg: rgba(255, 255, 255, 0.05);
--header-color-split: rgba(255, 255, 255, 0.15);
// logo
--logo-light-shadow: 0 3px 4px rgba(0, 0, 0, 0.6);
--logo-dark-shadow: 0 3px 4px rgba(0, 0, 0, 0.6);
//
--gradient-min: fade(#303030, 20%);
--gradient-max: fade(#303030, 40%);
//
--primary-fade-20: var(--primary-2);
--black--fade--85: rgba(255, 255, 255, 0.85);
--switch-shadow-color: 0 2px 4px rgb(0 35 11 / 20%);
// workfolw design
--node-wrap-box-color: #303030;
--node-wrap-box-before-color: rgba(255, 255, 255, 0.09); // 箭头旁边
--node-wrap-box-before-borde-color: rgba(255, 255, 255, 0.09); // 箭头
--auto-judge-before-color: @component-background; // 箭头背景
}
// 表单设计器主题覆盖
.snowy-theme-dark{
--hint-color: #888;
.form-designer-container-9136076486841527{
--form-designer-primary-color: var(--primary-6);
--primary-background-color: @component-background;
--layout-background-color: fade(#9867f7, 12%);
--layout-hover-bg-color: fade(#9867f7, 24%);
--title-text-color: fade(@black, 85%);
--border-color: var(--border-color-split);
--component-background: #141414;
}
--blue-1: #111d2c;
--blue-2: #112a45;
--blue-3: #15395b;
--blue-4: #164c7e;
--blue-5: #1765ad;
--blue-6: #177ddc;
--blue-7: #3c9ae8;
--blue-8: #65b7f3;
--blue-9: #8dcff8;
--blue-10: #b7e3fa;
--green-1: #162312;
--green-2: #1d3712;
--green-3: #274916;
--green-4: #306317;
--green-5: #3c8618;
--green-6: #49aa19;
--green-7: #6abe39;
--green-8: #8fd460;
--green-9: #b2e58b;
--green-10: #d5f2bb;
--red-1: #2a1215;
--red-2: #431418;
--red-3: #58181c;
--red-4: #791a1f;
--red-5: #a61d24;
--red-6: #f5222d;
--red-7: #e84749;
--red-8: #f37370;
--red-9: #f89f9a;
--red-10: #fac8c3;
--gold-1: #2b2111;
--gold-2: #443111;
--gold-3: #594214;
--gold-4: #7c5914;
--gold-5: #aa7714;
--gold-6: #d89614;
--gold-7: #e8b339;
--gold-8: #f3cc62;
--gold-9: #f8df8b;
--gold-10: #faedb5;
--purple-1: #1a1325;
--purple-2: #24163a;
--purple-3: #301c4d;
--purple-4: #3e2069;
--purple-5: #51258f;
--purple-6: #642ab5;
--purple-7: #854eca;
--purple-8: #ab7ae0;
--purple-9: #cda8f0;
--purple-10: #ebd7fa;
--cyan-1: #112123;
--cyan-2: #113536;
--cyan-3: #144848;
--cyan-4: #146262;
--cyan-5: #138585;
--cyan-6: #13a8a8;
--cyan-7: #33bcb7;
--cyan-8: #58d1c9;
--cyan-9: #84e2d8;
--cyan-10: #b2f1e8;
--pink-1: #291321;
--pink-2: #40162f;
--pink-3: #551c3b;
--pink-4: #75204f;
--pink-5: #a02669;
--pink-6: #cb2b83;
--pink-7: #e0529c;
--pink-8: #f37fb7;
--pink-9: #f8a8cc;
--pink-10: #fad2e3;
--orange-1: #2b1d11;
--orange-2: #442a11;
--orange-3: #593815;
--orange-4: #7c4a15;
--orange-5: #aa6215;
--orange-6: #d87a16;
--orange-7: #e89a3c;
--orange-8: #f3b765;
--orange-9: #f8cf8d;
--orange-10: #fae3b7;
--primary-radius: #141414;
--primary-1: var(--blue-1);
--primary-2: var(--blue-2);
--primary-3: var(--blue-3);
--primary-4: var(--blue-4);
--primary-5: var(--blue-5);
--primary-6: var(--blue-6);
--primary-7: var(--blue-7);
--primary-8: var(--blue-8);
--primary-9: var(--blue-9);
--primary-10: var(--blue-10);
--primary-color: var(--primary-6);
--primary-color-hover: var(--primary-5);
--primary-color-active: var(--primary-7);
--primary-color-outline: var(--primary-2);
--info-color: var(--primary-color);
--success-color: var(--green-6);
--processing-color: var(--blue-6);
--highlight-color: var(--red-5);
--warning-color: var(--gold-6);
--warning-color-hover: var(--gold-5);
--warning-color-active: var(--gold-7);
--warning-color-outline: var(--gold-2);
--error-color: var(--red-5);
--error-color-hover: var(--red-4);
--error-color-active: var(--red-7);
--error-color-outline: var(--red-2);
--body-background: @black;
--component-background: #141414;
--popover-background: #1f1f1f;
--popover-customize-border-color: #3a3a3a;
--text-color: fade(@white, 85%);
--text-color-secondary: fade(@white, 45%);
--text-color-inverse: @white;
--icon-color-hover: fade(@white, 75%);
--heading-color: fade(@white, 85%);
--item-hover-bg: fade(@white, 8%);
// Border color
--border-color-base: #434343;
--border-color-split: #303030;
//--border-color-inverse: @black;
//
--background-color-light: fade(@white, 4%);
--background-color-base: fade(@white, 8%);
// Disabled states
--disabled-color: fade(@white, 30%);
--disabled-bg: @background-color-base;
--disabled-color-dark: fade(@white, 30%);
// Shadow
--shadow-color: rgba(0, 0, 0, 0.45);
--shadow-color-inverse: @component-background;
--box-shadow-base: @shadow-2;
--shadow-1-up: 0 -6px 16px -8px rgba(0, 0, 0, 0.32),
0 -9px 28px 0 rgba(0, 0, 0, 0.2), 0 -12px 48px 16px rgba(0, 0, 0, 0.12);
--shadow-1-down: 0 6px 16px -8px rgba(0, 0, 0, 0.32),
0 9px 28px 0 rgba(0, 0, 0, 0.2), 0 12px 48px 16px rgba(0, 0, 0, 0.12);
--shadow-1-right: 6px 0 16px -8px rgba(0, 0, 0, 0.32),
9px 0 28px 0 rgba(0, 0, 0, 0.2), 12px 0 48px 16px rgba(0, 0, 0, 0.12);
--shadow-2: 0 3px 6px -4px rgba(0, 0, 0, 0.48),
0 6px 16px 0 rgba(0, 0, 0, 0.32), 0 9px 28px 8px rgba(0, 0, 0, 0.2);
// Buttons
--btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
--btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
--btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
--btn-default-bg: transparent;
--btn-default-ghost-color: @text-color;
--btn-default-ghost-border: fade(@white, 25%);
--btn-text-hover-bg: rgba(255, 255, 255, 0.03);
--btn-text-active-bg: rgba(255, 255, 255, 0.04);
// Checkbox
--checkbox-check-bg: transparent;
// Descriptions
--descriptions-bg: @background-color-light;
// Divider
--divider-color: rgba(255, 255, 255, 12%);
// Dropdown 有两个
--dropdown-menu-submenu-disabled-bg: transparent;
// Radio
--radio-dot-disabled-color: fade(@white, 20%);
--radio-solid-checked-color: @white;
// Radio buttons
--radio-disabled-button-checked-bg: fade(@white, 20%);
--radio-disabled-button-checked-color: @disabled-color;
// Layout
--layout-body-background: @body-background;
--layout-header-background: @popover-background;
--layout-trigger-background: #262626;
//--layout-sider-background-1: tint(#1f1f1f, 10%);
// Dropdown 有两个
--dropdown-menu-bg: @popover-background;
// Input
--input-placeholder-color: fade(@white, 30%);
--input-icon-color: fade(@white, 30%);
--input-bg: transparent;
--input-number-handler-active-bg: @item-hover-bg;
--input-icon-hover-color: fade(@white, 85%);
// Mentions
--mentions-dropdown-bg: @popover-background;
// Select
--select-dropdown-bg: @popover-background;
--select-background: transparent;
--select-clear-background: @component-background;
--select-selection-item-bg: fade(@white, 8);
--select-selection-item-border-color: @border-color-split;
--select-multiple-disabled-background: @component-background;
--select-multiple-item-disabled-color: #595959;
--select-multiple-item-disabled-border-color: @popover-background;
// Cascader
--cascader-bg: transparent;
--cascader-menu-bg: @popover-background;
--cascader-menu-border-color-split: @border-color-split;
// Tooltip
--tooltip-bg: #434343;
// Popover
--popover-bg: @popover-background;
// Modal
--modal-header-bg: @popover-background;
--modal-header-border-color-split: @border-color-split;
--modal-content-bg: @popover-background;
--modal-footer-border-color-split: @border-color-split;
// Progress
--progress-steps-item-bg: fade(@white, 8%);
// Menu
--menu-popup-bg: @popover-background;
--menu-dark-bg: @popover-background;
--menu-dark-inline-submenu-bg: @component-background;
// Table
--table-header-bg: #1d1d1d;
--table-header-sort-bg: #262626;
--table-body-sort-bg: fade(@white, 1%);
--table-row-hover-bg: #262626;
--table-expanded-row-bg: @table-header-bg;
--table-header-cell-split-color: fade(@white, 8%);
--table-header-sort-active-bg: #303030;
--table-header-filter-active-bg: #434343;
--table-filter-btns-bg: @popover-background;
--table-filter-dropdown-bg: @popover-background;
--table-expand-icon-bg: transparent;
// TimePicker
--picker-bg: transparent;
--picker-basic-cell-disabled-bg: #303030;
--picker-border-color: @border-color-split;
// Calendar
--calendar-bg: @popover-background;
--calendar-input-bg: @calendar-bg;
--calendar-border-color: transparent;
--calendar-full-bg: @component-background;
// Badge
--badge-text-color: @white;
// Rate
--rate-star-bg: fade(@white, 12%);
// Card
--card-actions-background: @component-background;
--card-skeleton-bg: #303030;
--card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.64),
0 3px 6px 0 rgba(0, 0, 0, 0.48), 0 5px 12px 4px rgba(0, 0, 0, 0.36);
// Comment
--comment-bg: transparent;
--comment-author-time-color: fade(@white, 30%);
--comment-action-hover-color: fade(@white, 65%);
// BackTop
--back-top-bg: var(--tooltip-bg);
--back-top-hover-bg: var(--border-color-split);
// Avatar
--avatar-bg: fade(@white, 30%);
// Switch
--switch-bg: @white;
// Pagination
--pagination-item-bg: transparent;
--pagination-item-bg-active: transparent;
--pagination-item-link-bg: transparent;
--pagination-item-disabled-color-active: @black;
--pagination-item-disabled-bg-active: fade(@white, 25%);
--pagination-item-input-bg: @pagination-item-bg;
// PageHeader
--page-header-back-color: @icon-color;
--page-header-ghost-bg: transparent;
// Slider
--slider-rail-background-color: #262626;
--slider-rail-background-color-hover: @border-color-base;
--slider-dot-border-color: @border-color-split;
--slider-dot-border-color-active: @primary-4;
// Tree
--tree-bg: transparent;
// Skeleton
--skeleton-to-color: fade(@white, 16%);
// Transfer
--transfer-item-hover-bg: #262626;
// Message
--message-notice-content-bg: @popover-background;
// List
--list-customize-card-bg: transparent;
// Drawer
--drawer-bg: @popover-background;
// Timeline
--timeline-color: @border-color-split;
--timeline-dot-color: @primary-color;
// Steps
--steps-nav-arrow-color: fade(@white, 20%);
--steps-background: transparent;
// Notification
--notification-bg: @popover-background;
// 侧边栏
--sidebar-light-shadow: 0 4px 4px rgba(0, 0, 0, 0.6);
--sidebar-dark-shadow: 0 4px 4px rgba(0, 0, 0, 0.6);
// 顶栏
--header-light-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
--header-dark-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
--header-tool-hover-bg: rgba(255, 255, 255, 0.05);
--header-dark-tool-hover-bg: rgba(255, 255, 255, 0.05);
--header-color-split: rgba(255, 255, 255, 0.15);
// logo
--logo-light-shadow: 0 3px 4px rgba(0, 0, 0, 0.6);
--logo-dark-shadow: 0 3px 4px rgba(0, 0, 0, 0.6);
//
--gradient-min: fade(#303030, 20%);
--gradient-max: fade(#303030, 40%);
// font
--font-color: #FFFFFF;
// header-bottom
--header-bottom: rgba(54, 54, 54, 0.6);
// breadcrumb-background
--breadcrumb-background: rgba(54, 54, 54, 0.6);
// background-color
--snowy-background-color: #141414;
// tag-background
--tag-background: rgba(56, 56, 56);
//
--primary-fade-20: var(--primary-2);
--black--fade--85: rgba(255, 255, 255, 0.85);
--switch-shadow-color: 0 2px 4px rgb(0 35 11 / 20%);
--card-above-color: #303030;
--card-above-border-color: #484848;
// workfolw design
--node-wrap-box-color: #303030;
--node-wrap-box-before-color: rgba(255, 255, 255, 0.09); // 箭头旁边
--node-wrap-box-before-borde-color: rgba(255, 255, 255, 0.09); // 箭头
--auto-judge-before-color: #141414; // 箭头背景
--cover-line-before-color: #141414;
}

View File

@ -11,7 +11,7 @@
import { generate } from '@ant-design/colors'
import tool from '../utils/tool'
import config from '../config'
import { ThemeModeEnum } from './enum'
import { themeEnum } from '@/layout/enum/themeEnum'
const changeColor = (newPrimaryColor, theme, darkClass = 'snowy-theme-dark') => {
return new Promise((resolve) => {
@ -20,7 +20,7 @@ const changeColor = (newPrimaryColor, theme, darkClass = 'snowy-theme-dark') =>
if (themeEle && themeEle.parentNode) {
themeEle.parentNode.removeChild(themeEle)
}
const isRealDark = theme === ThemeModeEnum.REAL_DARK
const isRealDark = theme === themeEnum.REAL_DARK
if (newPrimaryColor) {
const colors = generate(newPrimaryColor, isRealDark ? { theme: 'dark' } : {})
const rootClass = isRealDark ? `.${darkClass}` : ':root'

View File

@ -8,7 +8,7 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
/*
/**
* @Descripttion: 工具集
* @version: 1.1
* @LastEditors: yubaoshan
@ -20,10 +20,18 @@ const tool = {}
tool.data = {
set(table, settings) {
const _set = JSON.stringify(settings)
return localStorage.setItem(table, _set)
const SNOWYSTRING = table.slice(0, 6) === 'SNOWY_' && table !== 'SNOWY_SYS_BASE_CONFIG'
if (SNOWYSTRING) {
let localSetting = JSON.parse(localStorage.getItem('SNOWY_SETTING')) || {}
let newSetting = {}
newSetting[table] = _set
return localStorage.setItem('SNOWY_SETTING', JSON.stringify(Object.assign(localSetting, newSetting)))
} else return localStorage.setItem(table, _set)
},
get(table) {
let data = localStorage.getItem(table)
const SNOWYSTRING = table.slice(0, 6) === 'SNOWY_' && table !== 'SNOWY_SYS_BASE_CONFIG'
const SNOWY_SETTING = JSON.parse(localStorage.getItem('SNOWY_SETTING')) || {}
let data = SNOWYSTRING ? SNOWY_SETTING[table] : localStorage.getItem(table)
try {
data = JSON.parse(data)
} catch (err) {

View File

@ -3,7 +3,7 @@
<a-form-item name="email">
<a-input v-model:value="emailFormData.email" :placeholder="$t('login.emailPlaceholder')" size="large">
<template #prefix>
<mail-outlined style="color: rgba(0, 0, 0, 0.25)" />
<mail-outlined class="xn-color-00025" />
</template>
</a-input>
</a-form-item>
@ -16,12 +16,12 @@
size="large"
>
<template #prefix>
<mail-outlined style="color: rgba(0, 0, 0, 0.25)" />
<mail-outlined class="xn-color-00025" />
</template>
</a-input>
</a-col>
<a-col :span="7">
<a-button size="large" style="width: 100%" @click="getEmailValidCode" :disabled="state.smsSendBtn">{{
<a-button size="large" class="xn-wd" @click="getEmailValidCode" :disabled="state.smsSendBtn">{{
(!state.smsSendBtn && $t('login.getSmsCode')) || state.time + ' s'
}}</a-button>
</a-col>
@ -35,7 +35,7 @@
size="large"
>
<template #prefix>
<LockOutlined style="color: rgba(0, 0, 0, 0.25)" />
<LockOutlined class="xn-color-00025" />
</template>
</a-input-password>
</a-form-item>
@ -43,10 +43,10 @@
<a-form-item>
<a-row :gutter="8">
<a-col :span="7">
<a-button style="width: 100%" round size="large" href="/login">{{ $t('login.backLogin') }}</a-button>
<a-button class="xn-wd" round size="large" href="/login">{{ $t('login.backLogin') }}</a-button>
</a-col>
<a-col :span="17">
<a-button type="primary" style="width: 100%" :loading="islogin" round size="large" @click="submitReset">{{
<a-button type="primary" class="xn-wd" :loading="islogin" round size="large" @click="submitReset">{{
$t('login.restPassword')
}}</a-button>
</a-col>
@ -54,7 +54,7 @@
</a-form-item>
</a-form>
<a-modal
v-model:visible="visible"
v-model:open="visible"
:width="400"
:title="$t('login.machineValidation')"
@cancel="handleCancel"
@ -70,16 +70,12 @@
size="large"
>
<template #prefix>
<verified-outlined style="color: rgba(0, 0, 0, 0.25)" />
<verified-outlined class="xn-color-00025" />
</template>
</a-input>
</a-col>
<a-col :span="7">
<img
:src="validCodeBase64"
style="border: 1px solid var(--border-color-split); cursor: pointer; width: 100%; height: 40px"
@click="getPhonePicCaptcha"
/>
<img :src="validCodeBase64" class="xn-findform-line" @click="getPhonePicCaptcha" />
</a-col>
</a-row>
</a-form-item>
@ -121,23 +117,26 @@
formRules.value.emailValidCode = [required(), rules.number]
formRules.value.newPassword = [required()]
emailResetFormRef.value.validate().then(() => {
emailFormData.value.validCode = emailFormData.value.emailValidCode
emailFormData.value.validCodeReqNo = emailValidCodeReqNo.value
emailFormData.value.newPassword = smCrypto.doSm2Encrypt(emailFormData.value.newPassword)
islogin.value = true
userCenterApi
.userFindPasswordByEmail(emailFormData.value)
.then(() => {
router.replace({
path: '/'
emailResetFormRef.value
.validate()
.then(() => {
emailFormData.value.validCode = emailFormData.value.emailValidCode
emailFormData.value.validCodeReqNo = emailValidCodeReqNo.value
emailFormData.value.newPassword = smCrypto.doSm2Encrypt(emailFormData.value.newPassword)
islogin.value = true
userCenterApi
.userFindPasswordByEmail(emailFormData.value)
.then(() => {
router.replace({
path: '/'
})
message.success('找回成功')
})
message.success('找回成功')
})
.finally(() => {
islogin.value = false
})
})
.finally(() => {
islogin.value = false
})
})
.catch(() => {})
}
//

View File

@ -3,7 +3,7 @@
<a-form-item name="phone">
<a-input v-model:value="phoneFormData.phone" :placeholder="$t('login.phonePlaceholder')" size="large">
<template #prefix>
<mobile-outlined style="color: rgba(0, 0, 0, 0.25)" />
<mobile-outlined class="xn-color-00025" />
</template>
</a-input>
</a-form-item>
@ -16,12 +16,12 @@
size="large"
>
<template #prefix>
<mail-outlined style="color: rgba(0, 0, 0, 0.25)" />
<mail-outlined class="xn-color-00025" />
</template>
</a-input>
</a-col>
<a-col :span="7">
<a-button size="large" style="width: 100%" @click="getPhoneValidCode" :disabled="state.smsSendBtn">{{
<a-button size="large" class="xn-wd" @click="getPhoneValidCode" :disabled="state.smsSendBtn">{{
(!state.smsSendBtn && $t('login.getSmsCode')) || state.time + ' s'
}}</a-button>
</a-col>
@ -35,7 +35,7 @@
size="large"
>
<template #prefix>
<LockOutlined style="color: rgba(0, 0, 0, 0.25)" />
<LockOutlined class="xn-color-00025" />
</template>
</a-input-password>
</a-form-item>
@ -43,10 +43,10 @@
<a-form-item>
<a-row :gutter="8">
<a-col :span="7">
<a-button style="width: 100%" round size="large" href="/login">{{ $t('login.backLogin') }}</a-button>
<a-button class="xn-wd" round size="large" href="/login">{{ $t('login.backLogin') }}</a-button>
</a-col>
<a-col :span="17">
<a-button type="primary" style="width: 100%" :loading="islogin" round size="large" @click="submitReset">{{
<a-button type="primary" class="xn-wd" :loading="islogin" round size="large" @click="submitReset">{{
$t('login.restPassword')
}}</a-button>
</a-col>
@ -54,7 +54,7 @@
</a-form-item>
</a-form>
<a-modal
v-model:visible="visible"
v-model:open="visible"
:width="400"
:title="$t('login.machineValidation')"
@cancel="handleCancel"
@ -70,16 +70,12 @@
size="large"
>
<template #prefix>
<verified-outlined style="color: rgba(0, 0, 0, 0.25)" />
<verified-outlined class="xn-color-00025" />
</template>
</a-input>
</a-col>
<a-col :span="7">
<img
:src="validCodeBase64"
style="border: 1px solid var(--border-color-split); cursor: pointer; width: 100%; height: 40px"
@click="getPhonePicCaptcha"
/>
<img :src="validCodeBase64" class="xn-findform-line" @click="getPhonePicCaptcha" />
</a-col>
</a-row>
</a-form-item>
@ -122,23 +118,26 @@
formRules.value.phoneValidCode = [required(), rules.number]
formRules.value.newPassword = [required()]
phoneLoginFormRef.value.validate().then(() => {
phoneFormData.value.validCode = phoneFormData.value.phoneValidCode
phoneFormData.value.validCodeReqNo = phoneValidCodeReqNo.value
phoneFormData.value.newPassword = smCrypto.doSm2Encrypt(phoneFormData.value.newPassword)
islogin.value = true
userCenterApi
.userFindPasswordByPhone(phoneFormData.value)
.then(() => {
router.replace({
path: '/'
phoneLoginFormRef.value
.validate()
.then(() => {
phoneFormData.value.validCode = phoneFormData.value.phoneValidCode
phoneFormData.value.validCodeReqNo = phoneValidCodeReqNo.value
phoneFormData.value.newPassword = smCrypto.doSm2Encrypt(phoneFormData.value.newPassword)
islogin.value = true
userCenterApi
.userFindPasswordByPhone(phoneFormData.value)
.then(() => {
router.replace({
path: '/'
})
message.success('找回成功')
})
message.success('找回成功')
})
.finally(() => {
islogin.value = false
})
})
.finally(() => {
islogin.value = false
})
})
.catch(() => {})
}
//

View File

@ -78,7 +78,7 @@
</a-form-item>
<a-form-item>
<a href="/findpwd" style="color: #0d84ff">{{ $t('login.forgetPassword') }}</a>
<a href="/findpwd" class="xn-color-0d84ff">{{ $t('login.forgetPassword') }}</a>
</a-form-item>
<a-form-item>
<a-button type="primary" class="w-full" :loading="loading" round size="large" @click="login"
@ -213,24 +213,36 @@
//
const loginForm = ref()
const login = async () => {
loginForm.value.validate().then(async () => {
loading.value = true
const loginData = {
account: ruleForm.account,
// SM2使hash
password: smCrypto.doSm2Encrypt(ruleForm.password),
validCode: ruleForm.validCode,
validCodeReqNo: ruleForm.validCodeReqNo
}
// token
try {
const loginToken = await loginApi.login(loginData)
afterLogin(loginToken)
} catch (err) {
loading.value = false
loginCaptcha()
}
})
loginForm.value
.validate()
.then(async () => {
loading.value = true
const loginData = {
account: ruleForm.account,
// SM2使hash
password: smCrypto.doSm2Encrypt(ruleForm.password),
validCode: ruleForm.validCode,
validCodeReqNo: ruleForm.validCodeReqNo
}
// token
// loginApi.login(loginData).then((loginToken) => {
// afterLogin(loginToken)
// }).catch(() => {
// loading.value = false
// loginCaptcha()
// })
// token
try {
const loginToken = await loginApi.login(loginData)
const loginAfter = afterLogin(loginToken)
} catch (err) {
loading.value = false
loginCaptcha()
}
})
.catch(() => {})
}
const configLang = (key) => {
config.value.lang = key
@ -238,4 +250,7 @@
</script>
<style lang="less">
@import 'login';
.xn-color-0d84ff {
color: #0d84ff;
}
</style>

View File

@ -21,20 +21,20 @@
</a-input>
</a-col>
<a-col :span="7">
<a-button size="large" style="width: 100%" @click="getPhoneValidCode" :disabled="state.smsSendBtn">
<a-button size="large" class="xn-wd" @click="getPhoneValidCode" :disabled="state.smsSendBtn">
{{ (!state.smsSendBtn && $t('login.getSmsCode')) || state.time + ' s' }}
</a-button>
</a-col>
</a-row>
</a-form-item>
<a-form-item>
<a-button type="primary" style="width: 100%" :loading="loading" round size="large" @click="submitLogin">
<a-button type="primary" class="xn-wd" :loading="loading" round size="large" @click="submitLogin">
{{ $t('login.signIn') }}
</a-button>
</a-form-item>
</a-form>
<a-modal
v-model:visible="visible"
v-model:open="visible"
:width="400"
:title="$t('login.machineValidation')"
@cancel="handleCancel"
@ -57,7 +57,7 @@
<a-col :span="7">
<img
:src="validCodeBase64"
style="border: 1px solid var(--border-color-split); cursor: pointer; width: 100%; height: 40px"
class="xn-findform-line"
@click="getPhonePicCaptcha"
/>
</a-col>
@ -94,6 +94,7 @@
getPhonePicCaptcha()
})
}
//
const submitLogin = async () => {
formRules.value.phone = [required('请输入11位手机号'), rules.phone]
@ -105,14 +106,12 @@
phoneFormData.value.validCode = phoneFormData.value.phoneValidCode
// delete phoneFormData.value.phoneValidCode
phoneFormData.value.validCodeReqNo = phoneValidCodeReqNo.value
loading.value = true
try {
const token = await loginApi.loginByPhone(phoneFormData.value)
loginApi.loginByPhone(phoneFormData.value).then((token) => {
afterLogin(token)
} catch (err) {
}).catch((err) => {
loading.value = false
}
})
}
//

View File

@ -1,5 +1,5 @@
<template>
<div style="padding-bottom: 10px">
<div class="xn-pb10">
<a-row :gutter="16">
<a-col :span="6">
<a-card class="snowy-monitor-card" :bordered="false">
@ -15,7 +15,7 @@
<a-col :span="6">
<a-card class="snowy-monitor-card" :bordered="false">
<template #cover>
<verified-outlined style="color: rgb(255, 156, 110)" class="snowy-monitor-card-icon" />
<verified-outlined class="snowy-monitor-card-icon xn-color-ff9c6e" />
<div class="snowy-monitor-card-div">
<span class="snowy-monitor-card-span">最大签发令牌</span
><span class="snowy-monitor-card-span">{{ analysisObj.maxTokenCount }}</span>
@ -26,7 +26,7 @@
<a-col :span="6">
<a-card class="snowy-monitor-card" :bordered="false">
<template #cover>
<rise-outlined style="color: rgb(255, 133, 192)" class="snowy-monitor-card-icon" />
<rise-outlined class="snowy-monitor-card-icon xn-color-ff85c0" />
<div class="snowy-monitor-card-div">
<span class="snowy-monitor-card-span">1小时内新增</span
><span class="snowy-monitor-card-span">{{ analysisObj.oneHourNewlyAdded }}</span>
@ -37,7 +37,7 @@
<a-col :span="6">
<a-card class="snowy-monitor-card" :bordered="false">
<template #cover>
<pie-chart-outlined style="color: rgb(92, 219, 211)" class="snowy-monitor-card-icon" />
<pie-chart-outlined class="snowy-monitor-card-icon xn-color-5cdbd3" />
<div class="snowy-monitor-card-div">
<span class="snowy-monitor-card-span">B/C端占比</span
><span class="snowy-monitor-card-span">{{ analysisObj.proportionOfBAndC }}</span>
@ -80,4 +80,13 @@
.snowy-monitor-card-span {
font-size: 16px;
}
.xn-color-ff9c6e {
color: #ff9c6e;
}
.xn-color-ff85c0 {
color: #ff85c0;
}
.xn-color-5cdbd3 {
color: #5cdbd3;
}
</style>

View File

@ -2,7 +2,7 @@
<s-table ref="tableRef" :columns="columns" :data="loadDataB" :alert="false" bordered :row-key="(record) => record.id">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'avatar'">
<a-avatar :src="record.avatar" style="width: 25px; height: 25px" />
<a-avatar :src="record.avatar" class="xn-wh25" />
</template>
<template v-if="column.dataIndex === 'tokenNumber'">
{{ record.tokenSignList.length }}

View File

@ -2,7 +2,7 @@
<s-table ref="tableRef" :columns="columns" :data="loadDataC" :alert="false" bordered :row-key="(record) => record.id">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'avatar'">
<a-avatar :src="record.avatar" style="width: 25px; height: 25px" />
<a-avatar :src="record.avatar" class="xn-wh25" />
</template>
<template v-if="column.dataIndex === 'tokenNumber'">
{{ record.tokenSignList.length }}

View File

@ -2,7 +2,7 @@
<xn-form-container title="令牌列表" :width="650" :visible="visible" :destroy-on-close="true" @close="onClose">
<a-button
danger
style="margin-bottom: 10px"
class="xn-mb10"
:disabled="selectedRowKeys.length === 0"
:loading="beatchExitLoading"
@click="beachExitTokenValue"
@ -14,6 +14,7 @@
:row-selection="rowSelection"
bordered
:row-key="(record) => record.tokenValue"
size="small"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'tokenDevice'">

View File

@ -3,7 +3,7 @@
<a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
<a-row :gutter="24">
<a-col :span="8">
<a-form-item label="关键" name="searchKey">
<a-form-item label="关键" name="searchKey">
<a-input v-model:value="searchFormState.searchKey" placeholder="请输入用户名或昵称关键词"></a-input>
</a-form-item>
</a-col>
@ -15,7 +15,7 @@
</a-col>
<a-col :span="6">
<a-button type="primary" @click="tableRef.refresh(true)"></a-button>
<a-button style="margin: 0 8px" @click="reset"></a-button>
<a-button class="xn-mg08" @click="reset"></a-button>
</a-col>
</a-row>
</a-form>

View File

@ -6,7 +6,7 @@
:disabled="true"
v-model:value="formData.parentId"
v-model:treeExpandedKeys="defaultExpandedKeys"
style="width: 100%"
class="xn-wd"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择上级字典"
allow-clear
@ -27,11 +27,11 @@
<a-input v-model:value="formData.dictValue" placeholder="请输入字典值" allow-clear :disabled="true" />
</a-form-item>
<a-form-item label="排序:" name="sortCode">
<a-input-number style="width: 100%" v-model:value="formData.sortCode" :max="1000" />
<a-input-number class="xn-wd" v-model:value="formData.sortCode" :max="1000" />
</a-form-item>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose"></a-button>
<a-button class="xn-mr8" @click="onClose"></a-button>
<a-button type="primary" @click="onSubmit"></a-button>
</template>
</xn-form-container>
@ -94,12 +94,15 @@
})
//
const onSubmit = () => {
formRef.value.validate().then(() => {
bizDictApi.submitForm(formData.value).then(() => {
visible.value = false
emit('successful')
formRef.value
.validate()
.then(() => {
bizDictApi.submitForm(formData.value).then(() => {
visible.value = false
emit('successful')
})
})
})
.catch(() => {})
}
//
defineExpose({

View File

@ -13,7 +13,7 @@
</a-card>
</a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="19" :xl="19">
<a-card :bordered="false" style="margin-bottom: 10px">
<a-card :bordered="false" class="xn-mb10">
<a-form
ref="searchFormRef"
name="advanced_search"
@ -39,7 +39,7 @@
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" style="margin-bottom: 10px">
<a-card :bordered="false" class="xn-mb10">
<s-table
ref="tableRef"
:columns="columns"

View File

@ -10,7 +10,7 @@
<a-form-item label="上级机构:" name="parentId">
<a-tree-select
v-model:value="formData.parentId"
style="width: 100%"
class="xn-wd"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择上级机构"
allow-clear
@ -32,33 +32,27 @@
<a-select
v-model:value="formData.category"
:options="orgCategoryOptions"
style="width: 100%"
class="xn-wd"
placeholder="请选择机构分类"
/>
</a-form-item>
<a-form-item label="排序:" name="sortCode">
<a-input-number style="width: 100%" v-model:value="formData.sortCode" :max="100" />
<a-input-number class="xn-wd" v-model:value="formData.sortCode" :max="100" />
</a-form-item>
<a-form-item label="指定主管:" name="directorId">
<a-button type="link" style="padding-left: 0px" @click="openSelector(formData.directorId)"></a-button>
<a-tag v-if="formData.directorId && formData.directorName" color="orange" closable @close="closeUserTag">{{
formData.directorName
}}</a-tag>
<a-input v-show="false" v-model:value="formData.directorId" />
<xn-user-selector
:org-tree-api="selectorApiFunction.orgTreeApi"
:user-page-api="selectorApiFunction.userPageApi"
:user-list-by-id-list-api="selectorApiFunction.checkedUserListApi"
:radio-model="true"
v-model:value="formData.directorId"
/>
</a-form-item>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose"></a-button>
<a-button class="xn-mr8" @click="onClose"></a-button>
<a-button type="primary" :loading="submitLoading" @click="onSubmit"></a-button>
</template>
<user-selector-plus
ref="UserSelectorPlus"
:org-tree-api="selectorApiFunction.orgTreeApi"
:user-page-api="selectorApiFunction.userPageApi"
:checked-user-list-api="selectorApiFunction.checkedUserListApi"
:radio-model="true"
@onBack="userBack"
/>
</xn-form-container>
</template>
@ -66,14 +60,12 @@
import { required } from '@/utils/formRules'
import bizOrgApi from '@/api/biz/bizOrgApi'
import userCenterApi from '@/api/sys/userCenterApi'
import userSelectorPlus from '@/components/Selector/userSelectorPlus.vue'
import tool from '@/utils/tool'
// emit
const emit = defineEmits({ successful: null })
//
const visible = ref(false)
let UserSelectorPlus = ref()
const formRef = ref()
//
const formData = ref({})
@ -116,33 +108,13 @@
}
//
const formRules = {
parentId: [required('请选择上级机构')],
name: [required('请输入机构名称')],
category: [required('请选择机构分类')],
sortCode: [required('请选择排序')]
}
//
const orgCategoryOptions = tool.dictList('ORG_CATEGORY')
//
const openSelector = (id) => {
let checkedUserIds = []
checkedUserIds.push(id)
UserSelectorPlus.value.showUserPlusModal(checkedUserIds)
}
//
const userBack = (value) => {
if (value.length > 0) {
formData.value.directorId = value[0].id
formData.value.directorName = value[0].name
} else {
formData.value.directorId = ''
formData.value.directorName = ''
}
}
//
const closeUserTag = () => {
formData.value.directorId = ''
formData.value.directorName = ''
}
//
const onSubmit = () => {
formRef.value

View File

@ -14,7 +14,7 @@
</a-card>
</a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="19" :xl="19">
<a-card :bordered="false" style="margin-bottom: 10px">
<a-card :bordered="false" class="xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
@ -27,7 +27,7 @@
<template #icon><SearchOutlined /></template>
查询
</a-button>
<a-button class="snowy-buttom-left" @click="reset">
<a-button class="snowy-button-left" @click="reset">
<template #icon><redo-outlined /></template>
重置
</a-button>
@ -213,10 +213,7 @@
.ant-form-item {
margin-bottom: 0 !important;
}
.primaryAdd {
margin-right: 10px;
}
.snowy-buttom-left {
.snowy-button-left {
margin-left: 8px;
}
</style>

View File

@ -10,7 +10,7 @@
<a-form-item label="所属组织:" name="orgId">
<a-tree-select
v-model:value="formData.orgId"
style="width: 100%"
class="xn-wd"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择组织"
allow-clear
@ -32,17 +32,17 @@
<a-select
v-model:value="formData.category"
:options="positionCategoryOptions"
style="width: 100%"
class="xn-wd"
placeholder="请选择岗位分类"
>
</a-select>
</a-form-item>
<a-form-item label="排序:" name="sortCode">
<a-input-number style="width: 100%" v-model:value="formData.sortCode" :max="100" />
<a-input-number class="xn-wd" v-model:value="formData.sortCode" :max="100" />
</a-form-item>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose"></a-button>
<a-button class="xn-mr8" @click="onClose"></a-button>
<a-button type="primary" :loading="submitLoading" @click="onSubmit"></a-button>
</template>
</xn-form-container>

View File

@ -14,7 +14,7 @@
</a-card>
</a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="19" :xl="19">
<a-card :bordered="false" style="margin-bottom: 10px">
<a-card :bordered="false" class="xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
@ -27,7 +27,7 @@
<template #icon><SearchOutlined /></template>
查询
</a-button>
<a-button class="snowy-buttom-left" @click="reset">
<a-button class="snowy-button-left" @click="reset">
<template #icon><redo-outlined /></template>
重置
</a-button>
@ -210,10 +210,7 @@
.ant-form-item {
margin-bottom: 0 !important;
}
.primaryAdd {
margin-right: 10px;
}
.snowy-buttom-left {
.snowy-button-left {
margin-left: 8px;
}
</style>

View File

@ -49,7 +49,7 @@
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="出生日期:" name="birthday">
<a-date-picker v-model:value="formData.birthday" value-format="YYYY-MM-DD" style="width: 100%" />
<a-date-picker v-model:value="formData.birthday" value-format="YYYY-MM-DD" class="xn-wd" />
</a-form-item>
</a-col>
</a-row>
@ -58,7 +58,7 @@
<a-form-item label="选择组织:" name="orgId">
<a-tree-select
v-model:value="formData.orgId"
style="width: 100%"
class="xn-wd"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择组织"
allow-clear
@ -112,7 +112,7 @@
</a-col>
<a-col :span="8">
<a-form-item label="入职日期:" name="entryDate">
<a-date-picker v-model:value="formData.entryDate" value-format="YYYY-MM-DD" style="width: 100%" />
<a-date-picker v-model:value="formData.entryDate" value-format="YYYY-MM-DD" class="xn-wd" />
</a-form-item>
</a-col>
</a-row>
@ -137,7 +137,7 @@
>
<a-tree-select
v-model:value="positionInfo.orgId"
style="width: 100%"
class="xn-wd"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择机构"
allow-clear
@ -176,7 +176,7 @@
/>
</a-form-item>
</a-col>
<a-col :span="3" style="margin-top: 4px">
<a-col :span="3" class="xn-mt4">
<a-button size="small" type="primary" danger ghost @click="delDomains(index)"></a-button>
</a-col>
</a-row>
@ -316,13 +316,13 @@
</a-tabs>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose"></a-button>
<a-button class="xn-mr8" @click="onClose"></a-button>
<a-button type="primary" :loading="formLoading" @click="onSubmit"></a-button>
</template>
</xn-form-container>
</template>
<script setup>
<script setup name="bizUser">
import bizUserApi from '@/api/biz/bizUserApi'
import { required } from '@/utils/formRules'
import tool from '@/utils/tool'
@ -507,25 +507,28 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
//
let formDatas = JSON.parse(JSON.stringify(formData.value))
if (formDatas.positionJson && formDatas.positionJson.length > 0) {
formDatas.positionJson = JSON.stringify(formDatas.positionJson)
} else {
delete formDatas.positionJson
}
formLoading.value = true
bizUserApi
.submitForm(formDatas, formDatas.id)
.then(() => {
onClose()
emit('successful')
})
.finally(() => {
formLoading.value = false
})
})
formRef.value
.validate()
.then(() => {
//
let formDatas = JSON.parse(JSON.stringify(formData.value))
if (formDatas.positionJson && formDatas.positionJson.length > 0) {
formDatas.positionJson = JSON.stringify(formDatas.positionJson)
} else {
delete formDatas.positionJson
}
formLoading.value = true
bizUserApi
.submitForm(formDatas, formDatas.id)
.then(() => {
onClose()
emit('successful')
})
.finally(() => {
formLoading.value = false
})
})
.catch(() => {})
}
//
const genderOptions = tool.dictList('GENDER')

View File

@ -14,7 +14,7 @@
</a-card>
</a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="19" :xl="19">
<a-card :bordered="false" style="margin-bottom: 10px">
<a-card :bordered="false" class="xn-mb10">
<a-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
<a-row :gutter="24">
<a-col :span="8">
@ -39,7 +39,7 @@
<template #icon><SearchOutlined /></template>
{{ $t('common.searchButton') }}
</a-button>
<a-button class="snowy-buttom-left" @click="reset">
<a-button class="snowy-button-left" @click="reset">
<template #icon><redo-outlined /></template>
{{ $t('common.resetButton') }}
</a-button>
@ -418,14 +418,11 @@
.ant-form-item {
margin-bottom: 0 !important;
}
.primaryAdd {
margin-right: 10px;
}
.snowy-table-avatar {
margin-top: -10px;
margin-bottom: -10px;
}
.snowy-buttom-left {
.snowy-button-left {
margin-left: 8px;
}
</style>

View File

@ -19,7 +19,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -59,22 +59,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -16,7 +16,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -55,22 +55,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -19,7 +19,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>

View File

@ -22,7 +22,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -63,22 +63,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -16,7 +16,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -55,22 +55,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -22,7 +22,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -63,22 +63,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -22,7 +22,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -63,22 +63,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -1,6 +1,6 @@
<template>
<a-card
style="width: 100%"
class="xn-wd"
:bordered="false"
:tab-list="tabListNoTitle"
:active-tab-key="noTitleKey"

View File

@ -21,11 +21,11 @@
<a-input v-model:value="formData.remark" placeholder="请输入备注" allow-clear />
</a-form-item>
<a-form-item label="排序:" name="sortCode">
<a-input-number style="width: 100%" v-model:value="formData.sortCode" :max="1000" />
<a-input-number class="xn-wd" v-model:value="formData.sortCode" :max="1000" />
</a-form-item>
</a-form>
<template #footer>
<a-button style="margin-right: 8px" @click="onClose"></a-button>
<a-button class="xn-mr8" @click="onClose"></a-button>
<a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
</template>
</xn-form-container>
@ -66,18 +66,21 @@
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
configApi
.submitForm(formData.value, formData.value.id)
.then(() => {
onClose()
emit('successful')
})
.finally(() => {
submitLoading.value = false
})
})
formRef.value
.validate()
.then(() => {
submitLoading.value = true
configApi
.submitForm(formData.value, formData.value.id)
.then(() => {
onClose()
emit('successful')
})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
//

View File

@ -18,7 +18,7 @@
</a-button>
<a-input-search
v-model:value="searchFormState.searchKey"
placeholder="请输入关键"
placeholder="请输入关键"
enter-button
allowClear
@search="tableRef.refresh(true)"

View File

@ -22,7 +22,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -63,22 +63,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -1,16 +1,20 @@
<template>
<a-tabs v-model:activeKey="activeKey" tab-position="left">
<a-tab-pane key="xiaonuoSms" tab="小诺短信">
<xiaonuo-sms-form />
</a-tab-pane>
<a-tab-pane key="aliyunSms" tab="阿里短信">
<aliyunSmsForm />
<aliyun-sms-form />
</a-tab-pane>
<a-tab-pane key="tencentSms" tab="腾讯短信">
<tencentSmsForm />
<tencent-sms-form />
</a-tab-pane>
</a-tabs>
</template>
<script setup name="smsConfig">
import XiaonuoSmsForm from './xiaonuoSmsForm.vue'
import AliyunSmsForm from './aliyunSmsForm.vue'
import TencentSmsForm from './tencentSmsForm.vue'
const activeKey = ref('aliyunSms')
const activeKey = ref('xiaonuoSms')
</script>

View File

@ -25,7 +25,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -67,22 +67,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

View File

@ -0,0 +1,108 @@
<template>
<a-spin :spinning="loadSpinning">
<a-form
ref="formRef"
:model="formData"
:rules="formRules"
layout="vertical"
:label-col="{ ...layout.labelCol, offset: 0 }"
:wrapper-col="{ ...layout.wrapperCol, offset: 0 }"
>
<a-form-item name="SNOWY_SMS_XIAONUO_ACCESS_KEY_ID">
<template #label>
<a-tooltip>
<template #title> 通过官网申请短信或联系站长 </template>
<question-circle-outlined />
</a-tooltip>
&nbsp 小诺短信账号
</template>
<a-input v-model:value="formData.SNOWY_SMS_XIAONUO_ACCESS_KEY_ID" placeholder="请输入小诺短信账号" />
</a-form-item>
<a-form-item label="小诺短信秘钥:" name="SNOWY_SMS_XIAONUO_ACCESS_KEY_SECRET">
<a-input v-model:value="formData.SNOWY_SMS_XIAONUO_ACCESS_KEY_SECRET" placeholder="请输入小诺短信秘钥" />
</a-form-item>
<a-form-item label="发送短信URL" name="SNOWY_SMS_XIAONUO_REQUEST_URL">
<a-input v-model:value="formData.SNOWY_SMS_XIAONUO_REQUEST_URL" placeholder="请输入发送短信URL" />
</a-form-item>
<a-form-item name="SNOWY_SMS_XIAONUO_DEFAULT_SIGN_NAME">
<template #label>
<a-tooltip>
<template #title> 若账号跟密钥已绑定签名则此处配置签名后无效 </template>
<question-circle-outlined />
</a-tooltip>
&nbsp 短信签名
</template>
<a-input v-model:value="formData.SNOWY_SMS_XIAONUO_DEFAULT_SIGN_NAME" placeholder="请输入短信签名" />
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
</template>
<script setup name="xiaonuoSmsForm">
import { cloneDeep } from 'lodash-es'
import { required } from '@/utils/formRules'
import { message } from 'ant-design-vue'
import configApi from '@/api/dev/configApi'
const formRef = ref()
const formData = ref({})
const submitLoading = ref(false)
const loadSpinning = ref(true)
// ,
const param = {
category: 'SMS_XIAONUO'
}
configApi.configList(param).then((data) => {
loadSpinning.value = false
if (data) {
data.forEach((item) => {
formData.value[item.configKey] = item.configValue
})
} else {
message.warning('表单项不存在,请初始化数据库')
}
})
//
const formRules = {
SNOWY_SMS_XIAONUO_ACCESS_KEY_ID: [required('请输入小诺短信账号')],
SNOWY_SMS_XIAONUO_ACCESS_KEY_SECRET: [required('请输入小诺短信秘钥')],
SNOWY_SMS_XIAONUO_REQUEST_URL: [required('请输入发送短信URL')],
SNOWY_SMS_XIAONUO_DEFAULT_SIGN_NAME: [required('请输入短信签名')]
}
//
const onSubmit = () => {
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {
span: 4
},
wrapperCol: {
span: 12
}
}
</script>

View File

@ -88,7 +88,7 @@
<a-col :span="24">
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="resetForm"></a-button>
<a-button class="xn-ml10" @click="resetForm"></a-button>
</a-form-item>
</a-col>
</a-row>
@ -100,6 +100,7 @@
import { cloneDeep } from 'lodash-es'
import { required } from '@/utils/formRules'
import { message } from 'ant-design-vue'
import { globalStore } from '@/store'
import configApi from '@/api/dev/configApi'
import tool from '@/utils/tool'
import MenuTreeSelect from '@/components/TreeSelect/menuTreeSelect.vue'
@ -113,6 +114,7 @@
const imageUrl = ref('')
const menuTreeSelectRef = ref()
const loadSpinning = ref(true)
const store = globalStore()
// ,
const param = {
category: 'SYS_BASE'
@ -191,6 +193,11 @@
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
//
const shortcut = {
shortcut: menuTreeSelectRef.value.getSelectData()
}
submitParam.SNOWY_SYS_DEFAULT_WORKBENCH_DATA = JSON.stringify(shortcut)
submitParam.SNOWY_SYS_LOGO = submitParam.SNOWY_SYS_LOGO[0]
const param = Object.entries(submitParam).map((item) => {
return {
@ -198,17 +205,13 @@
configValue: item[1]
}
})
//
const shortcut = {
shortcut: menuTreeSelectRef.value.getSelectData()
}
param.push({
configKey: 'SNOWY_SYS_DEFAULT_WORKBENCH_DATA',
configValue: JSON.stringify(shortcut)
})
configApi
.configEditForm(param)
.then(() => {})
.then(() => {
// 使
tool.data.set('SNOWY_SYS_BASE_CONFIG', submitParam)
store.setSysBaseConfig(submitParam)
})
.finally(() => {
submitLoading.value = false
})

View File

@ -19,7 +19,7 @@
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="submitLoading" @click="onSubmit()"></a-button>
<a-button style="margin-left: 10px" @click="() => formRef.resetFields()">重置</a-button>
<a-button class="xn-ml10" @click="() => formRef.resetFields()">重置</a-button>
</a-form-item>
</a-form>
</a-spin>
@ -59,22 +59,25 @@
}
//
const onSubmit = () => {
formRef.value.validate().then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
formRef.value
.validate()
.then(() => {
submitLoading.value = true
let submitParam = cloneDeep(formData.value)
const param = Object.entries(submitParam).map((item) => {
return {
configKey: item[0],
configValue: item[1]
}
})
})
configApi
.configEditForm(param)
.then(() => {})
.finally(() => {
submitLoading.value = false
})
})
.catch(() => {})
}
const layout = {
labelCol: {

Some files were not shown because too many files have changed in this diff Show More