加入前端可配置编辑器

This commit is contained in:
marvzhang
2019-11-27 12:18:51 +08:00
parent 1674dce3bd
commit 0edda7b558
6 changed files with 406 additions and 147 deletions

View File

@@ -6,6 +6,7 @@ type Field struct {
Xpath string `yaml:"xpath" json:"xpath"`
Attr string `yaml:"attr" json:"attr"`
NextStage string `yaml:"next_stage" json:"next_stage"`
Remark string `yaml:"remark" json:"remark"`
}
type Stage struct {

View File

@@ -23,7 +23,7 @@
"cross-env": "^5.2.0",
"dayjs": "^1.8.6",
"echarts": "^4.1.0",
"element-ui": "2.4.6",
"element-ui": "2.13.0",
"font-awesome": "^4.7.0",
"js-cookie": "2.2.0",
"normalize.css": "7.0.0",

View File

@@ -42,97 +42,159 @@
<!--config detail-->
<el-row>
<el-form label-width="150px" ref="form" :model="spiderForm">
<el-form label-width="150px" ref="form" :model="spiderForm.config">
<el-col :span="11" :offset="1">
<el-form-item :label="$t('Crawl Type')">
<el-button-group>
<el-button v-for="type in crawlTypeList"
:key="type.value"
:type="type.value === spiderForm.crawl_type ? 'primary' : ''"
@click="onSelectCrawlType(type.value)">
{{$t(type.label)}}
</el-button>
</el-button-group>
</el-form-item>
<el-form-item :label="$t('Start URL')" prop="start_url" required>
<el-input v-model="spiderForm.start_url" :placeholder="$t('Start URL')"></el-input>
</el-form-item>
<el-form-item :label="$t('Obey robots.txt')">
<el-switch v-model="spiderForm.obey_robots_txt" :placeholder="$t('Obey robots.txt')"></el-switch>
</el-form-item>
<!-- <el-form-item :label="$t('Crawl Type')">-->
<!-- <el-button-group>-->
<!-- <el-button v-for="type in crawlTypeList"-->
<!-- :key="type.value"-->
<!-- :type="type.value === spiderForm.crawl_type ? 'primary' : ''"-->
<!-- @click="onSelectCrawlType(type.value)">-->
<!-- {{$t(type.label)}}-->
<!-- </el-button>-->
<!-- </el-button-group>-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="$t('Start URL')" prop="start_url" required>-->
<!-- <el-input v-model="spiderForm.config.start_url" :placeholder="$t('Start URL')"></el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="$t('Obey robots.txt')">-->
<!-- <el-switch v-model="spiderForm.obey_robots_txt" :placeholder="$t('Obey robots.txt')"></el-switch>-->
<!-- </el-form-item>-->
<!--<el-form-item :label="$t('URL Pattern')">-->
<!--<el-input v-model="spiderForm.url_pattern" :placeholder="$t('URL Pattern')"></el-input>-->
<!--</el-form-item>-->
</el-col>
<el-col :span="11" :offset="1">
<el-form-item :label="$t('Item Selector')"
v-if="['list','list-detail'].includes(spiderForm.crawl_type)">
<el-select style="width: 35%;margin-right: 10px;"
v-model="spiderForm.item_selector_type"
:placeholder="$t('Item Selector Type')">
<el-option value="xpath" :label="$t('XPath')"></el-option>
<el-option value="css" :label="$t('CSS')"></el-option>
</el-select>
<el-input style="width: calc(65% - 10px);"
v-model="spiderForm.item_selector"
:placeholder="$t('Item Selector')">
</el-input>
</el-form-item>
<el-form-item :label="$t('Pagination Selector')"
v-if="['list','list-detail'].includes(spiderForm.crawl_type)">
<el-select style="width: 35%;margin-right: 10px;"
v-model="spiderForm.pagination_selector_type"
:placeholder="$t('Pagination Selector Type')">
<el-option value="xpath" :label="$t('XPath')"></el-option>
<el-option value="css" :label="$t('CSS')"></el-option>
</el-select>
<el-input style="width: calc(65% - 10px);"
v-model="spiderForm.pagination_selector"
:placeholder="$t('Pagination Selector')">
</el-input>
</el-form-item>
<el-form-item :label="$t('Item Threshold')"
v-if="['list','list-detail'].includes(spiderForm.crawl_type)">
<el-input-number v-model="spiderForm.item_threshold"/>
</el-form-item>
<!-- <el-form-item :label="$t('Item Selector')"-->
<!-- v-if="['list','list-detail'].includes(spiderForm.crawl_type)">-->
<!-- <el-select style="width: 35%;margin-right: 10px;"-->
<!-- v-model="spiderForm.item_selector_type"-->
<!-- :placeholder="$t('Item Selector Type')">-->
<!-- <el-option value="xpath" :label="$t('XPath')"></el-option>-->
<!-- <el-option value="css" :label="$t('CSS')"></el-option>-->
<!-- </el-select>-->
<!-- <el-input style="width: calc(65% - 10px);"-->
<!-- v-model="spiderForm.item_selector"-->
<!-- :placeholder="$t('Item Selector')">-->
<!-- </el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="$t('Pagination Selector')"-->
<!-- v-if="['list','list-detail'].includes(spiderForm.crawl_type)">-->
<!-- <el-select style="width: 35%;margin-right: 10px;"-->
<!-- v-model="spiderForm.page"-->
<!-- :placeholder="$t('Pagination Selector Type')">-->
<!-- <el-option value="xpath" :label="$t('XPath')"></el-option>-->
<!-- <el-option value="css" :label="$t('CSS')"></el-option>-->
<!-- </el-select>-->
<!-- <el-input style="width: calc(65% - 10px);"-->
<!-- v-model="spiderForm.pagination_selector"-->
<!-- :placeholder="$t('Pagination Selector')">-->
<!-- </el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item :label="$t('Item Threshold')"-->
<!-- v-if="['list','list-detail'].includes(spiderForm.crawl_type)">-->
<!-- <el-input-number v-model="spiderForm.item_threshold"/>-->
<!-- </el-form-item>-->
</el-col>
</el-form>
</el-row>
<!--./config detail-->
<!--button group-->
<el-row class="button-group-container">
<div class="button-group">
<el-button type="danger" @click="onCrawl">{{$t('Run')}}</el-button>
<el-button type="primary" @click="onExtractFields" v-loading="extractFieldsLoading">{{$t('Extract Fields')}}
</el-button>
<el-button type="warning" @click="onPreview" v-loading="previewLoading">{{$t('Preview')}}</el-button>
<el-button type="success" @click="onSave" v-loading="saveLoading">{{$t('Save')}}</el-button>
</div>
</el-row>
<!--./button group-->
<el-row>
<el-col :span="12">
<el-row>
<span class="selector-type-item" @click="onClickSelectorType('css')">
<el-tag
:class="isCss ? 'active' : 'inactive'"
type="success"
>
CSS
</el-tag>
</span>
<span class="selector-type-item" @click="onClickSelectorType('xpath')">
<el-tag
:class="isXpath ? 'active' : 'inactive'"
type="primary"
>
XPath
</el-tag>
</span>
</el-row>
</el-col>
<!--list field list-->
<el-row v-if="['list','list-detail'].includes(spiderForm.crawl_type)"
class="list-fields-container">
<fields-table-view
type="list"
title="List Page Fields"
:fields="spiderForm.fields"
/>
<el-col :span="12">
<!--button group-->
<el-row class="button-group-container">
<div class="button-group">
<el-button type="danger" @click="onCrawl">{{$t('Run')}}</el-button>
<el-button type="primary" @click="onExtractFields" v-loading="extractFieldsLoading">{{$t('Extract Fields')}}
</el-button>
<el-button type="warning" @click="onPreview" v-loading="previewLoading">{{$t('Preview')}}</el-button>
<el-button type="success" @click="onSave" v-loading="saveLoading">{{$t('Save')}}</el-button>
</div>
</el-row>
<!--./button group-->
</el-col>
</el-row>
<el-collapse
v-model="activeNames"
>
<el-collapse-item
v-for="(stage, stageName) in spiderForm.config.stages"
:key="stageName"
>
<template slot="title">
{{stageName}}
</template>
<fields-table-view
type="list"
title="List Page Fields"
:fields="stage.fields"
:stage-names="Object.keys(spiderForm.config.stages)"
/>
<!-- <el-table-->
<!-- :data="stage.fields"-->
<!-- >-->
<!-- <el-table-column-->
<!-- v-for="fCol in fieldColumns"-->
<!-- :key="fCol.name"-->
<!-- :label="$t(fCol.label)"-->
<!-- >-->
<!-- <template slot-scope="scope">-->
<!-- <template v-if="fCol.name === 'selector_type'">-->
<!-- <el-tag :class="scope.row.css ? 'active' : 'inactive'" type="success">CSS</el-tag>-->
<!-- <el-tag :class="scope.row.xpath ? 'active' : 'inactive'" type="primary">XPath</el-tag>-->
<!-- </template>-->
<!-- <template v-else>-->
<!-- {{scope.row[fCol.name]}}-->
<!-- </template>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- </el-table>-->
</el-collapse-item>
</el-collapse>
<!--list field list-->
<!-- <el-row v-if="['list','list-detail'].includes(spiderForm.crawl_type)"-->
<!-- class="list-fields-container">-->
<!-- <fields-table-view-->
<!-- type="list"-->
<!-- title="List Page Fields"-->
<!-- :fields="spiderForm.fields"-->
<!-- />-->
<!-- </el-row>-->
<!--./list field list-->
<!--detail field list-->
<el-row v-if="['detail','list-detail'].includes(spiderForm.crawl_type)"
class="detail-fields-container"
style="margin-top: 10px;">
<fields-table-view
type="detail"
title="Detail Page Fields"
:fields="spiderForm.detail_fields"
/>
</el-row>
<!-- <el-row v-if="['detail','list-detail'].includes(spiderForm.crawl_type)"-->
<!-- class="detail-fields-container"-->
<!-- style="margin-top: 10px;">-->
<!-- <fields-table-view-->
<!-- type="detail"-->
<!-- title="Detail Page Fields"-->
<!-- :fields="spiderForm.detail_fields"-->
<!-- />-->
<!-- </el-row>-->
<!--./detail field list-->
</div>
</template>
@@ -146,9 +208,13 @@ import CrawlConfirmDialog from '../Common/CrawlConfirmDialog'
export default {
name: 'ConfigList',
components: { CrawlConfirmDialog, FieldsTableView },
components: {
CrawlConfirmDialog,
FieldsTableView
},
data () {
return {
activeNames: [],
crawlTypeList: [
{ value: 'list', label: 'List Only' },
{ value: 'detail', label: 'Detail Only' },
@@ -159,7 +225,15 @@ export default {
saveLoading: false,
dialogVisible: false,
crawlConfirmDialogVisible: false,
columnsDict: {}
columnsDict: {},
fieldColumns: [
{ name: 'name', label: 'Name' },
{ name: 'selector_type', label: 'Selector Type' },
{ name: 'selector', label: 'Selector' },
{ name: 'is_attr', label: 'Is Attribute' },
{ name: 'attr', label: 'Attribute' },
{ name: 'next_stage', label: 'Next Stage' }
]
}
},
computed: {
@@ -177,6 +251,24 @@ export default {
} else {
return []
}
},
isCss () {
let i = 0
Object.values(this.spiderForm.config.stages).forEach(stage => {
stage.fields.forEach(field => {
if (!field.css) i++
})
})
return i === 0
},
isXpath () {
let i = 0
Object.values(this.spiderForm.config.stages).forEach(stage => {
stage.fields.forEach(field => {
if (!field.xpath) i++
})
})
return i === 0
}
},
methods: {
@@ -294,7 +386,20 @@ export default {
value = value.trim()
if (value.length > 20) return value.substr(0, 20) + '...'
return value
}
},
onClickSelectorType (selectorType) {
Object.values(this.spiderForm.config.stages).forEach(stage => {
stage.fields.forEach(field => {
if (selectorType === 'css') {
if (field.xpath) field.xpath = ''
if (!field.css) field.css = 'body'
} else {
if (field.css) field.css = ''
if (!field.xpath) field.xpath = '//body'
}
})
})
},
},
created () {
// fields for list page
@@ -327,6 +432,9 @@ export default {
if (!this.spiderForm.pagination_selector_type) this.$set(this.spiderForm, 'pagination_selector_type', 'css')
if (this.spiderForm.obey_robots_txt == null) this.$set(this.spiderForm, 'obey_robots_txt', true)
if (this.spiderForm.item_threshold == null) this.$set(this.spiderForm, 'item_threshold', 10)
},
mounted () {
this.activeNames = Object.keys(this.spiderForm.config.stages)
}
}
</script>
@@ -335,7 +443,7 @@ export default {
.button-group-container {
margin-top: 10px;
border-bottom: 1px dashed #dcdfe6;
/*border-bottom: 1px dashed #dcdfe6;*/
padding-bottom: 20px;
}
@@ -345,7 +453,7 @@ export default {
.list-fields-container {
margin-top: 20px;
border-bottom: 1px dashed #dcdfe6;
/*border-bottom: 1px dashed #dcdfe6;*/
padding-bottom: 20px;
}
@@ -369,4 +477,17 @@ export default {
.el-table.table-header >>> .el-input .el-input__inner {
border-radius: 0;
}
.selector-type-item {
margin: 0 5px;
cursor: pointer;
}
.selector-type-item > .el-tag.active {
font-weight: bolder;
}
.selector-type-item > .el-tag.inactive {
opacity: 0.5;
}
</style>

View File

@@ -1,73 +1,104 @@
<template>
<div class="fields-table-view">
<el-row class="button-group-container">
<label class="title">{{$t(this.title)}}</label>
<div class="button-group">
<el-button type="primary" size="small" @click="addField" icon="el-icon-plus">{{$t('Add Field')}}</el-button>
</div>
</el-row>
<!-- <el-row class="button-group-container">-->
<!-- <label class="title">{{$t(this.title)}}</label>-->
<!-- <div class="button-group">-->
<!-- <el-button type="primary" size="small" @click="addField" icon="el-icon-plus">{{$t('Add Field')}}</el-button>-->
<!-- </div>-->
<!-- </el-row>-->
<el-row>
<el-table :data="fields"
class="table edit"
:header-cell-style="{background:'rgb(48, 65, 86)',color:'white'}"
border>
<el-table-column v-if="type === 'list' && spiderForm.crawl_type === 'list-detail'"
:label="$t('Detail Page URL')"
align="center">
>
<el-table-column class-name="action" width="80px" align="right">
<template slot-scope="scope">
<el-checkbox v-model="scope.row.is_detail"
@change="onCheck(scope.row)">
</el-checkbox>
<i class="action-item el-icon-copy-document" @click="onCopyField(scope.row)"></i>
<i class="action-item el-icon-remove-outline" @click="onRemoveField(scope.row)"></i>
<i class="action-item el-icon-circle-plus-outline" @click="onAddField(scope.row)"></i>
</template>
</el-table-column>
<el-table-column :label="$t('Field Name')" width="200px">
<el-table-column :label="$t('Field Name')" width="150px">
<template slot-scope="scope">
<el-input v-model="scope.row.name" :placeholder="$t('Field Name')"
@change="onNameChange(scope.row)"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('Query Type')" width="200px">
<el-table-column :label="$t('Selector Type')" width="150px" align="center" class-name="selector-type">
<template slot-scope="scope">
<el-select v-model="scope.row.type" :placeholder="$t('Query Type')">
<el-option value="css" :label="$t('CSS Selector')"></el-option>
<el-option value="xpath" :label="$t('XPath')"></el-option>
</el-select>
<span class="button-selector-item" @click="onClickSelectorType(scope.row, 'css')">
<el-tag
:class="scope.row.css ? 'active' : 'inactive'"
type="success"
>
CSS
</el-tag>
</span>
<span class="button-selector-item" @click="onClickSelectorType(scope.row, 'xpath')">
<el-tag
:class="scope.row.xpath ? 'active' : 'inactive'"
type="primary"
>
XPath
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column :label="$t('Query')" width="250px">
<el-table-column :label="$t('Selector')" width="200px">
<template slot-scope="scope">
<el-input v-model="scope.row.query" :placeholder="$t('Query')"></el-input>
</template>
</el-table-column>
<el-table-column :label="$t('Extract Type')" width="120px">
<template slot-scope="scope">
<el-select v-model="scope.row.extract_type" :placeholder="$t('Extract Type')">
<el-option value="text" :label="$t('Text')"></el-option>
<el-option value="attribute" :label="$t('Attribute')"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('Attribute')" width="250px">
<template slot-scope="scope">
<template v-if="scope.row.extract_type === 'attribute'">
<el-input v-model="scope.row.attribute"
:placeholder="$t('Attribute')">
</el-input>
<template v-if="scope.row.css">
<el-input v-model="scope.row.css" :placeholder="$t('CSS')"></el-input>
</template>
<template v-else>
<el-input v-model="scope.row.xpath" :placeholder="$t('XPath')"></el-input>
</template>
</template>
</el-table-column>
<el-table-column :label="$t('Action')" fixed="right" min-width="100px">
<el-table-column :label="$t('Is Attribute')" width="150px" align="center">
<template slot-scope="scope">
<div class="action-button-group">
<el-button size="mini"
style="margin-left:10px"
icon="el-icon-delete"
type="danger"
@click="deleteField(scope.$index)">
</el-button>
</div>
<span class="button-selector-item" @click="onClickIsAttribute(scope.row, false)">
<el-tag
:class="!scope.row.attr ? 'active' : 'inactive'"
type="success"
>
{{$t('Text')}}
</el-tag>
</span>
<span class="button-selector-item" @click="onClickIsAttribute(scope.row, true)">
<el-tag
:class="scope.row.attr ? 'active' : 'inactive'"
type="primary"
>
{{$t('Attribute')}}
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column :label="$t('Attribute')" width="200px">
<template slot-scope="scope">
<template v-if="scope.row.attr">
<el-input v-model="scope.row.attr" :placeholder="$t('Attribute')"/>
</template>
<template v-else>
<span style="margin-left: 15px">
N/A
</span>
</template>
</template>
</el-table-column>
<el-table-column :label="$t('Next Stage')" width="250px">
<template slot-scope="scope">
<el-select
v-model="scope.row.next_stage"
>
<el-option :label="$t('No Next Stage')" value=""/>
<el-option v-for="n in stageNames" :key="n" :label="n" :value="n"/>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('Remark')" width="auto" min-width="120px">
<template slot-scope="scope">
<el-input v-model="scope.row.remark" :placeholder="$t('Remark')"/>
</template>
</el-table-column>
</el-table>
@@ -91,6 +122,12 @@ export default {
type: String,
default: ''
},
stageNames: {
type: Array,
default () {
return []
}
},
fields: {
type: Array,
default () {
@@ -128,6 +165,57 @@ export default {
}
})
this.$st.sendEv('爬虫详情-配置', '设置详情页URL')
},
onClickSelectorType (row, selectorType) {
if (selectorType === 'css') {
if (row.xpath) row.xpath = ''
if (!row.css) row.css = 'body'
} else {
if (row.css) row.css = ''
if (!row.xpath) row.xpath = '//body'
}
},
onClickIsAttribute (row, isAttribute) {
if (!isAttribute) {
// 文本
if (row.attr) row.attr = ''
} else {
// 属性
if (!row.attr) row.attr = 'href'
}
},
onCopyField (row) {
for (let i = 0; i < this.fields.length; i++) {
if (row.name === this.fields[i].name) {
this.fields.splice(i, 0, JSON.parse(JSON.stringify(row)))
break
}
}
},
onRemoveField (row) {
for (let i = 0; i < this.fields.length; i++) {
if (row.name === this.fields[i].name) {
this.fields.splice(i, 1)
break
}
}
if (this.fields.length === 0) {
this.fields.push({
css: 'body',
next_stage: ''
})
}
},
onAddField (row) {
for (let i = 0; i < this.fields.length; i++) {
if (row.name === this.fields[i].name) {
this.fields.splice(i + 1, 0, {
css: 'body',
next_stage: ''
})
break
}
}
}
}
}
@@ -158,6 +246,50 @@ export default {
line-height: 36px;
}
.el-table.edit >>> .button-selector-item {
cursor: pointer;
margin: 0 5px;
}
.el-table.edit >>> .el-tag.inactive {
opacity: 0.5;
}
.el-table.edit >>> .action {
background: none !important;
border: none;
}
.el-table.edit >>> tr {
border: none;
}
.el-table.edit >>> tr th {
border-right: 1px solid rgb(220, 223, 230);
}
.el-table.edit >>> tr td:nth-child(2) {
border-left: 1px solid rgb(220, 223, 230);
}
.el-table.edit >>> tr td {
border-right: 1px solid rgb(220, 223, 230);
}
.el-table.edit::before {
background: none;
}
.el-table.edit >>> .action-item {
font-size: 14px;
margin-right: 5px;
cursor: pointer;
}
.el-table.edit >>> .action-item:last-child {
margin-right: 10px;
}
.button-group-container {
/*display: inline-block;*/
/*width: 100%;*/

View File

@@ -111,17 +111,24 @@
<div class="filter">
<div class="left">
<el-form :inline="true">
<el-form-item>
<el-select clearable @change="onSpiderTypeChange" placeholder="爬虫类型" size="small" v-model="filter.type">
<el-option v-for="item in types" :value="item.type" :key="item.type"
:label="item.type === 'customized'? '自定义':item.type "/>
</el-select>
</el-form-item>
<!-- <el-form-item>-->
<!-- <el-select clearable @change="onSpiderTypeChange" placeholder="爬虫类型" size="small" v-model="filter.type">-->
<!-- <el-option v-for="item in types" :value="item.type" :key="item.type"-->
<!-- :label="item.type === 'customized'? '自定义':item.type "/>-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<el-form-item>
<el-input clearable @keyup.enter.native="onSearch" size="small" placeholder="名称" v-model="filter.keyword">
<i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</el-form-item>
<el-form-item>
<el-button size="small" type="success"
class="btn refresh"
@click="onRefresh">
{{$t('Search')}}
</el-button>
</el-form-item>
</el-form>
</div>
<div class="right">
@@ -134,18 +141,13 @@
@click="onAdd">
{{$t('Add Spider')}}
</el-button>
<el-button size="small" type="success"
icon="el-icon-refresh"
class="btn refresh"
@click="onRefresh">
{{$t('Refresh')}}
</el-button>
</div>
</div>
<!--./filter-->
<!--tabs-->
<el-tabs v-model="activeTab" @tab-click="onClickTab">
<el-tabs v-model="filter.type" @tab-click="onClickTab">
<el-tab-pane :label="$t('All')" name="all"></el-tab-pane>
<el-tab-pane :label="$t('Configurable')" name="configurable"></el-tab-pane>
<el-tab-pane :label="$t('Customized')" name="customized"></el-tab-pane>
@@ -282,10 +284,9 @@ export default {
activeSpiderId: undefined,
filter: {
keyword: '',
type: ''
type: 'all'
},
types: [],
// tableData,
columns: [
{ name: 'display_name', label: 'Name', width: '160', align: 'left' },
{ name: 'type', label: 'Spider Type', width: '120' },
@@ -297,8 +298,7 @@ export default {
spiderFormRules: {
name: [{ required: true, message: 'Required Field', trigger: 'change' }]
},
fileList: [],
activeTab: 'all'
fileList: []
}
},
computed: {
@@ -499,6 +499,10 @@ export default {
this.onView(row)
}
},
onClickTab (tab) {
this.filter.type = tab.name
this.getList()
},
getList () {
let params = {
pageNum: this.pagination.pageNum,

View File

@@ -2954,9 +2954,10 @@ electron-to-chromium@^1.3.103:
version "1.3.113"
resolved "http://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.113.tgz#b1ccf619df7295aea17bc6951dc689632629e4a9"
element-ui@2.4.6:
version "2.4.6"
resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.4.6.tgz#524d3d4cac0b68745dda87311ef0d8fe541b5fc4"
element-ui@2.13.0:
version "2.13.0"
resolved "https://registry.npm.taobao.org/element-ui/download/element-ui-2.13.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felement-ui%2Fdownload%2Felement-ui-2.13.0.tgz#f6bb04e5b0a76ea5f62466044b774407ba4ebd2d"
integrity sha1-9rsE5bCnbqX2JGYES3dEB7pOvS0=
dependencies:
async-validator "~1.8.1"
babel-helper-vue-jsx-merge-props "^2.0.0"