Compare commits

..

127 Commits

Author SHA1 Message Date
e0cf0ae665 大屏项目初始化 2026-03-15 21:48:37 +08:00
2581b847e2 大屏项目初始化 2026-03-15 21:19:11 +08:00
51b2de579e 大屏页面初始化 2026-03-13 14:40:01 +08:00
30ac9fa3ee 大屏页面初始化 2026-03-13 11:02:22 +08:00
2a1c10422b 大屏页面初始化 2026-03-13 10:05:03 +08:00
be0a64e7bb 大屏页面初始化 2026-03-13 09:34:22 +08:00
69c1447568 大屏页面初始化 2026-03-13 09:24:00 +08:00
3937878cd0 大屏项目初始化 2026-03-12 23:29:01 +08:00
1091752222 大屏项目初始化 2026-03-12 22:59:32 +08:00
c113b6d3f1 大屏项目初始化 2026-03-12 22:46:15 +08:00
6990ae425b 大屏项目初始化 2026-03-12 22:44:55 +08:00
a930c3e17c 大屏项目初始化 2026-03-12 20:53:15 +08:00
7da3176fcb 大屏页面初始化 2026-03-12 18:07:50 +08:00
1478673678 大屏页面初始化 2026-03-12 11:25:46 +08:00
f3bb1ba118 大屏项目初始化 2026-03-11 23:09:00 +08:00
0763e1cc40 大屏项目初始化 2026-03-11 23:02:40 +08:00
ed212d6757 大屏项目初始化 2026-03-11 22:19:00 +08:00
429fe75039 大屏项目初始化 2026-03-11 21:53:42 +08:00
8069e437dc 大屏页面初始化 2026-03-11 18:20:02 +08:00
786217af69 大屏页面初始化 2026-03-11 18:06:39 +08:00
a560cf5acb 大屏页面初始化 2026-03-11 10:42:44 +08:00
ee7e32e738 大屏页面初始化 2026-03-11 10:32:51 +08:00
a65912bf8b 大屏页面初始化 2026-03-11 09:27:19 +08:00
b17a12c2d7 大屏页面初始化 2026-03-11 09:18:47 +08:00
c74e312115 大屏项目初始化 2026-03-10 23:44:04 +08:00
1998f83dc7 大屏项目初始化 2026-03-10 23:36:13 +08:00
8b19577b00 大屏项目初始化 2026-03-10 23:35:11 +08:00
518b227823 大屏项目初始化 2026-03-10 23:35:01 +08:00
66ba1f496e 大屏项目初始化 2026-03-10 23:34:35 +08:00
7558b6b6ac 大屏项目初始化 2026-03-10 23:30:36 +08:00
ddd39f73ff 大屏项目初始化 2026-03-10 23:29:10 +08:00
7725bb8cd8 大屏项目初始化 2026-03-10 23:26:47 +08:00
213fe276d1 大屏项目初始化 2026-03-10 23:06:48 +08:00
783a1749f7 大屏项目初始化 2026-03-10 23:06:39 +08:00
aa08785db5 大屏项目初始化 2026-03-10 23:06:16 +08:00
e6f7af148b 大屏页面初始化 2026-03-10 17:46:33 +08:00
904ef23fbe 大屏页面初始化 2026-03-10 15:00:20 +08:00
f2f9e56d2d 大屏页面初始化 2026-03-10 14:32:25 +08:00
982da00b50 大屏页面初始化 2026-03-10 14:25:15 +08:00
7cfb1b1d59 大屏页面初始化 2026-03-10 09:55:27 +08:00
1edb1887ab 大屏项目初始化 2026-03-09 22:55:13 +08:00
4a9c3d2cf2 大屏项目初始化 2026-03-09 22:05:19 +08:00
386854bf66 大屏页面初始化 2026-03-09 17:20:57 +08:00
03c61b4501 大屏项目初始化 2026-03-08 23:38:37 +08:00
2c81b1478c 大屏项目初始化 2026-03-08 16:52:55 +08:00
444e65c524 大屏项目初始化 2026-03-08 13:06:17 +08:00
f14cde0287 大屏项目初始化 2026-03-08 13:05:57 +08:00
a052721622 大屏项目初始化 2026-03-07 19:08:40 +08:00
b7bfdfd2bc 大屏项目初始化 2026-03-07 12:13:00 +08:00
d887108e1a 大屏页面初始化 2026-03-06 17:45:03 +08:00
a5d6906b5a 大屏页面初始化 2026-03-06 17:44:50 +08:00
9571a1e3f0 大屏页面初始化 2026-03-06 17:26:07 +08:00
5ce6c6800f 大屏页面初始化 2026-03-06 17:25:46 +08:00
b9d209f7ef 大屏页面初始化 2026-03-06 15:43:05 +08:00
4c62e80407 大屏页面初始化 2026-03-06 10:47:14 +08:00
526d16284e 大屏页面初始化 2026-03-06 09:41:56 +08:00
c892ceef95 大屏页面初始化 2026-03-06 09:33:11 +08:00
9a8a2c1bc0 大屏页面初始化 2026-03-06 09:30:18 +08:00
b582ef0232 大屏项目初始化 2026-03-05 23:42:02 +08:00
953f4e1211 大屏项目初始化 2026-03-05 23:23:48 +08:00
a56cbbb078 大屏项目初始化 2026-03-05 23:17:02 +08:00
3422f7d4cc 大屏项目初始化 2026-03-05 23:00:00 +08:00
4c1451f99b 大屏项目初始化 2026-03-05 22:49:06 +08:00
eb28c8c4a1 大屏项目初始化 2026-03-05 22:34:48 +08:00
6660606083 大屏项目初始化 2026-03-05 22:34:15 +08:00
3ee4fbfd8c 大屏项目初始化 2026-03-05 22:32:39 +08:00
6cd69428af 大屏页面初始化 2026-03-05 19:13:28 +08:00
856b54e83b 大屏页面初始化 2026-03-05 19:04:12 +08:00
cf65afb47f 大屏页面初始化 2026-03-05 18:33:45 +08:00
d3d713f131 大屏页面初始化 2026-03-05 18:32:39 +08:00
b9dbd542e7 大屏项目初始化 2026-03-04 23:42:19 +08:00
e23a1f3965 大屏页面初始化 2026-03-04 18:07:18 +08:00
7668e97467 大屏页面初始化 2026-03-04 17:12:20 +08:00
35b0667446 大屏页面初始化 2026-03-04 16:55:42 +08:00
f3e0444579 大屏页面初始化 2026-03-04 12:41:47 +08:00
2354fa1dc7 大屏页面初始化 2026-03-04 10:55:53 +08:00
0fd9ae43c6 大屏页面初始化 2026-03-04 10:05:05 +08:00
faaffef2f8 大屏页面初始化 2026-03-04 09:58:11 +08:00
9d4d641fa8 大屏项目初始化 2026-03-03 23:47:22 +08:00
424527c62f 大屏项目初始化 2026-03-03 23:05:02 +08:00
3df814d718 大屏页面初始化 2026-03-03 17:47:22 +08:00
24aeaef7cb 大屏页面初始化 2026-03-03 17:45:50 +08:00
d64a6ef700 大屏页面初始化 2026-03-03 17:39:57 +08:00
dec50927da 大屏页面初始化 2026-03-03 17:37:48 +08:00
353ed9cd22 大屏页面初始化 2026-03-03 17:35:54 +08:00
6f9562d121 大屏页面初始化 2026-03-03 10:42:25 +08:00
ab8928b7e3 大屏页面初始化 2026-03-03 09:51:26 +08:00
42b9bead15 大屏页面初始化 2026-03-03 09:37:37 +08:00
2528968f70 大屏项目初始化 2026-03-03 00:18:41 +08:00
34d7994b41 大屏项目初始化 2026-03-03 00:16:24 +08:00
5b7abef74c 大屏项目初始化 2026-03-03 00:16:12 +08:00
866aaab059 大屏项目初始化 2026-03-02 22:10:01 +08:00
cceaea375c 大屏页面初始化 2026-03-02 18:06:09 +08:00
e21ced5c40 大屏页面初始化 2026-03-02 14:33:24 +08:00
1de57f2089 大屏页面初始化 2026-03-02 13:57:22 +08:00
d609cc45f0 大屏页面初始化 2026-03-02 10:36:34 +08:00
d906e58f78 大屏项目初始化 2026-03-02 00:21:05 +08:00
7d3d82c0ab 大屏项目初始化 2026-03-01 23:29:36 +08:00
86c1e58644 大屏项目初始化 2026-03-01 23:21:08 +08:00
0a95924d34 大屏项目初始化 2026-03-01 23:06:58 +08:00
9781439820 大屏项目初始化 2026-03-01 22:16:05 +08:00
8f469c5345 大屏项目初始化 2026-03-01 22:04:09 +08:00
78e52da94f 大屏项目初始化 2026-03-01 21:28:05 +08:00
ee9569953d 大屏页面初始化 2026-02-28 17:06:42 +08:00
3548359387 大屏页面初始化 2026-02-28 10:59:21 +08:00
6dc7e99b4b 大屏项目初始化 2026-02-27 23:52:07 +08:00
f7f1e393c8 大屏页面初始化 2026-02-27 17:18:21 +08:00
dfa42f2a20 大屏页面初始化 2026-02-27 17:15:59 +08:00
7c66b7bab1 大屏页面初始化 2026-02-27 16:35:00 +08:00
ffbaa6e070 大屏页面初始化 2026-02-27 16:03:39 +08:00
a45b59bc99 大屏页面初始化 2026-02-27 16:03:09 +08:00
bffb0cf41f 大屏页面初始化 2026-02-27 15:52:28 +08:00
911aa34609 大屏页面初始化 2026-02-27 15:27:38 +08:00
516dee5056 大屏页面初始化 2026-02-27 14:51:21 +08:00
368127ae19 大屏页面初始化 2026-02-27 13:59:38 +08:00
381b5c6460 优化接口数据查询 2026-02-27 11:28:41 +08:00
65abf51219 优化接口数据查询 2026-02-27 11:23:12 +08:00
4d1d56a36b 登录token优化 2026-02-27 11:17:33 +08:00
7c65c549a0 登录token优化 2026-02-27 11:14:38 +08:00
c677027988 登录token优化 2026-02-27 11:11:02 +08:00
3b228a82ba 大屏项目初始化 2026-02-26 22:27:38 +08:00
5ee7b83db4 大屏项目初始化 2026-02-26 22:15:52 +08:00
ed0f624233 大屏项目初始化 2026-02-26 21:58:30 +08:00
36c8df2bc4 大屏项目初始化 2026-02-26 21:58:09 +08:00
28cb663bf3 大屏页面布局 2026-02-26 17:13:32 +08:00
44f744010e 大屏页面布局 2026-02-26 17:03:00 +08:00
1ce4596b6a 大屏页面布局 2026-02-26 15:35:34 +08:00
325 changed files with 25867 additions and 3149 deletions

186
pom.xml
View File

@@ -6,98 +6,151 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
<relativePath/>
</parent>
<groupId>com.mini</groupId>
<artifactId>my-bigScreen</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>bigScreen</name>
<description>bigScreen</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<!-- 阿里云镜像:解决依赖拉取问题 -->
<repositories>
<repository>
<id>aliyunmaven</id>
<name>Aliyun Maven Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
<properties>
<java.version>17</java.version>
<mybatis-spring.version>3.0.3</mybatis-spring.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version>
<mybatis-plus-join.version>1.4.4</mybatis-plus-join.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 2. 邮件依赖替换为Jakarta版本适配Spring Boot 3.x -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
<artifactId>jakarta.mail</artifactId>
<version>2.0.1</version>
</dependency>
<!-- 3. SSH/FTP/工具类依赖 -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.11.0</version> <!-- 请根据需要使用最新版本 -->
<version>3.11.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.52</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.31</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version>
</dependency>
<!-- Apache POI OOXML 包xlsx格式核心缺失项 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
<!-- 4. MyBatis-Plus核心强制排除旧版mybatis-spring -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
<version>${mybatis-plus.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.5.7</version>
<version>${mybatis-plus.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
<version>${mybatis-plus.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>${mybatis-plus-join.version}</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version> <!-- 最新版 -->
<version>2.3</version>
<exclusions>
<exclusion>
<groupId>org.apache.commons</groupId>
@@ -109,20 +162,8 @@
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.31</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 8. 数据库驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
@@ -134,49 +175,16 @@
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>6.5.4</version>
</dependency>
<!-- Actuator 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>3.5.5</version>
</dependency>
<!-- WebSocket支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 9. 简化开发 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@@ -186,6 +194,11 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
@@ -193,7 +206,7 @@
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 关键:生成可执行 JAR -->
<goal>repackage</goal>
</goals>
</execution>
</executions>
@@ -208,5 +221,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>myPro大屏可视化</title>
</head>
<body>
<div id="app"></div>

File diff suppressed because it is too large Load Diff

View File

@@ -13,13 +13,17 @@
"echarts": "^6.0.0",
"element-plus": "^2.13.2",
"file-saver": "^2.0.5",
"pinia": "^3.0.4",
"sql-formatter": "^15.7.2",
"v-scale-screen": "^2.3.0",
"vue": "^3.5.28",
"vue-router": "^5.0.3",
"vue3-draggable-next": "^4.1.4",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.4",
"sass": "^1.98.0",
"vite": "^7.3.1",
"vite-plugin-vue-devtools": "^8.0.6"
},

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1771407056700" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11634" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#FAAD14" p-id="11635"></path><path d="M564.098612 667.021061h0.543347a16.509388 16.509388 0 0 1 0 33.039674h-41.252571v37.156571a20.58449 20.58449 0 1 1-41.148082 0v-37.156571h-41.795918a16.509388 16.509388 0 0 1 0-32.997878h41.795918v-24.764081h-41.795918a16.509388 16.509388 0 0 1 0-32.997878h20.082939l-17.303511-53.331592-0.229877-0.731428a16.48849 16.48849 0 0 1 31.346939-10.177307l0.167183 0.501551v0.376164l20.48 63.320816h15.318204l20.500898-63.237224v-0.41796l0.397061-0.480653a16.48849 16.48849 0 0 1 31.346939 10.114613l-0.229877 0.689632-17.30351 53.331592h19.623183a16.509388 16.509388 0 0 1 0 33.039674h-41.252571v24.722285h40.709224z" fill="#FAAD14" p-id="11636"></path><path d="M623.929469 302.247184l-116.610612 116.610612-116.610612-116.610612a32.997878 32.997878 0 0 0-46.665143 46.665143l105.158531 105.200326h-55.985633a33.018776 33.018776 0 0 0 0 66.016653h82.254367V572.604082h-82.902204a35.651918 35.651918 0 0 0 0 71.282938h82.902204v71.471021a33.018776 33.018776 0 0 0 66.016653 0v-71.471021h82.12898a35.651918 35.651918 0 1 0 0-71.282938h-82.12898v-52.474776h82.776817a33.018776 33.018776 0 1 0 0-66.016653h-58.827755l105.15853-105.200326a32.997878 32.997878 0 0 0-46.665143-46.665143z" fill="#FFFFFF" p-id="11637"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1771407178032" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13001" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M64 512a448 448 0 1 0 896 0 448 448 0 1 0-896 0z" fill="#FFBD27" p-id="13002"></path><path d="M413.776 279.52l32.256 41.92h112.864l32.256-41.92 3.232-6.448v-3.232c0-6.448-3.232-9.664-9.68-12.896 0 0-45.152-9.664-83.84-9.664-38.72 0-83.856 9.664-83.856 9.664-16.128 6.448-3.232 22.576-3.232 22.576z m154.8 70.96H452.48C362.176 382.72 288 482.704 288 576.24c0 116.08 64.496 170.912 222.528 170.912 158.032 0 222.528-54.832 222.528-170.928 0-93.52-74.176-193.504-164.48-225.76z m22.576 238.64c6.448 0 12.896 6.448 12.896 12.912 0 6.448-6.448 12.896-12.896 12.896h-64.496v45.152c0 6.448-6.448 12.896-12.896 12.896-6.464 0-12.912-6.448-12.912-12.896v-41.92h-64.496c-6.448 0-12.896-6.464-12.896-12.912s6.448-12.896 12.896-12.896h64.496V556.88h-64.496c-6.448 0-12.896-6.448-12.896-12.912 0-6.448 6.448-12.896 12.896-12.896h67.728v-3.216s-3.232 0-3.232-3.232l-51.6-61.28c-6.448-3.216-6.448-12.896 0-19.344s16.128-3.232 19.36 3.232l45.152 51.6 45.136-51.6c6.464-6.464 12.912-6.464 19.36-3.232 6.448 6.448 6.448 12.896 3.216 19.36l-51.6 61.28c0 3.2-3.216 3.2-6.448 3.2h67.728c6.448 0 12.896 6.464 12.896 12.912s-6.448 12.896-12.896 12.896h-64.496v35.472h64.496z" fill="#FFFFFF" p-id="13003"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M366.157576 633.018182c17.066667-44.993939 29.478788-89.987879 63.612121-124.121212C209.454545 501.139394 35.684848 439.078788 35.684848 341.333333v107.054546c0 85.333333 147.393939 161.357576 330.472728 184.630303z" fill="#2E9CFE" /><path d="M380.121212 817.648485c-7.757576-27.927273-13.963636-55.854545-13.963636-85.333333 0-13.963636 3.10303-26.375758 4.654545-40.339394C195.490909 671.806061 37.236364 614.4 37.236364 530.618182v107.054545C34.133333 732.315152 162.909091 791.272727 380.121212 817.648485zM480.969697 448.387879c9.309091 0 18.618182-1.551515 27.927273-1.551515 65.163636-38.787879 145.842424-62.060606 231.175757-62.060606 20.169697 0 40.339394 3.10303 60.509091 4.654545 77.575758-34.133333 125.672727-80.678788 125.672727-131.878788v-63.612121C926.254545 88.436364 727.660606 4.654545 480.969697 4.654545 235.830303 4.654545 35.684848 89.987879 35.684848 193.939394v63.612121c0 105.50303 200.145455 190.836364 445.284849 190.836364z" fill="#2E9CFE" /><path d="M398.739394 878.157576C203.248485 862.642424 35.684848 809.890909 35.684848 721.454545v107.054546c0 105.50303 198.593939 189.284848 445.284849 189.284848 7.757576 0 17.066667-1.551515 24.824242-1.551515-55.854545-32.581818-77.575758-85.333333-107.054545-138.084848zM712.145455 423.563636c-155.151515 0-279.272727 133.430303-279.272728 297.890909s125.672727 297.890909 279.272728 297.89091c155.151515 0 279.272727-133.430303 279.272727-297.89091s-125.672727-297.890909-279.272727-297.890909z m94.642424 350.642425H505.793939v-89.987879H926.254545v89.987879h-119.466666z" fill="#2E9CFE" /></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="192.84px" viewBox="0 0 1062 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M735.744 701.44m-311.296 0a311.296 311.296 0 1 0 622.592 0 311.296 311.296 0 1 0-622.592 0Z" fill="#FFCAD8" /><path d="M804.864 659.456c5.12 1.024 10.24 1.536 14.336 2.048h47.104c4.608-0.512 8.704 1.024 11.776 5.12 3.072 3.584 4.096 11.264 4.096 11.264v33.792s-3.584 10.24-13.312 11.264h-81.92c-3.584-1.536-6.656-0.512-10.752 1.536-3.072 1.536-4.608 4.608-4.608 8.704 0 4.096 0 8.192 0.512 12.288 0.512 4.096 0.512 8.192 0.512 10.752 0 5.632 4.608 7.68 13.824 8.704h80.896c9.216-2.56 18.944 10.24 18.944 10.24v31.232s-12.288 13.824-25.088 10.24h-17.92c-7.68 3.584-14.848 4.096-23.04 4.608-8.192 0.512-14.848 1.024-21.504 0.512h-12.8c-8.192 0.512-12.8 14.336-12.8 14.336v55.808c0 4.608-3.584 6.656-10.752 6.656h-41.984c-11.264 0-16.896-4.608-16.896-14.336 0-3.584 0.512-11.264 0.512-23.04 0.512-11.776 0-19.456-0.512-24.064 0-4.096-0.512-7.68-1.536-8.704-0.512-1.024-3.072-2.048-8.192-2.048h-87.552c-12.288 0-18.432-6.144-18.432-18.944 0-2.56-0.512-6.144-1.536-11.264-1.024-4.096-1.024-8.704 0-13.824 1.024-4.608 2.56-9.216 4.096-13.312 2.048-3.584 5.632-5.12 11.264-4.096h96.256c2.048 0 2.56-2.048 2.56-7.68v-26.624c0-4.608-5.632-6.656-16.896-6.656h-81.92c-3.584 0-6.656 0-10.24-0.512-3.072-0.512-4.096-3.072-4.096-8.192v-18.432s-1.536-13.312-2.048-16.896c-2.048-5.632-0.512-10.752 4.096-13.824 4.608-4.096 10.752-5.632 17.92-5.632 7.68 1.024 15.36 1.024 24.576 0.512 8.704-0.512 15.36-0.512 19.968-3.584h16.384c5.632 3.072 7.68 1.024 5.632-2.048-1.536-2.048-7.68-9.216-16.384-20.992-9.728-12.8-18.944-25.088-29.184-38.4-12.288-15.872-26.112-33.28-41.984-52.736-6.144-10.752-5.12-18.944 4.096-25.6 4.608-3.072 9.728-6.144 14.336-10.752 5.12-4.096 11.264-8.192 17.408-11.776 9.216-5.632 17.92-0.512 26.624 15.36 3.072 4.096 8.192 10.752 15.872 20.48 8.192 10.24 15.872 19.968 24.576 31.744 8.192 11.776 15.872 21.504 24.064 30.208 7.68 9.216 11.776 14.848 12.8 16.384 1.536 3.584 5.12 6.144 10.24 6.656 5.12 1.024 9.216-0.512 10.752-4.096 1.024-2.048 4.608-8.192 11.776-17.92 6.656-9.728 14.336-20.48 23.04-32.768s15.872-24.064 23.552-34.816c7.68-10.752 12.288-17.408 13.824-19.968 4.608-6.656 8.704-10.752 11.776-12.8 3.072-2.048 8.704-1.536 15.872 1.536 4.608 2.048 9.728 4.608 14.848 8.704 5.632 3.584 9.728 6.656 11.264 8.704 10.24 10.752 13.312 18.944 8.704 27.136-1.536 3.072-6.656 10.24-14.848 20.992-8.704 11.264-17.408 23.552-27.136 35.84-10.24 12.8-18.432 24.576-26.624 35.84s-12.288 17.92-13.312 19.456c-0.512 3.584 0.512 6.656 5.12 7.68z" fill="#FFFFFF" /><path d="M26.624 189.952a382.976 189.952 0 1 0 765.952 0 382.976 189.952 0 1 0-765.952 0Z" fill="#FB7895" /><path d="M530.944 428.544C492.544 435.2 452.096 438.272 409.6 438.272c-182.272 0-334.848-62.976-373.76-147.968-6.144 13.824-9.728 27.648-9.728 42.496 0 104.96 171.52 189.952 382.976 189.952 10.24 0 19.968-0.512 30.208-1.024 24.576-36.352 55.296-67.584 91.648-93.184zM782.848 376.832c6.656-13.824 10.752-28.672 10.752-43.52 0-14.336-3.584-28.672-9.728-42.496-13.824 30.208-42.496 57.344-80.896 79.872 5.12-0.512 10.752-1.024 16.384-1.024 20.992 0.512 42.496 2.56 63.488 7.168z" fill="#FB7895" /><path d="M409.6 564.224c-182.272 0-334.848-62.976-373.76-147.968-6.144 13.824-9.728 27.648-9.728 42.496 0 101.888 161.792 185.344 365.568 189.952 4.608-29.696 13.312-57.856 25.6-84.48H409.6z" fill="#FB7895" /><path d="M388.096 688.64c-172.544-4.608-314.88-66.56-351.744-147.456-6.144 13.824-9.728 27.648-9.728 42.496 0 102.4 163.84 186.368 368.64 189.952-5.12-23.552-8.192-47.104-8.192-71.68 0.512-4.608 1.024-9.216 1.024-13.312z" fill="#FB7895" /><path d="M36.352 666.624c-6.144 13.824-9.728 27.648-9.728 42.496 0 104.96 171.52 189.952 382.976 189.952 14.336 0 28.16-0.512 41.984-1.024-18.432-25.6-33.28-53.248-44.032-83.456-181.248-1.024-332.8-64.512-371.2-147.968z" fill="#FB7895" /><path d="M484.864 935.424c-24.576 2.56-49.152 3.584-75.264 3.584-182.272 0-334.848-62.976-373.76-147.968-6.144 13.824-9.728 27.648-9.728 42.496 0 104.96 171.52 189.952 382.976 189.952 62.464 0 120.832-7.68 173.056-20.48-35.84-16.896-69.12-39.424-97.28-67.584z" fill="#FB7895" /></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M119.467 494.933c-5.12 0-8.534-1.706-11.947-5.12-6.827-6.826-6.827-17.066 0-23.893l238.933-238.933c6.827-6.827 17.067-6.827 23.894 0L512 368.64l226.987-226.987c6.826-6.826 17.066-6.826 23.893 0s6.827 17.067 0 23.894L523.947 404.48c-6.827 6.827-17.067 6.827-23.894 0L358.4 262.827 131.413 489.813c-3.413 3.414-6.826 5.12-11.946 5.12zM682.667 409.6h153.6v477.867h-153.6V409.6z" fill="#CDCDCD" /><path d="M836.267 904.533h-153.6c-10.24 0-17.067-6.826-17.067-17.066V409.6c0-10.24 6.827-17.067 17.067-17.067h153.6c10.24 0 17.066 6.827 17.066 17.067v477.867c0 10.24-6.826 17.066-17.066 17.066zM699.733 870.4H819.2V426.667H699.733V870.4zM426.667 563.2h153.6v324.267h-153.6V563.2z" fill="#CDCDCD" /><path d="M580.267 904.533h-153.6c-10.24 0-17.067-6.826-17.067-17.066V563.2c0-10.24 6.827-17.067 17.067-17.067h153.6c10.24 0 17.066 6.827 17.066 17.067v324.267c0 10.24-6.826 17.066-17.066 17.066zM443.733 870.4H563.2V580.267H443.733V870.4z m-119.466 34.133H187.733c-10.24 0-17.066-6.826-17.066-17.066V648.533c0-10.24 6.826-17.066 17.066-17.066h136.534c10.24 0 17.066 6.826 17.066 17.066v238.934c0 10.24-6.826 17.066-17.066 17.066zM204.8 870.4h102.4V665.6H204.8v204.8zM119.467 431.787c25.6 0 46.08 20.48 46.08 46.08 0 25.6-20.48 46.08-46.08 46.08s-46.08-20.48-46.08-46.08c0-25.6 20.48-46.08 46.08-46.08z" fill="#CDCDCD" /><path d="M119.467 541.013c-34.134 0-63.147-27.306-63.147-63.146s29.013-63.147 63.147-63.147 63.146 27.307 63.146 63.147-29.013 63.146-63.146 63.146z m0-92.16c-15.36 0-29.014 11.947-29.014 29.014s11.947 29.013 29.014 29.013 29.013-11.947 29.013-29.013-13.653-29.014-29.013-29.014zM358.4 192.853c25.6 0 46.08 20.48 46.08 46.08 0 25.6-20.48 46.08-46.08 46.08-25.6 0-46.08-20.48-46.08-46.08 0-25.6 20.48-46.08 46.08-46.08z" fill="#CDCDCD" /><path d="M358.4 302.08c-34.133 0-63.147-27.307-63.147-63.147s27.307-63.146 63.147-63.146 63.147 27.306 63.147 63.146-29.014 63.147-63.147 63.147z m0-92.16c-15.36 0-29.013 11.947-29.013 29.013s11.946 29.014 29.013 29.014 29.013-11.947 29.013-29.014S373.76 209.92 358.4 209.92zM512 346.453c25.6 0 46.08 20.48 46.08 46.08 0 25.6-20.48 46.08-46.08 46.08s-46.08-20.48-46.08-46.08c0-25.6 20.48-46.08 46.08-46.08z" fill="#CDCDCD" /><path d="M512 455.68c-34.133 0-63.147-27.307-63.147-63.147S476.16 329.387 512 329.387c34.133 0 63.147 27.306 63.147 63.146S546.133 455.68 512 455.68z m0-92.16c-15.36 0-29.013 11.947-29.013 29.013s11.946 29.014 29.013 29.014 29.013-11.947 29.013-29.014S527.36 363.52 512 363.52zM768 90.453c25.6 0 46.08 20.48 46.08 46.08s-20.48 46.08-46.08 46.08-46.08-20.48-46.08-46.08 20.48-46.08 46.08-46.08z" fill="#CDCDCD" /><path d="M768 199.68c-34.133 0-63.147-27.307-63.147-63.147S733.867 73.387 768 73.387s63.147 29.013 63.147 63.146S802.133 199.68 768 199.68z m0-92.16c-15.36 0-29.013 11.947-29.013 29.013s11.946 29.014 29.013 29.014 29.013-11.947 29.013-29.014S783.36 107.52 768 107.52z" fill="#CDCDCD" /></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M820.30848 447.99744 820.30848 311.91168c0-44.14848-36.28544-80.06528-80.88576-80.06528l-1.85728 0-40.46464-82.05056c-9.53344-19.32672-30.96576-32.31232-53.3312-32.31232-5.1392 0-10.19904 0.68608-15.04 2.03776L250.20032 225.3824c-4.21504 1.17888-7.98592 3.39712-11.01824 6.464l-3.2896 0c-44.60032 0-80.88576 35.9168-80.88576 80.06528l0 515.26656c0 44.14848 36.28544 80.06656 80.88576 80.06656l503.53152 0c44.60032 0 80.88576-35.91808 80.88576-80.06656L820.30976 691.0912c62.944-6.6496 111.65824-59.26656 111.65824-121.54624C931.96672 507.26272 883.25376 454.64576 820.30848 447.99744zM411.04 231.8464l231.22688-64.6656c0.42624-0.11904 0.91904-0.1792 1.46688-0.1792 3.53408 0 7.4816 2.44736 8.51968 4.5504l29.7344 60.29312L411.04 231.84512zM204.93184 311.91168c0-16.84608 13.88928-30.55104 30.96064-30.55104l503.53152 0c17.07136 0 30.96064 13.70496 30.96064 30.55104l0 135.3664-103.47136 0c-69.0304 0-125.1904 54.848-125.1904 122.2656 0 67.4176 56.16 122.2656 125.1904 122.2656l103.47136 0 0 135.36896c0 16.84608-13.88928 30.55232-30.96064 30.55232L235.89248 857.73056c-17.07136 0-30.96064-13.70624-30.96064-30.55232L204.93184 311.91168zM882.04288 569.54368c0 40.1152-33.76384 72.75136-75.26528 72.75136L666.91328 642.29504c-41.50144 0-75.26528-32.63616-75.26528-72.75136 0-40.1152 33.76384-72.75136 75.26528-72.75136l139.86432 0C848.27904 496.79232 882.04288 529.42848 882.04288 569.54368zM678.5792 513.09056c-31.7568 0-57.5936 25.57696-57.5936 57.01632 0 31.43808 25.8368 57.01504 57.5936 57.01504s57.5936-25.57696 57.5936-57.01504C736.1728 538.66752 710.336 513.09056 678.5792 513.09056zM686.24768 570.10688c0 3.9168-3.44064 7.10272-7.66848 7.10272s-7.66848-3.18592-7.66848-7.10272c0-3.9168 3.44064-7.104 7.66848-7.104S686.24768 566.19008 686.24768 570.10688z" fill="#272636" /></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M835.98 932.97H185.92c-40.42 0-73.31-32.88-73.31-73.31V457.22c0-40.42 32.88-73.31 73.31-73.31h650.05c40.42 0 73.31 32.88 73.31 73.31v402.43c0.01 40.43-32.88 73.32-73.3 73.32zM185.92 432.79c-13.48 0-24.44 10.96-24.44 24.44v402.43c0 13.48 10.96 24.44 24.44 24.44h650.05c13.47 0 24.44-10.96 24.44-24.44V457.22c0-13.48-10.97-24.44-24.44-24.44H185.92zM276.19 528.58h469.52v48.87H276.19zM276.19 648.06h469.52v48.87H276.19zM282.09 435.64l-42.32-24.43 146.47-253.69 392.85 226.82-26.55 46.01-42.33-24.44 2.13-3.68-308.22-177.95zM761.7 429.11L570.36 157.62l-102.19 72.02-28.16-39.95L582.15 89.51l219.5 311.45z" /></svg>

After

Width:  |  Height:  |  Size: 877 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M880.514012 124.087882c10.998926 0 19.998047 8.999121 19.998047 19.998047v735.928132c0 10.998926-8.999121 19.998047-19.998047 19.998047h-735.928132c-10.998926 0-19.998047-8.999121-19.998047-19.998047v-735.928132c0-10.998926 8.999121-19.998047 19.998047-19.998047h735.928132m0-59.994141h-735.928132c-44.195684 0-79.992188 35.796504-79.992188 79.992188v735.928132c0 44.195684 35.796504 79.992188 79.992188 79.992188h735.928132c44.195684 0 79.992188-35.796504 79.992189-79.992188v-735.928132c0-44.195684-35.796504-79.992188-79.992189-79.992188z" fill="#FF6600" opacity=".502" /><path d="M64.593692 244.076164h119.988282v59.994142h-119.988282z" fill="#FF6600" /><path d="M64.593692 720.029685h119.988282v59.994141h-119.988282z" fill="#FF6600" /><path d="M688.53276 276.17303h-48.795234L512.949907 503.150864l-126.687628-226.977834h-48.795235l139.186408 241.576409H370.463822v26.997363h121.388146v69.293233H370.463822v27.097354h121.388146v106.18963h42.295869V641.137389h121.388146v-27.097354H534.147837v-69.293233h121.388146v-26.997363H549.246363z" fill="#FF6600" /></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,67 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getHomeModuleList(params) {
return request({
url: '/biz/homeModuleUser/list',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getItemInfoList(params) {
return request({
url: '/biz/itemInfo/list',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getIndexInfoList(params) {
return request({
url: '/biz/indexInfo/list',
method: 'get',
params: params
})
}
/**
* 获取账号信息列表
*/
export function getErpAccountList(params) {
return request({
url: '/biz/erpAccount/list',
method: 'get',
params: params
})
}
/**
* 获取交易分类信息列表
*/
export function getErpCategoryList(params) {
return request({
url: '/biz/erpCategory/list',
method: 'get',
params: params
})
}
/**
* 获取明细信息列表
*/
export function getErpTransactionFlowList(params) {
return request({
url: '/biz/erpTransactionFlow/list',
method: 'get',
params: params
})
}

View File

@@ -0,0 +1,46 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getChartList(params) {
return request({
url: '/biz/chartInfo/list',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getChartListAll(params) {
return request({
url: '/biz/chartInfo/listAll',
method: 'get',
params: params
})
}
/**
* 保存/更新指标信息列表
*/
export function getChartSave(data) {
return request({
url: '/biz/chartInfo/save',
method: 'post',
data
})
}
/**
* 配置更新指标信息列表
*/
export function getChartSetting(data) {
return request({
url: '/biz/chartInfo/setting',
method: 'post',
data
})
}

View File

@@ -0,0 +1,13 @@
import request from '@/utils/request';
/**
* 导出用户信息
*/
export function download(params) {
return request({
url: '/biz/homeFiles/dwonload',
method: 'get',
params,
responseType: 'blob'
});
}

View File

@@ -0,0 +1,36 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getAlertList(params) {
return request({
url: '/biz/homeDesktop/alertList',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getNoteList(params) {
return request({
url: '/biz/homeDesktop/noteList',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getQuickList(params) {
return request({
url: '/biz/homeDesktop/quickList',
method: 'get',
params: params
})
}

View File

@@ -0,0 +1,63 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getHomeMenuList(params) {
return request({
url: '/biz/homeMenu/list',
method: 'get',
params: params
})
}
export function getHomeMenuPlist(params) {
return request({
url: '/biz/homeMenu/pList',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getUserMenuList(params) {
return request({
url: '/biz/homeMenu/userList',
method: 'get',
params: params
})
}
export function getTreeMenuList(params) {
return request({
url: '/biz/homeMenu/treeList',
method: 'get',
params: params
})
}
/**
* 保存菜单信息列表
*/
export function getHomeMenuSave(data) {
return request({
url: '/biz/homeMenu/save',
method: 'post',
data
})
}
/**
* 删除菜单信息
*/
export function getHomeMenuDelete(params) {
return request({
url: '/biz/homeMenu/delete',
method: 'post',
params: params
})
}

View File

@@ -0,0 +1,46 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getHomeModuleList(params) {
return request({
url: '/biz/homeModule/list',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getHomeModuleListAll(params) {
return request({
url: '/biz/homeModule/listAll',
method: 'get',
params: params
})
}
/**
* 删除信息列表
*/
export function getHomeModuleSave(data) {
return request({
url: '/biz/homeModule/save',
method: 'post',
data
})
}
/**
* 删除信息列表
*/
export function getHomeModuleDelete(params) {
return request({
url: '/biz/homeModule/delete',
method: 'post',
params: params
})
}

View File

@@ -0,0 +1,34 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getHomeModuleUserList(params) {
return request({
url: '/biz/homeModuleUser/userList',
method: 'get',
params: params
})
}
/**
* 保存信息列表
*/
export function getHomeModuleUserSave(data) {
return request({
url: '/biz/homeModuleUser/save',
method: 'post',
data
})
}
/**
* 删除信息列表
*/
export function getHomeModuleUserDelete(params) {
return request({
url: '/biz/homeModuleUser/delete',
method: 'post',
params: params
})
}

View File

@@ -0,0 +1,36 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getNotesList(params) {
return request({
url: '/biz/notes/list',
method: 'get',
params: params
})
}
/**
* 保存/修改
*/
export function getNotesSave(data) {
return request({
url: '/biz/notes/save',
method: 'post',
data
})
}
/**
* 删除
*/
export function getNotesDelete(params) {
return request({
url: '/biz/notes/delete',
method: 'post',
params: params
})
}

View File

@@ -0,0 +1,68 @@
import request from '@/utils/request';
/**
* 获取指标信息列表
*/
export function getHomeRoleList(params) {
return request({
url: '/biz/homeRole/list',
method: 'get',
params: params
})
}
/**
* 获取指标信息列表
*/
export function getHomeRoleUserList(params) {
return request({
url: '/biz/homeRole/userList',
method: 'get',
params: params
})
}
/**
* 获取角色授权的菜单
*/
export function getHomeRoleMenus(params) {
return request({
url: '/biz/homeRole/roleMenus',
method: 'get',
params: params
})
}
/**
* 给角色授权菜单
*/
export function getHomeRoleAssign(data) {
return request({
url: '/biz/homeRole/assign',
method: 'post',
data
})
}
/**
* 保存角色信息列表
*/
export function getHomeRoleSave(data) {
return request({
url: '/biz/homeRole/save',
method: 'post',
data
})
}
/**
* 删除角色信息
*/
export function getHomeRoleDelete(params) {
return request({
url: '/biz/homeRole/delete',
method: 'post',
params: params
})
}

View File

@@ -0,0 +1,46 @@
import request from '@/utils/request';
/**
* 获取用户信息列表
*/
export function getHomeUserList(params) {
return request({
url: '/biz/homeUser/list',
method: 'get',
params: params
})
}
/**
* 获取用户信息列表
*/
export function getHomeUserListAll(params) {
return request({
url: '/biz/homeUser/listAll',
method: 'get',
params: params
})
}
/**
* 保存用户信息列表
*/
export function getHomeUserSave(data) {
return request({
url: '/biz/homeUser/save',
method: 'post',
data
})
}
/**
* 删除用户信息
*/
export function getHomeUserDelete(params) {
return request({
url: '/biz/homeUser/delete',
method: 'post',
params: params
})
}

View File

@@ -0,0 +1,12 @@
import request from '@/utils/request';
/**
* 获取信息列表
*/
export function getWebsiteStorageList(params) {
return request({
url: '/biz/websiteStorage/list',
method: 'get',
params: params
})
}

View File

@@ -1,11 +1,11 @@
import request from '@/utils/request'
import request from '@/utils/request';
/**
* 登录接口
*/
export function login(data) {
return request({
url: 'userLogin',
url: '/userLogin',
method: 'post',
data
})
@@ -29,4 +29,28 @@ export function getUserInfo() {
url: '/userInfo',
method: 'get'
})
}
/**
* 修改密码
*/
export function updatePasswd(params) {
return request({
url: '/editPasswd',
method: 'post',
params: params
})
}
/**
* 初始化密码
*/
export function initPasswd(params) {
return request({
url: '/initPasswd',
method: 'post',
params: params
})
}

View File

@@ -0,0 +1,255 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px"
viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve">
<g id="LEFT_ARM_1">
<g id="XMLID_46_">
<path id="XMLID_290_" fill="#FFB96C" d="M294.4,485.5c-1.2,0.3-2.4,0.4-3.6,0.4c-0.2,0-0.4,0-0.6,0c-8.4-0.3-16.2-6.9-21-15.7
c-21.3-39.2-27.1-63.4-34.8-89.9c-1.9-6.6-1.4-13.3,1.4-18.1c2.2-3.7,5.7-6.4,10.5-6.9c0.6-0.1,1.1-0.1,1.7-0.1
c8.5,0,17.4,6.8,20.9,15.6l36.5,89.4C310,471.5,305,483.1,294.4,485.5z"/>
<path id="XMLID_280_" fill="#FCAF63" d="M290.2,485.9c-8.4-0.3-16.2-6.9-21-15.7c-21.3-39.2-27.1-63.4-34.8-89.9
c-1.9-6.6-1.4-13.3,1.4-18.1c9.5,23,40.7,98.5,50.3,116.1C287.6,481.1,289,483.6,290.2,485.9z"/>
</g>
</g>
<g id="LEFT_ARM_2">
<g id="XMLID_10_">
<g id="XMLID_42_">
<path id="XMLID_293_" fill="#FFB96C" d="M340.2,472c-0.9,2.3-2.1,4.1-3.3,5c-7.2,5.7-14.3,7-26.2,9.6c-11.9,2.6-24.2,2-29.9-3.4
c-1.9-1.8-3.3-3.8-4.1-5.9c-2.5-6-0.3-12.3,6.8-15c4.2-1.6,42.4-10.6,48.7-9.4C343.1,455,342.9,465.3,340.2,472z"/>
<path id="XMLID_279_" fill="#FCAF63" d="M340.2,472c-0.9,2.3-2.1,4.1-3.3,5c-7.2,5.7-14.3,7-26.2,9.6c-11.9,2.6-24.2,2-29.9-3.4
c-1.9-1.8-3.3-3.8-4.1-5.9c3.2,0.9,9.6,2.1,20.5,0.9C306.4,477.2,325.3,474.3,340.2,472z"/>
</g>
<g id="XMLID_41_">
<path id="XMLID_49_" fill="#FEA691" d="M330.2,453.6c-11.9,4-10.6,11.9-8.8,19.6c1.8,7.7,16.6,27.5,21.1,28.5s4.8-0.8,3.6-4.5
c-1.2-3.7-9.7-16.6-9.7-16.6s9.4,12.8,13.3,16.6c3.9,3.8,7.2,3.5,7.1-0.7c-0.2-4.2-9.5-18.3-9.5-18.3s8.2,10.8,11.6,14.5
s7.9,3.1,6.3-1.9c-1.5-5-8.3-15.9-8.3-15.9s6.6,11,11,12.1c4.4,1.1,1.7-5.5-3-13.9C360.3,464.5,345,448.6,330.2,453.6z"/>
<path id="XMLID_50_" fill="#FEA691" d="M320.2,464.6c0,0-2.5,9.8-0.9,18.4s4.3,10.3,6.1,10.7s2.4-1.9,1.5-5.6
c-0.9-3.7,1.5-11,1.5-11S325.8,462.4,320.2,464.6z"/>
</g>
</g>
</g>
<g id="BODY">
<g id="XMLID_70_">
<path id="XMLID_45_" fill="#FEC272" d="M273.1,496H172.2c0,0-13.9-97.2-5.1-121.1c2.5-6.8,5.8-12.3,9.6-16.9
c9.5-11.6,21.7-16.9,30-19c10.9-2.8,20.4-2.9,28.6-0.9c21.8,5.2,33.9,25.1,37.8,46.8C278.5,414.8,273.1,496,273.1,496z"/>
<path id="XMLID_43_" fill="#F9B35F" d="M207.6,358.5c-13.2,3.4-25.3,3-31-0.5c9.5-11.6,21.7-16.9,30-19
c10.9-2.8,20.4-2.9,28.6-0.9C237,344.9,224.6,354.1,207.6,358.5z"/>
</g>
</g>
<g id="RIGHT_ARM_1">
<g id="XMLID_16_">
<path id="XMLID_278_" fill="#FFCD92" d="M199,381.3c0,1.6-0.1,3.2-0.4,4.7c-4.1,26.6-27.7,90.6-27.7,90.6c-1.7,5.1-5.3,8.9-9.7,11
c-4.4,2-9.5,2.4-14.4,0.5c-7.6-3-11.2-10.2-11.2-18.1c0-1.7,0.2-3.4,0.5-5.1c6.9-37.3,19.2-68.2,29.3-90
c4.4-9.6,15.2-15.6,24.7-10.8c2.9,1.4,4.9,3.7,6.4,6.3C198.3,373.6,199,377.4,199,381.3z"/>
<path id="XMLID_273_" fill="#FCB765" d="M199,381.3c0,1.6-0.1,3.2-0.4,4.7c-4.1,26.6-27.7,90.6-27.7,90.6c-1.7,5.1-5.3,8.9-9.7,11
l35.2-117.3C198.3,373.6,199,377.4,199,381.3z"/>
</g>
</g>
<g id="RIGHT_ARM_2">
<g id="XMLID_17_">
<g id="XMLID_20_">
<path id="XMLID_285_" fill="#FFCD92" d="M230.5,487.4c-2.2,5.4-9.4,5.6-9.4,5.6s-20.2,1.2-37.7,1.2c-17.5,0-34.7,0-41.7-7.9
c-1.4-1.6-2.5-3.4-3.1-5.3c-2.6-7.3,0.7-16,11.1-20.4c7.5-3.2,16.1-2.1,25.5-1.2c9.4,0.9,27.5,5.1,38,7.9
c10.6,2.7,16.9,5.4,17.8,15.4C231.3,484.6,231,486.1,230.5,487.4z"/>
<path id="XMLID_284_" fill="#FCB765" d="M230.5,487.4c-2.2,5.4-9.4,5.6-9.4,5.6s-20.2,1.2-37.7,1.2c-17.5,0-34.7,0-41.7-7.9
c-1.4-1.6-2.5-3.4-3.1-5.3c7.8,2.4,18.3,5.2,26.7,5.9C189,488.9,220.4,487.9,230.5,487.4z"/>
</g>
<g id="XMLID_2_">
<g id="XMLID_18_">
<path id="XMLID_47_" fill="#FEA691" d="M251.6,471.3c0,1.6-0.2,3.3-0.6,5.1c-2.1,8.6-13.2,15.2-30.1,16.1
c-7.5,0.4-12.6,0.4-16-0.4c-4.4-1.1-6.1-3.7-6.5-9.1c-0.5-6.9,4.7-13.2,8.6-15.9c11.7-7.9,22.6-8.5,32.1-7.6
C247.6,460.4,251.5,465.1,251.6,471.3z"/>
<path id="XMLID_44_" fill="#FC8172" d="M251.6,471.3c0,1.6-0.2,3.3-0.6,5.1c-2.1,8.6-13.2,15.2-30.1,16.1
c-7.5,0.4-12.6,0.4-16-0.4c-0.4-1-0.7-2.2-0.8-3.7c-0.5-5.9,4.4-11.4,8.2-13.7c11.1-6.8,21.5-7.4,30.5-6.5
C246.8,468.5,249.7,469.6,251.6,471.3z"/>
</g>
<g id="XMLID_19_">
<path id="XMLID_48_" fill="#C7DCF9" d="M264.7,488.7c0,1.1-0.2,2.1-0.5,3.2h-55c-0.3-1-0.5-2.1-0.5-3.2c0-9.6,12.5-17.4,28-17.4
C252.2,471.3,264.7,479.1,264.7,488.7z"/>
<path id="XMLID_254_" opacity="0.55" fill="#C7DCF9" d="M263.5,490.2c0,0.6-0.1,1.1-0.4,1.7h-50.3c-0.3-0.6-0.4-1.1-0.4-1.7
c0-5.1,11.4-9.3,25.5-9.3C252.1,480.9,263.5,485.1,263.5,490.2z"/>
</g>
<g id="XMLID_37_">
<path id="XMLID_28_" fill="#FEA691" d="M252.6,456.6c3.6,0.4,6.1,2.7,7.5,7.5c1.4,4.8,1.7,11.9,0.2,14.4
c-1.5,2.5-4.3,3.6-7.4,1.1c-3.1-2.5-4.8-9.5-5.8-13C245.9,462,244.5,455.8,252.6,456.6z"/>
<g id="XMLID_4_">
<path id="XMLID_38_" fill="#FEA691" d="M239.2,454.3c3.9,0.4,5.5,3.7,7,8.7c1.5,5,1.8,12.6,0.2,15.3c-1.6,2.6-4.6,3.8-7.9,1.2
c-3.3-2.6-5.2-10.1-6.2-13.8C231,460.8,230.4,453.5,239.2,454.3z"/>
<path id="XMLID_79_" fill="#FF8D76" d="M243.1,480.8c-1.4,0.3-3,0-4.6-1.4c-3.3-2.6-5.1-10.1-6.2-13.8c-1.1-4-1.7-9.7,3-11.1
c-0.9,2.7-0.4,6.4,0.3,9.2c1,4.3,2.9,13,6.2,16C242.3,480.2,242.7,480.6,243.1,480.8z"/>
</g>
<path id="XMLID_39_" fill="#FEA691" d="M224.6,456.6c3.9,0.4,6.5,2.9,8,7.9s1.8,12.6,0.2,15.3c-1.6,2.6-4.6,3.8-7.9,1.2
c-3.3-2.6-5.2-10.1-6.2-13.8C217.4,462.3,215.9,455.7,224.6,456.6z"/>
<path id="XMLID_81_" fill="#FF8D76" d="M228.5,482.4c-1.1,0-2.3-0.5-3.5-1.5c-3.3-2.6-5.1-10.1-6.2-13.8
c-1-3.8-2.2-8.6,1.5-10.2c-0.3,2.5,0.4,5.7,1.1,8.3c1.2,4.5,3.3,13.7,7,17C228.4,482.4,228.4,482.4,228.5,482.4z"/>
<path id="XMLID_40_" fill="#FEA691" d="M211.5,459.4c3.4-1,6,2.7,7.3,7.3c1.3,4.6,1.7,11.6,0.2,14c-1.4,2.4-4.2,3.5-7.2,1.1
c-3-2.4-3.8-7-4.7-10.4C205.9,467,204.9,461.4,211.5,459.4z"/>
<path id="XMLID_82_" fill="#FF8D76" d="M215.2,483.2c-1.1,0-2.2-0.4-3.4-1.4c-3-2.4-3.8-7-4.7-10.4c-1.1-3.8-1.9-8.5,2.1-11
c-0.3,2.7,0.2,5.6,0.7,8.2c0.9,4.6,1.6,10.9,5,14.3C215,483,215.1,483.1,215.2,483.2z"/>
<path id="XMLID_78_" fill="#FF8D76" d="M256.8,480.9c-1.2,0.1-2.5-0.3-3.8-1.4c-3.1-2.5-4.8-9.5-5.8-13c-1-3.7-2.2-8.5,1.8-9.7
c0,2.2,0.5,4.7,1,6.9c1,4.5,2.9,13.5,6.1,16.7C256.4,480.6,256.6,480.8,256.8,480.9z"/>
</g>
</g>
</g>
</g>
<g id="HEAD">
<g id="XMLID_89_">
<path id="XMLID_6_" fill="#FF8D76"
d="M208.8,235c0,0,9.3-3.8,15.6,8.8c6.3,12.7-0.5,20.8-0.5,20.8S217.7,246.3,208.8,235z"/>
<path id="XMLID_173_" fill="#FEA691" d="M215.2,350.9c-8.8,4.1-14.9-1.6-14.9-1.6l-5.5-11.9l-9.9-21.4c14.9-3.2,27.9-5.7,27.9-5.7
l2.3,9.5l4.3,17.7C219.2,337.6,224,346.9,215.2,350.9z"/>
<path id="XMLID_171_" fill="#FF8D76" d="M215,319.9c-1.3,1.9-2.7,3.9-4.4,5.8c-4.3,5.1-10.5,9-15.9,11.7l-9.9-21.4
c14.9-3.2,27.9-5.7,27.9-5.7L215,319.9z"/>
<ellipse id="XMLID_3_" transform="matrix(0.8545 -0.5194 0.5194 0.8545 -114.0092 131.008)"
fill="#FEA691" cx="176.9" cy="269" rx="47.2" ry="57.7"/>
<path id="XMLID_12_" fill="#FF8D76" d="M217.4,308.8c-2.9,3.7-6.5,6.9-10.6,9.5c-22.3,13.5-53.7,2.4-70.3-24.8
c-16.4-26.9-12-59.6,9.7-73.4c-12.6,16.2-13.2,42.2,0.3,64.3c16.5,27.2,48,38.3,70.3,24.8C217,309.1,217.2,309,217.4,308.8z"/>
<path id="XMLID_5_" fill="#283575" d="M157.2,309.5c0,0-17-13.4-21.3-37.4s0.5-33.7,20.2-45.3c19.7-11.5,34.4-7.4,34.4-7.4
s-14-10.5-36.9-6.6c-22.9,3.8-46.2,25.6-41.4,58.4C117.2,306.2,138,316.5,157.2,309.5z"/>
<path id="XMLID_11_" fill="#F76F59" d="M177.8,246.8c-2.7,1.9,1.1,12,9.3,19.5c8.2,7.5,16.1,3.6,16.3-3.8
C203.6,254.9,187.1,240.2,177.8,246.8z"/>
<path id="XMLID_7_" fill="#FF8D76"
d="M137.5,291.4c0,0-16,15.7-1.6,29.4c13.8,13.1,31.2-4.5,31.2-4.5S142.7,303.2,137.5,291.4z"
/>
<path id="XMLID_250_" fill="#F76F59"
d="M139.4,300.5c0,0-9,8.8-0.9,16.6c7.8,7.4,17.6-2.5,17.6-2.5S142.3,307.2,139.4,300.5z"/>
</g>
</g>
<g id="HAIR">
<g id="XMLID_84_">
<path id="XMLID_8_" fill="#283575" d="M188.5,218.1c0,0,1.7-30.2-15.5-42.5c-19.4-13.8-30.3,6.1-23.9,22.4
c2.8,7.3,5.8,11.7,8.2,14.3c2.2,2.4,5.2,3.8,8.4,4L188.5,218.1z"/>
<path id="XMLID_9_" fill="#283575" d="M122.7,192.6c-15.3,12.2-2.1,29.7,4.2,34.4c2.7,2,5.6,3.6,8.1,4.8c3.4,1.6,7.4,1.4,10.6-0.6
l21.6-13.7C167.2,217.6,143,176.5,122.7,192.6z"/>
<path id="XMLID_51_" fill="#3C4E8E"
d="M159,172.3c-4.8,0.6,0.5,9.8,7.5,11S171.3,170.8,159,172.3z"/>
<path id="XMLID_61_" fill="#3C4E8E"
d="M128.7,192.8c-2.8,3.2,4.5,7.3,10.4,5.3C145.1,196.2,134.9,185.8,128.7,192.8z"/>
</g>
</g>
<g id="MOUTH">
<g id="XMLID_83_">
<path id="XMLID_177_" fill="#F76F59" d="M217.7,285c-0.5,1-1.2,1.9-2.2,2.7c-3.7,2.9-6.5,7.1-8.5,11.5c-1.8,3.9-6.4,8.5-10.7,5.2
c-0.5-0.4-1-0.8-1.4-1.4c-4.5-5.7-2.8-20.4,4.2-26.9c5.4-5,12.1-3.7,15.5-0.9C217.9,277.7,219.3,281.7,217.7,285z"/>
<path id="XMLID_175_" fill="#FF6EA9" d="M217.7,285c-0.5,1-1.2,1.9-2.2,2.7c-3.7,2.9-6.5,7.1-8.5,11.5c-1.8,3.9-6.4,8.5-10.7,5.2
c0-6.3,2.3-13.7,6.7-17.7C208.1,282.1,214.1,282.7,217.7,285z"/>
</g>
</g>
<g id="EYE_1">
<g id="XMLID_14_">
<path id="XMLID_86_" fill="#1C3177" d="M155.8,267.7c-3.2,0-5.7-1-5.9-1.1c-1.3-0.5-1.9-2-1.4-3.3c0.5-1.3,2-1.9,3.3-1.4
c0.1,0,5.6,2.2,8.6-1.1c3.9-4.1-0.2-9.4-0.3-9.6c-0.9-1.1-0.7-2.7,0.4-3.6c1.1-0.9,2.7-0.7,3.6,0.4c2.4,3.1,5.5,10.4,0,16.2
C161.6,266.9,158.5,267.7,155.8,267.7z"/>
</g>
</g>
<g id="EYE_2">
<g id="XMLID_15_">
<path id="XMLID_85_" fill="#1C3177" d="M193,242.6c-5.1,0-9.4-3.8-9.7-4c-1.1-0.9-1.1-2.6-0.2-3.6c0.9-1,2.5-1.1,3.6-0.2
c0,0,4.6,3.9,8,2.4c1.5-0.7,0.4-3.8,0.3-3.9c-0.5-1.3,0.1-2.8,1.5-3.3c1.3-0.5,2.8,0.1,3.3,1.4c1.5,3.7,1.1,8.6-3,10.4
C195.5,242.4,194.2,242.6,193,242.6z"/>
</g>
</g>
<g id="EYEBROW_1">
<g id="XMLID_1_">
<path id="XMLID_88_" fill="#1C3177" d="M142.4,261.7c-0.1,0-0.2,0-0.3,0c-1.4-0.2-2.4-1.4-2.3-2.8c0-0.3,0.7-6.3,4.9-12.4
c4.1-6.1,8.3-8.2,8.5-8.3c1.3-0.6,2.8-0.1,3.4,1.2c0.6,1.3,0.1,2.8-1.2,3.4c0,0-3.2,1.7-6.5,6.5c-3.4,5-4,10-4,10.1
C144.8,260.7,143.7,261.7,142.4,261.7z"/>
</g>
</g>
<g id="EYEBROW_2">
<g id="XMLID_13_">
<path id="XMLID_87_" fill="#1C3177" d="M179,232.1c-0.7,0-1.5-0.3-2-0.9c-0.9-1.1-0.7-2.7,0.4-3.6c0.3-0.2,2.6-2.1,6.7-3.3
c4.1-1.2,7.5-1.1,7.6-1.1c1.4,0,2.5,1.2,2.5,2.6c0,1.4-1.3,2.5-2.6,2.5c0,0-2.7-0.1-6,0.9c-3.2,1-5,2.4-5,2.4
C180.1,231.9,179.6,232.1,179,232.1z"/>
</g>
</g>
<g id="TABLE">
<g id="XMLID_248_">
<path id="XMLID_22_" fill="#99ADF9" d="M505.9,506.3H98.3c-4,0-7.2-3.2-7.2-7.2l0,0c0-4,3.2-7.2,7.2-7.2h407.6
c4,0,7.2,3.2,7.2,7.2l0,0C513.1,503,509.9,506.3,505.9,506.3z"/>
<path id="XMLID_198_" fill="#789FEF" d="M505.2,506.3H157.1c-4,0-7.2-3.2-7.2-7.2l0,0c0-4,3.2-7.2,7.2-7.2h348.1
c4,0,7.2,3.2,7.2,7.2l0,0C512.5,503,509.2,506.3,505.2,506.3z"/>
</g>
</g>
<g id="DESKTOP">
<g id="XMLID_186_">
<g id="XMLID_182_">
<path id="XMLID_25_" fill="#E1ECFF" d="M457.6,444.7h-186c-3.3,0-5.9-3-5.3-6.3l19.8-118.7c0.4-2.6,2.7-4.5,5.3-4.5h186
c3.3,0,5.9,3,5.3,6.3L463,440.2C462.5,442.8,460.3,444.7,457.6,444.7z"/>
<path id="XMLID_29_" fill="#C7DCF9" d="M461.6,444.7H278c-3.3,0-5.9-3-5.3-6.3l19.8-118.7c0.4-2.6,2.7-4.5,5.3-4.5h183.6
c3.3,0,5.9,3,5.3,6.3l-19.8,118.7C466.5,442.8,464.2,444.7,461.6,444.7z"/>
<path id="XMLID_31_" fill="#B7D4F7" d="M442.2,432H302c-2.6,0-4.5-2.3-4.1-4.8l15.1-90.6c0.3-2,2.1-3.5,4.1-3.5h140.1
c2.6,0,4.5,2.3,4.1,4.8l-15.1,90.6C445.9,430.5,444.2,432,442.2,432z"/>
</g>
<g id="XMLID_183_">
<path id="XMLID_24_" fill="#E1ECFF" d="M382.3,484.5h-25.5c-2.4,0-4.2-2-4-4.4l6.7-66.5c0.2-2,1.9-3.6,4-3.6H389
c2.4,0,4.2,2,4,4.4l-6.7,66.5C386.1,483,384.4,484.5,382.3,484.5z"/>
<path id="XMLID_26_" fill="#C7DCF9" d="M384.1,484.5h-23.4c-1.5,0-2.7-1.3-2.6-2.8l7-69.4c0.1-1.3,1.2-2.3,2.6-2.3h23.4
c1.5,0,2.7,1.3,2.6,2.8l-7,69.4C386.5,483.5,385.4,484.5,384.1,484.5z"/>
</g>
<g id="XMLID_185_">
<path id="XMLID_27_" fill="#E1ECFF" d="M424.7,491.8H318.5c-2.2,0-4-1.8-4-4V479c0-2.2,1.8-4,4-4h106.2c2.2,0,4,1.8,4,4v8.8
C428.7,490,426.9,491.8,424.7,491.8z"/>
<path id="XMLID_30_" fill="#C7DCF9" d="M426.9,491.8H327c-2.3,0-4.2-1.9-4.2-4.2v-8.4c0-2.3,1.9-4.2,4.2-4.2h99.9
c2.3,0,4.2,1.9,4.2,4.2v8.4C431,489.9,429.2,491.8,426.9,491.8z"/>
</g>
</g>
</g>
<g id="SIGN">
<g id="XMLID_21_">
<path id="XMLID_77_" fill="#FF97C9" d="M501.5,298.7c-1,0.7-2.3,1.2-3.7,1.2H402c-4.8,0-7.8-5-5.6-9.3l34.8-66.9l12.8-24.6
c2.4-4.5,8.8-4.5,11.2,0l48.2,91.5C505,293.6,503.9,297,501.5,298.7z"/>
<path id="XMLID_74_" fill="#FC72BB" d="M501.5,298.7c-1,0.7-2.3,1.2-3.7,1.2H402c-4.8,0-7.8-5-5.6-9.3l34.8-66.9L420.6,280
c-0.9,4.8,2.4,9.3,7.3,9.9L501.5,298.7z"/>
<g id="XMLID_32_">
<g id="XMLID_34_">
<path id="XMLID_35_" fill="#FFFFFF" d="M445.2,264.1l-5-35.8c-0.2-1.2,0.8-2.3,2-2.3h14.2c1.2,0,2.1,1.1,2,2.3l-5,35.8
c-0.1,1-1,1.7-2,1.7h-4.3C446.1,265.9,445.3,265.1,445.2,264.1z"/>
</g>
<circle id="XMLID_33_" fill="#FFFFFF" cx="449.3" cy="279.1" r="7.1"/>
</g>
</g>
</g>
<g id="STAR">
<path id="XMLID_195_" fill="#FEC272" d="M189.7,141.2l1.8,3.7c0.2,0.5,0.7,0.8,1.2,0.8l4,0.6c1.3,0.2,1.8,1.7,0.8,2.6l-2.9,2.8
c-0.4,0.4-0.5,0.9-0.4,1.4l0.7,4c0.2,1.2-1.1,2.2-2.2,1.6l-3.6-1.9c-0.4-0.2-1-0.2-1.4,0l-3.6,1.9c-1.1,0.6-2.4-0.4-2.2-1.6l0.7-4
c0.1-0.5-0.1-1-0.4-1.4l-2.9-2.8c-0.9-0.9-0.4-2.4,0.8-2.6l4-0.6c0.5-0.1,0.9-0.4,1.2-0.8l1.8-3.7
C187.5,140.1,189.1,140.1,189.7,141.2"/>
</g>
<g id="BUBBLE_1">
<path id="XMLID_23_" fill="#F0F6FF" d="M384.3,170.2c0,13.8-4,26.6-10.9,37.5c0,0,0,0,0,0c-2.5,5.8-15.1,30.3-57.9,49.9
c-44.9,20.6-84.7,5.9-84.7,5.9s24.5-16.1,19.9-43c-3.6-21.2-6.1-36.4-5.9-49.2c0-0.4,0-0.7,0-1.1c0-38.5,31.2-69.7,69.7-69.7
S384.3,131.7,384.3,170.2z"/>
</g>
<g id="BUBBLE_2">
<path id="XMLID_187_" fill="#C7DCF9" d="M238.1,216.5c-3.7-9.5-7.4-9.1-9.9-7.3c-2.3,1.7-2.8,7.6,0,13.2s5.6,5.2,8.4,3.7
S239.8,220.9,238.1,216.5z"/>
</g>
<g id="BUBBLE_3">
<path id="XMLID_249_" fill="#C7DCF9" d="M361.6,237.5c-15.4,8.2-14,14.5-10.4,18.3c3.4,3.6,13.5,3.2,22.4-2.6s7.8-10.5,4.7-15
C375.2,233.7,368.7,233.6,361.6,237.5z"/>
</g>
<g id="Layer_23">
<g id="XMLID_75_">
<g id="XMLID_73_">
<path id="XMLID_274_" fill="#99ADF9" d="M364.6,191.8c0,3.8-1.8,7.6-5.2,9.9c-7.4,5.2-19.2,12.9-32.2,19.7
c-8.3,4.4-15.5,7.8-21.3,10.3c-4.8,2.1-10.1,1.7-14.4-0.7c-2.9-1.6-5.3-4.1-6.8-7.3c-1-2.2-1.6-4.6-1.6-7c0-2,0.4-4,1.1-6
l0.4-1.1l17.2-43.6l-23,12.4c-6.4,3.4-14.3,1.2-18-5.1c-0.1-0.2-0.2-0.4-0.3-0.6c-1-2-1.5-4.1-1.5-6.2c0-4.4,2.2-8.8,6.2-11.3
c7.3-4.7,17.9-11.1,30.5-17.8c24.2-12.9,36.2-11.3,39.6-4.8c3.4,6.6-16.1,61.8-16.1,61.8s26.1-13.1,28.9-14.1
c6-2.3,12.7,0.5,15.4,6.3c0.6,1.3,0.9,2.7,1,4.1C364.6,191.2,364.6,191.5,364.6,191.8z"/>
<path id="XMLID_267_" fill="#7D9AF9" d="M309.1,158.8c-5.8,14.3-19.5,47.2-24.3,50.9l17.2-43.6l-23,12.4
c-6.4,3.5-14.5,1.2-18.1-5.2c0.2-0.2,0,0,0.2-0.2c2.3,1.1,6.4,2.1,12.5-0.9c11.4-5.4,24.4-12.5,31.9-16.6
C307.6,154.5,310,156.6,309.1,158.8z"/>
<path id="XMLID_264_" fill="#7D9AF9" d="M364.6,191.8c0,3.8-1.8,7.6-5.2,9.9c-7.4,5.2-19.2,12.9-32.2,19.7
c-8.3,4.4-15.5,7.8-21.3,10.3c-4.8,2.1-10.1,1.7-14.4-0.7c0.9-0.2,1.8-0.4,2.6-0.7c4.5-1.5,24.8-12.2,44.7-22.9
c13-7,21.8-12.6,25.7-16.4C364.6,191.2,364.6,191.5,364.6,191.8z"/>
</g>
<path id="XMLID_76_" opacity="0.49" fill="#FFFFFF" d="M314.2,131.6c-9,3-13.6,8.6-2.8,8s16.5-6.1,14.6-7.9
C324.1,130,318.3,130.2,314.2,131.6z"/>
<path id="XMLID_80_" opacity="0.49" fill="#FFFFFF" d="M348.7,182.6c-5.7,1.9-8.7,5.5-1.8,5.1c6.9-0.4,10.5-3.9,9.3-5
C354.9,181.6,351.3,181.7,348.7,182.6z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1,539 @@
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View File

@@ -0,0 +1,25 @@
@font-face {
font-family: "iconfont"; /* Project id */
src: url('iconfont.ttf?t=1695101560017') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-xiangxiajiantoucuxiao:before {
content: "\e8c8";
}
.icon-xiangshangjiantoucuxiao:before {
content: "\e8d8";
}
.icon-hr:before {
content: "\e60a";
}

View File

@@ -0,0 +1 @@
window._iconfont_svg_string_='<svg><symbol id="icon-xiangxiajiantoucuxiao" viewBox="0 0 1024 1024"><path d="M597.333333 640V85.333333c0-23.68-19.285333-42.666667-43.093333-42.666666h-84.48A42.666667 42.666667 0 0 0 426.666667 85.333333v554.666667H341.717333c-47.232 0-62.805333 30.549333-34.56 68.266667l153.642667 204.8c28.501333 37.973333 74.112 37.717333 102.4 0l153.6-204.8c28.501333-37.973333 12.8-68.266667-34.517333-68.266667H597.333333z" fill="#3D3D3D" ></path></symbol><symbol id="icon-xiangshangjiantoucuxiao" viewBox="0 0 1024 1024"><path d="M426.752 344.149333v554.666667c0 23.68 19.285333 42.666667 43.093333 42.666667h84.48a42.666667 42.666667 0 0 0 43.093334-42.666667v-554.666667h84.949333c47.232 0 62.805333-30.549333 34.56-68.266666l-153.642667-204.8c-28.501333-37.973333-74.112-37.717333-102.4 0l-153.6 204.8c-28.501333 37.973333-12.8 68.266667 34.517334 68.266666h84.949333z" fill="#3D3D3D" ></path></symbol><symbol id="icon-hr" viewBox="0 0 1024 1024"><path d="M63.6 489.6h896.7v44.8H63.6z" ></path></symbol></svg>',function(n){var t=(t=document.getElementsByTagName("script"))[t.length-1],e=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var i,o,c,a,d,s=function(t,e){e.parentNode.insertBefore(t,e)};if(e&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}i=function(){var t,e=document.createElement("div");e.innerHTML=n._iconfont_svg_string_,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?s(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),i()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(c=i,a=n.document,d=!1,r(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,l())})}function l(){d||(d=!0,c())}function r(){try{a.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}l()}}(window);

View File

@@ -0,0 +1,30 @@
{
"id": "",
"name": "",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "2076401",
"name": " 向下箭头粗小",
"font_class": "xiangxiajiantoucuxiao",
"unicode": "e8c8",
"unicode_decimal": 59592
},
{
"icon_id": "2076428",
"name": "向上箭头粗小",
"font_class": "xiangshangjiantoucuxiao",
"unicode": "e8d8",
"unicode_decimal": 59608
},
{
"icon_id": "19657558",
"name": "横线",
"font_class": "hr",
"unicode": "e60a",
"unicode_decimal": 58890
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<circle cx="84" cy="50" r="10" fill="#d4ecc9">
<animate attributeName="r" repeatCount="indefinite" dur="0.25s" calcMode="spline" keyTimes="0;1" values="10;0" keySplines="0 0.5 0.5 1" begin="0s"></animate>
<animate attributeName="fill" repeatCount="indefinite" dur="1s" calcMode="discrete" keyTimes="0;0.25;0.5;0.75;1" values="#d4ecc9;#82d5ab;#51d6a9;#95d8b4;#d4ecc9" begin="0s"></animate>
</circle><circle cx="16" cy="50" r="10" fill="#d4ecc9">
<animate attributeName="r" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;10;10;10" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="0s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="0s"></animate>
</circle><circle cx="50" cy="50" r="10" fill="#95d8b4">
<animate attributeName="r" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;10;10;10" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.25s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.25s"></animate>
</circle><circle cx="84" cy="50" r="10" fill="#51d6a9">
<animate attributeName="r" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;10;10;10" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.5s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.5s"></animate>
</circle><circle cx="16" cy="50" r="10" fill="#82d5ab">
<animate attributeName="r" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;10;10;10" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.75s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="1s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.75s"></animate>
</circle>
<!-- [ldio] generated by https://loading.io/ --></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,148 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="title"
width="60%"
destroy-on-close
@close="handleClose"
class="uni-custom-dialog"
>
<slot />
<template #footer>
<div class="dialog-footer">
<el-button
type="info"
icon="Refresh"
@click="handleReset"
v-if="showReset"
:disabled="loading"
>
重置
</el-button>
<el-button
icon="Close"
@click="handleCancel"
:disabled="loading"
>
取消
</el-button>
<el-button
type="primary"
icon="Check"
@click="handleConfirm"
class="save-btn"
:loading="loading"
:disabled="loading"
>
{{ confirmText }}
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { defineProps, defineEmits, ref, watch } from 'vue'
import { Refresh, Close, Check } from '@element-plus/icons-vue'
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
title: {
type: String,
default: '操作弹窗'
},
showReset: {
type: Boolean,
default: true
},
confirmText: {
type: String,
default: '保存'
},
loading: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:modelValue', 'close', 'reset', 'confirm'])
const dialogVisible = ref(props.modelValue)
watch(() => props.modelValue, (newVal) => {
dialogVisible.value = newVal
})
watch(dialogVisible, (newVal) => {
emit('update:modelValue', newVal)
})
const handleClose = () => {
emit('close')
dialogVisible.value = false
}
const handleCancel = () => {
dialogVisible.value = false
emit('close')
}
const handleReset = () => {
emit('reset')
}
const handleConfirm = () => {
emit('confirm')
}
</script>
<style>
.uni-custom-dialog .el-dialog__header {
border-bottom: 1px solid #1989fa !important;
padding-bottom: 15px !important;
margin-bottom: 10px !important;
}
.uni-custom-dialog .el-dialog__footer {
border-top: 1px solid #1989fa !important;
padding-top: 15px !important;
margin-top: 10px !important;
}
.uni-custom-dialog .dialog-footer {
display: flex;
gap: 12px;
justify-content: flex-end;
width: 100%;
}
.uni-custom-dialog .save-btn {
font-weight: 600;
padding: 8px 20px;
}
.uni-custom-dialog .el-button--info:hover {
background-color: #409eff !important;
border-color: #409eff !important;
color: #ffffff !important;
}
.uni-custom-dialog .dialog-footer .el-button--default:hover {
background-color: #f0f2f5 !important;
border-color: #c6cdd4 !important;
color: #303133 !important;
}
.uni-custom-dialog .dialog-footer .el-button--primary:hover {
background-color: #0d82e0 !important;
border-color: #0d82e0 !important;
color: #ffffff !important;
}
.uni-custom-dialog .el-button--loading {
pointer-events: none;
}
</style>

View File

@@ -0,0 +1,151 @@
<template>
<div class="edit-pwd-container">
<el-form ref="pwdFormRef" :model="pwdForm" :rules="pwdRules" label-width="80px" class="pwd-form">
<el-form-item label="原密码" prop="oldPassword">
<el-input v-model="pwdForm.oldPassword" type="password" placeholder="请输入原密码" show-password size="default" maxlength="20"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="pwdForm.newPassword" type="password" placeholder="请输入新密码" show-password size="default" maxlength="20"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="pwdForm.confirmPassword" type="password" placeholder="请确认新密码" show-password size="default" maxlength="20"></el-input>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, reactive, onUnmounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { updatePasswd } from '@/api/user'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const emit = defineEmits(['success'])
const pwdFormRef = ref(null)
const isMounted = ref(true)
let submitPromise = null
const pwdForm = reactive({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
const pwdRules = reactive({
oldPassword: [{ required: true, message: '请输入原密码', trigger: 'blur' }],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
],
confirmPassword: [
{ required: true, message: '请确认新密码', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!isMounted.value) return callback()
if (value === '') {
callback(new Error('请确认新密码'))
} else if (value !== pwdForm.newPassword) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
},
trigger: 'blur'
}
]
})
const submitForm = async () => {
if (!isMounted.value || !pwdFormRef.value || submitPromise) return
try {
submitPromise = true
await pwdFormRef.value.validate()
const reqParams = {
oldPasswd: pwdForm.oldPassword,
password: pwdForm.newPassword,
userId: userStore.loginUser?.userId || ''
}
const res = await updatePasswd(reqParams)
ElMessage.success(res.msg);
await new Promise(res => setTimeout(res, 800))
isMounted.value && emit('success')
return true
} catch (error) {
console.error('修改密码接口异常:', error)
return false
} finally {
if (isMounted.value) {
submitPromise = null
}
}
}
const resetForm = () => {
if (!isMounted.value || !pwdFormRef.value) return
pwdFormRef.value.resetFields()
}
onUnmounted(() => {
isMounted.value = false
submitPromise = null
})
defineExpose({ submitForm, resetForm })
</script>
<style scoped>
.edit-pwd-container {
border: none !important;
background: transparent !important;
padding: 0 !important;
box-shadow: none !important;
}
.pwd-form {
width: 100%;
}
:deep(.el-form-item) {
margin-bottom: 16px;
}
:deep(.el-form-item:last-child) {
margin-bottom: 0;
}
:deep(.el-input) {
--el-input-height: 32px !important;
width: 100%;
--el-input-border-radius: 4px;
}
:deep(.el-form-item__label) {
font-size: 13px;
color: #303133;
font-weight: 500;
}
:deep(.el-input__wrapper) {
padding: 0 10px;
height: 32px !important;
}
:deep(.el-input__inner) {
height: 32px !important;
line-height: 32px !important;
font-size: 13px;
}
:deep(.el-input__suffix-inner) {
height: 32px !important;
display: flex;
align-items: center;
justify-content: center;
}
:deep(.el-icon) {
font-size: 14px !important;
}
</style>

View File

@@ -0,0 +1,867 @@
<template>
<el-container class="app-container" style="height: 100vh; overflow: hidden;">
<el-header class="app-header">
<div class="header-left">
<img :src="LogoImg" class="app-logo" />
<div class="logo">{{ systemTitle }}</div>
</div>
<div class="header-right">
<el-tooltip content="全屏模式" placement="bottom">
<el-button text @click="handleFullScreen" class="header-icon-btn">
<el-icon size="18"><FullScreen /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip content="大屏展示" placement="bottom">
<el-button text @click="handleBigScreen" class="header-icon-btn">
<el-icon size="18"><Monitor /></el-icon>
</el-button>
</el-tooltip>
<el-dropdown trigger="click" @command="handleCommand">
<div class="user-info">
<el-avatar bg-color="#409eff"><el-icon><User /></el-icon></el-avatar>
<span class="user-name">{{ userStore.loginUser?.uname }}</span>
<el-icon><ArrowDown /></el-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="modifyPwd">
<el-icon><Lock /></el-icon>
<span>修改密码</span>
</el-dropdown-item>
<el-dropdown-item command="logout">
<el-icon><SwitchButton /></el-icon>
<span>退出系统</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<el-container>
<el-aside :width="isCollapse ? '64px' : '200px'" class="app-aside">
<div class="aside-wrapper">
<div class="menu-scroll-container" :style="{ overflowY: isCollapse ? 'hidden' : 'auto' }">
<el-menu
:default-active="activeTabKey"
:collapse="isCollapse"
background-color="#1f2d3d"
text-color="#e5e9f2"
active-text-color="#409eff"
:collapse-transition="false"
class="app-menu"
unique-opened
@open="handleSubMenuOpen"
>
<template v-for="menu in menuList" :key="menu.menuId">
<el-sub-menu v-if="menu.menuType === 'M' && menu.children && menu.children.length > 0" :index="menu.menuId">
<template #title>
<el-icon><component :is="menu.menuIcon || 'Menu'" /></el-icon>
<span>{{ menu.menuName }}</span>
</template>
<template #default>
<template v-for="child in menu.children" :key="child.menuId">
<el-menu-item :index="child.menuId" @click="handleMenuClick(child)">
<el-icon><component :is="child.menuIcon || 'Menu'" /></el-icon>
<template #title>{{ child.menuName }}</template>
</el-menu-item>
</template>
</template>
</el-sub-menu>
<el-menu-item v-else-if="menu.menuType === 'C'" :index="menu.menuId" @click="handleMenuClick(menu)">
<el-icon><component :is="menu.menuIcon || 'Menu'" /></el-icon>
<template #title>{{ menu.menuName }}</template>
</el-menu-item>
</template>
</el-menu>
</div>
<div class="aside-footer">
<el-button
circle
@click="toggleSidebar"
class="fold-btn"
:class="{ 'fold-btn-active': isFoldBtnActive }"
@mousedown="() => isFoldBtnActive = true"
@mouseup="() => isFoldBtnActive = false"
@mouseleave="() => isFoldBtnActive = false"
>
<el-icon size="16">
<component :is="isCollapse ? 'Expand' : 'Fold'" />
</el-icon>
</el-button>
</div>
</div>
</el-aside>
<el-main class="main-container">
<div class="tabs-container">
<div class="tabs-wrapper">
<div
class="tab-item"
:class="{ 'tab-active': activeTabKey === 'dashboard' }"
@click="switchToHome"
>
<el-icon class="tab-icon"><House /></el-icon>
<span class="tab-text">首页</span>
</div>
<el-button text class="scroll-btn" @click="scrollTab('left')" :disabled="tabs.length === 0">
<el-icon size="14"><ArrowLeft /></el-icon>
</el-button>
<div class="tabs-scroll-box" ref="tabsScrollRef">
<div class="dynamic-tabs">
<div
v-for="tab in tabs"
:key="tab.key"
class="tab-item"
:class="{ 'tab-active': activeTabKey === tab.key }"
@click="switchTab(tab)"
>
<span class="tab-text">{{ tab.name }}</span>
<el-icon class="tab-close-icon" @click.stop="closeTab(tab.key)">
<Close />
</el-icon>
</div>
</div>
</div>
<el-button text class="scroll-btn" @click="scrollTab('right')" :disabled="tabs.length === 0">
<el-icon size="14"><ArrowRight /></el-icon>
</el-button>
<el-button text class="close-all-btn" @click="closeAllTabs" :disabled="tabs.length === 0">
<el-icon size="14"><Close /></el-icon>
关闭全部
</el-button>
</div>
</div>
<div class="content-container">
<div class="content-wrapper">
<router-view />
</div>
</div>
</el-main>
</el-container>
<el-dialog
v-model="showEditPwdDialog"
title="修改密码"
width="35%"
:close-on-click-modal="false"
@close="handleEditPwdDialogClose"
:before-close="handleDialogBeforeClose"
class="custom-pwd-dialog"
>
<EditPswd ref="editPwdRef" @success="handlePwdModifySuccess" />
<template #footer>
<div class="dialog-footer">
<el-button size="default" @click="closeEditPwdDialog">取消</el-button>
<el-button size="default" type="primary" @click="submitEditPwd">确定</el-button>
</div>
</template>
</el-dialog>
</el-container>
</template>
<script setup>
import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getUserMenuList } from '@/api/bizMenu'
import LogoImg from '@/assets/logo.png'
import EditPswd from './components/editPswd.vue'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const isMounted = ref(false)
const systemTitle = ref("myPro管理系统")
const router = useRouter()
const route = useRoute()
const menuList = ref([])
const isCollapse = ref(false)
const tabsScrollRef = ref(null)
const tabs = ref([])
const activeTabKey = ref('dashboard')
const isFoldBtnActive = ref(false)
const menuNameMap = ref({})
const showEditPwdDialog = ref(false)
const editPwdRef = ref(null)
const openedSubMenuKeys = ref([])
const getMenuList = async () => {
if (!isMounted.value) return
try {
const reqParams = {
roleId: userStore.loginUser.roleId,
}
const res = await getUserMenuList(reqParams);
menuList.value = res || [];
const setMenuNameMap = (menus) => {
menus.forEach(menu => {
if (menu.menuId) menuNameMap.value[menu.menuId] = menu.menuName;
if (menu.children && menu.children.length > 0) setMenuNameMap(menu.children);
});
}
setMenuNameMap(menuList.value);
} catch (error) {
console.log(error);
}
}
const handleMenuClick = (menu) => {
if (!isMounted.value) return
const key = menu.menuId;
const name = menu.menuName;
if (!tabs.value.some(tab => tab.key === key)) {
tabs.value.push({ key, name });
}
activeTabKey.value = key;
if (menu.path) {
router.push(menu.path).catch(err => {
if (!err.message.includes('NavigationDuplicated')) {
ElMessage.warning('路由跳转失败');
}
});
} else {
ElMessage.info('该菜单暂无路由路径');
}
}
const handleSubMenuOpen = (key) => {
openedSubMenuKeys.value = [key];
}
const switchTab = (tab) => {
if (!isMounted.value) return
activeTabKey.value = tab.key;
const findMenu = (menus, menuId) => {
for (const menu of menus) {
if (menu.menuId === menuId) return menu;
if (menu.children && menu.children.length > 0) {
const res = findMenu(menu.children, menuId);
if (res) return res;
}
}
return null;
}
const menu = findMenu(menuList.value, tab.key);
if (menu && menu.path) {
router.push(menu.path).catch(err => {
if (!err.message.includes('NavigationDuplicated')) {
ElMessage.warning('路由跳转失败');
}
});
}
}
const closeTab = (key) => {
if (!isMounted.value) return
const idx = tabs.value.findIndex(tab => tab.key === key);
if (idx === -1) return;
if (activeTabKey.value === key) {
const nextTab = tabs.value[idx + 1] || tabs.value[idx - 1];
if (nextTab) {
activeTabKey.value = nextTab.key;
switchTab(nextTab);
} else {
switchToHome();
}
}
tabs.value.splice(idx, 1);
}
const closeAllTabs = () => {
if (!isMounted.value) return
ElMessageBox.confirm('确定关闭所有标签页?', '提示', {
type: 'warning',
closeOnClickModal: false,
showClose: false
}).then(() => {
tabs.value = [];
switchToHome();
ElMessage.success('已关闭所有标签页');
});
}
const switchToHome = () => {
if (!isMounted.value) return
activeTabKey.value = 'dashboard';
router.push('/dashboard').catch(err => {
if (!err.message.includes('NavigationDuplicated')) {
ElMessage.warning('路由跳转失败');
}
});
}
const scrollTab = (dir) => {
if (!isMounted.value) return
const el = tabsScrollRef.value;
if (el && tabs.value.length > 0) {
const scrollBox = el.querySelector('.dynamic-tabs');
if (scrollBox) {
scrollBox.scrollBy({ left: dir === 'left' ? -200 : 200, behavior: 'smooth' });
}
}
}
const toggleSidebar = () => {
if (!isMounted.value) return
isCollapse.value = !isCollapse.value;
}
const handleFullScreen = () => {
if (!isMounted.value) return
try {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
ElMessage.success('已进入全屏模式');
} else {
document.exitFullscreen();
ElMessage.success('已退出全屏模式');
}
} catch (error) {
ElMessage.error('全屏操作失败,请检查浏览器权限');
}
}
const handleBigScreen = () => {
const { href } = router.resolve({
path: "/bigScreen"
});
window.open(href, '_blank');
}
const handleCommand = (cmd) => {
if (!isMounted.value) return
if (cmd === 'logout') {
ElMessageBox.confirm('确定退出系统吗?', '退出确认', {
type: 'info',
closeOnClickModal: false,
showClose: false
})
.then(() => {
userStore.logout();
ElMessage.success('退出成功');
router.push('/login');
})
.catch(() => {
ElMessage.info('用户取消退出系统');
});
}
if (cmd === 'modifyPwd') {
nextTick(() => {
showEditPwdDialog.value = true;
});
}
}
const closeEditPwdDialog = () => {
if (!isMounted.value) return
showEditPwdDialog.value = false;
nextTick(() => {
if (editPwdRef.value) {
editPwdRef.value.resetForm();
}
});
}
const handleEditPwdDialogClose = () => {
closeEditPwdDialog();
}
const handleDialogBeforeClose = (done) => {
nextTick(() => {
done();
});
}
const submitEditPwd = () => {
if (!isMounted.value || !editPwdRef.value || !showEditPwdDialog.value) return
editPwdRef.value.submitForm().catch(err => {
if (err && !err.message.includes('cancel')) {
ElMessage.error(err.message);
}
});
}
const handlePwdModifySuccess = () => {
if (!isMounted.value) return
closeEditPwdDialog();
userStore.logout();
router.push('/login');
}
onUnmounted(() => {
isMounted.value = false
})
onMounted(() => {
isMounted.value = true
getMenuList();
watch(() => route.path, (newPath) => {
if (!isMounted.value) return
activeTabKey.value = newPath === '/dashboard' ? 'dashboard' : route.params.menuId || activeTabKey.value;
}, { immediate: true });
})
</script>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.app-logo {
height: 45px;
width: 52px;
display: inline-block;
vertical-align: middle;
}
.app-container {
--primary-color: #4285f4;
--primary-dark: #1f2d3d;
--menu-hover-bg: #2c3e50;
--tab-bg: #e8eaed;
--tab-active-bg: #4285f4;
--tab-text: #333;
--tab-active-text: #fff;
--tab-height: 28px;
--tab-radius: 14px;
--tab-padding: 0 12px;
--divider-color: #e6e6e6;
--header-bg-color: #c6e2ff;
background: url('@/assets/desktop.png') no-repeat center center fixed;
background-size: cover;
position: relative;
}
.app-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.85);
z-index: 0;
}
.app-container > * {
position: relative;
z-index: 1;
}
.app-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 60px !important;
background: rgba(198, 226, 255, 0.9) !important;
border-bottom: 1px solid #e6e6e6;
}
.header-left {
display: flex;
align-items: center;
gap: 12px;
}
.logo {
font-size: 18px;
font-weight: 600;
color: var(--primary-color);
letter-spacing: 1px;
line-height: 45px;
}
.header-right {
display: flex;
align-items: center;
gap: 12px;
}
.header-icon-btn {
width: 40px;
height: 40px;
color: #333 !important;
border-radius: 4px;
transition: all 0.2s;
}
.header-icon-btn:hover {
background: var(--header-bg-color) !important;
color: var(--primary-color) !important;
}
.user-info {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 6px 12px;
border-radius: 6px;
transition: all 0.2s;
}
.user-info:hover {
background: var(--header-bg-color);
color: var(--primary-color);
}
.user-name {
font-size: 14px;
color: #333;
}
.app-aside {
background: rgba(31, 45, 61, 0.95) !important;
height: calc(100vh - 60px) !important;
transition: width 0.2s ease;
}
.aside-wrapper {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.menu-scroll-container {
flex: 1;
padding-right: 0;
height: calc(100% - 60px);
scrollbar-width: thin;
scrollbar-color: #2c3e50 #1f2d3d;
}
.menu-scroll-container:not([style*="overflowY: hidden"]) {
padding-right: 2px;
}
.menu-scroll-container::-webkit-scrollbar {
width: 4px;
}
.menu-scroll-container::-webkit-scrollbar-track {
background: #1f2d3d;
border-radius: 2px;
}
.menu-scroll-container::-webkit-scrollbar-thumb {
background: #2c3e50;
border-radius: 2px;
}
.menu-scroll-container::-webkit-scrollbar-thumb:hover {
background: #409eff;
}
.app-menu {
height: 100%;
border-right: none !important;
}
.aside-footer {
height: 60px;
display: flex;
align-items: center;
justify-content: center;
background: var(--primary-dark);
border-top: 1px solid #2c3e50;
}
.fold-btn {
width: 40px;
height: 40px;
background: transparent;
color: #e5e9f2;
border: none;
transition: all 0.2s;
}
.fold-btn:hover {
background: #2c3e50;
color: #409eff;
}
.fold-btn-active {
background: #0f48b9 !important;
color: #fff !important;
}
.main-container {
height: calc(100vh - 60px) !important;
display: flex;
flex-direction: column;
background: rgba(248, 249, 250, 0.9) !important;
overflow: hidden;
padding: 0 !important;
}
.tabs-container {
padding: 4px 8px;
background: rgba(241, 243, 244, 0.95) !important;
border-bottom: 1px solid #e6e6e6;
}
.tabs-wrapper {
display: flex;
align-items: center;
gap: 8px;
height: 36px;
}
.scroll-btn {
width: 28px;
height: 28px;
border-radius: 50%;
border: none;
background: #fff;
color: #666;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
transition: all 0.2s;
}
.scroll-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.scroll-btn:hover:not(:disabled) {
background: #e8eaed;
color: #333;
}
.tabs-scroll-box {
flex: 1;
overflow: hidden;
min-width: 0;
}
.dynamic-tabs {
display: flex;
gap: 4px;
overflow-x: auto;
height: 100%;
align-items: center;
padding: 0 2px;
}
.dynamic-tabs::-webkit-scrollbar {
display: none;
}
.tab-item {
display: flex;
align-items: center;
gap: 6px;
padding: var(--tab-padding);
height: var(--tab-height);
border-radius: var(--tab-radius);
background: var(--tab-bg);
color: var(--tab-text);
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
box-shadow: 0 1px 1px rgba(0,0,0,0.05);
}
.tab-active {
background: var(--tab-active-bg) !important;
color: var(--tab-active-text) !important;
}
.tab-icon {
font-size: 14px;
margin-right: 4px;
}
.tab-close-icon {
width: 16px;
height: 16px;
opacity: 0.8;
transition: all 0.2s;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.tab-close-icon:hover {
opacity: 1;
background: rgba(0,0,0,0.1);
transform: none;
}
.tab-active .tab-close-icon {
color: #fff;
}
.close-all-btn {
padding: 0 12px;
height: 28px;
border-radius: 14px;
background: #fff;
color: #666;
font-size: 12px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
transition: all 0.2s;
}
.close-all-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.close-all-btn:hover:not(:disabled) {
background: #e8eaed;
color: #333;
}
.content-container {
flex: 1;
padding: 0 !important;
overflow: hidden !important;
display: flex !important;
flex-direction: column !important;
}
.content-wrapper {
margin: 8px;
padding: 2px;
background: rgba(255, 255, 255, 0.95) !important;
border: 1px solid #e6e6e6;
border-radius: 8px;
flex: 1 !important;
height: calc(100% - 16px) !important;
min-height: 0 !important;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
display: flex !important;
flex-direction: column !important;
}
:deep(.el-sub-menu .el-menu) {
background-color: transparent !important;
}
:deep(.el-sub-menu .el-menu-item) {
padding-left: 40px !important;
}
:deep(.el-sub-menu .el-sub-menu .el-menu-item) {
padding-left: 60px !important;
}
:deep(.el-menu-item.is-active) {
background-color: var(--menu-hover-bg) !important;
color: #409eff !important;
}
:deep(.el-sub-menu__title:hover) {
background-color: var(--menu-hover-bg) !important;
}
:deep(.el-menu-item:hover) {
background-color: var(--menu-hover-bg) !important;
}
:deep(.el-dropdown-menu) {
border: 1px solid #e6e6e6;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
}
:deep(.el-tooltip__popper) {
padding: 4px 8px;
font-size: 12px;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
}
:deep(.custom-pwd-dialog) {
--dialog-divider: #c6e2ff;
--footer-height: 50px;
}
:deep(.custom-pwd-dialog .el-dialog) {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
border: none !important;
background: transparent !important;
padding: 0 !important;
}
:deep(.custom-pwd-dialog .el-dialog__header) {
padding: 8px 20px;
border-bottom: 1px solid var(--dialog-divider);
background: #fff !important;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
margin: 0 !important;
height: auto !important;
}
:deep(.custom-pwd-dialog .el-dialog__title) {
font-size: 16px;
font-weight: 500;
color: #333;
line-height: 1.4;
}
:deep(.custom-pwd-dialog .el-dialog__body) {
padding: 12px 20px;
border-bottom: 1px solid var(--dialog-divider);
background: #fff !important;
margin: 0 !important;
height: auto !important;
}
:deep(.custom-pwd-dialog .el-dialog__footer) {
height: var(--footer-height) !important;
line-height: var(--footer-height) !important;
padding: 0 20px !important;
border-top: 1px solid var(--dialog-divider);
background: #fff !important;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
text-align: right !important;
margin: 0 !important;
min-height: unset !important;
display: block !important;
}
.dialog-footer {
display: inline-flex !important;
align-items: center !important;
justify-content: flex-end !important;
gap: 12px;
margin: 0 !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
:deep(.dialog-footer .el-button) {
margin: 0 !important;
vertical-align: middle !important;
}
:deep(.custom-pwd-dialog .el-dialog__headerbtn) {
top: 8px !important;
}
:deep(.el-menu--collapse .el-sub-menu__popup) {
min-width: 180px !important;
width: 180px !important;
}
:deep(.el-menu--collapse .el-sub-menu__title .el-sub-menu__icon-arrow) {
display: none !important;
}
:deep(.el-menu--collapse .el-menu-item .el-icon) {
margin-right: 0 !important;
}
</style>

View File

@@ -0,0 +1,115 @@
<template>
<div class="search-section">
<div class="search-grid-container">
<slot />
<div
v-for="i in getPlaceHolderCount()"
:key="`placeholder-${i}`"
class="placeholder-item"
></div>
<div class="search-btn-group">
<el-button type="primary" icon="Search" @click="handleSearch">
查询
</el-button>
<el-button type="info" icon="Refresh" @click="handleReset">
重置
</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, defineEmits, onMounted, nextTick } from 'vue'
import { Search, Refresh } from '@element-plus/icons-vue'
const emit = defineEmits(['search', 'reset'])
const conditionCount = ref(0)
const handleSearch = () => {
emit('search')
}
const handleReset = () => {
emit('reset')
}
onMounted(async () => {
await nextTick()
calcConditionCount()
})
const calcConditionCount = () => {
const items = document.querySelectorAll('.search-grid-container .search-item')
conditionCount.value = items ? items.length : 0
}
const getPlaceHolderCount = () => {
const currentRowRemain = 4 - (conditionCount.value % 4)
return currentRowRemain === 0 ? 3 : currentRowRemain - 1
}
</script>
<style scoped>
.search-section {
width: 100%;
margin-bottom: 16px;
flex-shrink: 0;
padding-bottom: 12px;
border-bottom: 1px solid #e5e7eb;
box-sizing: border-box;
}
.search-grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
width: 100%;
box-sizing: border-box;
align-items: stretch;
}
.search-grid-container :deep(.search-item) {
margin-bottom: 0 !important;
width: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
}
.search-grid-container :deep(.search-input),
.search-grid-container :deep(.search-select) {
width: 100%;
box-sizing: border-box;
}
.placeholder-item {
width: 100%;
height: 1px;
visibility: hidden;
}
.search-btn-group {
display: flex;
gap: 8px;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
box-sizing: border-box;
}
@media (max-width: 768px) {
.search-grid-container {
grid-template-columns: 1fr !important;
gap: 10px;
}
.placeholder-item {
display: none;
}
.search-btn-group {
justify-content: center;
padding: 8px 0;
}
}
</style>

View File

@@ -0,0 +1,310 @@
<template>
<div class="el-card el-card--border" ref="wrapperRef" style="height: 100%; border: none; box-shadow: none;">
<el-input
v-if="showSearch"
v-model="filterText"
:placeholder="searchPlaceholder"
clearable
class="filter-search-input"
@clear="handleClear"
size="default"
/>
<el-divider v-if="showSearch" direction="horizontal" class="filter-tree-divider" />
<div
class="list-container"
:style="{ height: listHeight, width: '100%', overflow: 'hidden' }"
>
<el-scrollbar height="100%" :native="true">
<div
class="list-item"
v-for="item in filteredList"
:key="item[nodeKey]"
@mouseenter="() => hoveredItemId = item[nodeKey]"
@mouseleave="() => handleItemLeave(item[nodeKey])"
@click="() => handleItemClick(item)"
:class="{ 'list-item--selected': selectedItemId === item[nodeKey] }"
>
<span class="item-text">{{ item[labelKey] }}</span>
<div class="action-wrapper" v-show="hoveredItemId === item[nodeKey]">
<el-dropdown
trigger="click"
placement="bottom"
:teleported="false"
@click.stop
>
<el-button size="mini" icon="MoreFilled" circle class="action-btn" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="() => handleEdit(item)">
<el-icon class="menu-icon"><Edit /></el-icon>
</el-dropdown-item>
<el-dropdown-item @click="() => handleDelete(item)" divided>
<el-icon class="menu-icon"><Delete /></el-icon>
</el-dropdown-item>
<el-dropdown-item @click="() => handleView(item)">
<el-icon class="menu-icon"><View /></el-icon>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</el-scrollbar>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, defineProps, defineEmits, withDefaults, onMounted, onUnmounted, nextTick, computed } from 'vue'
import { ElInput, ElDivider, ElScrollbar, ElDropdown, ElButton, ElDropdownMenu, ElDropdownItem, ElIcon } from 'element-plus'
import { Edit, Delete, View, MoreFilled, Search } from '@element-plus/icons-vue'
export interface ListItem {
[key: string]: any
}
const emit = defineEmits([
'edit',
'delete',
'view',
'item-click',
'search-change',
'search-clear'
])
const props = withDefaults(
defineProps<{
listData: ListItem[]
showSearch?: boolean
searchPlaceholder?: string
nodeKey?: string
labelKey?: string
customFilter?: (value: string, data: ListItem) => boolean
autoHeight?: boolean
minHeight?: string | number
defaultValue?: string | number
}>(),
{
showSearch: true,
searchPlaceholder: '请输入关键词过滤',
nodeKey: 'id',
labelKey: 'label',
autoHeight: true,
minHeight: '100px',
defaultValue: ''
}
)
const filterText = ref('')
const wrapperRef = ref<HTMLDivElement>(null)
const hoveredItemId = ref<string | number | null>(null)
const selectedItemId = ref<string | number | null>(props.defaultValue)
const listHeight = computed(() => {
if (!props.autoHeight || !wrapperRef.value) return props.minHeight as string
const wrapperHeight = wrapperRef.value.clientHeight
const searchHeight = props.showSearch ? 40 : 0
const dividerHeight = props.showSearch ? (16 + 2) : 0
const baseMargin = 8
return `${wrapperHeight - searchHeight - dividerHeight - baseMargin}px`
})
const filteredList = computed(() => {
if (!filterText.value) return props.listData
if (props.customFilter) {
return props.listData.filter(item => props.customFilter!(filterText.value, item))
}
return props.listData.filter(item => {
const label = item[props.labelKey] || ''
return label.toString().includes(filterText.value)
})
})
watch(
() => filterText.value,
(val) => {
emit('search-change', val)
},
{ immediate: false }
)
watch(() => props.defaultValue, (val) => {
selectedItemId.value = val
}, { immediate: true })
const handleItemLeave = (id: string | number) => {
hoveredItemId.value = null
}
const handleItemClick = (item: ListItem) => {
selectedItemId.value = item[props.nodeKey]
emit('item-click', item)
}
const handleEdit = (item: ListItem) => {
emit('edit', item)
hoveredItemId.value = null
}
const handleDelete = (item: ListItem) => {
emit('delete', item)
hoveredItemId.value = null
}
const handleView = (item: ListItem) => {
emit('view', item)
hoveredItemId.value = null
}
const handleClear = () => {
filterText.value = ''
emit('search-clear')
}
onMounted(async () => {
await nextTick()
const resizeHandler = () => void listHeight.value
window.addEventListener('resize', resizeHandler)
if (window.ResizeObserver && wrapperRef.value) {
const observer = new ResizeObserver(resizeHandler)
observer.observe(wrapperRef.value)
onUnmounted(() => {
observer.disconnect()
window.removeEventListener('resize', resizeHandler)
})
}
})
defineExpose({
doFilter: (value: string) => {
filterText.value = value
},
clearSearch: handleClear,
getFilteredList: () => filteredList.value,
setSelected: (id: string | number) => {
selectedItemId.value = id
},
getSelected: () => {
return filteredList.value.find(item => item[props.nodeKey] === selectedItemId.value)
}
})
</script>
<style scoped>
:deep(.el-card) {
height: 100%;
border: none;
box-shadow: none;
padding: 0;
}
.filter-search-input {
margin: 0 2px !important;
width: calc(100% - 4px) !important;
}
.filter-tree-divider {
margin: 2px 2px 0 2px !important;
height: 1px;
}
.list-container {
position: relative;
}
.list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 8px;
cursor: pointer;
transition: background-color 0.2s ease;
border-radius: 4px;
margin-bottom: 2px;
position: relative;
overflow: visible;
user-select: none;
--el-tree-node-content-hover-bg-color: #f0f9ff;
--el-tree-node-selected-bg-color: #e6f7ff;
--el-tree-node-selected-color: #1890ff;
}
.list-item:hover {
background-color: var(--el-tree-node-content-hover-bg-color);
}
.list-item--selected {
background-color: var(--el-tree-node-selected-bg-color) !important;
color: var(--el-tree-node-selected-color) !important;
}
.list-item--selected .item-text {
color: var(--el-tree-node-selected-color) !important;
}
.item-text {
flex: 1;
font-size: 14px;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 4px;
}
.action-wrapper {
position: relative;
z-index: 100;
flex-shrink: 0;
}
.action-btn {
width: 24px;
height: 24px;
padding: 0;
background-color: transparent !important;
border: none !important;
color: #909399 !important;
transition: all 0.2s ease;
}
.action-btn:hover {
color: #1890ff !important;
background-color: #f5f7fa !important;
}
.menu-icon {
width: 18px;
height: 18px;
color: #606266;
display: block;
margin: 0 auto;
}
:deep(.el-dropdown-menu) {
min-width: 40px !important;
padding: 4px 0;
z-index: 9999;
}
:deep(.el-dropdown-item) {
text-align: center;
padding: 6px 8px !important;
}
:deep(.el-dropdown__popper) {
position: absolute !important;
top: 100% !important;
left: 50% !important;
margin-top: 4px !important;
transform: translateX(-50%) !important;
z-index: 9999 !important;
}
:deep(.el-scrollbar__wrap) {
overflow-x: hidden;
}
</style>

View File

@@ -0,0 +1,172 @@
<template>
<div class="el-card el-card--border" ref="wrapperRef" style="height: 100%; border: none; box-shadow: none;">
<el-input
v-if="showSearch"
v-model="filterText"
:placeholder="searchPlaceholder"
clearable
class="filter-search-input"
@clear="handleClear"
size="default"
/>
<el-divider v-if="showSearch" direction="horizontal" class="filter-tree-divider" />
<el-tree
ref="treeRef"
:data="treeData"
:props="treeProps"
:default-expand-all="defaultExpandAll"
:filter-node-method="filterNode"
:node-key="nodeKey"
:accordion="accordion"
:height="treeHeight"
class="el-tree--highlight-current"
@node-click="handleNodeClick"
v-bind="$attrs"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, defineProps, defineEmits, withDefaults, onMounted, onUnmounted, nextTick, computed } from 'vue'
import type { ElTree } from 'element-plus'
export interface FilterTreeNode {
id: string
label: string
children?: FilterTreeNode[]
[key: string]: any
}
interface TreePropsCompat {
children?: string
label?: string
disabled?: string | boolean
isLeaf?: string | boolean
[key: string]: any
}
const emit = defineEmits(['node-click', 'search-change', 'search-clear'])
const props = withDefaults(
defineProps<{
treeData: FilterTreeNode[]
treeProps?: TreePropsCompat
showSearch?: boolean
searchPlaceholder?: string
defaultExpandAll?: boolean
nodeKey?: string | number
customFilter?: (value: string, data: FilterTreeNode) => boolean
autoHeight?: boolean
minHeight?: string | number
accordion?: boolean
}>(),
{
showSearch: true,
searchPlaceholder: '请输入关键词过滤',
defaultExpandAll: false,
nodeKey: 'id',
autoHeight: true,
minHeight: '100px',
treeProps: (): TreePropsCompat => ({
children: 'children',
label: 'label'
}),
accordion: true
}
)
const filterText = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>()
const wrapperRef = ref<HTMLDivElement>(null)
const treeHeight = computed(() => {
if (!props.autoHeight || !wrapperRef.value) return props.minHeight as string
const wrapperHeight = wrapperRef.value.clientHeight
const searchHeight = props.showSearch ? 40 : 0
const dividerHeight = props.showSearch ? (16 + 2) : 0
const baseMargin = 8
return `${wrapperHeight - searchHeight - dividerHeight - baseMargin}px`
})
watch(
() => filterText.value,
(val) => {
emit('search-change', val)
treeRef.value?.filter(val)
},
{ immediate: false }
)
const filterNode = (value: string, data: FilterTreeNode): boolean => {
if (props.customFilter) return props.customFilter(value, data)
if (!value) return true
return data.label.includes(value)
}
const handleNodeClick = (...args: any[]) => {
const data = args[0] as FilterTreeNode
emit('node-click', data)
}
const handleClear = () => {
filterText.value = ''
emit('search-clear')
treeRef.value?.filter('')
}
onMounted(async () => {
await nextTick()
const resizeHandler = () => void treeHeight.value
window.addEventListener('resize', resizeHandler)
if (window.ResizeObserver && wrapperRef.value) {
const observer = new ResizeObserver(resizeHandler)
observer.observe(wrapperRef.value)
onUnmounted(() => {
observer.disconnect()
window.removeEventListener('resize', resizeHandler)
})
}
})
defineExpose({
doFilter: (value: string) => {
filterText.value = value
treeRef.value?.filter(value)
},
getTreeRef: () => treeRef.value,
clearSearch: handleClear
})
</script>
<style scoped>
:deep(.el-card) {
height: 100%;
border: none;
box-shadow: none;
padding: 0;
}
.filter-search-input {
margin: 0 2px !important;
width: calc(100% - 4px) !important;
}
.filter-tree-divider {
margin: 2px 2px 0 2px !important;
height: 1px;
}
:deep(.el-tree) {
width: 100%;
--el-tree-node-content-hover-bg-color: #f0f9ff;
--el-tree-node-selected-bg-color: #e6f7ff;
--el-tree-node-selected-color: #1890ff;
}
:deep(.el-tree-node__content) {
cursor: pointer;
padding: 4px 8px;
}
</style>

View File

@@ -0,0 +1,194 @@
<template>
<div class="resizable-layout-container">
<div
class="sidebar-container"
:style="{ width: sidebarWidth + 'px' }"
>
<slot name="sidebar"></slot>
<div
class="resize-handle"
:class="{ dragging: isDragging }"
@mousedown="startDrag"
>
<div class="handle-indicator"></div>
</div>
</div>
<div class="main-container">
<slot name="main"></slot>
</div>
</div>
</template>
<script setup>
import { ref, onUnmounted, defineProps, defineEmits, watch, onMounted } from 'vue'
const props = defineProps({
defaultWidth: {
type: Number,
default: 180
},
minWidth: {
type: Number,
default: 160
},
maxWidth: {
type: Number,
default: 420
},
bgColor: {
type: String,
default: '#f8f9fa'
},
handleWidth: {
type: Number,
default: 8
}
})
const emit = defineEmits(['width-change'])
const sidebarWidth = ref(props.defaultWidth)
const isDragging = ref(false)
let startMouseX = 0
let startWidth = 0
watch([() => props.defaultWidth], (newVal) => {
sidebarWidth.value = newVal[0]
})
const startDrag = (e) => {
isDragging.value = true
startMouseX = e.clientX
startWidth = sidebarWidth.value
document.body.style.cursor = 'ew-resize'
document.body.style.userSelect = 'none'
e.preventDefault()
e.stopPropagation()
}
const onMouseMove = (e) => {
if (!isDragging.value) return
const offsetX = e.clientX - startMouseX
const targetWidth = startWidth + offsetX
if (targetWidth >= props.minWidth && targetWidth <= props.maxWidth) {
sidebarWidth.value = targetWidth
if (Date.now() % 5 === 0) {
emit('width-change', targetWidth)
}
}
}
const onMouseUp = () => {
if (!isDragging.value) return
isDragging.value = false
document.body.style.cursor = ''
document.body.style.userSelect = ''
emit('width-change', sidebarWidth.value)
}
onMounted(() => {
document.addEventListener('mousemove', onMouseMove)
document.addEventListener('mouseup', onMouseUp)
})
onUnmounted(() => {
document.removeEventListener('mousemove', onMouseMove)
document.removeEventListener('mouseup', onMouseUp)
})
defineExpose({
setSidebarWidth: (width) => {
const safeWidth = Math.max(props.minWidth, Math.min(props.maxWidth, width))
sidebarWidth.value = safeWidth
emit('width-change', safeWidth)
}
})
</script>
<style scoped>
.resizable-layout-container {
width: 100%;
height: 100vh;
display: flex;
overflow: hidden;
}
.sidebar-container {
height: 100%;
border-right: none;
display: flex;
flex-direction: column;
position: relative;
flex-shrink: 0;
background-color: v-bind(bgColor) !important;
padding: 0;
margin: 0;
}
.sidebar-container > div:first-child {
flex: 1;
padding: 16px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #d0d0d0 v-bind(bgColor);
box-sizing: border-box;
width: 100%;
}
.sidebar-container > div:first-child::-webkit-scrollbar { width: 6px }
.sidebar-container > div:first-child::-webkit-scrollbar-track { background: v-bind(bgColor) }
.sidebar-container > div:first-child::-webkit-scrollbar-thumb {
background: #d0d0d0;
border-radius: 3px;
}
.sidebar-container > div:first-child::-webkit-scrollbar-thumb:hover { background: #b0b0b0 }
.resize-handle {
position: absolute;
right: 0;
top: 0;
width: v-bind(handleWidth + 'px');
height: 100%;
cursor: ew-resize;
z-index: 999;
background-color: transparent;
margin-left: 0;
padding-right: 0;
box-shadow: inset -4px 0 0 transparent;
}
.handle-indicator {
width: 1px;
height: 100%;
background-color: #e5e7eb;
position: absolute;
right: 0;
top: 0;
transition: background-color 0.1s ease;
}
.resize-handle:hover .handle-indicator {
background-color: #d1d5db;
}
.resize-handle.dragging .handle-indicator {
background-color: #9ca3af;
}
.main-container {
flex: 1;
height: 100%;
overflow: hidden;
min-width: 0;
width: auto;
background-color: v-bind(bgColor) !important;
padding: 0;
margin: 0;
}
</style>

View File

@@ -0,0 +1,150 @@
<template>
<div class="custom-table-wrapper">
<div class="table-container">
<el-table
:data="tableData"
v-loading="loading"
class="data-table"
height="100%"
border="false"
>
<slot name="columns" />
</el-table>
<div v-if="tableData.length === 0 && !loading" class="empty-tip">
<el-empty description="暂无数据" />
</div>
</div>
<div class="pagination-footer">
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
background
/>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
tableData: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
},
pagination: {
type: Object,
default: () => ({
pageNum: 1,
pageSize: 20,
total: 0
})
}
})
const emit = defineEmits(['size-change', 'current-change'])
const handleSizeChange = (val) => {
emit('size-change', val)
}
const handleCurrentChange = (val) => {
emit('current-change', val)
}
</script>
<style scoped>
.custom-table-wrapper {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
gap: 4px;
box-sizing: border-box;
}
.table-container {
flex: 1;
width: 100%;
height: 100%;
overflow: hidden !important;
padding: 4px;
box-sizing: border-box;
}
.data-table {
width: 100%;
--el-table-header-text-color: #333;
--el-table-row-hover-bg-color: #f8f9fa;
--el-table-border-color: transparent !important;
}
.data-table :deep(.el-table),
.data-table :deep(.el-table__header-wrapper),
.data-table :deep(.el-table__body-wrapper),
.data-table :deep(.el-table__footer-wrapper) {
border: none !important;
}
.data-table :deep(.el-table__cell) {
border: none !important;
border-right: none !important;
border-bottom: 1px solid #f0f0f0 !important;
}
.data-table :deep(.el-table__header .el-table__cell) {
border-bottom: 1px solid #e5e7eb !important;
}
.empty-tip {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.pagination-footer {
width: 100%;
display: flex;
justify-content: flex-end;
padding: 4px;
margin: 0;
border: none !important;
border-radius: 0;
border-top: 1px solid #e5e7eb !important;
flex-shrink: 0;
background: transparent;
z-index: 10;
box-sizing: border-box;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar {
width: 4px;
height: 4px;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 4px;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
</style>

View File

@@ -0,0 +1,134 @@
<template>
<div class="custom-table-wrapper">
<div class="table-container">
<el-table
:data="tableData"
v-loading="loading"
class="data-table"
height="100%"
border="false"
:tree-props="treeProps"
lazy
:load="loadChildren"
:row-key="rowKey"
>
<slot name="columns" />
</el-table>
<div v-if="tableData.length === 0 && !loading" class="empty-tip">
<el-empty description="暂无数据" />
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
// 定义组件属性
const props = defineProps({
tableData: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
},
treeProps: {
type: Object,
default: () => ({
children: 'children',
hasChildren: 'hasChildren'
})
},
rowKey: {
type: String,
default: 'menuId'
}
})
const emit = defineEmits(['load-children'])
const loadChildren = (row, treeNode, resolve) => {
emit('load-children', { row, treeNode, resolve })
}
</script>
<style scoped>
.custom-table-wrapper {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
gap: 4px;
box-sizing: border-box;
}
.table-container {
flex: 1;
width: 100%;
height: 100%;
overflow: hidden !important;
padding: 4px;
box-sizing: border-box;
}
.data-table {
width: 100%;
--el-table-header-text-color: #333;
--el-table-row-hover-bg-color: #f8f9fa;
--el-table-border-color: transparent !important;
}
.data-table :deep(.el-table),
.data-table :deep(.el-table__header-wrapper),
.data-table :deep(.el-table__body-wrapper),
.data-table :deep(.el-table__footer-wrapper) {
border: none !important;
}
.data-table :deep(.el-table__cell) {
border: none !important;
border-right: none !important;
border-bottom: 1px solid #f0f0f0 !important;
}
.data-table :deep(.el-table__header .el-table__cell) {
border-bottom: 1px solid #e5e7eb !important;
}
.empty-tip {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar {
width: 4px;
height: 4px;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 4px;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
.table-container :deep(.el-table__body-wrapper)::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
.data-table :deep(.el-table__expand-icon) {
color: #666;
font-size: 14px;
}
.data-table :deep(.el-table__expand-icon--expanded) {
transform: rotate(90deg);
}
</style>

View File

@@ -1,16 +1,20 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import VScaleScreen from 'v-scale-screen'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import * as echarts from 'echarts'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import "@/assets/font/pangshizidao.ttf";
const app = createApp(App)
// 注册路由
app.use(createPinia())
app.use(router)
app.use(ElementPlus, {
locale: zhCn,

View File

@@ -1,62 +1,173 @@
import { createRouter, createWebHistory } from 'vue-router'
import { createRouter, createWebHashHistory } from 'vue-router'
import Login from '@/views/Login.vue'
import Page404 from '@/views/error/404.vue'
import Layout from '@/components/Layout/index.vue'
import Dashboard from '@/views/desktop/index.vue'
import BigScreen from '@/views/screen/index.vue'
import BigScreenWork from '@/views/screen/Work/index.vue'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
const bigScreenModules = import.meta.glob('../views/screen/**/{index,list}.vue', {
eager: false,
import: 'default'
})
const generateBigScreenRoutes = () => {
const routes = []
Object.entries(bigScreenModules).forEach(([filePath, module]) => {
if (filePath === '../views/screen/index.vue') return;
const routePath = filePath
.replace('../views', '')
.replace('.vue', '')
.toLowerCase();
const finalPath = routePath;
const routeName = finalPath
.split('/')
.filter(Boolean)
.map(seg => seg.charAt(0).toUpperCase() + seg.slice(1))
.join('')
|| 'SystemRoleIndex';
routes.push({
path: finalPath,
name: routeName,
component: module,
meta: { requiresAuth: true }
})
})
return routes
}
const modules = import.meta.glob('../views/**/{index,list}.vue', {
eager: false,
import: 'default'
})
const generateRoutes = () => {
const routes = []
Object.entries(modules).forEach(([filePath, module]) => {
const excludePaths = [
'views/Login.vue',
'views/desktop/index.vue',
'views/error/',
'views/screen/',
]
if (excludePaths.some(path => filePath.includes(path))) {
return
}
const routePath = filePath
.replace('../views', '')
.replace('.vue', '')
.toLowerCase()
const routeName = routePath
.split('/')
.filter(Boolean)
.map(seg => seg.charAt(0).toUpperCase() + seg.slice(1))
.join('')
routes.push({
path: routePath,
name: routeName || 'SystemRoleIndex',
component: module,
meta: { requiresAuth: true }
})
})
return routes
}
// 路由规则
const routes = [
{
path: '/',
redirect: '/login' // 默认跳登录页
},
{
path: '/login',
name: 'Login',
component: Login,
meta: {
isPublic: true // 标记为公开页面,无需登录
}
meta: { isPublic: true }
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard,
meta: {
requiresAuth: true // 需要登录验证
}
path: '/bigScreen',
name: 'BigScreen',
component: BigScreen,
meta: { requiresAuth: true },
children: [
{
path: '/bigScreenWork',
name: 'BigScreenWork',
component: BigScreenWork
},
...generateBigScreenRoutes(),
]
},
{
path: '/',
redirect: '/login'
},
{
path: '/layout',
component: Layout,
meta: { requiresAuth: true },
children: [
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard
},
...generateRoutes(),
{
path: '/:pathMatch(.*)*',
name: 'Layout404',
component: Page404
}
]
},
// 关键补充404兜底路由匹配所有未定义的前端路由
{
path: '/:pathMatch(.*)*',
redirect: '/login' // 未知路径重定向到登录页
name: 'Page404',
component: () => import('@/views/error/404.vue'),
meta: { requiresAuth: true }
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL || '/'),
history: createWebHashHistory(import.meta.env.BASE_URL || './'),
routes,
scrollBehavior() {
return { top: 0 }
}
})
// 路由守卫:验证登录状态(优化版,增加容错)
router.beforeEach((to, from, next) => {
// 公开页面直接放行
if (to.meta.isPublic) {
next()
return
}
// 非公开页面校验token
const userStore = useUserStore()
try {
const token = localStorage.getItem('token')
if (token && token.trim() !== '') {
if (userStore.isLoggedIn()) {
next()
} else {
next({ path: '/login', query: { redirect: to.fullPath } })
ElMessage.warning('请先登录后再操作!')
next({
path: '/login',
query: { redirect: to.fullPath },
replace: true
})
}
} catch (error) {
next('/login')
console.error('路由守卫校验失败:', error)
ElMessage.error('登录状态校验失败,请重新登录!')
next({
path: '/login',
replace: true
})
}
})

View File

@@ -0,0 +1,32 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore('user', () => {
const token = ref(localStorage.getItem('token') || '')
const loginUser = ref(JSON.parse(localStorage.getItem('loginUser')) || null)
const login = (userInfo) => {
token.value = userInfo.token
loginUser.value = userInfo.loginUser
localStorage.setItem('token', token.value)
localStorage.setItem('loginUser', JSON.stringify(loginUser.value))
}
const logout = () => {
token.value = ''
loginUser.value = null
localStorage.removeItem('token')
localStorage.removeItem('loginUser')
}
const isLoggedIn = () => {
return !!token.value
}
return {
token,
loginUser,
login,
logout,
isLoggedIn
}
})

View File

@@ -1,5 +1,5 @@
import axios from 'axios'
import { ElMessage } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import router from '@/router'
const service = axios.create({
@@ -10,40 +10,157 @@ const service = axios.create({
}
})
const WHITE_LIST = ['userLogin', 'register']
service.interceptors.request.use(
(config) => {
// 登录接口不添加token
if (!config.url?.includes('/userLogin')) {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = token;
}
const requestUrl = config.url || '';
const isWhite = WHITE_LIST.some(item => requestUrl.includes(item));
if (isWhite) {
return config;
}
return config
const token = localStorage.getItem('token');
if (!token) {
handleLoginExpired();
return Promise.reject({ config, message: '未获取到token' });
}
config.headers.Authorization = `Bearer ${token}`;
return config;
},
(error) => {
console.error('【请求拦截器错误】', error);
return Promise.reject(error)
return Promise.reject(error);
}
)
service.interceptors.response.use(
(response) => {
const res = response.data;
if (res.code !== 200) {
ElMessage.error(res.msg || '接口请求失败');
if (res.code === 401) {
localStorage.removeItem('token');
router.push('/login').catch(err => console.warn('路由跳转失败', err));
}
if (response.config.responseType === 'blob') {
return handleFileExport(response);
}
if (res.code === 401) {
handleLoginExpired();
return Promise.reject(res);
}
return res.data;
if (res.code === 200) {
return res.data;
}
ElMessage.error(res.msg || '请求失败');
return Promise.reject(res);
},
(error) => {
console.error('【响应拦截器错误】', error);
const requestUrl = error?.config?.url || '';
const isWhite = WHITE_LIST.some(item => requestUrl.includes(item));
if (error.config?.responseType === 'blob') {
handleFileExportError(error);
return Promise.reject(error);
}
if (isWhite) {
const errMsg = error?.response?.data?.msg || '登录请求失败,请检查网络或账号信息';
ElMessage.error(errMsg);
return Promise.reject(error);
}
if (error?.response?.status === 401) {
handleLoginExpired();
return Promise.reject(error);
}
if (!error.response) {
ElMessage.error('网络异常,请检查网络连接后重试');
return Promise.reject(error);
}
return Promise.reject(error);
}
)
export default service
function handleLoginExpired() {
localStorage.removeItem('token');
if (router.currentRoute.path !== '/login') {
router.push('/login').catch(() => {});
ElMessage.error('登录已失效,请重新登录');
}
}
function handleFileExport(response) {
const blob = response.data;
const defaultFileName = 'file.xlsx';
return new Promise((resolve, reject) => {
if (!blob || blob.size === 0) {
ElMessage.error('文件大小异常,请稍后重试');
reject(new Error('文件内容为空'));
return;
}
const reader = new FileReader();
reader.onerror = function() {
reject(new Error('文件解析失败'));
};
reader.onload = function() {
try {
const res = JSON.parse(reader.result);
ElMessage.error(res.msg || '文件下载失败');
reject(res);
} catch (e) {
let fileName = defaultFileName;
const disposition = response.headers['content-disposition'];
if (disposition) {
const match = disposition.match(/filename\*=UTF-8''(.+)/i);
if (match && match[1]) {
fileName = decodeURIComponent(match[1]);
}
}
if (response.config.downloadFileName) {
fileName = typeof response.config.downloadFileName === 'function'
? response.config.downloadFileName(response)
: response.config.downloadFileName;
}
try {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
const clickEvent = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
link.dispatchEvent(clickEvent);
setTimeout(() => {
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}, 100);
resolve(blob);
} catch (downloadError) {
ElMessage.error('文件下载失败,请手动重试');
reject(downloadError);
}
}
};
reader.readAsText(blob);
});
}
function handleFileExportError(error) {
if (error.response?.status === 401) {
handleLoginExpired();
} else if (error.message.includes('timeout')) {
ElMessage.error('文件下载超时,请稍后重试');
} else {
ElMessage.error('文件下载失败,请稍后重试');
}
}
export default service;

View File

@@ -22,7 +22,7 @@
type="password"
placeholder="请输入密码"
size="large"
show-password
show-password
prefix-icon="Lock"
></el-input>
</el-form-item>
@@ -33,7 +33,7 @@
class="login-btn"
size="small"
:loading="isLoading"
round
round
>
登录
</el-button>
@@ -47,10 +47,14 @@
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
import { login } from '@/api/user'
import { useUserStore } from '@/stores/user'
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const loginFormRef = ref(null)
const loginForm = ref({
username: '',
@@ -64,19 +68,19 @@ const isLoading = ref(false)
const handleLogin = async () => {
try {
await loginFormRef.value.validate()
isLoading.value = true
const res = await login(loginForm.value)
localStorage.setItem('token', res.token)
localStorage.setItem('username', res.username)
ElMessage.success('登录成功!')
await loginFormRef.value.validate();
isLoading.value = true;
const res = await login(loginForm.value);
userStore.login(res);
ElMessage.success('登录成功!');
const redirect = route.query.redirect || '/dashboard';
setTimeout(() => {
router.push('/dashboard');
}, 3000)
router.push(redirect);
}, 300);
} catch (error) {
ElMessage.error(res.msg);
}finally{
isLoading.value = false;
console.log(error);
} finally {
isLoading.value = false;
}
}
</script>
@@ -115,7 +119,7 @@ const handleLogin = async () => {
left: 0;
width: 100%;
height: 100%;
background: url('@/assets/login-bg.png') no-repeat center center;
background: url('@/assets/login-box.png') no-repeat center center;
background-size: cover;
opacity: 0.95;
}

View File

@@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@@ -0,0 +1,147 @@
<template>
<el-form
:model="formData"
:rules="formRules"
ref="formRef"
label-width="100px"
class="dialog-form-container"
>
<div class="form-row">
<div class="form-col">
<el-form-item label="模块名称" prop="moduleName">
<el-input
v-model="formData.moduleName"
placeholder="请输入模块名称"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="模块编码" prop="moduleCode">
<el-input
v-model="formData.moduleCode"
placeholder="请输入模块编码"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-col">
<el-form-item label="大屏标题" prop="titleName">
<el-input
v-model="formData.titleName"
placeholder="请输入大屏标题"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="路由地址" prop="path">
<el-input
v-model="formData.path"
placeholder="请输入路由地址"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-col">
<el-form-item label="模块描述" prop="remark">
<el-input
v-model="formData.remark"
placeholder="请输入模块描述"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="模块状态" prop="ustatus">
<el-select
v-model="formData.ustatus"
placeholder="请选择模块状态"
clearable
>
<el-option label="停用" value="0" />
<el-option label="在用" value="1" />
<el-option label="锁定" value="2" />
</el-select>
</el-form-item>
</div>
</div>
</el-form>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const props = defineProps({
formData: {
type: Object,
required: true,
default: () => ({
moduleName: '',
ustatus: '1',
})
},
isEdit: {
type: Boolean,
default: false
}
})
const formRef = ref(null)
const formRules = {
moduleName: [ { required: true, message: '请输入角色名称', trigger: 'blur' } ],
ustatus: [ { required: true, message: '请选择角色状态', trigger: 'change' } ],
}
const validate = async () => {
if (!formRef.value) return false
try {
const valid = await formRef.value.validate()
return valid
} catch (error) {
console.error('表单验证失败:', error)
return false
}
}
const resetForm = () => {
if (formRef.value) formRef.value.resetFields()
}
defineExpose({ validate, resetForm })
</script>
<style scoped>
.dialog-form-container {
width: 100%;
padding: 16px;
margin: 0;
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 8px;
box-sizing: border-box;
}
.form-row {
display: flex;
flex-wrap: wrap;
width: 100%;
margin: 0 0 16px 0;
gap: 20px;
}
.form-row:last-child { margin-bottom: 0; }
.form-col { flex: 1; min-width: 180px; }
</style>

View File

@@ -0,0 +1,151 @@
<template>
<ResizablePage
:defaultWidth="200"
:minWidth="150"
:maxWidth="420"
bgColor="#fff"
>
<template #sidebar>
<div class="sidebar-content">
<FilterSelect
:list-data="listData"
node-key="moduleId"
label-key="moduleName"
@item-click="handleItemClick"
@edit="handleEdit"
@delete="handleDelete"
@view="handleView"
/>
</div>
</template>
<template #main>
<div class="main-content">
<vUser @refresh-role-list="getListData" :formParams="FormValues" />
</div>
</template>
</ResizablePage>
<PDialog
v-model="dialogVisible"
title="模块授权"
:loading="saveLoading"
@close="handleDialogClose"
@reset="handleDialogReset"
@confirm="handleSave"
>
<vRole ref="formComponentRef" :form-data="roleForm" />
</PDialog>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import ResizablePage from '@/components/Table/proResizable.vue'
import FilterSelect from '@/components/Table/proFilterSelect.vue'
import PDialog from '@/components/Dialog/proDialog.vue'
import { getHomeModuleListAll, getHomeModuleDelete } from '@/api/bizModule'
import { getHomeModuleUserSave } from '@/api/bizModuleUser'
import vUser from './list.vue'
import vRole from './role.vue'
const FormValues = ref({
moduleId: ''
});
const listData = ref([]);
const saveLoading = ref(false)
const dialogVisible = ref(false)
const roleForm = ref({})
const formComponentRef = ref(null)
const getListData = async () => {
try {
const res = await getHomeModuleListAll();
listData.value = res || []
} catch (error) {
console.log(error);
}
}
const handleItemClick = (item) => {
FormValues.value.moduleId = item.moduleId
}
const handleEdit = (item) => {
roleForm.value = { ...item }
dialogVisible.value = true
}
const handleDelete = (item) => {
ElMessageBox.confirm('确定要删除当前模块吗?', '删除确认', {
type: 'warning',
closeOnClickModal: false,
showClose: false
})
.then(async () => {
const reqParams = {
moduleId: item.moduleId
}
const res = await getHomeModuleDelete(reqParams);
ElMessage.success(res.msg);
getListData();
})
.catch(() => {
ElMessage.info('已取消删除模块操作');
});
}
const handleView = (item) => {
console.log('查看', item)
}
const handleDialogClose = () => {
roleForm.value = {}
dialogVisible.value = false
}
const handleDialogReset = () => {
if (formComponentRef.value) {
formComponentRef.value.resetForm()
ElMessage.info('表单已重置')
}
}
const handleSave = async () => {
if (formComponentRef.value) {
const isValid = await formComponentRef.value.validate()
if (!isValid) {
ElMessage.warning('表单验证失败,请检查必填项');
return
}
}
const reqParams = {
... roleForm.value
}
saveLoading.value = true
const res = await getHomeModuleUserSave(reqParams);
setTimeout(() => {
ElMessage.success(res.msg)
saveLoading.value = false
dialogVisible.value = false
getListData()
}, 500)
}
onMounted(() => {
getListData();
})
</script>
<style scoped>
.sidebar-content {
width: 100%;
height: 100%;
}
.main-content {
padding: 0px;
height: 100%;
}
</style>

View File

@@ -0,0 +1,289 @@
<template>
<div class="data-manage-page">
<CSearch
@search="handleSearch"
@reset="handleReset"
>
<el-form-item label="用户名称:" class="search-item">
<el-input
v-model="searchForm.uname"
placeholder="请输入用户名称"
clearable
class="search-input"
/>
</el-form-item>
<el-form-item label="登录账户:" class="search-item">
<el-input
v-model="searchForm.userName"
placeholder="请输入登录账户"
clearable
class="search-input"
/>
</el-form-item>
<el-form-item label="用户状态:" class="search-item">
<el-select
v-model="searchForm.ustatus"
placeholder="请选择用户状态"
clearable
class="search-select"
>
<el-option label="停用" value="0" />
<el-option label="在用" value="1" />
<el-option label="锁定" value="2" />
</el-select>
</el-form-item>
</CSearch>
<div class="main-wrapper">
<div class="main-section">
<div class="action-section">
<el-button type="primary" icon="Plus" @click="handleAdd">
新增
</el-button>
</div>
<STable
:table-data="tableData"
:loading="loading"
:pagination="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #columns>
<el-table-column prop="createTime" label="记录日期" width="180" fixed="left" />
<el-table-column prop="userName" label="登录账户" />
<el-table-column prop="uname" label="用户名称" />
<el-table-column prop="sex" label="性别" />
<el-table-column prop="email" label="电子邮箱" width="225" show-overflow-tooltip="true" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="moduleName" label="模块名称" sortable />
<el-table-column prop="moduleCode" label="模块编码" />
<el-table-column prop="ustatus" label="状态" min-width="100" align="center">
<template #default="scope">
<el-tag
:type="scope.row.ustatus === '1' ? 'success' : scope.row.ustatus === '2' ? 'warning' : 'danger'"
>
{{ getStatusText(scope.row.ustatus) }}
</el-tag>
</template>
</el-table-column>
</template>
</STable>
</div>
</div>
<PDialog
v-model="dialogVisible"
:loading="saveLoading"
:title="isEdit ? '编辑数据' : '新增数据'"
@close="handleDialogClose"
@reset="handleDialogReset"
@confirm="handleSave"
>
<VForm ref="formComponentRef" :form-data="formData" :is-edit="isEdit" />
</PDialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Download, Edit, Delete } from '@element-plus/icons-vue'
import { getHomeModuleSave, getHomeModuleList } from '@/api/bizModule'
import CSearch from '@/components/Search/proSearch.vue'
import STable from '@/components/Table/proTable.vue'
import PDialog from '@/components/Dialog/proDialog.vue'
import VForm from './form.vue'
const props = defineProps({
formParams: {
type: Object,
default: () => ({})
}
})
const formComponentRef = ref(null)
const emit = defineEmits(['refresh-role-list'])
const loading = ref(false)
const saveLoading = ref(false)
const searchForm = reactive({
uname: '',
ustatus: '',
userName: ''
})
const pagination = reactive({
pageNum: 1,
pageSize: 20,
total: 0
})
const tableData = ref([])
const dialogVisible = ref(false)
const isEdit = ref(false)
const formData = ref({})
async function getDataList() {
loading.value = true
try {
const reqParmas = {
pageNum: pagination.pageNum,
pageSize: pagination.pageSize,
... searchForm,
...props.formParams,
}
const res = await getHomeModuleList(reqParmas);
pagination.total = res.total;
tableData.value = res.list || [];
} catch (error) {
console.error('获取数据失败:', error);
tableData.value = []
} finally {
loading.value = false
}
}
const getStatusText = (status) => {
const statusMap = {
'0': '停用',
'1': '在用',
'2': '锁定'
}
return statusMap[status] || '未知'
}
const handleSearch = () => {
pagination.pageNum = 1
getDataList()
}
const handleReset = () => {
Object.assign(searchForm, {
uname: '',
ustatus: '',
userName: ''
})
pagination.pageNum = 1
getDataList()
}
const handleAdd = () => {
isEdit.value = false
formData.value = {}
dialogVisible.value = true
}
const handleEdit = (row) => {
isEdit.value = true
formData.value = { ...row }
dialogVisible.value = true
}
const handleSizeChange = (val) => {
pagination.pageSize = val
getDataList()
}
const handleCurrentChange = (val) => {
pagination.pageNum = val
getDataList()
}
const handleDialogClose = () => {
formData.value = {}
isEdit.value = false
dialogVisible.value = false
}
const handleDialogReset = () => {
if (formComponentRef.value) {
formComponentRef.value.resetForm()
ElMessage.info('表单已重置')
}
}
const handleSave = async () => {
if (formComponentRef.value) {
const isValid = await formComponentRef.value.validate()
if (!isValid) {
ElMessage.warning('表单验证失败,请检查必填项')
return
}
}
try {
saveLoading.value = true;
const reqParams = {
...formData.value
}
const res = await getHomeModuleSave(reqParams);
ElMessage.success(res.msg)
dialogVisible.value = false
emit('refresh-role-list')
} catch (error) {
console.error('获取数据失败:', error);
} finally {
saveLoading.value = false;
}
}
watch(
() => props.formParams,
() => {
getDataList();
},
{ deep: true, immediate: true }
)
onMounted(() => {
getDataList()
})
</script>
<style scoped>
.data-manage-page {
width: 100%;
height: 100%;
padding: 4px;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden !important;
}
.main-wrapper {
flex: 1;
width: 100%;
overflow: hidden;
min-height: 0;
border: 1px solid #e5e7eb;
border-radius: 6px;
margin: 0 !important;
padding: 0;
box-sizing: border-box;
}
.main-section {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.action-section {
padding: 12px 16px;
border-bottom: 1px solid #e5e7eb;
display: flex;
gap: 8px;
flex-shrink: 0;
}
:deep(.el-button .el-icon) {
margin-right: 4px;
}
</style>

View File

@@ -0,0 +1,303 @@
<template>
<el-form
:model="formData"
:rules="formRules"
ref="formRef"
label-width="100px"
class="dialog-form-container"
>
<div class="form-row">
<div class="form-col">
<el-form-item label="模块标识" prop="moduleId">
<el-input
v-model="formData.moduleId"
placeholder="请输入模块标识"
disabled
clearable
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="模块名称" prop="moduleName">
<el-input
v-model="formData.moduleName"
placeholder="请输入模块名称"
clearable
/>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-col">
<el-form-item label="模块编号" prop="moduleCode">
<el-input
v-model="formData.moduleCode"
placeholder="请输入模块编号"
clearable
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="路由地址" prop="path">
<el-input
v-model="formData.path"
placeholder="请输入路由地址"
clearable
/>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-col">
<el-form-item label="大屏标题" prop="titleName">
<el-input
v-model="formData.titleName"
placeholder="请输入大屏标题"
clearable
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="模块状态" prop="ustatus">
<el-select
v-model="formData.ustatus"
placeholder="请选择模块状态"
clearable
>
<el-option label="停用" value="0" />
<el-option label="在用" value="1" />
<el-option label="锁定" value="2" />
</el-select>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-col">
<el-form-item label="模块描述" prop="remark">
<el-input
v-model="formData.remark"
placeholder="请输入模块描述"
type="textarea"
clearable
/>
</el-form-item>
</div>
</div>
<div class="form-row transfer-row">
<div class="form-col transfer-col">
<el-form-item label="权限列表" prop="userIds">
<el-transfer
filterable
v-model="formData.userIds"
:data="transferData"
:props="{ key: 'userId', label: 'uname' }"
:titles="['待选用户', '已选用户']"
placeholder="请输入权限名称搜索"
class="full-width-transfer"
/>
</el-form-item>
</div>
</div>
</el-form>
</template>
<script setup>
import { ref, onMounted, nextTick, watch } from 'vue'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import { getHomeUserListAll } from '@/api/bizUser'
import { getHomeModuleUserList } from '@/api/bizModuleUser'
const props = defineProps({
formData: {
type: Object,
required: true,
default: () => ({
moduleId: '',
moduleName: '',
moduleCode: '',
path: '',
titleName: '',
remark: '',
ustatus: '',
userIds: []
})
},
isEdit: {
type: Boolean,
default: false
}
})
const formRef = ref(null)
const transferData = ref([])
const formRules = {
moduleName: [ { required: true, message: '请输入模块名称', trigger: 'blur' } ],
moduleCode: [ { required: true, message: '请输入模块编号', trigger: 'blur' } ],
path: [ { required: true, message: '请输入路由地址', trigger: 'blur' } ],
titleName: [ { required: true, message: '请输入大屏标题', trigger: 'blur' } ],
ustatus: [ { required: true, message: '请选择模块状态', trigger: 'blur' } ],
userIds: [
{ required: true, message: '请至少选择一个用户', trigger: 'change' }
]
}
const validate = async () => {
if (!formRef.value) return false
try {
const valid = await formRef.value.validate()
return valid
} catch (error) {
console.error('表单验证失败:', error)
return false
}
}
const resetForm = () => {
if (formRef.value) formRef.value.resetFields()
}
async function getUserData() {
try {
const res = await getHomeUserListAll()
transferData.value = res || []
} catch (error) {
console.error('获取数据失败:', error);
transferData.value = []
}
}
async function getUserModuleData() {
try {
const reqParams = {
moduleId: props.formData.moduleId
}
const res = await getHomeModuleUserList(reqParams)
if (Array.isArray(res)) {
const selectedUserIds = res.map(item => item.userId).filter(Boolean)
nextTick(() => {
props.formData.userIds = selectedUserIds
})
}
} catch (error) {
console.error('获取数据失败:', error);
transUserData.value = []
}
}
watch(() => props.formData.moduleId, () => {
if (props.formData.moduleId) {
getUserModuleData()
}
}, { immediate: true })
onMounted(() => {
getUserData()
getUserModuleData()
})
defineExpose({ validate, resetForm })
</script>
<style scoped>
.dialog-form-container {
width: 100%;
padding: 16px;
margin: 0;
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 8px;
box-sizing: border-box;
}
.form-row {
display: flex;
flex-wrap: wrap;
width: 100%;
margin: 0 0 16px 0;
gap: 20px;
}
.form-row:last-child {
margin-bottom: 0;
}
.form-col {
flex: 1;
min-width: 180px;
box-sizing: border-box;
}
.transfer-col {
width: 100% !important;
min-width: unset !important;
padding: 0 !important;
margin: 0 !important;
}
.full-width-transfer {
width: 100% !important;
box-sizing: border-box !important;
padding: 0 !important;
margin: 0 !important;
}
:deep(.el-transfer) {
width: 100% !important;
display: flex !important;
flex-direction: row !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
padding: 0 !important;
margin: 0 !important;
box-sizing: border-box !important;
}
:deep(.el-transfer-panel) {
flex: 1 1 0 !important;
width: calc((100% - 50px) / 2) !important;
max-width: calc((100% - 50px) / 2) !important;
min-width: 150px !important;
padding: 0 !important;
margin: 0 !important;
box-sizing: border-box !important;
}
:deep(.el-transfer__buttons) {
flex: 0 0 50px !important;
display: flex !important;
flex-direction: row !important;
justify-content: center !important;
align-items: center !important;
padding: 0 5px !important;
gap: 5px !important;
height: 100% !important;
margin: auto 0 !important;
}
:deep(.el-transfer__button) {
width: 45px !important;
height: 32px !important;
line-height: 32px !important;
padding: 0 !important;
font-size: 14px !important;
border-radius: 4px !important;
border: 1px solid #dcdfe6 !important;
}
:deep(.el-transfer__button .el-icon) {
display: block !important;
margin: 0 !important;
color: #666 !important;
font-size: 14px !important;
}
:deep(.el-form-item) {
margin: 0 !important;
padding: 0 !important;
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<div class="table-container">
<div class="main-card">
<div class="title-section">
<h1 class="title">预警信息</h1>
</div>
<div class="divider"></div>
<div class="content-section">
<div class="table-wrapper">
<el-table
:data="tableData"
border
:header-cell-style="{ background: '#f5f7fa' }"
style="width: 100%;"
height="100%"
v-loading="loading"
>
<el-table-column
prop="createTime"
label="记录日期"
width="180"
fixed="left"
/>
<el-table-column
prop="alertTitle"
label="主题"
width="120"
show-overflow-tooltip="true"
/>
<el-table-column
prop="alertContent"
label="内容"
show-overflow-tooltip="true"
/>
<el-table-column
label="级别"
width="80"
>
<template #default="scope">
{{ priorityDict[scope.row.alertLevel] || '未知' }}
</template>
</el-table-column>
<el-table-column
prop="alertType"
label="类型"
width="100"
/>
<el-table-column
prop="sourceSystem"
label="来源系统"
width="120"
/>
</el-table>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElTable, ElTableColumn, ElLoading } from 'element-plus'
import { getAlertList } from '@/api/bizHome'
const tableData = ref([])
const loading = ref(true)
const priorityDict = {
1: '紧急',
2: '高',
3: '中',
4: '低'
}
async function getDataList() {
try {
const res = await getAlertList()
tableData.value = res || []
} catch (error) {
console.error('获取数据失败:', error)
tableData.value = []
}finally{
loading.value = false;
}
}
onMounted(async () => {
getDataList();
})
</script>
<style scoped>
.table-container {
width: 100%;
height: 100%;
padding: 2px;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
.main-card {
width: 100%;
height: 100%;
border: 1px solid #e5e7eb;
border-radius: 4px;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.title-section {
width: 100%;
height: 36px;
padding: 0 12px;
box-sizing: border-box;
background-color: #f9fafb;
display: flex;
align-items: center;
margin: 0;
border-radius: 4px 4px 0 0;
}
.title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin: 0;
}
.divider {
width: 100%;
height: 1px;
background-color: #e5e7eb;
}
.content-section {
flex: 1;
width: 100%;
padding: 8px;
box-sizing: border-box;
overflow: hidden;
margin: 0;
display: flex;
align-items: center;
}
.table-wrapper {
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: auto;
}
:deep(.el-table td),
:deep(.el-table th.is-leaf) {
border-right: none !important;
}
:deep(.el-table__fixed-left) {
border-right: none !important;
box-shadow: none !important;
}
:deep(.el-table__fixed-right) {
border-left: none !important;
box-shadow: none !important;
}
:deep(.el-table__fixed td),
:deep(.el-table__fixed th) {
border-right: none !important;
}
:deep(.el-table__body-wrapper) {
overflow: auto;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar) {
display: block;
height: 6px;
width: 6px;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar-track) {
background: #f1f1f1;
border-radius: 3px;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar-thumb) {
background: #dcdfe6;
border-radius: 3px;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar-thumb:hover) {
background: #c0c4cc;
}
:deep(.el-table th) {
font-weight: 600;
color: #303133;
}
</style>

View File

@@ -0,0 +1,441 @@
<template>
<div class="table-container">
<div class="main-card">
<div class="title-section">
<h1 class="title">年度便签汇总堆积图</h1>
<div class="tab-switcher">
<div
v-for="tab in tabDict"
:key="tab.key"
class="tab-item"
:class="{ 'tab-active': activeTabKey === tab.key }"
@click="switchTab(tab.key)"
>
{{ tab.label }}
</div>
</div>
</div>
<div class="divider"></div>
<div class="content-section">
<div ref="chartRef" class="chart-container"></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, watch, onUnmounted, nextTick } from 'vue'
import * as echarts from 'echarts'
import { getItemInfoList } from '@/api/bizApi'
const tabDict = [
{ key: 'NOTE_YEAR_Y001', label: '类型' },
{ key: 'NOTE_YEAR_Y002', label: '月份' },
]
const activeTabKey = ref(tabDict[0].key)
const indexCode = ref(activeTabKey.value)
const tableData = ref([])
const chartRef = ref(null)
let myChart = null
let resizeObserver = null
let resizeHandler = null
let seriesStatus = {
'待开始': true,
'进行中': true,
'已完成': true
}
const currentYear = new Date().getFullYear().toString();
async function getDataList() {
try {
const reqParams = {
reqParam: currentYear,
itemCode: indexCode.value,
}
const res = await getItemInfoList(reqParams)
tableData.value = res || []
await nextTick()
renderChart()
} catch (error) {
console.error('获取数据失败:', error)
tableData.value = []
}
}
function switchTab(tabKey) {
if (activeTabKey.value === tabKey) return
activeTabKey.value = tabKey
indexCode.value = tabKey
getDataList()
}
function calculateTotal() {
const xAxisData = tableData.value.map(item => item.xaxis || '')
const index01Data = tableData.value.map(item => Math.round(Number(item.index01) || 0))
const index02Data = tableData.value.map(item => Math.round(Number(item.index02) || 0))
const index03Data = tableData.value.map(item => Math.round(Number(item.index03) || 0))
return xAxisData.map((_, index) => {
let total = 0
if (seriesStatus['待开始']) total += index01Data[index]
if (seriesStatus['进行中']) total += index02Data[index]
if (seriesStatus['已完成']) total += index03Data[index]
return total
})
}
function renderChart() {
if (!chartRef.value || tableData.value.length === 0) return
if (myChart) {
myChart.dispose()
}
myChart = echarts.init(chartRef.value)
const xAxisData = tableData.value.map(item => item.xaxis || '')
const index01Data = tableData.value.map(item => Math.round(Number(item.index01) || 0))
const index02Data = tableData.value.map(item => Math.round(Number(item.index02) || 0))
const index03Data = tableData.value.map(item => Math.round(Number(item.index03) || 0))
const totalData = calculateTotal()
const option = {
animationDuration: 1000,
animationEasing: 'cubicOut',
animationDelay: function(idx) {
return idx * 50;
},
legend: {
show: true,
orient: 'horizontal',
top: '8%',
left: 'center',
textStyle: { fontSize: 12, color: '#303133' },
itemWidth: 14,
itemHeight: 14,
itemGap: 20,
data: ['待开始', '进行中', '已完成'],
selected: seriesStatus,
selectable: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
triggerOn: 'mousemove'
},
padding: 12,
formatter: function(params) {
let total = 0
const validParams = params.filter(param => param.seriesName !== '总数')
validParams.forEach(param => total += param.value)
let html = `<div style="text-align: center; font-weight: bold; font-size: 12px; margin-bottom: 8px; color: #303133;">${params[0].axisValue}</div>`
html += `<table style="width: 100%; border-collapse: collapse; font-size: 12px;">`
html += `<tr style="background: #f5f7fa;">
${validParams.map(param => `<th style="padding: 6px; border: 1px solid #e6e6e6; text-align: center;">${param.seriesName}</th>`).join('')}
<th style="padding: 6px; border: 1px solid #e6e6e6; text-align: center;">总数</th>
</tr>`
html += `<tr>
${validParams.map(param => `<td style="padding: 6px; border: 1px solid #e6e6e6; text-align: center; color: #303133;">${param.value}</td>`).join('')}
<td style="padding: 6px; border: 1px solid #e6e6e6; text-align: center; color: #303133; font-weight: bold;">${total}</td>
</tr>`
html += `</table>`
return html
},
position: function (point, params, dom, rect, size) {
return [point[0] + 10, point[1] - 10]
},
textStyle: { fontSize: 12 },
triggerOn: 'mousemove|click',
triggerOff: 'mouseout',
enterable: false,
leaveDelay: 0,
hideDelay: 0,
alwaysShowContent: false,
showContent: true
},
grid: {
left: '5%',
right: '5%',
bottom: '8%',
top: '18%',
containLabel: true
},
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#e5e7eb' } },
axisTick: { alignWithLabel: true, length: 6 },
axisLabel: { interval: 0, fontSize: 12, color: '#606266', rotate: 0, margin: 10 }
},
yAxis: {
type: 'value',
name: '数量',
nameTextStyle: { fontSize: 12, color: '#606266' },
axisLine: { lineStyle: { color: '#e5e7eb' } },
axisTick: { show: true },
axisLabel: {
formatter: (value) => `${Math.round(value)}`,
fontSize: 12,
color: '#606266'
},
minInterval: 1,
splitNumber: 5,
splitLine: { lineStyle: { color: '#f5f5f5' } },
max: function(value) {
return value.max * 1.2
}
},
series: [
{
name: '待开始',
type: 'bar',
stack: 'total',
data: index01Data,
barWidth: '12%',
itemStyle: { color: '#f56c6c' },
label: {
show: true,
position: 'inside',
formatter: '{c}',
fontSize: 10,
color: '#fff',
fontWeight: 'bold'
},
animationDelay: function(idx) {
return idx * 30;
}
},
{
name: '进行中',
type: 'bar',
stack: 'total',
data: index02Data,
barWidth: '12%',
itemStyle: { color: '#409eff' },
label: {
show: true,
position: 'inside',
formatter: '{c}',
fontSize: 10,
color: '#fff',
fontWeight: 'bold'
},
animationDelay: function(idx) {
return idx * 30 + 100;
}
},
{
name: '已完成',
type: 'bar',
stack: 'total',
data: index03Data,
barWidth: '12%',
itemStyle: { color: '#67c23a' },
label: {
show: true,
position: 'inside',
formatter: '{c}',
fontSize: 10,
color: '#fff',
fontWeight: 'bold'
},
animationDelay: function(idx) {
return idx * 30 + 200;
}
},
{
name: '总数',
type: 'bar',
stack: 'total',
data: new Array(xAxisData.length).fill(0),
barWidth: '12%',
itemStyle: { color: 'transparent' },
label: {
show: true,
position: 'top',
formatter: function(params) {
return totalData[params.dataIndex]
},
fontSize: 11,
color: '#303133',
fontWeight: 'bold'
},
animationDelay: function(idx) {
return idx * 30 + 300;
}
}
]
}
myChart.setOption(option)
myChart.on('legendselectchanged', function(params) {
seriesStatus[params.name] = params.selected[params.name]
const newTotalData = calculateTotal()
myChart.setOption({
series: [{
name: '总数',
label: {
formatter: function(p) {
return newTotalData[p.dataIndex]
}
}
}]
})
})
myChart.resize()
}
function watchContainerResize() {
if (!chartRef.value) return
resizeObserver = new ResizeObserver(() => {
if (myChart) myChart.resize()
})
resizeObserver.observe(chartRef.value)
resizeHandler = () => {
if (myChart) myChart.resize()
}
window.addEventListener('resize', resizeHandler)
}
function cleanUp() {
if (resizeObserver) resizeObserver.disconnect()
if (resizeHandler) window.removeEventListener('resize', resizeHandler)
if (myChart) {
myChart.off('legendselectchanged')
myChart.dispose()
myChart = null
}
resizeObserver = null
resizeHandler = null
}
watch(tableData, () => {
renderChart()
}, { deep: true })
onMounted(async () => {
await getDataList()
watchContainerResize()
})
onUnmounted(() => {
cleanUp()
})
</script>
<style scoped>
.table-container {
width: 100%;
height: 100%;
padding: 2px;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
.main-card {
width: 100%;
height: 100%;
border: 1px solid #e5e7eb;
border-radius: 4px;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.title-section {
width: 100%;
height: 36px;
padding: 0 12px;
box-sizing: border-box;
background-color: #f9fafb;
display: flex;
align-items: center;
justify-content: space-between;
margin: 0;
border-radius: 4px 4px 0 0;
}
.title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin: 0;
}
.tab-switcher {
display: flex;
gap: 8px;
overflow-x: auto;
scrollbar-width: none;
}
.tab-switcher::-webkit-scrollbar {
display: none;
}
.tab-item {
padding: 4px 12px;
font-size: 12px;
color: #606266;
cursor: pointer;
transition: all 0.2s;
position: relative;
white-space: nowrap;
}
.tab-item:hover {
color: #409eff;
}
.tab-item.tab-active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: #409eff;
border-radius: 1px;
}
.divider {
width: 100%;
height: 1px;
background-color: #e5e7eb;
}
.content-section {
flex: 1;
width: 100%;
padding: 8px;
box-sizing: border-box;
overflow: hidden;
margin: 0;
display: flex;
align-items: center;
}
.chart-container {
width: 100%;
height: 100%;
min-height: 300px;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,222 @@
<template>
<div class="table-container">
<div class="main-card">
<div class="title-section">
<h1 class="title">便签信息</h1>
</div>
<div class="divider"></div>
<div class="content-section">
<div class="table-wrapper">
<el-table
:data="tableData"
border
:header-cell-style="{ background: '#f5f7fa' }"
style="width: 100%;"
height="100%"
v-loading="loading"
>
<el-table-column
prop="createTime"
label="记录日期"
width="180"
fixed="left"
/>
<el-table-column
prop="title"
label="主题"
width="120"
show-overflow-tooltip="true"
/>
<el-table-column
prop="content"
label="内容"
show-overflow-tooltip="true"
/>
<el-table-column
label="级别"
width="80"
>
<template #default="scope">
{{ priorityDict[scope.row.priority] || '未知' }}
</template>
</el-table-column>
<el-table-column
label="类型"
width="100"
>
<template #default="scope">
{{ typeDict[scope.row.type] || '未知' }}
</template>
</el-table-column>
<el-table-column
label="状态"
width="100"
>
<template #default="scope">
{{ stausDict[scope.row.ustatus] || '未知' }}
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElTable, ElTableColumn } from 'element-plus'
import { getNoteList } from '@/api/bizHome'
const tableData = ref([])
const loading = ref(true)
const priorityDict = {
low: '低',
medium: '中',
high: '高'
}
const typeDict = {
work: '工作',
life: '生活',
study: '学习',
other: '其他'
}
const stausDict = {
todo: '待开始',
doing: '进行中',
done: '已完成'
}
async function getDataList() {
try {
const res = await getNoteList()
tableData.value = res || []
} catch (error) {
console.error('获取数据失败:', error)
tableData.value = []
} finally {
loading.value = false
}
}
onMounted(async () => {
getDataList()
})
</script>
<style scoped>
.table-container {
width: 100%;
height: 100%;
padding: 2px;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
.main-card {
width: 100%;
height: 100%;
border: 1px solid #e5e7eb;
border-radius: 4px;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.title-section {
width: 100%;
height: 36px;
padding: 0 12px;
box-sizing: border-box;
background-color: #f9fafb;
display: flex;
align-items: center;
margin: 0;
border-radius: 4px 4px 0 0;
}
.title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin: 0;
}
.divider {
width: 100%;
height: 1px;
background-color: #e5e7eb;
}
.content-section {
flex: 1;
width: 100%;
padding: 8px;
box-sizing: border-box;
overflow: hidden;
margin: 0;
display: flex;
align-items: center;
}
.table-wrapper {
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: auto;
}
:deep(.el-table td),
:deep(.el-table th.is-leaf) {
border-right: none !important;
}
:deep(.el-table__fixed-left) {
border-right: none !important;
box-shadow: none !important;
}
:deep(.el-table__fixed-right) {
border-left: none !important;
box-shadow: none !important;
}
:deep(.el-table__fixed td),
:deep(.el-table__fixed th) {
border-right: none !important;
}
:deep(.el-table__body-wrapper) {
overflow: auto;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar) {
display: block;
height: 6px;
width: 6px;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar-track) {
background: #f1f1f1;
border-radius: 3px;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar-thumb) {
background: #dcdfe6;
border-radius: 3px;
}
:deep(.el-table__body-wrapper::-webkit-scrollbar-thumb:hover) {
background: #c0c4cc;
}
:deep(.el-table th) {
font-weight: 600;
color: #303133;
}
</style>

View File

@@ -0,0 +1,318 @@
<template>
<div class="quick-login-container">
<div class="main-card">
<div class="title-section">
<h1 class="title">快捷登录</h1>
</div>
<div class="divider"></div>
<div class="content-section">
<div class="card-list-wrapper" ref="cardWrapperRef">
<div class="card-list-inner" ref="cardListRef">
<div
v-for="(item, index) in loginList"
:key="index"
class="login-card"
@click="handleCardClick(item)"
>
<div class="icon-wrapper">
<img :src="item.iconClass" :alt="item.systemName" class="login-icon" />
</div>
<div class="card-name">{{ item.systemName }}</div>
</div>
</div>
</div>
<el-button
class="control-btn left-btn"
@click="scrollCards('left')"
:disabled="isLeftDisabled"
circle
size="mini"
icon="ArrowLeft"
></el-button>
<el-button
class="control-btn right-btn"
@click="scrollCards('right')"
:disabled="isRightDisabled"
circle
size="mini"
icon="ArrowRight"
></el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { ElButton, ElMessage } from 'element-plus'
import { getQuickList } from '@/api/bizHome'
const loginList = ref()
const loading = ref(true)
async function getDataList() {
try {
const res = await getQuickList()
loginList.value = res || []
} catch (error) {
console.error('获取数据失败:', error)
loginList.value = []
}finally{
loading.value = false;
}
}
const cardWrapperRef = ref(null)
const cardListRef = ref(null)
const scrollLeft = ref(0)
const isLeftDisabled = computed(() => {
return scrollLeft.value <= 0
})
const isRightDisabled = computed(() => {
if (!cardWrapperRef.value || !cardListRef.value) return true
const maxScroll = cardListRef.value.scrollWidth - cardWrapperRef.value.clientWidth
return Math.round(scrollLeft.value) >= Math.round(maxScroll)
})
const scrollCards = (direction) => {
if (!cardListRef.value || !cardWrapperRef.value) return
const step = 128
const maxScroll = cardListRef.value.scrollWidth - cardWrapperRef.value.clientWidth
let newScroll = scrollLeft.value
if (direction === 'left') {
newScroll = Math.max(0, newScroll - step)
} else {
newScroll = Math.min(maxScroll, newScroll + step)
}
if (newScroll !== scrollLeft.value) {
cardListRef.value.scrollTo({
left: newScroll,
behavior: 'smooth'
})
scrollLeft.value = newScroll
}
}
const handleCardClick = (item) => {
window.open(item.homepageUrl,"_blank");
}
onMounted(() => {
getDataList()
const list = cardListRef.value
if (list) {
scrollLeft.value = list.scrollLeft
list.addEventListener('scroll', () => {
scrollLeft.value = list.scrollLeft
})
}
window.addEventListener('resize', () => {
if (cardListRef.value) {
scrollLeft.value = cardListRef.value.scrollLeft
}
})
})
onUnmounted(() => {
const list = cardListRef.value
if (list) {
list.removeEventListener('scroll', () => {})
}
window.removeEventListener('resize', () => {})
})
watch([isLeftDisabled, isRightDisabled], () => {}, { immediate: true })
</script>
<style scoped>
.quick-login-container {
width: 100%;
height: 100%;
padding: 2px;
margin: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 2px;
overflow: hidden;
}
.main-card {
width: 100%;
height: 100%;
border: 1px solid #e5e7eb;
border-radius: 4px;
background-color: #fff;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.title-section {
width: 100%;
height: 36px;
padding: 0 12px;
box-sizing: border-box;
background-color: #f9fafb;
display: flex;
align-items: center;
margin: 0;
border-radius: 4px 4px 0 0;
}
.title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin: 0;
}
.divider {
width: 100%;
height: 1px;
background-color: #e5e7eb;
}
.content-section {
flex: 1;
width: 100%;
padding: 0;
box-sizing: border-box;
overflow: hidden;
margin: 0;
display: flex;
align-items: center;
position: relative;
}
.control-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 100;
width: 16px !important;
height: 16px !important;
padding: 0 !important;
background-color: #e6f7ff !important;
border-color: #91d5ff !important;
color: #1890ff !important;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
transition: all 0.1s ease;
}
.control-btn:disabled {
background-color: #f0f8fb !important;
border-color: #b3d8ea !important;
color: #8cbfe8 !important;
opacity: 0.6;
}
.control-btn:not(:disabled):active {
background-color: #bae7ff !important;
border-color: #69c0ff !important;
color: #096dd9 !important;
}
.left-btn {
left: 4px;
}
.right-btn {
right: 4px;
}
.card-list-wrapper {
width: 100%;
height: calc(100% - 16px);
padding: 8px 20px;
box-sizing: border-box;
overflow: hidden;
}
.card-list-inner {
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: hidden;
}
.card-list-inner::-webkit-scrollbar {
display: block;
height: 6px;
}
.card-list-inner::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.card-list-inner::-webkit-scrollbar-thumb {
background: #dcdfe6;
border-radius: 3px;
}
.card-list-inner::-webkit-scrollbar-thumb:hover {
background: #c0c4cc;
}
.card-list-inner {
display: flex;
gap: 8px;
align-items: center;
height: 100%;
}
.login-card {
height: 100%;
width: 120px;
padding: 8px;
border-radius: 8px;
background-color: #f5f7fa;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
cursor: pointer;
transition: all 0.2s ease;
box-sizing: border-box;
flex-shrink: 0;
}
.login-card:hover {
transform: translateY(-2px);
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
background-color: #eff6ff;
}
.icon-wrapper {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.login-icon {
width: 100%;
height: 80px;
object-fit: cover;
border-radius: 6px;
}
.card-name {
font-size: 13px;
color: #303133;
font-weight: 500;
text-align: center;
line-height: 1.4;
flex-shrink: 0;
width: 100%;
margin: 0;
padding-top: 8px;
}
</style>

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