| 
			
	
	
	
						  
						
						
						
	
 步骤 2 : 模仿和排错 步骤 3 : 拓扑图点亮 步骤 4 : 创建子模块 步骤 5 : pom.xml 步骤 6 : application.yml 步骤 7 : TrendTradingBackTestViewApplication 步骤 8 : ViewController 步骤 9 : static 步骤 10 : header.html 步骤 11 : footer.html 步骤 12 : view.html 
					老规矩,先下载右上角的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。  
					
				首先启动 EurekaServerApplication 然后启动 TrendTradingBackTestViewApplication 注: 记得运行redis-server.exe 以启动 redis 服务器 访问地址: http://127.0.0.1:8041/ 就可以看到如图所示的视图效果了,虽然看上去很苍白。。。但是是把架子搭起来了~~ 
					在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。 
 
					
				模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。 采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。 推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。 这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来 这里提供了绿色安装和使用教程:diffmerge 下载和使用教程 
					创建子模块: trend-trading-backtest-view
					 
					
				
					增加 spring-boot-starter-thymeleaf jar, 用于对 thymeleaf 进行支持
					 
					
				<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>cn.how2j.trend</groupId>
        <artifactId>trendParentProject</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>trend-trading-backtest-view</artifactId>
    <dependencies>
        <!-- springboot web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>
 
								
										
									
								
							
					增加 thymeleaf 的配置
					 
					
				eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: trend-trading-backtest-view
  thymeleaf:
    mode: LEGACYHTML5
    encoding: UTF-8
    content-type: text/html
    cache: false
 
									
								eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
spring:
  application:
    name: trend-trading-backtest-view
  thymeleaf:
    mode: LEGACYHTML5
    encoding: UTF-8
    content-type: text/html
    cache: false
								
								
					启动类
					 
					
				package cn.how2j.trend;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NetUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil; 
@SpringBootApplication
@EnableEurekaClient
public class TrendTradingBackTestViewApplication {
    public static void main(String[] args) {
        int port = 0;
        int defaultPort = 8041;
        int eurekaServerPort = 8761;
        if(NetUtil.isUsableLocalPort(eurekaServerPort)) {
        	System.err.printf("检查到端口%d 未启用,判断 eureka 服务器没有启动,本服务无法使用,故退出%n", eurekaServerPort );
        	System.exit(1);
        }
        if(null!=args && 0!=args.length) {
        	for (String arg : args) {
				if(arg.startsWith("port=")) {
					String strPort= StrUtil.subAfter(arg, "port=", true);
					if(NumberUtil.isNumber(strPort)) {
						port = Convert.toInt(strPort);
					}
				}
			}
        }        
        
        if(0==port) {
            Future<Integer> future = ThreadUtil.execAsync(() ->{
                int p = 0;
                System.out.printf("请于5秒钟内输入端口号, 推荐  %d ,超过5秒将默认使用 %d ",defaultPort,defaultPort);
                Scanner scanner = new Scanner(System.in);
                while(true) {
                    String strPort = scanner.nextLine();
                    if(!NumberUtil.isInteger(strPort)) {
                        System.err.println("只能是数字");
                        continue;
                    }
                    else {
                        p = Convert.toInt(strPort);
                        scanner.close();
                        break;
                    }
                }
                return p;
        });
        try{
            port=future.get(5,TimeUnit.SECONDS);
        }
        catch (InterruptedException | ExecutionException | TimeoutException e){
            port = defaultPort;
        }        	
        }
        
        if(!NetUtil.isUsableLocalPort(port)) {
            System.err.printf("端口%d被占用了,无法启动%n", port );
            System.exit(1);
        }
        new SpringApplicationBuilder(TrendTradingBackTestViewApplication.class).properties("server.port=" + port).run(args);
    	
    }
}
 
								
										
									
								
							
					控制类
					 
					
				package cn.how2j.trend.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
 
@Controller
public class ViewController {
	@GetMapping("/")
	public String view() throws Exception {
		return "view";
	}
}
 
									
								package cn.how2j.trend.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
 
@Controller
public class ViewController {
	@GetMapping("/")
	public String view() throws Exception {
		return "view";
	}
}
								
								
					增加静态资源,一大堆 js 和 css. 放在右上角 static里了。解压 复制到 resources 目录下去 
					
				简单说下: 1. bootstrap 就是 bootstrap了。。。 2. bootstrap-datepicker 是日期控件 3. axois 是做 ajax的 4. chartjs 是画图表的 5. jquery 就是 jquery 了 6. vue 就是 vue 了。。。 
					这是个被包含的文件,放在 如图所示的位置。 
					
				简单说下做了什么: 1. 包含了各种 css 2. 包含了各种 js 3. 自定义了几个函数: formatMoney 格式化金额 formatNumber 格式化数字 4. 创建几个过滤器方便 vue 来使用 formatMoneyFilter 格式化金额 formatNumberFilter 格式化数字 formatDateFilter 格式化日期 <template th:fragment="html(title)" > 
  <link href="css/bootstrap/3.3.6/bootstrap.min.css" rel="stylesheet">
  <link href="css/bootstrap-datepicker/1.8.0/bootstrap-datepicker.min.css" rel="stylesheet">
  <script src="js/jquery/2.0.0/jquery.min.js"></script>
  <script src="js/bootstrap/3.3.6/bootstrap.min.js"></script>
  <script src="js/vue/2.5.16/vue.min.js"></script>
  <script src="js/chartjs/2.8.0/chart.min.js"></script>
  <script src="js/axios/0.17.1/axios.min.js"></script>
  <script src="js/bootstrap-datepicker/1.8.0/bootstrap-datepicker.min.js"></script>
  <script src="js/bootstrap-datepicker/1.8.0/bootstrap-datepicker.zh-CN.min.js"></script>
  
  <script>
    function formatMoney(num){
        num = new String(num);
        num = num.toString().replace(/\$|\,/g,'');
        if(isNaN(num))
            num = "0";
        sign = (num == (num = Math.abs(num)));
        num = Math.floor(num*100+0.50000000001);
        cents = num%100;
        num = Math.floor(num/100).toString();
        if(cents<10)
            cents = "0" + cents;
        for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
            num = num.substring(0,num.length-(4*i+3))+','+
                num.substring(num.length-(4*i+3));
        return (((sign)?'':'-') + num + '.' + cents);
    }
    
    function formatNumber(number, decimals, dec_point, thousands_sep) {
        /*
        * 参数说明:
        * number:要格式化的数字
        * decimals:保留几位小数
        * dec_point:小数点符号
        * thousands_sep:千分位符号
        * */
        number = (number + '').replace(/[^0-9+-Ee.]/g, '');
        var n = !isFinite(+number) ? 0 : +number,
            prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
            sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
            dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
            s = '',
            toFixedFix = function (n, prec) {
                var k = Math.pow(10, prec);
                return '' + Math.ceil(n * k) / k;
            };
     
        s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
        var re = /(-?\d+)(\d{3})/;
        while (re.test(s[0])) {
            s[0] = s[0].replace(re, "$1" + sep + "$2");
        }
     
        if ((s[1] || '').length < prec) {
            s[1] = s[1] || '';
            s[1] += new Array(prec - s[1].length + 1).join('0');
        }
        return s.join(dec);
    }    
    
    Vue.filter("formatMoneyFilter", function(value){
        return formatMoney(value);
    });
    Vue.filter("formatNumberFilter", function(value,decimal){
    	if(decimal==null)
    		decimal = 0;
    	
        return formatNumber(value,decimal, ".",",");
    });
    Vue.filter('formatDateFilter', function (value, formatString) {
        if(null==value)
            return "";
        formatString = formatString || 'YYYY-MM-DD';
        return moment(value).format(formatString);
    });
	</script>	
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 	
	<meta name="viewport"
	content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
	<meta name="apple-mobile-web-app-capable" content="yes">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title th:text="${title}" ></title>
</template>
 
								
										
									
								
							
					页脚
					 
					
				<div th:fragment="html">
    <div style="margin:0px auto;text-align:center">
    	powered by <a href="http://how2j.cn">how2j.cn</a> version 1.0.1 
    </div> 
</div>
 
									
								<div th:fragment="html">
    <div style="margin:0px auto;text-align:center">
    	powered by <a href="http://how2j.cn">how2j.cn</a> version 1.0.1 
    </div> 
</div>
								
								
					业务页面,做了如下几件事情。 
					
				1. 在开始包含了 header.html 2. 在结束包含了 footer.html 3. 做了 vue 的框架搭建,虽然什么都没做,先放在这里了,后面直接在这个基础上修改就是了 4. 有一些当前页面的简单 样式 5. 工作区div: workingArea 6. 本页唯一的内容,一个span: <span class="label label-info">回测参数</span> <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:include="include/header::html('趋势投资模拟回测')" ></head>
<body >
<script>
    $(function(){
	        var data4Vue = {
			};
            //ViewModel
            var vue = new Vue({
                el: '#workingArea',
                data: data4Vue,
                mounted:function(){ //mounted 表示这个 Vue 对象加载成功了
                    this.init();
                },
                methods: {
                    init:function(){
                    }
                }
            });
    });
    
</script>
<style>
table.inputTable{
	width:100%;
}
table.inputTable td{
	padding:20px 20px;
}
table{
	margin:20px;
}
div#workingArea{
	margin:50px;
}
</style>
<div id="workingArea">
	        <span class="label label-info">回测参数</span>
</div>
<div th:replace="include/footer::html" ></div>
</body>
</html>
 
								
										
									
								
							
				HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。
			 
			 
			
			
			
			
			
		
			
			提问之前请登陆
			
		 
		提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢	
	 
 |