feat(性能测试): 版本对比 (#8993)
* feat(性能测试): 版本对比 --user=郭雨琦 版本对比 Co-authored-by: guoyuqi <xiaomeinvG@126.com>
This commit is contained in:
parent
9fe0c329ee
commit
74bca78188
|
@ -156,6 +156,7 @@
|
||||||
projectId() {
|
projectId() {
|
||||||
return getCurrentProjectID();
|
return getCurrentProjectID();
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
<template>
|
||||||
|
<div class="compare-class">
|
||||||
|
<el-card style="width: 50%;" ref="old">
|
||||||
|
<p>1</p>
|
||||||
|
<span>v1</span>
|
||||||
|
<h4>1</h4>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form :inline="true">
|
||||||
|
<el-form-item :label="$t('load_test.name') ">
|
||||||
|
<el-input :disabled="true" :placeholder="$t('load_test.input_name')" v-model="oldData.name"
|
||||||
|
class="input-with-select"
|
||||||
|
size="small"
|
||||||
|
maxlength="30" show-word-limit/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-tooltip :content="$t('commons.follow')" placement="bottom" effect="dark" v-if="!showFollow">
|
||||||
|
<i class="el-icon-star-off" style="color: #783987; font-size: 25px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; " />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip :content="$t('commons.cancel')" placement="bottom" effect="dark" v-if="showFollow">
|
||||||
|
<i class="el-icon-star-on" style="color: #783987; font-size: 28px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; "/>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-tabs v-model="active" @tab-click="clickTab">
|
||||||
|
<el-tab-pane :label="$t('load_test.basic_config')" class="advanced-config">
|
||||||
|
<performance-basic-config :is-read-only="true" :test="oldData" @fileChange="fileChange" ref="basicConfig" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="$t('load_test.pressure_config')" class="advanced-config">
|
||||||
|
<performance-pressure-config :is-read-only="true" :test="oldData" :test-id="oldData.id" @fileChange="fileChange" ref="pressureConfig"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
|
||||||
|
<performance-advanced-config :read-only="true" :test-id="oldData.id" ref="advancedConfig"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-card>
|
||||||
|
<el-card style="width: 50%;" ref="new">
|
||||||
|
<p>v1</p>
|
||||||
|
<span>1</span>
|
||||||
|
<h4>2</h4>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form :inline="true">
|
||||||
|
<el-form-item :label="$t('load_test.name') ">
|
||||||
|
<el-input :disabled="true" :placeholder="$t('load_test.input_name')" v-model="newData.name"
|
||||||
|
class="input-with-select"
|
||||||
|
size="small"
|
||||||
|
maxlength="30" show-word-limit/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-tooltip :content="$t('commons.follow')" placement="bottom" effect="dark" v-if="!newShowFollow">
|
||||||
|
<i class="el-icon-star-off" style="color: #783987; font-size: 25px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; " />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip :content="$t('commons.cancel')" placement="bottom" effect="dark" v-if="newShowFollow">
|
||||||
|
<i class="el-icon-star-on" style="color: #783987; font-size: 28px; margin-right: 15px;cursor: pointer;position: relative; top: 5px; "/>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-tabs v-model="active" @tab-click="clickTab">
|
||||||
|
<el-tab-pane :label="$t('load_test.basic_config')" class="advanced-config">
|
||||||
|
<performance-basic-config :is-read-only="true" :test="newData" @fileChange="fileNewChange" ref="newBasicConfig" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="$t('load_test.pressure_config')" class="advanced-config">
|
||||||
|
<performance-pressure-config :is-read-only="true" :test="newData" :test-id="newData.id" @fileChange="fileNewChange" ref="newPressureConfig" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
|
||||||
|
<performance-advanced-config :read-only="true" :test-id="newData.id" ref="newAdvancedConfig" />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-card>
|
||||||
|
<button @click="getDiff"></button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import EditPerformanceTest from "@/business/components/performance/test/EditPerformanceTest";
|
||||||
|
import PerformancePressureConfig from "@/business/components/performance/test/components/PerformancePressureConfig";
|
||||||
|
import PerformanceBasicConfig from "@/business/components/performance/test/components/PerformanceBasicConfig";
|
||||||
|
import PerformanceAdvancedConfig from "@/business/components/performance/test/components/PerformanceAdvancedConfig";
|
||||||
|
import {patch} from "@/business/components/performance/v_node_diff";
|
||||||
|
import Vue from "vue";
|
||||||
|
|
||||||
|
const {diff} = require("@/business/components/performance/v_node_diff");
|
||||||
|
|
||||||
|
|
||||||
|
export default{
|
||||||
|
name:"DiffVersion",
|
||||||
|
components:{
|
||||||
|
EditPerformanceTest,
|
||||||
|
PerformancePressureConfig,
|
||||||
|
PerformanceBasicConfig,
|
||||||
|
PerformanceAdvancedConfig,
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
oldData:{
|
||||||
|
type:Object
|
||||||
|
},
|
||||||
|
newData:{
|
||||||
|
type:Object
|
||||||
|
},
|
||||||
|
showFollow:{
|
||||||
|
type:Boolean
|
||||||
|
},
|
||||||
|
newShowFollow:{
|
||||||
|
type:Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return{
|
||||||
|
active: '0',
|
||||||
|
oldDataJson:{
|
||||||
|
|
||||||
|
},
|
||||||
|
newDataJson:{
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
getDiff(){
|
||||||
|
let oldVnode = this.$refs.old
|
||||||
|
let vnode = this.$refs.new
|
||||||
|
//oldVnode.style.backgroundColor = "rgb(241,200,196)";
|
||||||
|
console.log(this.$refs.old)
|
||||||
|
console.log(this.$refs.new)
|
||||||
|
diff(oldVnode,vnode);
|
||||||
|
|
||||||
|
},
|
||||||
|
clickTab(tab) {
|
||||||
|
if (tab.index === '1') {
|
||||||
|
this.$refs.pressureConfig.calculateTotalChart();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fileChange(threadGroups) {
|
||||||
|
let handler = this.$refs.pressureConfig;
|
||||||
|
let csvSet = new Set;
|
||||||
|
threadGroups.forEach(tg => {
|
||||||
|
tg.threadNumber = tg.threadNumber || 10;
|
||||||
|
tg.duration = tg.duration || 10;
|
||||||
|
tg.durationHours = Math.floor(tg.duration / 3600);
|
||||||
|
tg.durationMinutes = Math.floor((tg.duration / 60 % 60));
|
||||||
|
tg.durationSeconds = Math.floor((tg.duration % 60));
|
||||||
|
tg.rampUpTime = tg.rampUpTime || 5;
|
||||||
|
tg.step = tg.step || 5;
|
||||||
|
tg.rpsLimit = tg.rpsLimit || 10;
|
||||||
|
tg.threadType = tg.threadType || 'DURATION';
|
||||||
|
tg.iterateNum = tg.iterateNum || 1;
|
||||||
|
tg.iterateRampUp = tg.iterateRampUp || 10;
|
||||||
|
|
||||||
|
if (tg.csvFiles) {
|
||||||
|
tg.csvFiles.map(item => csvSet.add(item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let csvFiles = [];
|
||||||
|
for (const f of csvSet) {
|
||||||
|
csvFiles.push({name: f, csvSplit: false, csvHasHeader: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$set(handler, "threadGroups", threadGroups);
|
||||||
|
|
||||||
|
this.$refs.basicConfig.threadGroups = threadGroups;
|
||||||
|
this.$refs.pressureConfig.threadGroups = threadGroups;
|
||||||
|
this.$refs.advancedConfig.csvFiles = csvFiles;
|
||||||
|
|
||||||
|
this.$refs.pressureConfig.resourcePoolChange();
|
||||||
|
handler.calculateTotalChart();
|
||||||
|
},
|
||||||
|
fileNewChange(threadGroups) {
|
||||||
|
let handler = this.$refs.newPressureConfig;
|
||||||
|
let csvSet = new Set;
|
||||||
|
threadGroups.forEach(tg => {
|
||||||
|
tg.threadNumber = tg.threadNumber || 10;
|
||||||
|
tg.duration = tg.duration || 10;
|
||||||
|
tg.durationHours = Math.floor(tg.duration / 3600);
|
||||||
|
tg.durationMinutes = Math.floor((tg.duration / 60 % 60));
|
||||||
|
tg.durationSeconds = Math.floor((tg.duration % 60));
|
||||||
|
tg.rampUpTime = tg.rampUpTime || 5;
|
||||||
|
tg.step = tg.step || 5;
|
||||||
|
tg.rpsLimit = tg.rpsLimit || 10;
|
||||||
|
tg.threadType = tg.threadType || 'DURATION';
|
||||||
|
tg.iterateNum = tg.iterateNum || 1;
|
||||||
|
tg.iterateRampUp = tg.iterateRampUp || 10;
|
||||||
|
|
||||||
|
if (tg.csvFiles) {
|
||||||
|
tg.csvFiles.map(item => csvSet.add(item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let csvFiles = [];
|
||||||
|
for (const f of csvSet) {
|
||||||
|
csvFiles.push({name: f, csvSplit: false, csvHasHeader: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$set(handler, "threadGroups", threadGroups);
|
||||||
|
|
||||||
|
this.$refs.newBasicConfig.threadGroups = threadGroups;
|
||||||
|
this.$refs.newPressureConfig.threadGroups = threadGroups;
|
||||||
|
this.$refs.newAdvancedConfig.csvFiles = csvFiles;
|
||||||
|
|
||||||
|
this.$refs.newPressureConfig.resourcePoolChange();
|
||||||
|
handler.calculateTotalChart();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.compare-class{
|
||||||
|
display: flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -76,6 +76,18 @@
|
||||||
|
|
||||||
<ms-change-history ref="changeHistory"/>
|
<ms-change-history ref="changeHistory"/>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
:fullscreen="true"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="100%"
|
||||||
|
>
|
||||||
|
<diff-version :old-data="oldData" :show-follow="showFollow" :new-data="newData" :new-show-follow="newShowFollow" ></diff-version>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible=false">取 消</el-button>
|
||||||
|
<el-button type="primary" >确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
</ms-main-container>
|
</ms-main-container>
|
||||||
</ms-container>
|
</ms-container>
|
||||||
</template>
|
</template>
|
||||||
|
@ -91,6 +103,7 @@ import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
||||||
import MsChangeHistory from "../../history/ChangeHistory";
|
import MsChangeHistory from "../../history/ChangeHistory";
|
||||||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||||
import MsTipButton from "@/business/components/common/components/MsTipButton";
|
import MsTipButton from "@/business/components/common/components/MsTipButton";
|
||||||
|
import DiffVersion from "@/business/components/performance/test/DiffVersion";
|
||||||
|
|
||||||
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
|
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
|
||||||
const versionHistory = requireComponent.keys().length > 0 ? requireComponent("./version/VersionHistory.vue") : {};
|
const versionHistory = requireComponent.keys().length > 0 ? requireComponent("./version/VersionHistory.vue") : {};
|
||||||
|
@ -108,14 +121,19 @@ export default {
|
||||||
MsMainContainer,
|
MsMainContainer,
|
||||||
MsChangeHistory,
|
MsChangeHistory,
|
||||||
'MsVersionHistory': versionHistory.default,
|
'MsVersionHistory': versionHistory.default,
|
||||||
|
DiffVersion
|
||||||
},
|
},
|
||||||
inject: [
|
inject: [
|
||||||
'reload'
|
'reload'
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
dialogVisible:false,
|
||||||
result: {},
|
result: {},
|
||||||
test: {schedule: {}, follows: []},
|
test: {schedule: {}, follows: []},
|
||||||
|
oldData: {schedule: {}, follows: []},
|
||||||
|
newData:{schedule: {}, follows: []},
|
||||||
|
newShowFollow:false,
|
||||||
savePath: "/performance/save",
|
savePath: "/performance/save",
|
||||||
editPath: "/performance/edit",
|
editPath: "/performance/edit",
|
||||||
runPath: "/performance/run",
|
runPath: "/performance/run",
|
||||||
|
@ -183,7 +201,10 @@ export default {
|
||||||
},
|
},
|
||||||
importAPITest() {
|
importAPITest() {
|
||||||
let apiTest = this.$store.state.test;
|
let apiTest = this.$store.state.test;
|
||||||
|
console.log("输出vuex的test")
|
||||||
|
console.log(apiTest)
|
||||||
if (apiTest && apiTest.name) {
|
if (apiTest && apiTest.name) {
|
||||||
|
console.log("set test name")
|
||||||
this.$set(this.test, "name", apiTest.name);
|
this.$set(this.test, "name", apiTest.name);
|
||||||
if (apiTest.jmx.scenarioId) {
|
if (apiTest.jmx.scenarioId) {
|
||||||
this.$refs.basicConfig.importScenario(apiTest.jmx.scenarioId);
|
this.$refs.basicConfig.importScenario(apiTest.jmx.scenarioId);
|
||||||
|
@ -220,7 +241,10 @@ export default {
|
||||||
this.$store.commit("clearTest");
|
this.$store.commit("clearTest");
|
||||||
} else {
|
} else {
|
||||||
let scenarioJmxs = this.$store.state.scenarioJmxs;
|
let scenarioJmxs = this.$store.state.scenarioJmxs;
|
||||||
|
console.log("输出vuex的scenarioJmxs")
|
||||||
|
console.log(scenarioJmxs)
|
||||||
if (scenarioJmxs && scenarioJmxs.name) {
|
if (scenarioJmxs && scenarioJmxs.name) {
|
||||||
|
console.log("set scenarioJmxs name")
|
||||||
this.$set(this.test, "name", scenarioJmxs.name);
|
this.$set(this.test, "name", scenarioJmxs.name);
|
||||||
let relateApiList = [];
|
let relateApiList = [];
|
||||||
if (scenarioJmxs.jmxs) {
|
if (scenarioJmxs.jmxs) {
|
||||||
|
@ -444,6 +468,7 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
fileChange(threadGroups) {
|
fileChange(threadGroups) {
|
||||||
|
console.log("zou")
|
||||||
let handler = this.$refs.pressureConfig;
|
let handler = this.$refs.pressureConfig;
|
||||||
|
|
||||||
let csvSet = new Set;
|
let csvSet = new Set;
|
||||||
|
@ -524,7 +549,27 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
compare(row) {
|
compare(row) {
|
||||||
// console.log(row);
|
this.oldData = this.test;
|
||||||
|
this.$get('/performance/get/' + row.id+"/"+this.test.refId, response => {
|
||||||
|
this.$get('/performance/get/' + response.data.id, res => {
|
||||||
|
if (res.data) {
|
||||||
|
this.newData = res.data;
|
||||||
|
this.$get('/performance/test/follow/' + response.data.id, resp => {
|
||||||
|
if(resp.data&&resp.data.follows){
|
||||||
|
for (let i = 0; i <resp.data.follows.length; i++) {
|
||||||
|
if(resp.data.follows[i]===this.currentUser().id){
|
||||||
|
this.newShowFollow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if(this.newData){
|
||||||
|
this.dialogVisible = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
checkout(row) {
|
checkout(row) {
|
||||||
//let test = this.versionData.filter(v => v.versionId === row.id)[0];
|
//let test = this.versionData.filter(v => v.versionId === row.id)[0];
|
||||||
|
@ -580,4 +625,5 @@ export default {
|
||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,347 @@
|
||||||
|
|
||||||
|
function isUndef (v) {
|
||||||
|
return v === undefined || v === null
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDef (v){
|
||||||
|
return v !== undefined && v !== null
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTrue (v){
|
||||||
|
return v === true
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeMap (str, expectsLowerCase){
|
||||||
|
const map = Object.create(null)
|
||||||
|
const list= str.split(',')
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
map[list[i]] = true
|
||||||
|
}
|
||||||
|
return expectsLowerCase
|
||||||
|
? val => map[val.toLowerCase()]
|
||||||
|
: val => map[val]
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTextInputType = makeMap('text,number,password,search,email,tel,url')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该比较方法认为 1,2 -> 2,1 版本是有改变的
|
||||||
|
* @param oldDom 被比较的节点
|
||||||
|
* @param newDom 比较的节点
|
||||||
|
*/
|
||||||
|
export function diff (oldDom, newDom) {
|
||||||
|
let diffNode = {
|
||||||
|
oldNodeArray:[],
|
||||||
|
nodeArray:[],
|
||||||
|
}
|
||||||
|
//两个独立的节点传过来 首先单独比较一下他们最外层的 dom的_vnode 如果 _vnode 相同 则认为最外层节点相同,否则更改样式 return
|
||||||
|
let oldVnode = oldDom._vnode;
|
||||||
|
let newVnode = newDom._vnode;
|
||||||
|
|
||||||
|
if (isUndef(newVnode)) { //如果新节点不存在
|
||||||
|
//如果旧节点存在, 旧节点加背景颜色
|
||||||
|
if (isDef(oldVnode)) {
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (isUndef(oldVnode)) {//如果旧节点不存在
|
||||||
|
//如果新节点存在, 新节点加背景颜色
|
||||||
|
if (isDef(newVnode)) {
|
||||||
|
diffNode.nodeArray.push(newVnode.elm)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sameVnode(oldVnode, newVnode)) {
|
||||||
|
//逐层比较节点 这里涉及到 children 的length 可能不同的情况
|
||||||
|
diffDetail(oldVnode, newVnode,diffNode)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm)
|
||||||
|
diffNode.nodeArray.push(newVnode.elm)
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStyle(diffNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeStyle(diffNode){
|
||||||
|
console.log("查看结果");
|
||||||
|
console.log(diffNode.oldNodeArray);
|
||||||
|
console.log(diffNode.nodeArray);
|
||||||
|
for (let i = 0; i < diffNode.oldNodeArray.length; i++) {
|
||||||
|
if(diffNode.oldNodeArray[i]==='comment'||isUndef(diffNode.oldNodeArray[i].style)){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if(diffNode.oldNodeArray[i].className==='cell'){
|
||||||
|
let rowVnodeElm = findRowVnodeElm(diffNode.oldNodeArray[i]);
|
||||||
|
rowVnodeElm.style.setProperty("background-color","rgb(241,200,196)",'important')
|
||||||
|
}else{
|
||||||
|
changeStyleBySubset(diffNode.oldNodeArray[i],"rgb(241,200,196)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < diffNode.nodeArray.length; i++) {
|
||||||
|
if(diffNode.nodeArray[i]==='comment'||isUndef(diffNode.nodeArray[i].style)){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if(diffNode.nodeArray[i].className==='cell'){
|
||||||
|
let rowVnodeElm = findRowVnodeElm(diffNode.nodeArray[i]);
|
||||||
|
rowVnodeElm.style.setProperty("background-color","rgb(215, 243, 215)",'important')
|
||||||
|
}else{
|
||||||
|
changeStyleBySubset(diffNode.nodeArray[i],"rgb(215, 243, 215)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeStyleBySubset(vnodeElm,color){
|
||||||
|
if(isDef(vnodeElm.children)&&vnodeElm.children.length>0){
|
||||||
|
if(isDef(vnodeElm.style)){
|
||||||
|
vnodeElm.style.setProperty("background-color",color,'important')
|
||||||
|
}
|
||||||
|
for (let i = 0; i < vnodeElm.children.length; i++) {
|
||||||
|
changeStyleBySubset(vnodeElm.children[i],color);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
if(isDef(vnodeElm.style)){
|
||||||
|
vnodeElm.style.setProperty("background-color",color,'important')
|
||||||
|
}else {
|
||||||
|
vnodeElm.parentNode.style.setProperty("background-color",color,'important')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param oldChildren Array<VNode>
|
||||||
|
* @param newChildren Array<VNode>
|
||||||
|
* @param diffNode
|
||||||
|
* @param isCompareChildren
|
||||||
|
*/
|
||||||
|
function diffChildren(oldChildren,newChildren,diffNode,isCompareChildren){
|
||||||
|
let oldLength = oldChildren.length;
|
||||||
|
let newLength = newChildren.length;
|
||||||
|
//如果isCompareChildren===true,证明是需要轮巡比较table内容是否有重复的,方法是比较每个元素的最子dom,tr的td的length是相等的;
|
||||||
|
if(isCompareChildren===true){
|
||||||
|
let oldIndexArray = [];
|
||||||
|
let newIndexArray = [];
|
||||||
|
//现在oldChildren,newChildren是tbody的所有数据,
|
||||||
|
for (let i = 0; i < oldLength; i++) {
|
||||||
|
for (let j = 0; j < newLength; j++) {
|
||||||
|
let sameNode = {
|
||||||
|
nodeArray:[],
|
||||||
|
}
|
||||||
|
//找每行的数据,但是行是确定的,这里应该直接比较每个tr的 children
|
||||||
|
sameDetail(oldChildren[i],newChildren[j],sameNode)
|
||||||
|
if(sameNode.nodeArray.length>0){
|
||||||
|
//证明oldChildren[i]与 newChildren[j] 是相同的 内容
|
||||||
|
if(oldIndexArray.indexOf(i) === -1){
|
||||||
|
oldIndexArray.push(i);
|
||||||
|
}
|
||||||
|
if(newIndexArray.indexOf(j) === -1){
|
||||||
|
newIndexArray.push(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let j = 0; j < oldLength; j++) {
|
||||||
|
if(oldIndexArray.indexOf(j)===-1){
|
||||||
|
diffNode.oldNodeArray.push(oldChildren[j].elm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < newLength; j++) {
|
||||||
|
if(newIndexArray.indexOf(j)===-1){
|
||||||
|
diffNode.nodeArray.push(newChildren[j].elm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}else {
|
||||||
|
//取二者公共长度
|
||||||
|
let childrenLength = Math.min(oldLength, newLength);
|
||||||
|
for (let i = 0; i < childrenLength; i++) {
|
||||||
|
let oldVnode = oldChildren[i]
|
||||||
|
let newVnode = newChildren[i]
|
||||||
|
diffDetail(oldVnode,newVnode,diffNode)
|
||||||
|
}
|
||||||
|
for (let i = childrenLength; i <= (oldLength - childrenLength); i++) {
|
||||||
|
if(oldChildren[i]){
|
||||||
|
diffNode.oldNodeArray.push(oldChildren[i].elm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = childrenLength; i <= (newLength - childrenLength); i++) {
|
||||||
|
if(newChildren[i]){
|
||||||
|
diffNode.nodeArray.push(newChildren[i].elm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function sameChildren(oldChildren,newChildren,sameNode){
|
||||||
|
let oldLength = oldChildren.length;
|
||||||
|
for (let i = 0; i < oldLength; i++) {
|
||||||
|
let oldVnode = oldChildren[i]
|
||||||
|
let newVnode = newChildren[i]
|
||||||
|
sameDetail(oldVnode,newVnode,sameNode)
|
||||||
|
if(sameNode.nodeArray.length===0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sameDetail(oldVnode,newVnode,sameNode){
|
||||||
|
if(isDef(oldVnode.child)&&isDef(newVnode.child)){
|
||||||
|
let ovnode = oldVnode.child._vnode;
|
||||||
|
let nvnode = newVnode.child._vnode;
|
||||||
|
sameDetail(ovnode,nvnode,sameNode)
|
||||||
|
if(sameNode.nodeArray.length===0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isDef(oldVnode.children)&&isDef(newVnode.children)){
|
||||||
|
sameChildren(oldVnode.children,newVnode.children,sameNode)
|
||||||
|
}
|
||||||
|
//剩最后的子节点的时候,分类型做判断
|
||||||
|
if(isUndef(oldVnode.child)&&isUndef(newVnode.child)&&isUndef(oldVnode.children)&&isUndef(newVnode.children)){
|
||||||
|
if(isDef(oldVnode.text)&&isDef(newVnode.text)){
|
||||||
|
if(oldVnode.text===newVnode.text){
|
||||||
|
sameNode.nodeArray.push(newVnode.elm);
|
||||||
|
}else{
|
||||||
|
sameNode.nodeArray = [];
|
||||||
|
}
|
||||||
|
}else if(isDef(oldVnode.tag)&&isDef(newVnode.tag)){
|
||||||
|
if(oldVnode.tag==='input'&&newVnode.tag==='input'){
|
||||||
|
if(oldVnode.elm.value===newVnode.elm.value){
|
||||||
|
if(oldVnode.elm.checked!==undefined&&newVnode.elm.checked!==undefined){
|
||||||
|
if(oldVnode.elm.checked===newVnode.elm.checked){
|
||||||
|
sameNode.nodeArray.push(newVnode.elm);
|
||||||
|
}else {
|
||||||
|
sameNode.nodeArray = [];
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
sameNode.nodeArray.push(newVnode.elm);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
sameNode.nodeArray = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(sameVnode(oldVnode,newVnode)){
|
||||||
|
sameNode.nodeArray.push(newVnode.elm);
|
||||||
|
}else{
|
||||||
|
sameNode.nodeArray = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function diffDetail(oldVnode,newVnode,diffNode){
|
||||||
|
if(isDef(oldVnode.child)&&isUndef(newVnode.child)){
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.child._vnode.elm);
|
||||||
|
}
|
||||||
|
if(isDef(oldVnode.children)&&isUndef(newVnode.children)){
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||||
|
}
|
||||||
|
if(isUndef(oldVnode.child)&&isDef(newVnode.child)){
|
||||||
|
diffNode.nodeArray.push(newVnode.child._vnode.elm);
|
||||||
|
}
|
||||||
|
if(isUndef(oldVnode.children)&&isDef(newVnode.children)){
|
||||||
|
diffNode.nodeArray.push(newVnode.elm);
|
||||||
|
}
|
||||||
|
if(isDef(oldVnode.child)&&isDef(newVnode.child)){
|
||||||
|
let ovnode = oldVnode.child._vnode;
|
||||||
|
let nvnode = newVnode.child._vnode;
|
||||||
|
diffDetail(ovnode,nvnode,diffNode)
|
||||||
|
}
|
||||||
|
if(isDef(oldVnode.children)&&isDef(newVnode.children)){
|
||||||
|
//处理节点数据结构为table的情况
|
||||||
|
let isCompareChildren = false;
|
||||||
|
if(oldVnode.tag==='tbody'&&newVnode.tag==='tbody'){
|
||||||
|
isCompareChildren = true;
|
||||||
|
}else
|
||||||
|
if(isDef(oldVnode.elm.className)&&isDef(newVnode.elm.className)){
|
||||||
|
if(oldVnode.elm.className==='el-collapse'&&newVnode.elm.className==='el-collapse'){
|
||||||
|
isCompareChildren = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diffChildren(oldVnode.children,newVnode.children,diffNode,isCompareChildren)
|
||||||
|
}
|
||||||
|
//剩最后的子节点的时候,分类型做判断
|
||||||
|
if(isUndef(oldVnode.child)&&isUndef(newVnode.child)&&isUndef(oldVnode.children)&&isUndef(newVnode.children)){
|
||||||
|
|
||||||
|
if(isDef(oldVnode.text)&&isDef(newVnode.text)){
|
||||||
|
if(oldVnode.text!==newVnode.text){
|
||||||
|
if(isDef(oldVnode.elm.style)){
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||||
|
}else {
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm.parentNode);
|
||||||
|
}
|
||||||
|
if(isDef(newVnode.elm.style)){
|
||||||
|
diffNode.nodeArray.push(newVnode.elm);
|
||||||
|
}else {
|
||||||
|
diffNode.nodeArray.push(newVnode.elm.parentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if(isDef(oldVnode.tag)&&isDef(newVnode.tag)){
|
||||||
|
if(oldVnode.tag==='input'&&newVnode.tag==='input'){
|
||||||
|
if(oldVnode.elm.value!==newVnode.elm.value){
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||||
|
diffNode.nodeArray.push(newVnode.elm);
|
||||||
|
}else {
|
||||||
|
if(oldVnode.elm.checked!==undefined&&newVnode.elm.checked!==undefined){
|
||||||
|
if(oldVnode.elm.checked!==newVnode.elm.checked){
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||||
|
diffNode.nodeArray.push(newVnode.elm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!sameVnode(oldVnode,newVnode)){
|
||||||
|
diffNode.oldNodeArray.push(oldVnode.elm);
|
||||||
|
diffNode.nodeArray.push(newVnode.elm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function sameVnode (a, b) {
|
||||||
|
return (
|
||||||
|
a.key === b.key &&
|
||||||
|
a.asyncFactory === b.asyncFactory && (
|
||||||
|
(
|
||||||
|
a.tag === b.tag &&
|
||||||
|
a.isComment === b.isComment &&
|
||||||
|
isDef(a.data) === isDef(b.data) &&
|
||||||
|
sameInputType(a, b)
|
||||||
|
) || (
|
||||||
|
isTrue(a.isAsyncPlaceholder) &&
|
||||||
|
isUndef(b.asyncFactory.error)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function findRowVnodeElm(nodeElm){
|
||||||
|
if(nodeElm.localName==="td"||nodeElm.className==="cell"){
|
||||||
|
return findRowVnodeElm(nodeElm.parentNode)
|
||||||
|
}else if(nodeElm.localName==="tr"){
|
||||||
|
return nodeElm;
|
||||||
|
}else {
|
||||||
|
return nodeElm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sameInputType (a, b) {
|
||||||
|
if (a.tag !== 'input') return true
|
||||||
|
let i
|
||||||
|
const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type
|
||||||
|
const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type
|
||||||
|
return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/* @flow */
|
||||||
|
import {Component, ComponentOptions, VNodeComponentOptions, VNodeData} from "vue";
|
||||||
|
|
||||||
|
export default class VNode {
|
||||||
|
tag: string | void;
|
||||||
|
data: VNodeData | void;
|
||||||
|
children: VNode[];
|
||||||
|
text: string | void;
|
||||||
|
elm: Node | void;
|
||||||
|
ns: string | void;
|
||||||
|
context: Component | void; // rendered in this component's scope
|
||||||
|
key: string | number | void;
|
||||||
|
componentOptions: VNodeComponentOptions | void;
|
||||||
|
componentInstance: Component | void; // component instance
|
||||||
|
parent: VNode | void; // component placeholder node
|
||||||
|
|
||||||
|
// strictly internal
|
||||||
|
raw: boolean; // contains raw HTML? (server only)
|
||||||
|
isStatic: boolean; // hoisted static node
|
||||||
|
isRootInsert: boolean; // necessary for enter transition check
|
||||||
|
isComment: boolean; // empty comment placeholder?
|
||||||
|
isCloned: boolean; // is a cloned node?
|
||||||
|
isOnce: boolean; // is a v-once node?
|
||||||
|
asyncFactory: Function | void; // async component factory function
|
||||||
|
asyncMeta: Object | void;
|
||||||
|
isAsyncPlaceholder: boolean;
|
||||||
|
ssrContext: Object | void;
|
||||||
|
fnContext: Component | void; // real context vm for functional nodes
|
||||||
|
fnOptions: any; // for SSR caching
|
||||||
|
devtoolsMeta: Object; // used to store functional render context for devtools
|
||||||
|
fnScopeId: string; // functional scope id support
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
tag?: string | void,
|
||||||
|
data?: VNodeData | void,
|
||||||
|
children?: VNode[],
|
||||||
|
text?: string | void,
|
||||||
|
elm?: Node | void,
|
||||||
|
context?: Component | void,
|
||||||
|
componentOptions?: VNodeComponentOptions | void,
|
||||||
|
asyncFactory?: Function | void
|
||||||
|
) {
|
||||||
|
this.tag = tag
|
||||||
|
this.data = data
|
||||||
|
this.children = children
|
||||||
|
this.text = text
|
||||||
|
this.elm = elm
|
||||||
|
this.ns = undefined
|
||||||
|
this.context = context
|
||||||
|
this.fnContext = undefined
|
||||||
|
this.fnOptions = undefined
|
||||||
|
this.fnScopeId = undefined
|
||||||
|
this.key = data && data.key
|
||||||
|
this.componentOptions = componentOptions
|
||||||
|
this.componentInstance = undefined
|
||||||
|
this.parent = undefined
|
||||||
|
this.raw = false
|
||||||
|
this.isStatic = false
|
||||||
|
this.isRootInsert = true
|
||||||
|
this.isComment = false
|
||||||
|
this.isCloned = false
|
||||||
|
this.isOnce = false
|
||||||
|
this.asyncFactory = asyncFactory
|
||||||
|
this.asyncMeta = undefined
|
||||||
|
this.isAsyncPlaceholder = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED: alias for componentInstance for backwards compat.
|
||||||
|
/* istanbul ignore next */
|
||||||
|
get child (): Component | void {
|
||||||
|
return this.componentInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cloneVNode (vnode: VNode): VNode {
|
||||||
|
const cloned = new VNode(
|
||||||
|
vnode.tag,
|
||||||
|
vnode.data,
|
||||||
|
// #7975
|
||||||
|
// clone children array to avoid mutating original in case of cloning
|
||||||
|
// a child.
|
||||||
|
vnode.children && vnode.children.slice(),
|
||||||
|
vnode.text,
|
||||||
|
vnode.elm,
|
||||||
|
vnode.context,
|
||||||
|
vnode.componentOptions,
|
||||||
|
vnode.asyncFactory
|
||||||
|
)
|
||||||
|
cloned.ns = vnode.ns
|
||||||
|
cloned.isStatic = vnode.isStatic
|
||||||
|
cloned.key = vnode.key
|
||||||
|
cloned.isComment = vnode.isComment
|
||||||
|
cloned.fnContext = vnode.fnContext
|
||||||
|
cloned.fnOptions = vnode.fnOptions
|
||||||
|
cloned.fnScopeId = vnode.fnScopeId
|
||||||
|
cloned.asyncMeta = vnode.asyncMeta
|
||||||
|
cloned.isCloned = true
|
||||||
|
return cloned
|
||||||
|
}
|
Loading…
Reference in New Issue