加入项目管理

This commit is contained in:
marvzhang
2020-02-02 12:29:28 +08:00
parent 59520e02a9
commit 075a899c2f
10 changed files with 694 additions and 9 deletions

View File

@@ -18,6 +18,21 @@
<el-form-item :label="$t('Spider Name')">
<el-input v-model="spiderForm.display_name" :placeholder="$t('Spider Name')" :disabled="isView"></el-input>
</el-form-item>
<el-form-item :label="$t('Project')" prop="project_id" required>
<el-select
v-model="spiderForm.project_id"
:placeholder="$t('Project')"
filterable
>
<el-option value="000000000000000000000000" :label="$t('No project')"/>
<el-option
v-for="p in projectList"
:key="p._id"
:value="p._id"
:label="p.name"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('Source Folder')">
<el-input v-model="spiderForm.src" :placeholder="$t('Source Folder')" disabled></el-input>
</el-form-item>
@@ -127,6 +142,9 @@ export default {
...mapGetters('user', [
'token'
]),
...mapState('project', [
'projectList'
]),
isShowRun () {
if (this.spiderForm.type === 'customized') {
return !!this.spiderForm.cmd
@@ -180,6 +198,15 @@ export default {
onUploadError () {
this.uploadLoading = false
}
},
async created () {
// fetch project list
await this.$store.dispatch('project/getProjectList')
// 兼容项目ID
if (!this.spiderForm.project_id) {
this.spiderForm.project_id = '000000000000000000000000'
}
}
}
</script>

View File

@@ -72,6 +72,7 @@ export default {
'Create Directory': '新建目录',
'Create File': '新建文件',
'Add Node': '添加节点',
'Add Project': '添加项目',
// 主页
'Total Tasks': '总任务数',

View File

@@ -16,6 +16,7 @@ import stats from './modules/stats'
import setting from './modules/setting'
import version from './modules/version'
import tour from './modules/tour'
import project from './modules/project'
import getters from './getters'
Vue.use(Vuex)
@@ -37,6 +38,7 @@ const store = new Vuex.Store({
setting,
version,
tour,
project,
// 统计
stats
},

View File

@@ -0,0 +1,48 @@
import request from '../../api/request'
const state = {
projectForm: {},
projectList: []
}
const getters = {}
const mutations = {
SET_PROJECT_FORM: (state, value) => {
state.projectForm = value
},
SET_PROJECT_LIST: (state, value) => {
state.projectList = value
}
}
const actions = {
getProjectList ({ state, commit }) {
request.get('/projects')
.then(response => {
if (response.data.data) {
commit('SET_PROJECT_LIST', response.data.data.map(d => {
if (!d.spiders) d.spiders = []
return d
}))
}
})
},
addProject ({ state }) {
request.put('/projects', state.projectForm)
},
editProject ({ state }, id) {
request.post(`/projects/${id}`, state.projectForm)
},
removeProject ({ state }, id) {
request.delete(`/projects/${id}`)
}
}
export default {
namespaced: true,
state,
getters,
mutations,
actions
}

View File

@@ -156,7 +156,7 @@ const user = {
},
// 新增全局变量
addGlobalVariable ({ commit, state }) {
return request.post(`/variable`, state.globalVariableForm)
return request.put(`/variable`, state.globalVariableForm)
.then(() => {
state.globalVariableForm = {}
})

View File

@@ -1,14 +1,287 @@
<template>
<div class="app-container">
<!--add popup-->
<el-dialog
:visible.sync="dialogVisible"
width="640px"
:before-close="onDialogClose">
<el-form label-width="180px"
class="add-form"
:model="projectForm"
:inline-message="true"
ref="projectForm"
label-position="right">
<el-form-item :label="$t('Proejct Name')" prop="name" required>
<el-input id="name" v-model="projectForm.name" :placeholder="$t('Project Name')"></el-input>
</el-form-item>
<el-form-item :label="$t('Project Description')" prop="description">
<el-input
id="description"
type="textarea"
v-model="projectForm.description"
:placeholder="$t('Project Description')"
/>
</el-form-item>
<el-form-item :label="$t('Tags')" prop="tags">
<el-select
id="tags"
v-model="projectForm.tags"
:placeholder="$t('Enter Tags')"
allow-create
filterable
multiple
>
</el-select>
</el-form-item>
</el-form>
<!--取消保存-->
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="onDialogClose">{{$t('Cancel')}}</el-button>
<el-button id="btn-submit" size="small" type="primary" @click="onAddSubmit">{{$t('Submit')}}</el-button>
</span>
</el-dialog>
<!--./add popup-->
<div class="action-wrapper">
<div class="buttons">
<el-button
icon="el-icon-plus"
type="primary"
size="small"
@click="onAdd"
>
{{$t('Add Project')}}
</el-button>
</div>
</div>
<div class="content">
<div v-if="projectList.length === 0" class="empty-list">
{{ $t('You have no projects created. You can create a project by clicking the "Add" button.')}}
</div>
<ul v-else class="list">
<li
class="item"
v-for="item in projectList"
:key="item._id"
@click="onView(item)"
>
<el-card
class="item-card"
>
<i class="btn-edit fa fa-edit" @click="onEdit(item)"></i>
<i class="btn-close fa fa-trash-o" @click="onRemove(item)"></i>
<el-row>
<h4 class="title">{{ item.name }}</h4>
</el-row>
<el-row>
<div class="spider-count">
{{$t('Spider Count')}}: {{ item.spiders.length }}
</div>
</el-row>
<el-row class="description-wrapper">
<div class="description">
{{ item.description }}
</div>
</el-row>
<el-row class="tags-wrapper">
<div class="tags">
<el-tag
v-for="(tag, index) in item.tags"
:key="index"
size="mini"
class="tag"
>
{{ tag }}
</el-tag>
</div>
</el-row>
</el-card>
</li>
</ul>
</div>
</div>
</template>
<script>
import {
mapState
} from 'vuex'
export default {
name: 'ProjectList'
name: 'ProjectList',
data () {
return {
defaultTags: [],
dialogVisible: false
}
},
computed: {
...mapState('project', [
'projectForm',
'projectList'
])
},
methods: {
onDialogClose () {
this.dialogVisible = false
},
onAdd () {
this.isEdit = false
this.dialogVisible = true
this.$store.commit('project/SET_PROJECT_FORM', { tags: [] })
this.$st.sendEv('项目', '添加项目')
},
onAddSubmit () {
this.$refs.projectForm.validate(res => {
if (res) {
const form = JSON.parse(JSON.stringify(this.projectForm))
if (this.isEdit) {
this.$request.post(`/projects/${this.projectForm._id}`, form).then(response => {
if (response.data.error) {
this.$message.error(response.data.error)
return
}
this.dialogVisible = false
this.$store.dispatch('project/getProjectList')
this.$message.success(this.$t('The project has been saved'))
})
} else {
this.$request.put('/projects', form).then(response => {
if (response.data.error) {
this.$message.error(response.data.error)
return
}
this.dialogVisible = false
this.$store.dispatch('project/getProjectList')
this.$message.success(this.$t('The project has been added'))
})
}
}
})
this.$st.sendEv('项目', '提交项目')
},
onEdit (row) {
this.$store.commit('project/SET_PROJECT_FORM', row)
this.dialogVisible = true
this.isEdit = true
this.$st.sendEv('项目', '修改项目')
},
onRemove (row) {
this.$confirm(this.$t('Are you sure to delete the project?'), this.$t('Notification'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.$store.dispatch('project/removeProject', row._id)
.then(() => {
setTimeout(() => {
this.$store.dispatch('project/getProjectList')
this.$message.success(this.$t('The project has been removed'))
}, 100)
})
}).catch(() => {
})
this.$st.sendEv('项目', '删除项目')
},
onView (row) {
this.$router.push({
name: 'SpiderList',
params: {
project_id: row._id
}
})
}
},
async created () {
await this.$store.dispatch('project/getProjectList')
}
}
</script>
<style scoped>
.action-wrapper {
text-align: right;
padding-bottom: 10px;
border-bottom: 1px solid #EBEEF5;
}
.list {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-wrap: wrap;
}
.list .item {
width: 320px;
margin: 10px;
}
.list .item .item-card {
position: relative;
cursor: pointer;
}
.list .item .item-card .title {
margin: 10px 0 0 0;
}
.list .item .item-card .spider-count {
font-size: 12px;
color: grey;
font-weight: bolder;
}
.list .item .item-card .description-wrapper {
padding-bottom: 5px;
margin-bottom: 0;
border-bottom: 1px solid #EBEEF5;
}
.list .item .item-card .description {
font-size: 12px;
color: grey;
}
.list .item .item-card .tags {
margin-bottom: -5px;
}
.list .item .item-card .tags .tag {
margin: 0 5px 5px 0;
}
.list .item .item-card .el-row {
margin-bottom: 5px;
}
.list .item .item-card .el-row:last-child {
margin-bottom: 0;
}
.list .item .item-card .btn-edit {
z-index: 1;
color: grey;
position: absolute;
top: 11px;
right: 40px;
}
.list .item .item-card .btn-close {
z-index: 1;
color: grey;
position: absolute;
top: 10px;
right: 10px;
}
.empty-list {
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
height: calc(100vh - 240px);
}
</style>

View File

@@ -58,6 +58,20 @@
<el-form-item :label="$t('Display Name')" prop="display_name" required>
<el-input id="display-name" v-model="spiderForm.display_name" :placeholder="$t('Display Name')"/>
</el-form-item>
<el-form-item :label="$t('Project')" prop="project_id" required>
<el-select
v-model="spiderForm.project_id"
:placeholder="$t('Project')"
filterable
>
<el-option
v-for="p in projectList"
:key="p._id"
:value="p._id"
:label="p.name"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('Execute Command')" prop="cmd" required>
<el-input id="cmd" v-model="spiderForm.cmd" :placeholder="$t('Execute Command')"/>
</el-form-item>
@@ -104,6 +118,20 @@
<el-form-item :label="$t('Display Name')" prop="display_name" required>
<el-input v-model="spiderForm.display_name" :placeholder="$t('Display Name')"/>
</el-form-item>
<el-form-item :label="$t('Project')" prop="project_id" required>
<el-select
v-model="spiderForm.project_id"
:placeholder="$t('Project')"
filterable
>
<el-option
v-for="p in projectList"
:key="p._id"
:value="p._id"
:label="p.name"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('Template')" prop="template" required>
<el-select id="template" v-model="spiderForm.template" :value="spiderForm.template"
:placeholder="$t('Template')">
@@ -147,7 +175,29 @@
<!-- </el-select>-->
<!-- </el-form-item>-->
<el-form-item>
<el-input clearable @keyup.enter.native="onSearch" size="small" placeholder="名称" v-model="filter.keyword">
<el-select
v-model="filter.project_id"
size="small"
:placeholder="$t('Project')"
@change="getList"
>
<el-option value="" :label="$t('All Projects')"/>
<el-option
v-for="p in projectList"
:key="p._id"
:value="p._id"
:label="p.name"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-input
v-model="filter.keyword"
size="small"
:placeholder="$t('Spider Name')"
clearable
@keyup.enter.native="onSearch"
>
<i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</el-form-item>
@@ -335,6 +385,7 @@ export default {
crawlConfirmDialogVisible: false,
activeSpiderId: undefined,
filter: {
project_id: '',
keyword: '',
type: 'all'
},
@@ -491,6 +542,9 @@ export default {
...mapGetters('user', [
'token'
]),
...mapState('project', [
'projectList'
]),
uploadForm () {
return {
name: this.spiderForm.name,
@@ -518,6 +572,7 @@ export default {
},
onAdd () {
this.$store.commit('spider/SET_SPIDER_FORM', {
project_id: '000000000000000000000000',
template: this.templateList[0]
})
this.addDialogVisible = true
@@ -737,20 +792,27 @@ export default {
sort_key: this.sort.sortKey,
sort_direction: this.sort.sortDirection,
keyword: this.filter.keyword,
type: this.filter.type
type: this.filter.type,
project_id: this.filter.project_id
}
await this.$store.dispatch('spider/getSpiderList', params)
}
},
async created () {
// fetch spider types
// await this.getTypes()
// fetch project list
await this.$store.dispatch('project/getProjectList')
// project id
if (this.$route.params.project_id) {
this.filter.project_id = this.$route.params.project_id
}
// fetch spider list
await this.getList()
// fetch template list
await this.$store.dispatch('spider/getTemplateList')
},
mounted () {
const vm = this