未验证 提交 9827f3db 编写于 作者: P Phil Hughes

moved fetching trace to store

improved components by making them unaware of the store
removed need for extra CSS - all achieved with BS classes
上级 e59d9a0c
<script> <script>
import { mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import _ from 'underscore'; import _ from 'underscore';
import axios from '../../../lib/utils/axios_utils';
import CiIcon from '../../../vue_shared/components/ci_icon.vue'; import CiIcon from '../../../vue_shared/components/ci_icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
import Icon from '../../../vue_shared/components/icon.vue'; import Icon from '../../../vue_shared/components/icon.vue';
const scrollPositions = { const scrollPositions = {
top: 'top', top: 0,
bottom: 'bottom', bottom: 1,
}; };
export default { export default {
...@@ -22,14 +21,10 @@ export default { ...@@ -22,14 +21,10 @@ export default {
data() { data() {
return { return {
scrollPos: scrollPositions.top, scrollPos: scrollPositions.top,
loading: true,
}; };
}, },
computed: { computed: {
...mapState('pipelines', ['detailJob']), ...mapState('pipelines', ['detailJob']),
rawUrl() {
return `${this.detailJob.path}/raw`;
},
isScrolledToBottom() { isScrolledToBottom() {
return this.scrollPos === scrollPositions.bottom; return this.scrollPos === scrollPositions.bottom;
}, },
...@@ -43,10 +38,8 @@ export default { ...@@ -43,10 +38,8 @@ export default {
mounted() { mounted() {
this.getTrace(); this.getTrace();
}, },
beforeDestroy() {
clearTimeout(this.timeout);
},
methods: { methods: {
...mapActions('pipelines', ['fetchJobTrace', 'setDetailJob']),
scrollDown() { scrollDown() {
this.$refs.buildTrace.scrollTo(0, this.$refs.buildTrace.scrollHeight); this.$refs.buildTrace.scrollTo(0, this.$refs.buildTrace.scrollHeight);
}, },
...@@ -66,37 +59,19 @@ export default { ...@@ -66,37 +59,19 @@ export default {
this.scrollPos = ''; this.scrollPos = '';
} }
}), }),
getTrace(state = null) { getTrace() {
return axios return this.fetchJobTrace().then(() => this.scrollDown());
.get(`${this.detailJob.path}/trace`, {
params: {
state,
},
})
.then(({ data }) => {
this.loading = !data.complete;
this.detailJob.output = data.append ? `${this.detailJob.output}${data.html}` : data.html;
if (!data.complete) {
this.timeout = setTimeout(() => this.getTrace(data.state), 4000);
}
})
.then(() => this.$nextTick())
.then(() => this.scrollDown());
}, },
}, },
scrollPositions,
}; };
</script> </script>
<template> <template>
<div class="ide-pipeline build-page"> <div class="ide-pipeline build-page d-flex flex-column flex-fill">
<header <header class="ide-tree-header ide-pipeline-header">
class="ide-tree-header ide-pipeline-header"
>
<button <button
class="btn btn-default btn-sm" class="btn btn-default btn-sm d-flex"
@click="() => { $store.state.pipelines.detailJob = null; $store.dispatch('setRightPane', 'pipelines-list') }" @click="setDetailJob(null)"
> >
<icon <icon
name="chevron-left" name="chevron-left"
...@@ -104,37 +79,37 @@ export default { ...@@ -104,37 +79,37 @@ export default {
{{ __('View jobs') }} {{ __('View jobs') }}
</button> </button>
</header> </header>
<div <div class="top-bar d-flex">
class="top-bar" <div class="ide-job-details d-flex align-items-center">
>
<div class="ide-job-details float-left">
<ci-icon <ci-icon
class="append-right-4" class="append-right-4 d-flex"
:status="detailJob.status" :status="detailJob.status"
:borderless="true" :borderless="true"
:size="24" :size="24"
/> />
{{ detailJob.name }} <span>
<a {{ detailJob.name }}
:href="detailJob.path" <a
target="_blank" :href="detailJob.path"
class="ide-external-link prepend-left-4" target="_blank"
> class="ide-external-link"
{{ jobId }} >
<icon {{ jobId }}
name="external-link" <icon
:size="12" name="external-link"
/> :size="12"
</a> />
</a>
</span>
</div> </div>
<div class="controllers float-right"> <div class="controllers ml-auto">
<a <a
v-tooltip v-tooltip
:title="__('Show complete raw')" :title="__('Show complete raw')"
data-placement="top" data-placement="top"
data-container="body" data-container="body"
class="controllers-buttons" class="controllers-buttons"
:href="rawUrl" :href="detailJob.rawPath"
target="_blank" target="_blank"
> >
<i <i
...@@ -155,9 +130,7 @@ export default { ...@@ -155,9 +130,7 @@ export default {
:disabled="isScrolledToTop" :disabled="isScrolledToTop"
@click="scrollUp" @click="scrollUp"
> >
<icon <icon name="scroll_up" />
name="scroll_up"
/>
</button> </button>
</div> </div>
<div <div
...@@ -173,51 +146,26 @@ export default { ...@@ -173,51 +146,26 @@ export default {
:disabled="isScrolledToBottom" :disabled="isScrolledToBottom"
@click="scrollDown" @click="scrollDown"
> >
<icon <icon name="scroll_down" />
name="scroll_down"
/>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<pre <pre
class="build-trace" class="build-trace mb-0"
ref="buildTrace" ref="buildTrace"
@scroll="scrollBuildLog" @scroll="scrollBuildLog"
> >
<code <code
class="bash" class="bash"
v-html="detailJob.output" v-html="detailJob.output"
></code> >
</code>
<div <div
v-show="loading" v-show="detailJob.isLoading"
class="build-loader-animation" class="build-loader-animation"
> >
</div> </div>
</pre> </pre>
</div> </div>
</template> </template>
<style scoped>
.build-trace-container {
flex: 1;
display: flex;
flex-direction: column;
}
.ide-tree-header .btn {
display: flex;
}
.ide-job-details {
display: flex;
}
.ide-job-details .ci-status-icon {
height: 0;
}
.build-trace {
margin-bottom: 0;
}
</style>
...@@ -18,6 +18,11 @@ export default { ...@@ -18,6 +18,11 @@ export default {
return `#${this.job.id}`; return `#${this.job.id}`;
}, },
}, },
methods: {
clickViewLog() {
this.$emit('clickViewLog', this.job);
},
},
}; };
</script> </script>
...@@ -43,16 +48,10 @@ export default { ...@@ -43,16 +48,10 @@ export default {
</a> </a>
</span> </span>
<button <button
class="btn btn-default btn-sm" class="btn btn-default btn-sm ml-auto"
@click="() => { $store.state.pipelines.detailJob = job; $store.dispatch('setRightPane', 'jobs-detail') }" @click="clickViewLog"
> >
{{ __('View log') }} {{ __('View log') }}
</button> </button>
</div> </div>
</template> </template>
<style scoped>
.btn {
margin-left: auto;
}
</style>
...@@ -19,7 +19,7 @@ export default { ...@@ -19,7 +19,7 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions('pipelines', ['fetchJobs', 'toggleStageCollapsed']), ...mapActions('pipelines', ['fetchJobs', 'toggleStageCollapsed', 'setDetailJob']),
}, },
}; };
</script> </script>
...@@ -38,6 +38,7 @@ export default { ...@@ -38,6 +38,7 @@ export default {
:stage="stage" :stage="stage"
@fetch="fetchJobs" @fetch="fetchJobs"
@toggleCollapsed="toggleStageCollapsed" @toggleCollapsed="toggleStageCollapsed"
@clickViewLog="setDetailJob"
/> />
</template> </template>
</div> </div>
......
...@@ -48,6 +48,9 @@ export default { ...@@ -48,6 +48,9 @@ export default {
toggleCollapsed() { toggleCollapsed() {
this.$emit('toggleCollapsed', this.stage.id); this.$emit('toggleCollapsed', this.stage.id);
}, },
clickViewLog(job) {
this.$emit('clickViewLog', job);
},
}, },
}; };
</script> </script>
...@@ -101,6 +104,7 @@ export default { ...@@ -101,6 +104,7 @@ export default {
v-for="job in stage.jobs" v-for="job in stage.jobs"
:key="job.id" :key="job.id"
:job="job" :job="job"
@clickViewLog="clickViewLog"
/> />
</template> </template>
</div> </div>
......
...@@ -4,6 +4,7 @@ import { __ } from '../../../../locale'; ...@@ -4,6 +4,7 @@ import { __ } from '../../../../locale';
import flash from '../../../../flash'; import flash from '../../../../flash';
import Poll from '../../../../lib/utils/poll'; import Poll from '../../../../lib/utils/poll';
import service from '../../../services'; import service from '../../../services';
import { rightSidebarViews } from '../../../constants';
import * as types from './mutation_types'; import * as types from './mutation_types';
let eTagPoll; let eTagPoll;
...@@ -77,6 +78,25 @@ export const fetchJobs = ({ dispatch }, stage) => { ...@@ -77,6 +78,25 @@ export const fetchJobs = ({ dispatch }, stage) => {
export const toggleStageCollapsed = ({ commit }, stageId) => export const toggleStageCollapsed = ({ commit }, stageId) =>
commit(types.TOGGLE_STAGE_COLLAPSE, stageId); commit(types.TOGGLE_STAGE_COLLAPSE, stageId);
export const setDetailJob = ({ commit }, job) => commit(types.SET_DETAIL_JOB, job); export const setDetailJob = ({ commit, dispatch }, job) => {
commit(types.SET_DETAIL_JOB, job);
dispatch('setRightPane', job ? rightSidebarViews.jobsDetail : rightSidebarViews.pipelines, {
root: true,
});
};
export const requestJobTrace = ({ commit }) => commit(types.REQUEST_JOB_TRACE);
export const receiveJobTraceError = ({ commit }) => commit(types.RECEIVE_JOB_TRACE_ERROR);
export const receiveJobTraceSuccess = ({ commit }, data) =>
commit(types.RECEIVE_JOB_TRACE_SUCCESS, data);
export const fetchJobTrace = ({ dispatch, state }) => {
dispatch('requestJobTrace');
return axios
.get(`${state.detailJob.path}/trace`, { params: { format: 'json' } })
.then(({ data }) => dispatch('receiveJobTraceSuccess', data))
.catch(() => dispatch('requestJobTraceError'));
};
export default () => {}; export default () => {};
...@@ -9,3 +9,7 @@ export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS'; ...@@ -9,3 +9,7 @@ export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
export const TOGGLE_STAGE_COLLAPSE = 'TOGGLE_STAGE_COLLAPSE'; export const TOGGLE_STAGE_COLLAPSE = 'TOGGLE_STAGE_COLLAPSE';
export const SET_DETAIL_JOB = 'SET_DETAIL_JOB'; export const SET_DETAIL_JOB = 'SET_DETAIL_JOB';
export const REQUEST_JOB_TRACE = 'REQUEST_JOB_TRACE';
export const RECEIVE_JOB_TRACE_ERROR = 'RECEIVE_JOB_TRACE_ERROR';
export const RECEIVE_JOB_TRACE_SUCCESS = 'RECEIVE_JOB_TRACE_SUCCESS';
...@@ -64,6 +64,16 @@ export default { ...@@ -64,6 +64,16 @@ export default {
})); }));
}, },
[types.SET_DETAIL_JOB](state, job) { [types.SET_DETAIL_JOB](state, job) {
state.detailJob = job; state.detailJob = { ...job };
},
[types.REQUEST_JOB_TRACE](state) {
state.detailJob.isLoading = true;
},
[types.RECEIVE_JOB_TRACE_ERROR](state) {
state.detailJob.isLoading = false;
},
[types.RECEIVE_JOB_TRACE_SUCCESS](state, data) {
state.detailJob.isLoading = false;
state.detailJob.output = data.html;
}, },
}; };
...@@ -4,6 +4,8 @@ export const normalizeJob = job => ({ ...@@ -4,6 +4,8 @@ export const normalizeJob = job => ({
name: job.name, name: job.name,
status: job.status, status: job.status,
path: job.build_path, path: job.build_path,
rawPath: `${job.build_path}/raw`,
started: job.started, started: job.started,
output: '', output: '',
isLoading: false,
}); });
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
.top-bar { .top-bar {
height: 35px; height: 35px;
min-height: 35px;
background: $gray-light; background: $gray-light;
border: 1px solid $border-color; border: 1px solid $border-color;
color: $gl-text-color; color: $gl-text-color;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册