commit
4ac653bc58
65 changed files with 9126 additions and 0 deletions
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
/.classpath |
||||
/.project |
||||
/.settings |
||||
/bin/ |
||||
/logs |
||||
/target |
||||
/src/main/resources/static |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
server.port=80 |
||||
server.servlet.context-path=/ |
||||
|
||||
dev=on |
||||
|
||||
# mysql数据源配置 |
||||
spring.datasource.url=jdbc:mysql://cluster3.xumy.vip:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true |
||||
spring.datasource.username=root |
||||
spring.datasource.password=123456 |
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<configuration debug="false"> |
||||
|
||||
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径--> |
||||
<property name="LOG_HOME" value="D:/logs/cube" /> |
||||
|
||||
<!--控制台日志, 控制台输出 --> |
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符--> |
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> |
||||
</encoder> |
||||
</appender> |
||||
|
||||
<!--文件日志, 按照每天生成日志文件 --> |
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
||||
<!--日志文件输出的文件名--> |
||||
<FileNamePattern>${LOG_HOME}/admin-%d{yyyy-MM-dd}.log</FileNamePattern> |
||||
<!--日志文件保留天数--> |
||||
<MaxHistory>30</MaxHistory> |
||||
</rollingPolicy> |
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> |
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> |
||||
</encoder> |
||||
<!--日志文件最大的大小--> |
||||
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> |
||||
<MaxFileSize>10MB</MaxFileSize> |
||||
</triggeringPolicy> |
||||
</appender> |
||||
|
||||
<logger name="vip.xumy" level="DEBUG"/> |
||||
|
||||
<!--myibatis log configure--> |
||||
<logger name="com.apache.ibatis" level="TRACE"/> |
||||
<logger name="java.sql.Connection" level="DEBUG"/> |
||||
<logger name="java.sql.Statement" level="DEBUG"/> |
||||
<logger name="java.sql.PreparedStatement" level="DEBUG"/> |
||||
|
||||
<!-- 日志输出级别 --> |
||||
<root level="INFO"> |
||||
<appender-ref ref="STDOUT" /> |
||||
<appender-ref ref="FILE"/> |
||||
</root> |
||||
</configuration> |
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-parent</artifactId> |
||||
<version>2.7.1</version> |
||||
</parent> |
||||
<groupId>vip.xumy.cube</groupId> |
||||
<artifactId>xumy_cube</artifactId> |
||||
<version>1.0.0</version> |
||||
<name>xumy_cube</name> |
||||
<description>data cube</description> |
||||
<packaging>jar</packaging> |
||||
|
||||
<properties> |
||||
<java.version>17</java.version> |
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
||||
|
||||
<core.version>1.0.0</core.version> |
||||
<log4j.version>1.2.17</log4j.version> |
||||
<fastjson.version>1.2.46</fastjson.version> |
||||
<druid.version>1.1.9</druid.version> |
||||
<mybatis-plus.version>3.5.1</mybatis-plus.version> |
||||
<mybatis.pagehelper.version>1.4.6</mybatis.pagehelper.version> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
|
||||
<dependency> |
||||
<groupId>vip.xumy.core</groupId> |
||||
<artifactId>xumy_core</artifactId> |
||||
<version>${core.version}</version> |
||||
</dependency> |
||||
|
||||
<!-- 代码修改之后可以实时生效,该模块在完整的打包环境下运行的时候会被禁用 --> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-devtools</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-web</artifactId> |
||||
</dependency> |
||||
|
||||
<!--junit 测试包--> |
||||
<dependency> |
||||
<groupId>org.junit.jupiter</groupId> |
||||
<artifactId>junit-jupiter-api</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
|
||||
<!-- mysql驱动 --> |
||||
<dependency> |
||||
<groupId>mysql</groupId> |
||||
<artifactId>mysql-connector-java</artifactId> |
||||
<scope>runtime</scope> |
||||
</dependency> |
||||
<!-- alibaba的druid数据库连接池 --> |
||||
<dependency> |
||||
<groupId>com.alibaba</groupId> |
||||
<artifactId>druid-spring-boot-starter</artifactId> |
||||
<version>${druid.version}</version> |
||||
</dependency> |
||||
|
||||
<!-- mybatis plus--> |
||||
<dependency> |
||||
<groupId>com.baomidou</groupId> |
||||
<artifactId>mybatis-plus-boot-starter</artifactId> |
||||
<version>${mybatis-plus.version}</version> |
||||
</dependency> |
||||
|
||||
<!-- mybatis的pagehelper --> |
||||
<dependency> |
||||
<groupId>com.github.pagehelper</groupId> |
||||
<artifactId>pagehelper-spring-boot-starter</artifactId> |
||||
<version>${mybatis.pagehelper.version}</version> |
||||
</dependency> |
||||
|
||||
</dependencies> |
||||
|
||||
<!-- Package as an executable jar --> |
||||
<build> |
||||
<finalName>dataCube</finalName> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
<configuration> |
||||
<executable>true</executable> |
||||
</configuration> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
|
||||
</project> |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
package vip.xumy.cube; |
||||
|
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; |
||||
import org.springframework.context.annotation.Bean; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType; |
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
||||
|
||||
import vip.xumy.cube.conf.CubeRescueInitializer; |
||||
|
||||
|
||||
public class CubeRescueApplocation extends SpringBootServletInitializer { |
||||
|
||||
public static void main(String[] args) { |
||||
Class<?>[] arr = new Class<?>[] {CubeRescueInitializer.class}; |
||||
SpringApplication.run(arr,args); |
||||
} |
||||
|
||||
/** |
||||
* 分页插件 |
||||
*/ |
||||
@Bean |
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() { |
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); |
||||
return interceptor; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
package vip.xumy.cube.conf; |
||||
|
||||
import org.mybatis.spring.annotation.MapperScan; |
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
||||
import org.springframework.boot.builder.SpringApplicationBuilder; |
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; |
||||
import org.springframework.context.annotation.ComponentScan; |
||||
|
||||
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class) |
||||
@ComponentScan(value = "vip.xumy.cube, vip.xumy.core") |
||||
@MapperScan({"vip.xumy.cube.mapper"}) |
||||
public class CubeRescueInitializer extends SpringBootServletInitializer { |
||||
|
||||
@Override |
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { |
||||
return builder.sources(CubeRescueInitializer.class); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,91 @@
@@ -0,0 +1,91 @@
|
||||
package vip.xumy.cube.ctrl; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.web.bind.annotation.DeleteMapping; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PathVariable; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.PutMapping; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import com.github.pagehelper.Page; |
||||
|
||||
import vip.xumy.core.pojo.com.BaseResponse; |
||||
import vip.xumy.core.pojo.com.PageResponse; |
||||
import vip.xumy.cube.pojo.Cube4Mysql; |
||||
import vip.xumy.cube.pojo.Dimension; |
||||
import vip.xumy.cube.service.DataCubeService; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2023年3月10日 |
||||
*/ |
||||
|
||||
@RestController |
||||
@RequestMapping("cube") |
||||
public class CubeController { |
||||
@Autowired |
||||
private DataCubeService cubeService; |
||||
|
||||
@GetMapping |
||||
public BaseResponse list(Cube4Mysql example) { |
||||
return new BaseResponse(cubeService.list(example)); |
||||
} |
||||
|
||||
@GetMapping("{name}") |
||||
public BaseResponse getCube(@PathVariable("name") String name) { |
||||
return new BaseResponse(cubeService.get(name)); |
||||
} |
||||
|
||||
@PostMapping |
||||
public BaseResponse save(@RequestBody Cube4Mysql cube) { |
||||
cubeService.save(cube); |
||||
return new BaseResponse(true, "添加立方体成功!"); |
||||
} |
||||
|
||||
@PostMapping("dimension") |
||||
public BaseResponse saveDimesion(@RequestBody Dimension dimension) { |
||||
cubeService.save(dimension); |
||||
return new BaseResponse(true, "添加纬度成功!"); |
||||
} |
||||
|
||||
@PutMapping("init") |
||||
public BaseResponse initCube(@RequestBody String name) { |
||||
cubeService.init(name); |
||||
return new BaseResponse(true, "初始化立方体成功!"); |
||||
} |
||||
|
||||
@PostMapping("assemble") |
||||
public BaseResponse assembleCube(@RequestBody String name) { |
||||
cubeService.assemble(name); |
||||
return new BaseResponse(true, "构建任务提交成功!"); |
||||
} |
||||
|
||||
@DeleteMapping |
||||
public BaseResponse delete(String name) { |
||||
cubeService.delete(name); |
||||
return new BaseResponse(true, "删除立方体成功!"); |
||||
} |
||||
|
||||
@DeleteMapping("dimension") |
||||
public BaseResponse delete(Dimension dimension) { |
||||
cubeService.delete(dimension); |
||||
return new BaseResponse(true, "删除维度成功!"); |
||||
} |
||||
|
||||
@PutMapping("select") |
||||
public BaseResponse selectCube(@RequestBody Cube4Mysql cube) { |
||||
Page<Object> page = cube.startPage(); |
||||
List<Map<String, Object>> list = cubeService.select(cube); |
||||
PageResponse<Map<String, Object>> result = new PageResponse<>(list, page.getTotal()); |
||||
return new BaseResponse(result); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
package vip.xumy.cube.mapper; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.apache.ibatis.annotations.Delete; |
||||
import org.apache.ibatis.annotations.Insert; |
||||
import org.apache.ibatis.annotations.Mapper; |
||||
import org.apache.ibatis.annotations.Param; |
||||
import org.apache.ibatis.annotations.Select; |
||||
import org.apache.ibatis.annotations.Update; |
||||
|
||||
import vip.xumy.cube.pojo.Cube4Mysql; |
||||
import vip.xumy.cube.pojo.Dimension; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2023年3月10日 |
||||
*/ |
||||
|
||||
@Mapper |
||||
public interface DataCubeMapper { |
||||
|
||||
@Select(""" |
||||
<script> |
||||
SELECT * FROM cube |
||||
<where> |
||||
<if test="name != null"> AND name = #{name} </if> |
||||
<if test="source != null"> AND source = #{source} </if> |
||||
<if test="status != null"> AND status = #{status} </if> |
||||
</where> |
||||
ORDER BY create_time DESC |
||||
</script> |
||||
""") |
||||
List<Cube4Mysql> list(Cube4Mysql example); |
||||
|
||||
@Select(""" |
||||
<script> |
||||
SELECT * FROM cube_dimession |
||||
<where> |
||||
<if test="cubeName != null"> AND cube_name = #{cubeName} </if> |
||||
<if test="type != null"> AND type = #{type} </if> |
||||
</where> |
||||
</script> |
||||
""") |
||||
List<Dimension> dimesions(@Param("cubeName") String cubeName, @Param("type") Integer type); |
||||
|
||||
@Update(""" |
||||
<script> |
||||
UPDATE cube |
||||
<set> |
||||
<if test="status != null"> status = #{status}, </if> |
||||
assemble_time = #{assembleTime}, |
||||
<if test="total != null"> total = #{total}, </if> |
||||
</set> |
||||
WHERE name = #{name} |
||||
</script> |
||||
""") |
||||
void update(Cube4Mysql cube); |
||||
|
||||
@Insert("INSERT INTO cube VALUES (#{name}, #{source}, #{sourceTimer}, #{remark}, #{status}, #{createTime}, #{assembleTime}, #{total})") |
||||
void insert(Cube4Mysql cube); |
||||
|
||||
@Insert("INSERT INTO cube_dimession VALUES (#{cubeName}, #{column}, #{dimesion}, #{remark}, #{type}, #{dataType}, #{length})") |
||||
void insertDimession(Dimension dimesion); |
||||
|
||||
@Delete(""" |
||||
DELETE FROM cube WHERE name = #{name}; |
||||
DELETE FROM cube_dimession WHERE cubeName = #{name}; |
||||
""") |
||||
void delete(String name); |
||||
|
||||
@Delete(""" |
||||
DELETE FROM cube_dimession WHERE cubeName = #{name} AND column = #{column}; |
||||
""") |
||||
void deleteDimension(Dimension dimension); |
||||
|
||||
} |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
package vip.xumy.cube.mapper; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.ibatis.annotations.Insert; |
||||
import org.apache.ibatis.annotations.Mapper; |
||||
import org.apache.ibatis.annotations.Select; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2023年3月10日 |
||||
*/ |
||||
|
||||
@Mapper |
||||
public interface SqlExcuter { |
||||
|
||||
@Insert("${sql}") |
||||
int excute(String sql); |
||||
|
||||
@Select("${sql}") |
||||
List<Map<String, Object>> select(String sql); |
||||
|
||||
} |
@ -0,0 +1,125 @@
@@ -0,0 +1,125 @@
|
||||
package vip.xumy.cube.pojo; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
import java.util.stream.Stream; |
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
import vip.xumy.core.utils.CombineUtil; |
||||
import vip.xumy.core.utils.DateUtil; |
||||
import vip.xumy.core.utils.StringUtil; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2023年3月9日 |
||||
*/ |
||||
|
||||
@Setter |
||||
@Getter |
||||
public class Cube4Mysql extends PageParam { |
||||
private String scheme = "cube"; |
||||
|
||||
private List<Dimension> dimensions; |
||||
private List<Dimension> data; |
||||
private String name; |
||||
private String source; |
||||
private String sourceTimer; |
||||
private String remark; |
||||
private Integer status; |
||||
private String createTime; |
||||
private String assembleTime; |
||||
private Integer total; |
||||
|
||||
@JSONField(serialize = false) |
||||
private String curTime; |
||||
|
||||
public String destorySql() { |
||||
StringBuilder sb = new StringBuilder("DROP TABLE IF EXISTS ").append("`").append(name).append("`;"); |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public String initSql() { |
||||
StringBuilder sb = new StringBuilder(destorySql()).append(" CREATE TABLE ").append("`").append(name) |
||||
.append("` (`id` int(11) NOT NULL AUTO_INCREMENT"); |
||||
for (Dimension dimesion : dimensions) { |
||||
sb.append(", `").append(dimesion.getColumn()).append("` "); |
||||
switch (dimesion.getDataType()) { |
||||
case 0: |
||||
sb.append("varchar(").append(dimesion.getLength()).append(") "); |
||||
break; |
||||
case 1: |
||||
sb.append("int(11) "); |
||||
break; |
||||
case 2: |
||||
sb.append("datetime "); |
||||
break; |
||||
} |
||||
sb.append("DEFAULT NULL"); |
||||
} |
||||
for (Dimension dimesion : data) { |
||||
sb.append(", `").append(dimesion.getColumn()).append("` int(11) NOT NULL"); |
||||
} |
||||
sb.append(", PRIMARY KEY (`id`), UNIQUE KEY `base_index` (`"); |
||||
List<String> columns = dimensions.stream().map(x -> x.getColumn()).collect(Collectors.toList()); |
||||
sb.append(StringUtil.join(columns, "`,`")).append("`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public String assembleSql() { |
||||
curTime = DateUtil.format(new Date(), DateUtil.FORMAT19_LINE_YYYYMMDDHHMMSS); |
||||
List<String> columns = Stream.of(dimensions, data).flatMap(x -> x.stream()).map(x -> "`" + x.getColumn() + "`") |
||||
.collect(Collectors.toList()); |
||||
StringBuilder sb = new StringBuilder("INSERT INTO `").append(name).append("` (") |
||||
.append(StringUtil.join(columns)).append(") "); |
||||
new ArrayList<>(dimensions).addAll(data); |
||||
List<List<Integer>> combine = CombineUtil.combine(dimensions.size()); |
||||
List<String> combineSql = combine.stream().map(x -> assembleOneCombination(x)).collect(Collectors.toList()); |
||||
sb.append(StringUtil.join(combineSql.toArray(new String[0]), " UNION ")).append(";"); |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public String assembleOneCombination(List<Integer> list) { |
||||
StringBuilder sb = new StringBuilder("SELECT "); |
||||
for (int i = 0; i < dimensions.size(); i++) { |
||||
if (list.contains(i + 1)) { |
||||
sb.append(dimensions.get(i).getDimesion()); |
||||
} else { |
||||
sb.append("null"); |
||||
} |
||||
sb.append(","); |
||||
} |
||||
List<String> sumArr = data.stream().map(x -> x.getDimesion()).collect(Collectors.toList()); |
||||
List<String> array = list.stream().map(x -> dimensions.get(x - 1).getDimesion()).collect(Collectors.toList()); |
||||
sb.append(StringUtil.join(sumArr)).append(" FROM `").append(source).append("`"); |
||||
if (!StringUtil.isEmpty(sourceTimer)) { |
||||
sb.append(" WHERE `").append(sourceTimer).append("` <= '").append(curTime).append("'"); |
||||
if (assembleTime != null) { |
||||
sb.append(" AND `").append(sourceTimer).append("` > '").append(assembleTime).append("'"); |
||||
} |
||||
} |
||||
sb.append(" GROUP BY ").append(StringUtil.join(array)); |
||||
return sb.toString(); |
||||
} |
||||
|
||||
public String selectSql() { |
||||
StringBuilder sb = new StringBuilder("SELECT * FROM `").append(name).append("` WHERE 1=1"); |
||||
for (Dimension dimesion : dimensions) { |
||||
if (!dimesion.isChecked()) { |
||||
sb.append(" AND `").append(dimesion.getColumn()).append("` IS NULL"); |
||||
} else if (StringUtil.isEmpty(dimesion.getValue())) { |
||||
sb.append(" AND `").append(dimesion.getColumn()).append("` IS NOT NULL"); |
||||
} else { |
||||
sb.append(" AND `").append(dimesion.getColumn()).append("` = ").append(dimesion.getValue()); |
||||
} |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
package vip.xumy.cube.pojo; |
||||
|
||||
import lombok.Getter; |
||||
import lombok.Setter; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2023年3月10日 |
||||
*/ |
||||
|
||||
@Setter |
||||
@Getter |
||||
public class Dimension { |
||||
private String scheme = "dimension"; |
||||
|
||||
private String cubeName; |
||||
private String column; |
||||
private String dimesion; |
||||
private String remark; |
||||
private Integer type; |
||||
private Integer dataType; |
||||
private Integer length; |
||||
|
||||
private boolean checked = true; |
||||
private String value; |
||||
|
||||
public Dimension(String srouce, String column) { |
||||
super(); |
||||
this.dimesion = srouce; |
||||
this.column = column; |
||||
this.dataType = 1; |
||||
} |
||||
|
||||
public Dimension(String srouce, String column, Integer dataType, Integer length) { |
||||
super(); |
||||
this.dimesion = srouce; |
||||
this.column = column; |
||||
this.dataType = dataType; |
||||
this.length = length; |
||||
} |
||||
|
||||
public Dimension() { |
||||
super(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
package vip.xumy.cube.pojo; |
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import com.github.pagehelper.PageHelper; |
||||
|
||||
import lombok.Setter; |
||||
import vip.xumy.core.golbal.GlobalConstant; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2022年7月21日 |
||||
*/ |
||||
|
||||
@Setter |
||||
public class PageParam { |
||||
|
||||
@TableField(exist = false) |
||||
private Integer page; |
||||
@TableField(exist = false) |
||||
private Integer size; |
||||
|
||||
@JSONField(serialize = false) |
||||
public <T> Page<T> getPage() { |
||||
if (page == null) { |
||||
page = 1; |
||||
} |
||||
if (size == null) { |
||||
size = GlobalConstant.DEF_SIZE; |
||||
} else if (size > GlobalConstant.MAX_SIZE) { |
||||
size = GlobalConstant.MAX_SIZE; |
||||
} |
||||
return new Page<>(page, size); |
||||
} |
||||
|
||||
@JSONField(serialize = false) |
||||
public <T> com.github.pagehelper.Page<T> startPage() { |
||||
if (page == null) { |
||||
page = 1; |
||||
} |
||||
if (size == null) { |
||||
size = GlobalConstant.DEF_SIZE; |
||||
} else if (size > GlobalConstant.MAX_SIZE) { |
||||
size = GlobalConstant.MAX_SIZE; |
||||
} |
||||
return PageHelper.startPage(page, size); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
package vip.xumy.cube.service; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
import org.springframework.transaction.annotation.Transactional; |
||||
|
||||
import lombok.extern.log4j.Log4j2; |
||||
import vip.xumy.core.exception.CoreException; |
||||
import vip.xumy.core.utils.DateUtil; |
||||
import vip.xumy.core.utils.StringUtil; |
||||
import vip.xumy.cube.mapper.DataCubeMapper; |
||||
import vip.xumy.cube.mapper.SqlExcuter; |
||||
import vip.xumy.cube.pojo.Cube4Mysql; |
||||
import vip.xumy.cube.pojo.Dimension; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2023年3月10日 |
||||
*/ |
||||
|
||||
@Log4j2 |
||||
@Service |
||||
@Transactional |
||||
public class DataCubeService { |
||||
@Autowired |
||||
private DataCubeMapper cubeMapper; |
||||
@Autowired |
||||
private SqlExcuter sqlExcuter; |
||||
|
||||
public List<Cube4Mysql> list(Cube4Mysql example) { |
||||
return cubeMapper.list(example); |
||||
} |
||||
|
||||
public void save(Cube4Mysql cube) { |
||||
if (StringUtil.isEmpty(cube.getName(), cube.getSource())) { |
||||
throw new CoreException("立方体表名称和立方体数据源表名称不能为空"); |
||||
} |
||||
cube.setStatus(0); |
||||
cube.setCreateTime(DateUtil.format(new Date(), DateUtil.FORMAT19_LINE_YYYYMMDDHHMMSS)); |
||||
cube.setAssembleTime(null); |
||||
cube.setTotal(0); |
||||
cubeMapper.insert(cube); |
||||
} |
||||
|
||||
public void save(Dimension dimension) { |
||||
if (StringUtil.isEmpty(dimension.getCubeName(), dimension.getColumn(), dimension.getDimesion())) { |
||||
throw new CoreException("立方体表名,维度字段名和数据源表字段名均不能为空"); |
||||
} |
||||
if (dimension.getType() == null || dimension.getDataType() == null) { |
||||
throw new CoreException("纬度类型和数据类型均不能为空"); |
||||
} |
||||
cubeMapper.insertDimession(dimension); |
||||
reset(dimension.getCubeName()); |
||||
} |
||||
|
||||
public void init(String cubeName) { |
||||
Cube4Mysql cube = get(cubeName); |
||||
if (cube.getDimensions() == null || cube.getDimensions().isEmpty()) { |
||||
throw new CoreException("该立方体未添加任何维度,无法完成初始化"); |
||||
} |
||||
String initSql = cube.initSql(); |
||||
cube.setStatus(1); |
||||
cube.setTotal(0); |
||||
cube.setAssembleTime(null); |
||||
sqlExcuter.excute(initSql); |
||||
cubeMapper.update(cube); |
||||
} |
||||
|
||||
public void assemble(String name) { |
||||
Cube4Mysql cube = get(name); |
||||
if (cube.getStatus() == 0) { |
||||
throw new CoreException("该立方体尚未初始化"); |
||||
} |
||||
if (cube.getStatus() == 2) { |
||||
throw new CoreException("该立方体正在构建中"); |
||||
} |
||||
cube.setStatus(2); |
||||
cubeMapper.update(cube); |
||||
new Thread(() -> { |
||||
try { |
||||
String assembleSql = cube.assembleSql(); |
||||
int total = sqlExcuter.excute(assembleSql); |
||||
cube.setAssembleTime(cube.getCurTime()); |
||||
cube.setTotal(cube.getTotal() + total); |
||||
cube.setStatus(1); |
||||
} catch (Exception e) { |
||||
log.error("立方体:" + name + "构建失败", e); |
||||
cube.setStatus(3); |
||||
} |
||||
cubeMapper.update(cube); |
||||
}).start(); |
||||
|
||||
} |
||||
|
||||
public Cube4Mysql get(String name) { |
||||
Cube4Mysql example = new Cube4Mysql(); |
||||
example.setName(name); |
||||
List<Cube4Mysql> list = cubeMapper.list(example); |
||||
if (list == null || list.isEmpty()) { |
||||
return null; |
||||
} |
||||
Cube4Mysql cube = list.get(0); |
||||
cube.setDimensions(cubeMapper.dimesions(name, 0)); |
||||
cube.setData(cubeMapper.dimesions(name, 1)); |
||||
return cube; |
||||
} |
||||
|
||||
public void delete(String name) { |
||||
Cube4Mysql cube = get(name); |
||||
cubeMapper.delete(name); |
||||
String destorySql = cube.destorySql(); |
||||
sqlExcuter.excute(destorySql); |
||||
} |
||||
|
||||
public List<Map<String, Object>> select(Cube4Mysql cube) { |
||||
String selectSql = cube.selectSql(); |
||||
return sqlExcuter.select(selectSql); |
||||
} |
||||
|
||||
public void delete(Dimension dimension) { |
||||
cubeMapper.deleteDimension(dimension); |
||||
reset(dimension.getCubeName()); |
||||
} |
||||
|
||||
private void reset(String name) { |
||||
Cube4Mysql cube = get(name); |
||||
if (cube == null) { |
||||
throw new CoreException("立方体不存在"); |
||||
} |
||||
cube.setStatus(0); |
||||
cubeMapper.update(cube); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
logging.config:logback.xml |
||||
|
||||
spring.main.banner-mode=off |
||||
|
||||
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource |
||||
spring.datasource.driver-class-name=com.mysql.jdbc.Driver |
||||
spring.datasource.initialSize=1 |
||||
spring.datasource.minIdle=1 |
||||
spring.datasource.maxActive=20 |
||||
spring.datasource.maxWait=60000 |
||||
spring.datasource.timeBetweenEvictionRunsMillis=60000 |
||||
spring.datasource.minEvictableIdleTimeMillis=300000 |
||||
spring.datasource.validationQuery=SELECT 1 FROM DUAL |
||||
spring.datasource.keepAlive=true |
||||
spring.datasource.testWhileIdle=true |
||||
spring.datasource.testOnBorrow=false |
||||
spring.datasource.testOnReturn=false |
||||
spring.datasource.poolPreparedStatements=false |
||||
|
||||
mybatis.configuration.map-underscore-to-camel-case=true |
||||
|
||||
quartz.cron.clean.global=0 */10 * * * ? |
||||
|
||||
pagehelper.helperDialect=mysql |
||||
pagehelper.reasonable=true |
||||
pagehelper.supportMethodsArguments=true |
||||
pagehelper.params=count=countSql |
||||
pagehelper.returnPageInfo=check |
||||
|
||||
|
||||
#springdoc.swagger-ui.path=/swagger-ui.html |
||||
#springdoc.swagger-ui.enabled=true |
||||
#springdoc.api-docs.path=/api-docs |
||||
#springdoc.api-docs.enabled=true |
||||
#springdoc.packages-to-scand=com.sprt |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
package com.sprt.g5s.test; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import vip.xumy.core.utils.CombineUtil; |
||||
import vip.xumy.cube.pojo.Cube4Mysql; |
||||
import vip.xumy.cube.pojo.Dimension; |
||||
|
||||
/** |
||||
* Ownership belongs to the company |
||||
* |
||||
* @author:mengyxu |
||||
* @date:2023年3月9日 |
||||
*/ |
||||
|
||||
public class CubeUtilTester { |
||||
|
||||
@Test |
||||
public void combineTester() { |
||||
List<List<Integer>> combine = CombineUtil.combine(4, 2); |
||||
System.out.println(combine); |
||||
|
||||
List<List<Integer>> combine1 = CombineUtil.combine(4); |
||||
System.out.println(combine1); |
||||
} |
||||
|
||||
@Test |
||||
public void CubeTester() { |
||||
List<Dimension> dimensions = new ArrayList<>(); |
||||
dimensions.add(new Dimension("imsi", "imsi", 0, 15)); |
||||
dimensions.add(new Dimension("app", "app", 0, 32)); |
||||
dimensions.add(new Dimension("apptype", "app_type", 0, 32)); |
||||
dimensions.add(new Dimension("taskid", "task", 1, 11)); |
||||
dimensions.add(new Dimension("FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timestamp)/300)*300)", "time", 0, 20)); |
||||
List<Dimension> sums = new ArrayList<>(); |
||||
sums.add(new Dimension("SUM(toptraffic)", "up")); |
||||
sums.add(new Dimension("SUM(downtraffic)", "down")); |
||||
String srcTable = "trafficstatisticslog"; |
||||
String desTable = "trafficstatisticslog_loap"; |
||||
Cube4Mysql cube = new Cube4Mysql(); |
||||
cube.setName(desTable); |
||||
cube.setSource(srcTable); |
||||
cube.setSourceTimer("timestamp"); |
||||
cube.setDimensions(dimensions); |
||||
cube.setData(sums); |
||||
System.out.println(cube.initSql()); |
||||
System.out.println(cube.assembleSql()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
NODE_ENV="preview" |
||||
VUE_APP_BASE_URL="http://localhost" |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
NODE_ENV="production" |
||||
VUE_APP_BASE_URL="" |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
.DS_Store |
||||
node_modules |
||||
/dist |
||||
|
||||
|
||||
# local env files |
||||
.env.local |
||||
.env.*.local |
||||
|
||||
# Log files |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
|
||||
# Editor directories and files |
||||
.idea |
||||
.vscode |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
# lz_vue |
||||
|
||||
## Project setup |
||||
``` |
||||
yarn install |
||||
``` |
||||
|
||||
### Compiles and hot-reloads for development |
||||
``` |
||||
yarn serve |
||||
``` |
||||
|
||||
### Compiles and minifies for production |
||||
``` |
||||
yarn build |
||||
``` |
||||
|
||||
### Lints and fixes files |
||||
``` |
||||
yarn lint |
||||
``` |
||||
|
||||
### Customize configuration |
||||
See [Configuration Reference](https://cli.vuejs.org/config/). |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
module.exports = { |
||||
presets: [ |
||||
'@vue/cli-plugin-babel/preset' |
||||
] |
||||
} |
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
{ |
||||
"name": "lz_vue", |
||||
"version": "0.1.0", |
||||
"private": true, |
||||
"scripts": { |
||||
"dev": "vue-cli-service serve", |
||||
"build": "vue-cli-service build", |
||||
"lint": "vue-cli-service lint" |
||||
}, |
||||
"dependencies": { |
||||
"axios": "^0.19.2", |
||||
"core-js": "^3.8.3", |
||||
"element-plus": "2.2.10", |
||||
"js-md5": "^0.7.3", |
||||
"vue": "^3.2.13", |
||||
"vue-class-component": "^8.0.0-0", |
||||
"vue-router": "^4.0.3", |
||||
"vuex": "^4.0.0" |
||||
}, |
||||
"devDependencies": { |
||||
"@vue/cli-plugin-babel": "~5.0.0", |
||||
"@vue/cli-plugin-router": "~5.0.0", |
||||
"@vue/cli-plugin-typescript": "~5.0.0", |
||||
"@vue/cli-plugin-vuex": "~5.0.0", |
||||
"@vue/cli-service": "~5.0.0", |
||||
"sass": "^1.32.7", |
||||
"sass-loader": "^12.0.0", |
||||
"typescript": "~4.5.5" |
||||
}, |
||||
"eslintConfig": { |
||||
"root": true, |
||||
"env": { |
||||
"node": true |
||||
}, |
||||
"extends": [ |
||||
"plugin:vue/vue3-essential", |
||||
"@vue/typescript/recommended" |
||||
], |
||||
"parserOptions": { |
||||
"ecmaVersion": 2020 |
||||
}, |
||||
"rules": {} |
||||
}, |
||||
"browserslist": [ |
||||
"> 1%", |
||||
"last 2 versions", |
||||
"not dead", |
||||
"not ie 11" |
||||
] |
||||
} |
After Width: | Height: | Size: 479 KiB |
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html> |
||||
<html lang=""> |
||||
<head> |
||||
<meta charset="utf-8"> |
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0"> |
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> |
||||
<title></title> |
||||
</head> |
||||
<body> |
||||
<noscript> |
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> |
||||
</noscript> |
||||
<div id="app"></div> |
||||
<!-- built files will be auto injected --> |
||||
</body> |
||||
</html> |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
{ |
||||
"web_title_": "系统统一标题", |
||||
"web_title": "LTE无线系统", |
||||
"login_form_logo_show_": "登录页面是否显示logo标志", |
||||
"login_form_logo_show": false, |
||||
"home_logo_type_": "主页logo类型,1-标题文字,!1-图片", |
||||
"home_logo_type": 1, |
||||
"web_version_": "", |
||||
"web_version": "3.0.0" |
||||
} |
@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
|
||||
<template> |
||||
<el-container :style="appMain"> |
||||
<el-header class="app-head" :height="$store.state.sizes.headHeight"> |
||||
<Header v-show="$store.state.showHeader" ref="header" /> |
||||
</el-header> |
||||
<el-container :style="{ height: $store.state.sizes.mainHeight }"> |
||||
<el-aside :width="$store.state.sizes.asideWidth"> |
||||
<Aside v-show="$store.state.showAside"></Aside> |
||||
</el-aside> |
||||
<el-main class="app-main"> |
||||
<router-view /> |
||||
</el-main> |
||||
</el-container> |
||||
</el-container> |
||||
</template> |
||||
|
||||
<script> |
||||
// <script lang="ts" steup> |
||||
// import { reactive, onMounted } from "vue"; |
||||
// import { useStore } from "vuex"; |
||||
import Header from "@/components/main/header"; |
||||
import Aside from "@/components/main/aside"; |
||||
import { publik } from "@/config/apiUrl"; |
||||
|
||||
// const store = useStore(); |
||||
// const appMain = reactive({ |
||||
// height: "", |
||||
// backgroundImage: "url(" + publik.background + ")", |
||||
// }); |
||||
|
||||
// onMounted(() => { |
||||
// store.commit("updateState", { prop: "showHeader", value: false }); |
||||
// store.commit("updateState", { prop: "showAside", value: false }); |
||||
// store.commit("initSizes"); |
||||
// store.dispatch("getWebConfig"); |
||||
// appMain.height = store.state.sizes.height; |
||||
|
||||
// }); |
||||
|
||||
import { Options, Vue } from "vue-class-component"; |
||||
|
||||
@Options({ |
||||
components: { |
||||
Header, |
||||
Aside |
||||
}, |
||||
}) |
||||
export default class app extends Vue { |
||||
appMain = { |
||||
// backgroundImage: "url(" + publik.background + ")" |
||||
} |
||||
|
||||
init () { |
||||
this.$store.commit("setUserInfo", { type: 4 }); |
||||
this.$store.commit("initSizes"); |
||||
// this.$store.dispatch("getWebConfig"); |
||||
// this.$router.push("/") |
||||
} |
||||
created () { |
||||
//生命周期 - 创建完成(可以访问当前this实例) |
||||
this.init(); |
||||
this.appMain = { |
||||
height: this.$store.state.sizes.height, |
||||
// backgroundImage: "url(" + publik.background + ")" |
||||
}; |
||||
} |
||||
mounted () { |
||||
//生命周期 - 挂载完成(可以访问DOM元素) |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang='scss'> |
||||
@charset "UTF-8"; |
||||
// @import "./assets/style/element.scss"; |
||||
@import "./assets/style/base.scss"; |
||||
@import "./assets/style/dark.scss"; |
||||
|
||||
.app-main { |
||||
background-size: cover; |
||||
background-position: center; |
||||
background-repeat: no-repeat; |
||||
} |
||||
|
||||
.el-header, |
||||
.main-head, |
||||
.main-table { |
||||
padding: 0; |
||||
} |
||||
|
||||
#app { |
||||
background-color: #edf2f7; |
||||
} |
||||
|
||||
.app-head { |
||||
padding: 0px !important; |
||||
} |
||||
|
||||
.app-main { |
||||
padding: 0px !important; |
||||
} |
||||
</style> |
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,172 @@
@@ -0,0 +1,172 @@
|
||||
* { |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
html, |
||||
body { |
||||
min-width: 1200px; |
||||
font-size: 14px; |
||||
font-family: 'Microsoft YaHei'; |
||||
width: 100%; |
||||
height: 100%; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
input { |
||||
font-family: 'Microsoft YaHei'; |
||||
} |
||||
|
||||
.clearfix { |
||||
&:before, |
||||
&:after { |
||||
display: table; |
||||
content: ''; |
||||
} |
||||
&:after { |
||||
clear: both; |
||||
} |
||||
} |
||||
|
||||
a { |
||||
&:link, |
||||
&:visited { |
||||
all: inherit; |
||||
} |
||||
&:hover { |
||||
all: inherit; |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* 侦码侦听设备红绿灯显示 |
||||
*/ |
||||
|
||||
.grid-code-dev { |
||||
border-radius: 4px; |
||||
height: 4em; |
||||
} |
||||
.code-dev { |
||||
margin-right: 3%; |
||||
height: 100%; |
||||
float: right; |
||||
} |
||||
|
||||
// 公共样式文件 |
||||
// 带阴影的表格外的div |
||||
div.table-warpper { |
||||
padding: 0px; |
||||
margin: 0px; |
||||
border: 1px solid #20a0ff; |
||||
overflow: hidden; |
||||
// box-shadow: |
||||
// -10px 0 10px #C1C1C1, /*左边阴影*/ |
||||
// 10px 0 10px #C1C1C1, /*右边阴影*/ |
||||
// 0 -10px 10px #C1C1C1, /*顶部阴影*/ |
||||
// 0 10px 10px #C1C1C1; /*底边阴影*/ |
||||
} |
||||
|
||||
.tabs-header { |
||||
margin-left: 1em; |
||||
} |
||||
|
||||
.cur-table-warpper { |
||||
margin-left: -1em; |
||||
} |
||||
|
||||
// input[type=number]在ff和chrome中会出现上下的小三角箭头 |
||||
input::-webkit-outer-spin-button, |
||||
input::-webkit-inner-spin-button { |
||||
-webkit-appearance: none !important; |
||||
} |
||||
|
||||
input[type='number'] { |
||||
-moz-appearance: textfield; |
||||
} |
||||
|
||||
// 修改element |
||||
.el-picker-panel__link-btn { |
||||
display: none; |
||||
} |
||||
|
||||
.el-pagination__editor { |
||||
width: 55px !important; |
||||
} |
||||
|
||||
.search-row { |
||||
padding-bottom: 0.2em; |
||||
} |
||||
|
||||
.form-item-btn { |
||||
float: right; |
||||
width: 130px; |
||||
} |
||||
|
||||
.full-item-form .el-select, |
||||
.full-item-form .el-date-editor, |
||||
.full-item-form .el-autocomplete { |
||||
width: 100% !important; |
||||
} |
||||
|
||||
.search-row { |
||||
height: 40px; |
||||
// background-color: $main-color; |
||||
padding: 5px 15px; |
||||
} |
||||
|
||||
.query-mini { |
||||
width: 100px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-smart { |
||||
width: 120px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-short { |
||||
width: 150px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-medium { |
||||
width: 200px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-long { |
||||
width: 250px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-tree { |
||||
width: 300px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-longest { |
||||
width: 350px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-400 { |
||||
width: 400px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-450 { |
||||
width: 450px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.query-500 { |
||||
width: 500px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.auto-height { |
||||
height: 78vh; |
||||
overflow-x: hidden; |
||||
overflow-y: scroll; |
||||
} |
||||
|
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
// 定义一些全局变量样式 |
||||
|
||||
/* 文字相关 */ |
||||
$text-color-grey: rgba($color: #ffffff, $alpha: .7); |
||||
|
||||
/* 边框 */ |
||||
$border-color: #414A7A; |
||||
$border: 1px solid $border-color; |
||||
|
||||
/* 高亮背景色 */ |
||||
$active-background-color: #1D3B8A; |
||||
$active-background-color1: #3A7BFF; |
||||
$highlight-color: #8493E3; |
||||
|
||||
/* 主要颜色 */ |
||||
$main-color: #18295E; |
||||
$sec-color: #3E4583; |
||||
$email-background-color: rgba(0, 24, 106, 0.3); |
||||
|
||||
$success-color: #67C23A; |
||||
$warning-color: #E6A23C; |
||||
$danger-color: #F56C6C; |
||||
|
||||
// @import '_common.scss'; |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
.style-dark .el-table *, .style-dark .el-tabs *, |
||||
.style-dark .el-input__inner, |
||||
.style-dark h1, .style-dark h2, .style-dark small, .style-dark span, .style-dark div *{ |
||||
color: white; |
||||
} |
||||
|
||||
.style-dark .el-input__inner * { |
||||
background: none !important; |
||||
} |
||||
|
||||
.style-dark .el-dialog *{ |
||||
color: white !important; |
||||
} |
@ -0,0 +1,197 @@
@@ -0,0 +1,197 @@
|
||||
@import 'conf.scss'; |
||||
|
||||
.el-input__wrapper { |
||||
padding: 0px !important; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
.el-input__inner { |
||||
padding: 0px 15px !important; |
||||
} |
||||
|
||||
.el-input__suffix { |
||||
padding-right: 15px !important; |
||||
} |
||||
.el-input__prefix { |
||||
padding-left: 15px !important; |
||||
} |
||||
|
||||
.el-input__inner, |
||||
.el-input__wrapper { |
||||
background: transparent !important; |
||||
color: rgba(17, 17, 17, 0.747) !important; |
||||
} |
||||
|
||||
.el-dialog { |
||||
background-color: transparent !important; |
||||
// background-image: url('~@/assets/img/background.png') !important; |
||||
background-size: cover !important; |
||||
background-position: center !important; |
||||
background-repeat: no-repeat !important; |
||||
.el-dialog__title { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
} |
||||
|
||||
/* 针对table组件的修改 */ |
||||
.my-table { |
||||
.el-table { |
||||
font-size: 12px; |
||||
background-color: transparent; |
||||
color: rgba(17, 17, 17, 0.747); |
||||
thead { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
.cell { |
||||
text-align: center; |
||||
} |
||||
} |
||||
td, |
||||
th { |
||||
padding: 6px 0; |
||||
} |
||||
th, |
||||
tr { |
||||
background-color: transparent; |
||||
} |
||||
th.is-leaf { |
||||
// border-bottom: $border; |
||||
} |
||||
td { |
||||
// border-bottom: $border; |
||||
} |
||||
&::before { |
||||
// background-color: $border-color; |
||||
} |
||||
.el-table__body tr.current-row > td { |
||||
background: $active-background-color; |
||||
} |
||||
} |
||||
.el-table--enable-row-hover .el-table__body tr:hover > td { |
||||
background: rgba(226, 226, 226, 0.705); |
||||
} |
||||
.el-table .el-table__body tr.current-row > td { |
||||
background: rgba(226, 226, 226, 0.705); |
||||
} |
||||
.el-table__expand-icon { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
.el-table__body tr.hover-row > td { |
||||
background: rgba(226, 226, 226, 0.705); |
||||
} |
||||
} |
||||
.el-table--striped .el-table__body tr.el-table__row--striped td { |
||||
background: rgba($color: #9b9eb8, $alpha: 0.2); |
||||
} |
||||
|
||||
/* 针对表单组件的修改 */ |
||||
.my-form { |
||||
// display: inline-block; |
||||
color: rgba(17, 17, 17, 0.747); |
||||
.el-input__inner, |
||||
.el-textarea__inner { |
||||
background: transparent; |
||||
// border-color: $border-color; |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
.el-form-item__label { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
.el-checkbox { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
margin-right: 15px !important; |
||||
} |
||||
} |
||||
|
||||
/* 时间选取组件 */ |
||||
.my-date-picker { |
||||
display: inline-block; |
||||
.el-input__inner { |
||||
// border: $border; |
||||
.el-range-input { |
||||
background-color: transparent !important; |
||||
} |
||||
} |
||||
|
||||
.el-range-input { |
||||
background: $main-color; |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
.el-range-separator, |
||||
.el-input__inner, |
||||
.el-range__icon { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
} |
||||
|
||||
/* 针对tab组件修改 */ |
||||
.my-tabs { |
||||
.el-tabs__item { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
.el-tabs__nav-wrap::after { |
||||
height: 0px; |
||||
} |
||||
} |
||||
|
||||
// /* 分页 */ |
||||
// .el-pagination { |
||||
// .el-pagination__total { |
||||
// color: rgba(17, 17, 17, 0.747); |
||||
// } |
||||
// } |
||||
|
||||
.my-pagination { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
.el-input__inner, |
||||
.el-textarea__inner { |
||||
background: transparent; |
||||
// border-color: $border-color; |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
.el-form-item__label { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
.el-pagination { |
||||
.btn-next { |
||||
background-color: transparent !important; |
||||
.el-icon-arrow-right { |
||||
color: #50a3f7; |
||||
&:hover { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
} |
||||
} |
||||
.btn-prev { |
||||
background-color: transparent !important; |
||||
.el-icon-arrow-left { |
||||
color: #50a3f7; |
||||
&:hover { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
} |
||||
} |
||||
.el-pager { |
||||
li { |
||||
background: transparent; |
||||
} |
||||
.number { |
||||
color: #50a3f7 !important; |
||||
} |
||||
.is-active { |
||||
color: rgba(17, 17, 17, 0.747) !important; |
||||
} |
||||
.el-icon-more { |
||||
color: #50a3f7 !important; |
||||
} |
||||
.el-icon-d-arrow-left { |
||||
border-radius: 10px; |
||||
} |
||||
.el-icon-d-arrow-right { |
||||
border-radius: 10px; |
||||
} |
||||
} |
||||
.el-pagination__jump { |
||||
color: rgba(17, 17, 17, 0.747); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
<template> |
||||
<el-row> |
||||
<el-button v-if="slice.length < 8" type="success" icon="el-icon-plus" size="small" class="form-item-btn" |
||||
@click="add">FLOW |
||||
</el-button> |
||||
</el-row> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref, defineProps } from "vue"; |
||||
|
||||
const store = useStore(); |
||||
const prop = defineProps({ |
||||
slice: { |
||||
type: Array, |
||||
default: [] |
||||
}, |
||||
prefix: { |
||||
type: String, |
||||
defaul: null |
||||
} |
||||
}); |
||||
const add = () => { |
||||
prop.slice.push({}); |
||||
} |
||||
|
||||
onMounted(() => { }); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
</style> |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
<template> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref } from "vue"; |
||||
|
||||
const { state, commit, dispatch } = useStore(); |
||||
|
||||
onMounted(() => {}); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
</style> |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
<template> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref } from "vue"; |
||||
|
||||
const { state, commit, dispatch } = useStore(); |
||||
|
||||
onMounted(() => {}); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
</style> |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
import ListTable from './listTable.vue'; |
||||
import ModifyForm from './modifyForm.vue'; |
||||
import SearchRow from './searchRow.vue'; |
||||
|
||||
export { ListTable, ModifyForm, SearchRow }; |
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
<template> |
||||
<div class="my-table"> |
||||
<el-table :data="page ? data.data : data" |
||||
:height="height ? height : (page ? state.sizes.pTableHei : state.sizes.tableHei)" highlight-current-row> |
||||
<el-table-column v-for="item in props" :key="item.code" :prop="item.code" :label="item.name" |
||||
:min-width="item.width" :width="item.type ? item.width : ''" :align="item.align ? item.align : 'center'" |
||||
show-overflow-tooltip :formatter="formatter"> |
||||
<template v-if="item.slot" #default="scope"> |
||||
<slot :name="item.code" :row="scope.row"></slot> |
||||
</template> |
||||
</el-table-column> |
||||
<el-table-column v-if="prop.actionWidth" label="操作" :min-width="prop.actionWidth" align="center"> |
||||
<template #default="scope"> |
||||
<slot v-bind:row="scope.row" v-bind:$index="scope.$index"></slot> |
||||
</template> |
||||
</el-table-column> |
||||
</el-table> |
||||
</div> |
||||
<div v-if="page" class="my-pagination"> |
||||
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" |
||||
:current-page="example.page" :page-sizes="[10, 20, 50, 100, 200]" :page-size="example.size" |
||||
layout="total, sizes, prev, pager, next, jumper" :total="data.total"> |
||||
</el-pagination> |
||||
</div> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref, defineEmits, defineProps } from "vue"; |
||||
import { formatter } from "@/plugs/formatter"; |
||||
const prop = defineProps({ |
||||
data: { |
||||
type: Object, |
||||
default: null |
||||
}, |
||||
props: { |
||||
type: Array<any>(), |
||||
default: [], |
||||
}, |
||||
page: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
actionWidth: { |
||||
type: Number, |
||||
default: null, |
||||
}, |
||||
height: { |
||||
type: Number, |
||||
default: null, |
||||
}, |
||||
example: { |
||||
type: Object, |
||||
default: null, |
||||
}, |
||||
}); |
||||
const { state } = useStore(); |
||||
|
||||
const emit = defineEmits(["query"]); |
||||
|
||||
const handleSizeChange = (val) => { |
||||
prop.example.size = val; |
||||
emit("query"); |
||||
}; |
||||
|
||||
const handleCurrentChange = (val) => { |
||||
prop.example.page = val; |
||||
emit("query"); |
||||
}; |
||||
|
||||
onMounted(() => { }); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
</style> |
@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
|
||||
<template> |
||||
<div class="modify-form"> |
||||
<el-form label-position="right" :class="class" :label-width="width ? width + 'px' : ''" :model="param" |
||||
ref="modifyForm" :rules="rules"> |
||||
<slot></slot> |
||||
<el-form-item class="form-btns"> |
||||
<el-button type="primary" @click="formConfirm">确定</el-button> |
||||
<el-button type="primary" @click="emit('cancel')">取消</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
</div> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref, watch } from "vue"; |
||||
import { FormInstance } from "element-plus"; |
||||
|
||||
const store = useStore(); |
||||
const prop = defineProps({ |
||||
width: { |
||||
type: Number, |
||||
default: 80, |
||||
}, |
||||
param: { |
||||
type: Object, |
||||
default: {}, |
||||
}, |
||||
rules: { |
||||
type: Object, |
||||
default: {}, |
||||
}, |
||||
class: { |
||||
type: String, |
||||
default: '', |
||||
}, |
||||
}); |
||||
const emit = defineEmits(["confirm", "cancel"]); |
||||
const modifyForm = ref<FormInstance>(); |
||||
|
||||
const formConfirm = () => { |
||||
if (!modifyForm.value) return; |
||||
modifyForm.value?.validate((valid, fields) => { |
||||
if (valid) { |
||||
emit("confirm"); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
const clearValidate = () => { |
||||
modifyForm.value?.clearValidate(); |
||||
} |
||||
|
||||
watch(() => prop.param, () => { |
||||
modifyForm.value?.clearValidate(); |
||||
}); |
||||
|
||||
onMounted(() => { |
||||
}); |
||||
</script> |
||||
<style lang="scss"> |
||||
//@import url(); 引入公共css类 |
||||
.modify-form { |
||||
.el-select { |
||||
width: 100%; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
<template> |
||||
<div class="search-row"> |
||||
<div class="left"> |
||||
<span class="title" v-if="title">{{ title }}</span> |
||||
<el-button v-if="fresh" type="info" icon="Refresh" @click="emit('query')"> </el-button> |
||||
<el-button v-if="add" type="primary" icon="Plus" @click="emit('add')"> 添加 </el-button> |
||||
<el-button v-if="del" type="danger" icon="Delete" @click="emit('delete')"> 删除 </el-button> |
||||
<slot name="left"></slot> |
||||
</div> |
||||
<div class="query"> |
||||
<slot></slot> |
||||
<el-button v-if="query" type="primary" icon="Search" @click="emit('query')"> 查询 </el-button> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref } from "vue"; |
||||
import { defineProps, defineEmits } from "vue"; |
||||
// import { Refresh, Plus, Delete, Search } from "@element-plus/icons-vue"; |
||||
|
||||
const store = useStore(); |
||||
|
||||
const prop = defineProps({ |
||||
title: { |
||||
type: String, |
||||
default: null |
||||
}, |
||||
fresh: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
add: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
del: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
query: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
}); |
||||
|
||||
const emit = defineEmits(["query", "add", "delete"]); |
||||
|
||||
onMounted(() => { }); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
.query { |
||||
text-align: right; |
||||
float: right; |
||||
} |
||||
|
||||
.left { |
||||
float: left; |
||||
} |
||||
|
||||
.title { |
||||
font-weight: bold; |
||||
padding-right: 15px; |
||||
font-size: 20px; |
||||
min-width: 20px; |
||||
} |
||||
</style> |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
export const publik = { |
||||
background: '/background.jpg', |
||||
webConfig: '/webConfig.json', |
||||
login: '/public/login', |
||||
logout: '/public/logout', |
||||
info: '/public/info', |
||||
updatePassword: 'public/password', |
||||
}; |
||||
|
||||
export const user = { |
||||
root: '/sys/user', |
||||
lock: '/sys//user/lock', |
||||
reset: '/sys//user/reset', |
||||
clean: '/sys//user/data', |
||||
}; |
||||
|
||||
export const log = { |
||||
sys: '/sys/log', |
||||
service: '/sys//log/service', |
||||
export: '/sys//log/service/export', |
||||
}; |
||||
|
||||
export const cube = { |
||||
root: '/cube', |
||||
init: '/cube/init', |
||||
assemble: '/cube/assemble', |
||||
select: '/cube/select', |
||||
dimension: '/cube/dimension' |
||||
}; |
@ -0,0 +1,257 @@
@@ -0,0 +1,257 @@
|
||||
/* eslint-disable */ |
||||
import axios from 'axios'; |
||||
import store from '@/store'; |
||||
import { loading, close, showMessage } from './element'; |
||||
|
||||
const config = { |
||||
baseURL: process.env.VUE_APP_BASE_URL ? '/api' : '', |
||||
timeout: 60 * 1000, |
||||
withCredentials: true, // Check cross-site Access-Control
|
||||
}; |
||||
|
||||
const _axios = axios.create(config); |
||||
_axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; |
||||
_axios.defaults.headers.put['Content-Type'] = 'application/json;charset=UTF-8'; |
||||
_axios.defaults.headers.delete['Content-Type'] = |
||||
'application/json;charset=UTF-8'; |
||||
|
||||
let curMsg: any; |
||||
|
||||
// Add a request interceptor
|
||||
_axios.interceptors.request.use( |
||||
function (config) { |
||||
const time = new Date().getTime().toString(); |
||||
const params = config.params; |
||||
if (params) { |
||||
delEmpty(params); |
||||
params.t = time; |
||||
} |
||||
const data = config.data; |
||||
if (data != null && typeof data === 'object') { |
||||
delEmpty(data); |
||||
data.t = time; |
||||
} |
||||
return config; |
||||
}, |
||||
function (error) { |
||||
return Promise.reject(error); |
||||
} |
||||
); |
||||
|
||||
function delEmpty(data) { |
||||
if (data) { |
||||
for (const item in data) { |
||||
if (data.hasOwnProperty(item)) { |
||||
const val = data[item]; |
||||
if ((val == null || val == 'null' || val == '') && val !== 0) { |
||||
delete data[item]; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Add a response interceptor
|
||||
_axios.interceptors.response.use( |
||||
function (response) { |
||||
return response.data; |
||||
}, |
||||
function (error) { |
||||
return Promise.reject(error); |
||||
} |
||||
); |
||||
|
||||
export function post(url, data?, noMsg?, noLoading?) { |
||||
return new Promise((resolve, reject) => { |
||||
if (!noLoading) { |
||||
loading(); |
||||
} |
||||
_axios.post(url, data).then( |
||||
(response: any) => { |
||||
handResponse(response, resolve, noMsg, noLoading); |
||||
}, |
||||
(err) => { |
||||
handError(err, reject, noMsg, noLoading); |
||||
} |
||||
); |
||||
}); |
||||
} |
||||
|
||||
export function get(url, data?, noMsg?, noLoading?) { |
||||
return new Promise((resolve, reject) => { |
||||
if (!noLoading) { |
||||
loading(); |
||||
} |
||||
_axios.get(url, { params: data }).then( |
||||
(response: any) => { |
||||
handResponse(response, resolve, noMsg, noLoading); |
||||
}, |
||||
(err) => { |
||||
handError(err, reject, noMsg, noLoading); |
||||
} |
||||
); |
||||
}); |
||||
} |
||||
|
||||
export function put(url, data?, noMsg?, noLoading?) { |
||||
return new Promise((resolve, reject) => { |
||||
if (!noLoading) { |
||||
loading(); |
||||
} |
||||
_axios.put(url, data).then( |
||||
(response: any) => { |
||||
handResponse(response, resolve, noMsg, noLoading); |
||||
}, |
||||
(err) => { |
||||
handError(err, reject, noMsg, noLoading); |
||||
} |
||||
); |
||||
}); |
||||
} |
||||
|
||||
export function delate(url, data?, noMsg?, noLoading?) { |
||||
return new Promise((resolve, reject) => { |
||||
if (!noLoading) { |
||||
loading(); |
||||
} |
||||
// _axios.delete(url, data).then(
|
||||
// (response: any) => {
|
||||
// handResponse(response, resolve, noMsg, noLoading);
|
||||
// },
|
||||
// (err) => {
|
||||
// handError(err, reject, noMsg, noLoading);
|
||||
// }
|
||||
// );
|
||||
_axios({ |
||||
method: 'delete', |
||||
url: url, |
||||
data: data, |
||||
}).then( |
||||
(response: any) => { |
||||
handResponse(response, resolve, noMsg, noLoading); |
||||
}, |
||||
(err) => { |
||||
handError(err, reject, noMsg, noLoading); |
||||
} |
||||
); |
||||
}); |
||||
} |
||||
|
||||
function handResponse(response, resolve, noMsg, noLoading) { |
||||
if (!noLoading) { |
||||
close(); |
||||
} |
||||
if (response.success) { |
||||
if (response.message) { |
||||
if (!noMsg) { |
||||
showMessage('success', response.message, false); |
||||
} |
||||
resolve(true); |
||||
} else if (response.data) { |
||||
resolve(response.data); |
||||
} else { |
||||
resolve(true); |
||||
} |
||||
} else { |
||||
if (response.message == 'session timeout') { |
||||
store.dispatch('logout'); |
||||
} |
||||
if (response.message == 'no permission') { |
||||
response.message = '您暂无权访问,请联系管理员添加'; |
||||
} |
||||
if (response.message?.indexOf('no permission for ') == 0) { |
||||
response.message = '此权限尚未开放,请勿越权访问'; |
||||
} |
||||
if (!noMsg) { |
||||
showMessage('error', response.message, false); |
||||
} |
||||
response.error && console.log(response.error); |
||||
resolve(false); |
||||
} |
||||
} |
||||
|
||||
function handError(err, reject, noMsg, noLoading) { |
||||
if (!noLoading) { |
||||
close(); |
||||
} |
||||
if (!noMsg) { |
||||
showMessage('error', '网络错误', false); |
||||
} |
||||
reject(err); |
||||
} |
||||
|
||||
export function upload(file, url, data) { |
||||
return new Promise((resolve, reject) => { |
||||
let param = new FormData(); // 创建form对象
|
||||
param.append('file', file); // 通过append向form对象添加数据
|
||||
if (data) { |
||||
for (const item in data) { |
||||
if (data.hasOwnProperty(item)) { |
||||
param.append(item, data[item]); // 添加form表单中其他数据
|
||||
} |
||||
} |
||||
} |
||||
let config = { |
||||
headers: { 'Content-Type': 'multipart/form-data' }, |
||||
}; |
||||
_axios.post(url, param, config).then( |
||||
(response) => { |
||||
handResponse(response, resolve, false, false); |
||||
}, |
||||
(err) => { |
||||
handError(err, reject, false, false); |
||||
} |
||||
); |
||||
}); |
||||
} |
||||
|
||||
export function download(fileName, url, data = {}, callBack?) { |
||||
loading(); |
||||
_axios({ |
||||
method: 'get', |
||||
url: url, // 请求地址
|
||||
params: data, // 参数
|
||||
responseType: 'blob', // 表明返回服务器返回的数据类型
|
||||
}).then( |
||||
(response: any) => { |
||||
close(); |
||||
const reader = new FileReader() as any; |
||||
reader.readAsText(response); |
||||
reader.onload = function () { |
||||
try { |
||||
const result = JSON.parse(reader.result); |
||||
if (typeof result === 'object') { |
||||
showMessage('error', result.message, false); |
||||
if (callBack != null) { |
||||
callBack(false); |
||||
} |
||||
return; |
||||
} |
||||
} catch (err) {} |
||||
const blob = new Blob([response], { |
||||
type: 'application/vnd.ms-excel', |
||||
}); |
||||
const navigator = window.navigator as any; |
||||
if (navigator.msSaveOrOpenBlob) { |
||||
navigator.msSaveBlob(blob, fileName); |
||||
} else { |
||||
const link = document.createElement('a'); |
||||
link.href = window.URL.createObjectURL(blob); |
||||
link.download = fileName; |
||||
link.click(); |
||||
window.URL.revokeObjectURL(link.href); |
||||
} |
||||
if (callBack != null) { |
||||
callBack(true); |
||||
} |
||||
}; |
||||
}, |
||||
(err) => { |
||||
close(); |
||||
showMessage('error', '下载失败,网络异常!', false); |
||||
if (callBack != null) { |
||||
callBack(false); |
||||
} |
||||
} |
||||
); |
||||
} |
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
import { |
||||
ElLoading, |
||||
ElMessage, |
||||
ElNotification, |
||||
ElMessageBox, |
||||
Action, |
||||
} from 'element-plus'; |
||||
let curMsg; |
||||
let count = 0; |
||||
let instance; |
||||
|
||||
export function loading() { |
||||
if (count === 0) { |
||||
instance = ElLoading.service({ |
||||
lock: true, |
||||
text: '加载中...', |
||||
spinner: 'el-icon-loading', |
||||
background: 'rgba(0, 0, 0, 0.3)', |
||||
}); |
||||
} |
||||
count++; |
||||
} |
||||
|
||||
export function close() { |
||||
if (count <= 0) { |
||||
return; |
||||
} |
||||
if (--count === 0) { |
||||
instance.close(); |
||||
} |
||||
} |
||||
|
||||
export function showMessage(type, info, unClose) { |
||||
if (curMsg != null) { |
||||
curMsg.close(); |
||||
} |
||||
const tmp = ElMessage({ |
||||
type: type, |
||||
showClose: true, |
||||
message: info, |
||||
duration: 3000, |
||||
offset: 50, |
||||
}); |
||||
if (!unClose) { |
||||
curMsg = tmp; |
||||
} |
||||
} |
||||
|
||||
export function showNotify(type, title, message, position) { |
||||
if (position == null) { |
||||
position = 'top-right'; |
||||
} |
||||
ElNotification({ |
||||
title: title, |
||||
type: type, |
||||
message: message, |
||||
position: position, |
||||
duration: 0, |
||||
}); |
||||
} |
||||
|
||||
export function warning(msg) { |
||||
showMessage('warning', msg, false); |
||||
} |
||||
|
||||
export function error(msg) { |
||||
showMessage('error', msg, false); |
||||
} |
||||
|
||||
export function success(msg) { |
||||
showMessage('success', msg, false); |
||||
} |
||||
|
||||
export const confirm = (msg: string, title: string) => { |
||||
return new Promise((resolve, reject) => { |
||||
ElMessageBox.confirm(msg, title, { |
||||
confirmButtonText: '确定', |
||||
cancelButtonText: '取消', |
||||
buttonSize: 'default', |
||||
confirmButtonClass:'el-button--info', |
||||
type: 'warning', |
||||
}) |
||||
.then(resolve) |
||||
.catch(() => { |
||||
//do nothing
|
||||
}); |
||||
}); |
||||
}; |
@ -0,0 +1,90 @@
@@ -0,0 +1,90 @@
|
||||
export const menu = [ |
||||
{ |
||||
user: { |
||||
name: '账号管理', |
||||
active: true, |
||||
}, |
||||
log: { |
||||
name: '日志管理', |
||||
active: false, |
||||
}, |
||||
}, |
||||
{ |
||||
user: { |
||||
name: '账号管理', |
||||
active: true, |
||||
}, |
||||
case: { |
||||
name: '案件管理', |
||||
active: false, |
||||
}, |
||||
log: { |
||||
name: '日志管理', |
||||
active: false, |
||||
}, |
||||
}, |
||||
{ |
||||
target: { |
||||
name: '目标管理', |
||||
active: true, |
||||
}, |
||||
reconnCode: { |
||||
name: '侦码动态', |
||||
active: false, |
||||
unread: 0, |
||||
}, |
||||
reconnDynamic: { |
||||
name: '侦听动态', |
||||
active: false, |
||||
}, |
||||
reconnListen: { |
||||
name: '侦听详情', |
||||
active: false, |
||||
unread: 0, |
||||
}, |
||||
analysis: { |
||||
name: '人群分析', |
||||
active: false, |
||||
}, |
||||
flux: { |
||||
name: '流量管理', |
||||
active: false, |
||||
}, |
||||
history: { |
||||
name: '历史记录', |
||||
active: false, |
||||
}, |
||||
device: { |
||||
name: '设备管理', |
||||
active: true, |
||||
}, |
||||
log: { |
||||
name: '日志管理', |
||||
active: false, |
||||
}, |
||||
}, |
||||
{ |
||||
profile: { |
||||
name: '开户模版', |
||||
active: true, |
||||
}, |
||||
Subscriber: { |
||||
name: '开户', |
||||
active: false, |
||||
unread: 0, |
||||
}, |
||||
}, |
||||
{ |
||||
cube: { |
||||
name: '数据立方', |
||||
active: true, |
||||
}, |
||||
}, |
||||
]; |
||||
export const homes = [ |
||||
{ path: 'user', show: true }, |
||||
{ path: 'user', show: true }, |
||||
{ path: 'task', show: false }, |
||||
{ path: 'profile', show: true }, |
||||
{ path: 'cube', show: false }, |
||||
]; |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import { createApp } from 'vue'; |
||||
import App from './App.vue'; |
||||
import router from './router'; |
||||
import store from './store'; |
||||
|
||||
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' |
||||
|
||||
const app = createApp(App); |
||||
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { |
||||
app.component(key, component); |
||||
} |
||||
|
||||
app.use(ElementPlus, { size: 'default', locale: zhCn }); |
||||
app.use(store).use(router).mount('#app'); |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
export const units = { |
||||
0: 'bps', |
||||
1: 'Kbps', |
||||
2: 'Mbps', |
||||
3: 'Gbps', |
||||
4: 'Tbps', |
||||
}; |
||||
export const context = { |
||||
1: '主叫', |
||||
2: '被叫', |
||||
3: '主动', |
||||
}; |
||||
export const extens = { |
||||
'_X.': '_X.', |
||||
}; |
||||
export const appData = { |
||||
Dial: 'PJSIP/${EXTEN},60', |
||||
Macro: 'DIAL,${EXTEN}', |
||||
}; |
||||
export const activeStatus = { |
||||
A: '已启用', |
||||
I: '已禁用', |
||||
}; |
||||
export const smsType = { |
||||
R: '被叫', |
||||
S: '主叫', |
||||
}; |
||||
export const smsAction = { |
||||
Y: '拦截', |
||||
N: '放行', |
||||
}; |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
export const cube_status = { |
||||
0: '未初始化', |
||||
1: '空闲', |
||||
2: '执行构建中', |
||||
3: '构建失败', |
||||
}; |
||||
export const dimension_dataType = { |
||||
0: '字符', |
||||
1: '数字', |
||||
2: '时间', |
||||
}; |
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
export const device_Status = { |
||||
0: '自适应中', |
||||
1: '工作状态', |
||||
2: '告警', |
||||
3: '故障', |
||||
4: '启动中', |
||||
}; |
||||
export const device_cardTotalStatus = { |
||||
0: '初始状态', |
||||
1: '切换角色中', |
||||
2: '手动重启', |
||||
3: '禁用状态', |
||||
4: '异常', |
||||
5: '在线', |
||||
}; |
||||
export const device_cardCurrentStatus = { |
||||
0: '离线', |
||||
1: '小区未建立', |
||||
2: '小区建立中', |
||||
3: '小区建立成功', |
||||
4: '切换角色失败', |
||||
5: '查询配置失败', |
||||
6: '工作模式', |
||||
7: '功放查询配置失败', |
||||
}; |
||||
export const device_syncMode = { |
||||
0: '宏站同步', |
||||
1: 'GPS同步', |
||||
4: '自适应', |
||||
5: '无同步模式', |
||||
}; |
||||
export const powerMeasure = { |
||||
46: '40W', |
||||
42: '16W', |
||||
40: '10W', |
||||
37: '5W', |
||||
33: '2W', |
||||
30: '1W', |
||||
27: '0.5W', |
||||
24: '0.25W', |
||||
23: '0.2W', |
||||
20: '0.1W', |
||||
17: '0.05W', |
||||
10: '10MW', |
||||
0: '1MW', |
||||
}; |
||||
|
||||
const arr1 = [24, 42]; |
||||
const arr2 = [24, 46]; |
||||
const arr3 = [24, 33]; |
||||
const arr4 = [0, 23]; |
||||
export const powerRange = { |
||||
0: arr1, |
||||
1: arr1, |
||||
2: arr1, |
||||
3: arr1, |
||||
4: arr1, |
||||
5: arr2, |
||||
6: arr2, |
||||
7: arr3, |
||||
8: arr4, |
||||
}; |
||||
|
||||
export const device_interdict = { |
||||
1: '上网', |
||||
2: '短信', |
||||
4: 'volte通话', |
||||
8: '上行短信', |
||||
16: '下行短信', |
||||
}; |
||||
export const device_soldierNum = { |
||||
'-1': '侦模块', |
||||
1: '听模块', |
||||
}; |
||||
export const device_powerSwitch = { |
||||
0: '关', |
||||
1: '开', |
||||
}; |
||||
export const auto_conf_model = { |
||||
1: '普通机型', |
||||
2: '小米12', |
||||
}; |
||||
export const standards = { |
||||
0: 'GSM', |
||||
1: 'WCDMA', |
||||
}; |
||||
export const tac_cycle = { |
||||
0: '不自动更新', |
||||
1: '1分钟', |
||||
2: '2分钟', |
||||
5: '5分钟', |
||||
10: '10分钟', |
||||
30: '30分钟', |
||||
}; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
export * from './device'; |
||||
// import * as device from './device';
|
||||
// import * as base from './base';
|
||||
// export * from "./user";
|
||||
export * from "./5gs"; |
||||
export * from './base'; |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
import * as dict from './dict'; |
||||
|
||||
export const getValue = ( |
||||
row: any, |
||||
column: string, |
||||
value: any, |
||||
index?: number |
||||
) => { |
||||
if ((value == null || value == '') && value !== 0) { |
||||
return ''; |
||||
} |
||||
return value; |
||||
}; |
||||
|
||||
export const formatter = (row: any, column: any, value: any, index: number) => { |
||||
return formatterByDist(row.scheme + '_' + column.property, value); |
||||
}; |
||||
|
||||
export const formatterByDist = (dictKey, value) => { |
||||
if (!dictKey) { |
||||
return getValue(null, '', value); |
||||
} |
||||
const mapping = dict[dictKey]; |
||||
if (mapping == null) { |
||||
return getValue(null, '', value); |
||||
} |
||||
return mapping[value] == null ? value : mapping[value]; |
||||
}; |
||||
|
||||
export const formartLink = (row) => { |
||||
const up = row.ambr.uplink; |
||||
const down = row.ambr.downlink; |
||||
return ( |
||||
down.value + dict.units[down.unit] + '/' + up.value + dict.units[up.unit] |
||||
); |
||||
}; |
||||
|
||||
export const formartApn = (row, index) => { |
||||
const session = row.slice[0].session[index]; |
||||
if (session) { |
||||
return session.name + '-' + session.qos.index; |
||||
} else { |
||||
return ''; |
||||
} |
||||
}; |
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
import { useStore } from 'vuex'; |
||||
import { warning } from '@/config/element'; |
||||
const store = useStore(); |
||||
export const fddFreqs = [ |
||||
[ |
||||
//B38/41
|
||||
[37750, 38249], |
||||
[39650, 41589], |
||||
], |
||||
[ |
||||
//B39/34
|
||||
[38250, 38649], |
||||
[36200, 36349], |
||||
], |
||||
[ |
||||
//B40
|
||||
[38650, 39649], |
||||
], |
||||
]; |
||||
|
||||
export function repeat(arr) { |
||||
arr = JSON.parse(JSON.stringify(arr)); |
||||
arr.sort(); |
||||
for (let i = 1; i < arr.length; i++) { |
||||
if (arr[i - 1] == arr[i]) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
export function getIndex(freq) { |
||||
freq = parseInt(freq); |
||||
for (let i = 0; i < fddFreqs.length; i++) { |
||||
for (let j = 0; j < fddFreqs[i].length; j++) { |
||||
const arr = fddFreqs[i][j]; |
||||
if (arr[0] <= freq && freq <= arr[1]) { |
||||
return i; |
||||
} |
||||
} |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
export function getUsedFreqs(device) { |
||||
const arr: number[] = []; |
||||
store.state.devCode.forEach((item) => { |
||||
if (item.deviceId != device.deviceId && item.sModType == 'TDD') { |
||||
const freqs = [item.devFreq].concat(item.defaultFreq.split(',')); |
||||
for (let i = 0; i < freqs.length; i++) { |
||||
const freq = freqs[i]; |
||||
const index = getIndex(freq); |
||||
if (index > -1) { |
||||
arr.push(index); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
return arr; |
||||
} |
||||
|
||||
export function enable(device, freq) { |
||||
let enable = false; |
||||
freq = parseInt(freq); |
||||
const freqs = device.uarfcnband.split(','); |
||||
for (let i = 0; i < freqs.length; i++) { |
||||
const arr = freqs[i].split('-'); |
||||
if (freq >= parseInt(arr[0]) && freq <= parseInt(arr[1])) { |
||||
enable = true; |
||||
break; |
||||
} |
||||
} |
||||
return enable; |
||||
} |
||||
|
||||
export function checkFreq(device, freq) { |
||||
if (!enable(device, freq)) { |
||||
warning('配置的频点不在可配范围内,请确认后重新配置'); |
||||
return false; |
||||
} |
||||
if (device.sModType != 'TDD') { |
||||
return true; |
||||
} |
||||
const arr = getUsedFreqs(device); |
||||
const index = getIndex(freq); |
||||
if (index > -1 && arr.indexOf(index) > -1) { |
||||
warning('频点不能配置在其他TDD模块已配置频段内'); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
export function checkFreqs(device, freqs) { |
||||
if (repeat(freqs)) { |
||||
warning('轮询频点不可重复,请确认后重新配置'); |
||||
return false; |
||||
} |
||||
const arr = getUsedFreqs(device); |
||||
for (let i = 0; i < freqs.length; i++) { |
||||
if (!enable(device, freqs[i])) { |
||||
warning('第' + (i + 1) + '个轮询频点不在可配范围内,请确认后重新配置'); |
||||
return false; |
||||
} |
||||
const index = getIndex(freqs[i]); |
||||
if (index > -1 && arr.indexOf(index) > -1) { |
||||
warning('第' + (i + 1) + '个轮询频点不能配置在其他TDD模块已配置频段内'); |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
export function checkCycle(device) { |
||||
if (device.freqSwitchCycle < 10 || device.freqSwitchCycle > 120) { |
||||
warning('轮询周期必须为10-120之间的整数!'); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
export * from "./device"; |
||||
export * from "./user"; |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
export const validatePass = (rule: any, value: any, callback: any) => { |
||||
if (value === '') { |
||||
callback(new Error('请输入密码')); |
||||
} else { |
||||
const pwtest = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&_])[A-Za-z\d$@$!%*?&_]{6,}$/; |
||||
if (pwtest.test(value) == false) { |
||||
callback(new Error('密码格式有误')); |
||||
} |
||||
callback(); |
||||
} |
||||
}; |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; |
||||
import Login from '../views/main/login.vue'; |
||||
|
||||
const routes: Array<RouteRecordRaw> = [ |
||||
{ |
||||
path: '/', |
||||
redirect: '/cube', |
||||
}, |
||||
{ |
||||
path: '/login', |
||||
name: 'login', |
||||
component: () => import('../views/main/login.vue') |
||||
}, |
||||
{ |
||||
path: '/cube', |
||||
name: 'user', |
||||
component: () => import('../views/admin/cube.vue') |
||||
} |
||||
]; |
||||
|
||||
const router = createRouter({ |
||||
history: createWebHashHistory(), |
||||
routes, |
||||
}); |
||||
|
||||
export default router; |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
/* eslint-disable */ |
||||
declare module '*.vue' { |
||||
import type { DefineComponent } from 'vue' |
||||
const component: DefineComponent<{}, {}, any> |
||||
export default component |
||||
} |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
import axios from 'axios'; |
||||
import { publik } from '@/config/apiUrl'; |
||||
import router from '@/router'; |
||||
import {post, get, put, delate} from '@/config/axios'; |
||||
|
||||
export default { |
||||
getWebConfig({ commit }) { |
||||
axios.get(publik.webConfig).then((rsp) => { |
||||
commit('updateState', { prop: 'webConf', value: rsp.data }); |
||||
document.title = rsp.data.web_title; |
||||
}); |
||||
}, |
||||
logout({ commit }) { |
||||
put(publik.logout).then((rsp) => { |
||||
commit('updateState', { prop: 'user', value: {} }); |
||||
router.push('/'); |
||||
}); |
||||
}, |
||||
}; |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
import { createStore } from 'vuex'; |
||||
import state from './state'; |
||||
import mutations from './mutations'; |
||||
import actions from './actions'; |
||||
|
||||
export default createStore({ |
||||
state, |
||||
getters: {}, |
||||
mutations, |
||||
actions, |
||||
modules: {}, |
||||
}); |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
import { menu, homes } from '@/config/menu'; |
||||
import router from "@/router"; |
||||
|
||||
export default { |
||||
updateState(state, param) { |
||||
state[param.prop] = param.value; |
||||
}, |
||||
initSizes(state) { |
||||
const height = window.innerHeight; |
||||
const width = window.innerWidth; |
||||
state.widescreen = width > 1800; |
||||
const loginHeight = 350; |
||||
const loginWidth = 450; |
||||
const navHead = state.showHeader ? 60 : 0; |
||||
const aside = state.showAside ? 250 : 0; |
||||
const map = { |
||||
medium: 50, |
||||
small: 42, |
||||
mini: 35, |
||||
}; |
||||
const head = 40; |
||||
const pd = 15; |
||||
const page = 37; |
||||
state.sizes.height = height; |
||||
state.sizes.width = width; |
||||
state.sizes.fullHeight = height + 'px'; |
||||
state.sizes.loginHeight = loginHeight + 'px'; |
||||
state.sizes.loginWidth = loginWidth + 'px'; |
||||
state.sizes.loginTop = (height - loginHeight) * 0.4 + 'px'; |
||||
state.sizes.loginLeft = (width - loginWidth) * 0.49 + 'px'; |
||||
state.sizes.headHeight = navHead + 'px'; |
||||
state.sizes.asideWidth = aside + 'px'; |
||||
state.sizes.mainHead = head + 'px'; |
||||
state.sizes.mainHeight = height - navHead + 'px'; |
||||
state.sizes.tableHei = height - navHead - head - pd; |
||||
state.sizes.pTableHei = state.sizes.tableHei - page; |
||||
}, |
||||
setUserInfo(state, user) { |
||||
state.user = user; |
||||
const home = homes[user.type]; |
||||
state.showHeader = home.show; |
||||
state.showAside = home.show; |
||||
router.push(home.path); |
||||
state.curPath = home.path; |
||||
state.menu = JSON.parse(JSON.stringify(menu[user.type])); |
||||
}, |
||||
}; |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
export default { |
||||
sizes: {}, |
||||
webConf: {}, |
||||
showHeader: false, |
||||
showAside: false, |
||||
widescreen: true, |
||||
size: 'large', |
||||
user: {}, |
||||
menu: [], |
||||
curPath: '', |
||||
unread: { |
||||
msg: 0, |
||||
call: 0, |
||||
}, |
||||
}; |
@ -0,0 +1,139 @@
@@ -0,0 +1,139 @@
|
||||
<template> |
||||
<div id="cube"> |
||||
<SearchRow :example="example" @query="queryCube" :query="false" :del="false" title="数据立方" |
||||
@add="param = {}; flag.add = true;"></SearchRow> |
||||
<ListTable :data="list" :props="props" :actionWidth="220" :height="state.sizes.height - 90"> |
||||
<template #default="{ row: { name, status } }"> |
||||
<el-button type="primary" @click="showDetail(name)">管理</el-button> |
||||
<el-button v-if="status > 0" type="primary" @click="select(name)">查询</el-button> |
||||
<el-button type="danger" @click="delCube(name)">删除</el-button> |
||||
</template> |
||||
</ListTable> |
||||
</div> |
||||
|
||||
<el-dialog :title="selectTitle" v-model="flag.select" :close-on-click-modal="false" width="95%" top="2%"> |
||||
<CubeData :cube="cube"></CubeData> |
||||
</el-dialog> |
||||
|
||||
<el-dialog title="新增立方体" v-model="flag.add" width="50%" :close-on-click-modal="false"> |
||||
<ModifyForm :param="param" :rules="rules" @confirm="addCube" @cancel="flag.add = false" :width="100"> |
||||
<el-form-item label="立方体表名" prop="name"> |
||||
<el-input type="text" v-model="param.name" auto-complete="off" :maxlength="100"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="数据源表名" prop="source"> |
||||
<el-input type="text" v-model="param.source" auto-complete="off" :maxlength="100"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="源表时间字段" prop="sourceTimer"> |
||||
<el-input type="text" v-model="param.sourceTimer" auto-complete="off" :maxlength="64"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="备注(名称)" prop="remark"> |
||||
<el-input type="text" v-model="param.remark" auto-complete="off" :maxlength="64"></el-input> |
||||
</el-form-item> |
||||
</ModifyForm> |
||||
</el-dialog> |
||||
|
||||
<el-dialog title="立方体管理" v-model="flag.detail" width="98%" top="1%" :close-on-click-modal="false"> |
||||
<CubeDetail :name="cubeName" @fresh="select(cube.name)"></CubeDetail> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref } from "vue"; |
||||
import { cube as urls } from "@/config/apiUrl"; |
||||
import { ListTable, SearchRow, ModifyForm } from "@/components/template"; |
||||
import { confirm } from "@/config/element"; |
||||
import CubeData from "./cubeData.vue"; |
||||
import CubeDetail from "./cubeDetail.vue" |
||||
|
||||
const { state, commit, dispatch } = useStore(); |
||||
|
||||
const example = reactive<any>({ |
||||
}) |
||||
const list = ref<any>([]); |
||||
const flag = reactive({ |
||||
select: false, |
||||
add: false, |
||||
detail: false |
||||
}); |
||||
const selectTitle = ref<string>(""); |
||||
const cube = ref<any>({}); |
||||
const cubeName = ref<any>({}); |
||||
const param = ref<any>({}); |
||||
const rules = reactive({ |
||||
|
||||
}) |
||||
|
||||
const props = [ |
||||
{ code: 'name', name: '立方体表名', width: '180' }, |
||||
{ code: 'source', name: '数据源表', width: '180' }, |
||||
{ code: 'remark', name: '备注', width: '150' }, |
||||
{ code: 'status', name: '状态', width: '100' }, |
||||
{ code: 'total', name: '数据量', width: '120' }, |
||||
{ code: 'createTime', name: '创建时间', width: '170' }, |
||||
{ code: 'assembleTime', name: '最近构建成功时间', width: '170' }, |
||||
] |
||||
|
||||
const queryCube = () => { |
||||
get(urls.root, example).then(rsp => { |
||||
if (rsp) list.value = rsp; |
||||
}) |
||||
} |
||||
|
||||
const addCube = () => { |
||||
post(urls.root, param.value).then(rsp => { |
||||
rsp && queryCube; |
||||
}) |
||||
} |
||||
|
||||
const showDetail = name => { |
||||
cubeName.value = name; |
||||
flag.detail = true; |
||||
} |
||||
|
||||
const select = name => { |
||||
if (name == cube.value.name) { |
||||
flag.select = true; |
||||
return; |
||||
} |
||||
get(urls.root + '/' + name).then((rsp: any) => { |
||||
if (rsp) { |
||||
rsp.title = '立方体:' + rsp.name; |
||||
if (rsp.remark) rsp.title += ('(' + rsp.remark + ')'); |
||||
rsp.title += ' 数据查询'; |
||||
cube.value = rsp; |
||||
cube.value.page = 1; |
||||
cube.value.size = 20; |
||||
flag.select = true; |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const delCube = name => { |
||||
confirm('确认要删除立方体' + name + '吗?', '提示').then(() => { |
||||
delate(urls.root, name).then(rsp => { |
||||
rsp && queryCube() |
||||
}) |
||||
}); |
||||
} |
||||
|
||||
onMounted(() => { |
||||
queryCube(); |
||||
}); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
#cube { |
||||
padding: 20px; |
||||
} |
||||
|
||||
.cell-item { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.cube-item { |
||||
margin: 20px 50px; |
||||
} |
||||
</style> |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
<template> |
||||
<SearchRow :del="false" :add="false" :fresh="false" title="选择维度:" @query="selectData"> |
||||
<template v-slot:left> |
||||
<el-checkbox v-for="item in cube.dimensions" size="default" v-model="item.checked" :label="item.remark" /> |
||||
</template> |
||||
<template #default> |
||||
<el-input v-for="item in cube.dimensions" v-show="item.checked" v-model="item.value" class="query-medium" |
||||
clearable :placeholder="'输入' + item.remark" /> |
||||
</template> |
||||
</SearchRow> |
||||
<ListTable :example="cube" :data="data" :props="props" :height="state.sizes.height - 290" page @query="selectData"> |
||||
</ListTable> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { cube as urls } from "@/config/apiUrl"; |
||||
import { reactive, onMounted, ref, watch } from "vue"; |
||||
import { ListTable, SearchRow } from "@/components/template"; |
||||
|
||||
const { state, commit, dispatch } = useStore(); |
||||
const prop = defineProps({ |
||||
cube: { |
||||
type: Object, |
||||
default: {}, |
||||
} |
||||
}); |
||||
const data = ref<any>({ |
||||
total: 0, |
||||
data: [] |
||||
}); |
||||
const props = ref<any>([]); |
||||
|
||||
const selectData = () => { |
||||
init(prop.cube); |
||||
put(urls.select, prop.cube).then(rsp => { |
||||
data.value = rsp ? rsp : []; |
||||
}) |
||||
} |
||||
const init = (cube) => { |
||||
props.value = []; |
||||
if (cube && cube.dimensions) { |
||||
cube.dimensions.forEach(i => { |
||||
if (i.checked) |
||||
props.value.push({ code: i.column, name: i.remark, width: 150 }); |
||||
}); |
||||
} |
||||
if (cube && cube.data) { |
||||
cube.data.forEach(i => { |
||||
props.value.push({ code: i.column, name: i.remark, width: 150 }); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
// watch(() => prop.cube, (n, o) => { |
||||
// init(n); |
||||
// }); |
||||
|
||||
onMounted(() => { |
||||
init(prop.cube); |
||||
}); |
||||
|
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
</style> |
@ -0,0 +1,150 @@
@@ -0,0 +1,150 @@
|
||||
<template> |
||||
<el-descriptions class="margin-top" title="" :column="3" size="large" border> |
||||
<template #title> |
||||
<el-button type="warning" @click="initCube(name)">初始化</el-button> |
||||
<el-button :disabled="cube.status == 0" type="primary" @click="assemble(name)">构建</el-button> |
||||
<el-button type="primary" icon="Plus" @click="showAdd(0)"> 添加维度 </el-button> |
||||
<el-button type="primary" icon="Plus" @click="showAdd(1)"> 添加统计 </el-button> |
||||
</template> |
||||
<el-descriptions-item label="立方体表名">{{ cube.name }}</el-descriptions-item> |
||||
<el-descriptions-item label="数据源表名">{{ cube.source }}</el-descriptions-item> |
||||
<el-descriptions-item label="数据源时间字段">{{ cube.sourceTimer }}</el-descriptions-item> |
||||
<el-descriptions-item label="创建时间">{{ cube.createTime }}</el-descriptions-item> |
||||
<el-descriptions-item label="状态">{{ formatterByDist('cube_status', cube.status) }}</el-descriptions-item> |
||||
<el-descriptions-item label="数据量">{{ cube.total }}</el-descriptions-item> |
||||
<el-descriptions-item label="最近一次构建成功时间">{{ cube.assembleTime }}</el-descriptions-item> |
||||
</el-descriptions> <br> |
||||
<h2>维度信息</h2> |
||||
<ListTable :data="cube.dimensions" :props="props" :actionWidth="220" :height="state.sizes.height - 600"> |
||||
<template #default="{ row }"> |
||||
<!-- <el-button type="primary" @click="modify(row)">修改</el-button> --> |
||||
<el-button type="danger" @click="delDimension(row)">删除</el-button> |
||||
</template> |
||||
</ListTable> |
||||
<h2>统计信息</h2> |
||||
<ListTable :data="cube.data" :props="props" :actionWidth="220" :height="150"> |
||||
<template #default="{ row }"> |
||||
<!-- <el-button type="primary" @click="modify(row)">修改</el-button> --> |
||||
<el-button type="danger" @click="delDimension(row)">删除</el-button> |
||||
</template> |
||||
</ListTable> |
||||
|
||||
<el-dialog title="新增维度" v-model="flag.add" width="50%" :close-on-click-modal="false"> |
||||
<ModifyForm :param="dimension" :rules="rules" @confirm="addDimension" @cancel="flag.add = false" :width="100"> |
||||
<el-form-item label="维度表字段" prop="column"> |
||||
<el-input type="text" v-model="dimension.column" auto-complete="off" :maxlength="64"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="维度数据源" prop="dimesion"> |
||||
<el-input type="text" v-model="dimension.dimesion" auto-complete="off" :maxlength="255"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="备注(名称)" prop="remark"> |
||||
<el-input type="text" v-model="dimension.remark" auto-complete="off" :maxlength="64"></el-input> |
||||
</el-form-item> |
||||
<el-form-item label="字段数据类型" prop="dataType"> |
||||
<el-select v-model="dimension.dataType"> |
||||
<el-option v-for="(val, key, i) in dimension_dataType" :label="val" :value="Number(key)" |
||||
:key="key"></el-option> |
||||
</el-select> |
||||
</el-form-item> |
||||
<el-form-item v-if="dimension.dataType == 0" label="字段长度" prop="length"> |
||||
<el-input type="number" v-model="dimension.length" auto-complete="off" :max="255"></el-input> |
||||
</el-form-item> |
||||
</ModifyForm> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { useStore } from "vuex"; |
||||
import { cube as urls } from "@/config/apiUrl"; |
||||
import { get, post, put, delate } from "@/config/axios"; |
||||
import { reactive, onMounted, ref, watch } from "vue"; |
||||
import { confirm } from "@/config/element"; |
||||
import { formatterByDist } from "@/plugs/formatter"; |
||||
import { dimension_dataType } from "@/plugs/dict"; |
||||
import { ListTable, ModifyForm } from "@/components/template"; |
||||
|
||||
|
||||
const { state, commit, dispatch } = useStore(); |
||||
const prop = defineProps({ |
||||
name: { |
||||
type: String, |
||||
default: null, |
||||
} |
||||
}); |
||||
const cube = ref<any>({}); |
||||
const dimension = ref<any>({}); |
||||
const flag = reactive({ |
||||
add: false |
||||
}) |
||||
const rules = [ |
||||
|
||||
] |
||||
const props = [ |
||||
{ code: "column", name: '维度表字段', width: 150 }, |
||||
{ code: "dimesion", name: '维度数据源', width: 150 }, |
||||
{ code: "remark", name: '备注', width: 150 }, |
||||
{ code: "dataType", name: '数据类型', width: 120 }, |
||||
{ code: "length", name: '字段长度', width: 100 }, |
||||
] |
||||
|
||||
const getCube = () => { |
||||
get(urls.root + '/' + prop.name).then((rsp: any) => { |
||||
if (rsp) { |
||||
cube.value = rsp; |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const showAdd = type => { |
||||
dimension.value = { cubeName: cube.value.name, type: type }; |
||||
flag.add = true; |
||||
} |
||||
|
||||
const addDimension = () => { |
||||
post(urls.dimension, dimension.value).then(rsp => { |
||||
if (rsp) { |
||||
getCube(); |
||||
flag.add = false; |
||||
} |
||||
}) |
||||
} |
||||
|
||||
const modify = row => { } |
||||
|
||||
const delDimension = row => { |
||||
confirm('初始化后的立方体删除维度后需重新初始化,是否确认删除?', '提示').then(() => { |
||||
delate(urls.dimension, row).then(rsp => { |
||||
rsp && getCube() |
||||
}) |
||||
}); |
||||
} |
||||
|
||||
const initCube = name => { |
||||
confirm('初始化操作将清空立方体数据,请确认是否继续?', '提示').then(() => { |
||||
put(urls.init, name).then(rsp => { |
||||
if (rsp) { |
||||
getCube(); |
||||
} |
||||
}) |
||||
}); |
||||
} |
||||
|
||||
const assemble = name => { |
||||
confirm('是否确认提交构建任务?', '提示').then(() => { |
||||
post(urls.assemble, name).then(rsp => { |
||||
rsp && getCube() |
||||
}) |
||||
}); |
||||
} |
||||
|
||||
watch(() => prop.name, (n, o) => { |
||||
getCube(); |
||||
}) |
||||
|
||||
onMounted(() => { |
||||
getCube(); |
||||
}); |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
//@import url(); 引入公共css类 |
||||
</style> |
@ -0,0 +1,129 @@
@@ -0,0 +1,129 @@
|
||||
<template> |
||||
<el-row class="content"> |
||||
<el-col :span="state.widescreen ? 4 : 6" :offset="state.widescreen ? 10 : 9"> |
||||
<el-form :model="user" :rules="rules" ref="loginform" label-width="0px" :style="{ |
||||
'height': state.sizes.loginHeight, |
||||
'margin-top': state.sizes.loginTop |
||||
}" size="large"> |
||||
<!-- <img v-if="$store.state.webConf.login_form_logo_show" class="logo" src="../../assets/img/logo.png"> --> |
||||
<el-form-item> |
||||
<div class="lte-title"> {{ state.webConf.web_title }} </div> |
||||
</el-form-item> |
||||
<el-form-item prop="userId"> |
||||
<el-input v-model="user.userId" placeholder="用户名"></el-input> |
||||
</el-form-item> |
||||
<el-form-item prop="password"> |
||||
<el-input v-model="user.password" type="password" placeholder="密码"></el-input> |
||||
</el-form-item> |
||||
<el-form-item> |
||||
<el-button @click="login(loginform)">登 录</el-button> |
||||
</el-form-item> |
||||
</el-form> |
||||
</el-col> |
||||
</el-row> |
||||
</template> |
||||
|
||||
<script lang="ts" setup> |
||||
import { onBeforeMount, reactive, ref } from "vue"; |
||||
import { useStore } from "vuex"; |
||||
|
||||
import type { FormInstance } from "element-plus"; |
||||
import md5 from "js-md5"; |
||||
import { post } from "@/config/axios"; |
||||
import { publik } from "@/config/apiUrl"; |
||||
|
||||
const { state, commit } = useStore(); |
||||
|
||||
const loginform = ref<FormInstance>(); |
||||
const user = reactive({ |
||||
userId: "", |
||||
password: "", |
||||
}); |
||||
const rules = reactive({ |
||||
userId: [ |
||||
{ required: true, message: "请输入用户名", trigger: "blur" }, |
||||
{ min: 3, max: 8, message: "请输入3-8位的用户名", trigger: "blur" }, |
||||
], |
||||
password: [ |
||||
{ required: true, message: "请输入密码", trigger: "change" }, |
||||
{ min: 6, max: 16, message: "请输入6-16位的密码", trigger: "blur" }, |
||||
], |
||||
}); |
||||
const homes = ["user", "case", "task"]; |
||||
|
||||
onBeforeMount(() => { |
||||
commit("updateState", { prop: "showHeader", value: false }); |
||||
commit("updateState", { prop: "showAside", value: false }); |
||||
commit("initSizes"); |
||||
}); |
||||
|
||||
const login = async (formEl: FormInstance | undefined) => { |
||||
if (!formEl) return; |
||||
await formEl.validate((valid, fields) => { |
||||
if (valid) { |
||||
const param = JSON.parse(JSON.stringify(user)); |
||||
param.password = md5(param.password); |
||||
post(publik.login, param).then((response: any) => { |
||||
if (response) { |
||||
commit("setUserInfo", response); |
||||
commit("initSizes"); |
||||
// router.push(homes[response.type]); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
}; |
||||
</script> |
||||
<style lang='scss' scoped> |
||||
//@import url(); 引入公共css类 |
||||
.el-row.content { |
||||
padding: 16px; |
||||
z-index: 2; |
||||
} |
||||
|
||||
.lte-title { |
||||
font-size: 1.5em; |
||||
font-weight: bold; |
||||
text-align: center; |
||||
width: 100%; |
||||
color: rgb(49, 89, 143); |
||||
} |
||||
|
||||
.el-input { |
||||
margin: 12px 0; |
||||
} |
||||
|
||||
.el-button { |
||||
width: 100%; |
||||
} |
||||
|
||||
.el-row { |
||||
margin-bottom: 20px; |
||||
|
||||
&:last-child { |
||||
margin-bottom: 0; |
||||
} |
||||
} |
||||
|
||||
.el-col { |
||||
border-radius: 4px; |
||||
} |
||||
|
||||
#home { |
||||
position: relative; |
||||
width: 100%; |
||||
height: 100%; |
||||
overflow-x: hidden; |
||||
background: rgba(255, 255, 255, 0); |
||||
} |
||||
|
||||
.canvas { |
||||
position: fixed; |
||||
z-index: -1; // background-color: black; |
||||
} |
||||
|
||||
.logo { |
||||
width: 300px; |
||||
height: 90px; |
||||
} |
||||
</style> |
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"target": "esnext", |
||||
"module": "esnext", |
||||
"strict": true, |
||||
"jsx": "preserve", |
||||
"moduleResolution": "node", |
||||
"experimentalDecorators": true, |
||||
"skipLibCheck": true, |
||||
"esModuleInterop": true, |
||||
"allowSyntheticDefaultImports": true, |
||||
"forceConsistentCasingInFileNames": true, |
||||
"useDefineForClassFields": true, |
||||
"noImplicitAny": false, |
||||
"sourceMap": true, |
||||
"baseUrl": ".", |
||||
"types": [ |
||||
"webpack-env" |
||||
], |
||||
"paths": { |
||||
"@/*": [ |
||||
"src/*" |
||||
] |
||||
}, |
||||
"lib": [ |
||||
"esnext", |
||||
"dom", |
||||
"dom.iterable", |
||||
"scripthost" |
||||
] |
||||
}, |
||||
"include": [ |
||||
"src/**/*.ts", |
||||
"src/**/*.tsx", |
||||
"src/**/*.vue", |
||||
"tests/**/*.ts", |
||||
"tests/**/*.tsx" |
||||
], |
||||
"exclude": [ |
||||
"node_modules" |
||||
] |
||||
} |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
const { defineConfig } = require('@vue/cli-service'); |
||||
module.exports = defineConfig({ |
||||
transpileDependencies: true, |
||||
devServer: { |
||||
proxy: { |
||||
'/api': { |
||||
target: process.env.VUE_APP_BASE_URL, |
||||
// 允许跨域
|
||||
changeOrigin: true, |
||||
ws: true, |
||||
pathRewrite: { |
||||
'^/api': '', |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}); |
Loading…
Reference in new issue