484 lines
12 KiB
Vue
484 lines
12 KiB
Vue
<template>
|
|
<div>
|
|
<el-row>
|
|
<el-col>
|
|
<el-button @click="add()" type="success" style="margin-left: 20px;" size="mini"><i
|
|
class="el-icon-plus"></i>
|
|
新增
|
|
</el-button>
|
|
<div style="text-align: right;float:right;">
|
|
<el-input v-model="queryName" size="mini" autocomplete="off" placeholder="规则名称"
|
|
style="width:150px;margin-left:20px"
|
|
></el-input>
|
|
<el-select
|
|
v-model="queryCrawlMethod"
|
|
placeholder="采集模式"
|
|
style="width:150px;margin-left:20px"
|
|
size="mini">
|
|
<el-option
|
|
v-for="option in crawlMethodOptions"
|
|
:key="option.value"
|
|
:label="option.label"
|
|
:value="option.value">
|
|
</el-option>
|
|
</el-select>
|
|
<el-select
|
|
v-model="queryUseSelenium"
|
|
placeholder="渲染模式"
|
|
style="width:150px;margin-left:20px"
|
|
size="mini">
|
|
<el-option
|
|
v-for="option in useSeleniumOptions"
|
|
:key="option.value"
|
|
:label="option.label"
|
|
:value="option.value">
|
|
</el-option>
|
|
</el-select>
|
|
<el-button @click="query" type="primary" size="mini" style="margin-left:20px"><i
|
|
class="el-icon-search"></i> 查询
|
|
</el-button>
|
|
<el-button @click="resetSearch" type="info" size="mini" style="margin-left:20px"><i
|
|
class="el-icon-refresh"></i> 重置
|
|
</el-button>
|
|
</div>
|
|
<myTable :metaData="metadata" :tableData="tableData" :isLoading="isLoading"
|
|
@selection-change="handleSelectionChange">
|
|
<template #default="{ index }">
|
|
<el-button-group>
|
|
<el-button size="small" icon="fa fa-edit" @click="edit(tableData[index])"></el-button>
|
|
<el-button size="small" icon="fa fa-trash" @click="deleteOne(tableData[index].id)"></el-button>
|
|
</el-button-group>
|
|
</template>
|
|
</myTable>
|
|
<el-pagination
|
|
style="padding: 15px"
|
|
background
|
|
layout="total, prev, pager, next, jumper"
|
|
:total="total"
|
|
:current-page="pageIndex"
|
|
@current-change="onPageChange"
|
|
></el-pagination>
|
|
</el-col>
|
|
</el-row>
|
|
<parserDialog :metaData="metadata" :form="form" :dialogTitle="dialogTitle" :dialogVisible="dialogVisible"
|
|
:cronList="cronList" :depthList="depthList" :closeDialog="closeDialog" :submit="submit"
|
|
></parserDialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {mapActions, mapMutations, mapState} from 'vuex'
|
|
import Vue from 'vue'
|
|
import {
|
|
Button,
|
|
Card,
|
|
Checkbox,
|
|
Col,
|
|
Input,
|
|
InputNumber,
|
|
Message,
|
|
MessageBox,
|
|
Option,
|
|
Pagination,
|
|
Radio,
|
|
Select
|
|
} from 'element-ui'
|
|
import myTable from "../components/CustomTable";
|
|
import parserDialog from "./ParserDialog";
|
|
import rulesParserApi from "../../js/api/rulesParserApi";
|
|
import metaDictApi from "../../js/api/metaDictApi";
|
|
|
|
Vue.use(Card);
|
|
Vue.use(Button);
|
|
Vue.use(Input);
|
|
Vue.use(InputNumber);
|
|
Vue.use(Checkbox);
|
|
Vue.use(Col);
|
|
Vue.use(Radio);
|
|
Vue.use(Select);
|
|
Vue.use(Option);
|
|
Vue.use(Pagination);
|
|
|
|
export default {
|
|
name: "ParserTab",
|
|
components: {
|
|
'myTable': myTable,
|
|
'parserDialog': parserDialog
|
|
},
|
|
data() {
|
|
return {
|
|
modify: false,
|
|
pageIndex: 1,
|
|
queryName: "",
|
|
queryCrawlMethod: "",
|
|
queryUseSelenium: "",
|
|
useSeleniumOptions: [],
|
|
crawlMethodOptions: [],
|
|
multipleSelection: [],
|
|
dialogVisible: false,
|
|
dialogTitle: "",
|
|
form: {
|
|
parserName: "",
|
|
startUrl: "",
|
|
allowDomain: "",
|
|
crawlMethod: 0,
|
|
detailPageReg: "",
|
|
itemPropMap: [],
|
|
timeZone: 8,
|
|
depth: 2,
|
|
cron: "0 0 0/6 * * ?",
|
|
lengthLimit: "",
|
|
useSelenium: 0,
|
|
useProxy: 0,
|
|
},
|
|
formLabelWidth: "100px",
|
|
formInputWidth: "200px",
|
|
saveLoading: false,
|
|
deleteLoading: false,
|
|
depthList: [
|
|
{label: '只采起始站点', value: 0},
|
|
{label: '1', value: 1},
|
|
{label: '2', value: 2},
|
|
{label: '3', value: 3},
|
|
{label: '4', value: 4},
|
|
{label: '5', value: 5}
|
|
],
|
|
cronList: [],
|
|
}
|
|
},
|
|
methods: {
|
|
handleSelectionChange(val) {
|
|
this.multipleSelection = val;
|
|
},
|
|
setDataBody() {
|
|
let dataBody = {}
|
|
if (this.queryCrawlMethod.length > 0) {
|
|
dataBody.crawlMethod = this.queryCrawlMethod;
|
|
}
|
|
if (this.queryUseSelenium.length > 0) {
|
|
dataBody.useSelenium = this.queryUseSelenium;
|
|
}
|
|
if (this.queryName.length > 0) {
|
|
dataBody.parserName = this.queryName;
|
|
}
|
|
return dataBody;
|
|
},
|
|
onPageChange(index) {
|
|
this.pageIndex = index;
|
|
let dataBody = this.setDataBody()
|
|
this.queryPageable({
|
|
index: index - 1,
|
|
size: 10,
|
|
data: dataBody
|
|
});
|
|
},
|
|
add() {
|
|
this.resetFormData();
|
|
if (this.form.itemPropMap.length <= 0) {
|
|
this.addFieldRule()
|
|
}
|
|
this.dialogTitle = "添加记录";
|
|
this.saveLoading = false;
|
|
this.dialogVisible = true;
|
|
},
|
|
edit(data) {
|
|
this.dialogTitle = "修改记录";
|
|
this.form = JSON.parse(JSON.stringify(data));
|
|
this.form.itemPropMap = JSON.parse(this.form.itemPropMap.replaceAll("\\", "\\\\"));
|
|
let mapping = []
|
|
for (let propField in this.form.itemPropMap) {
|
|
mapping.push({field: propField, value: this.form.itemPropMap[propField]})
|
|
}
|
|
this.form.itemPropMap = mapping;
|
|
// console.log(this.form);
|
|
this.saveLoading = false;
|
|
this.dialogVisible = true;
|
|
},
|
|
deleteOne(id) {
|
|
MessageBox.confirm("是否删除?", "提示", {
|
|
confirmButtonText: "确认",
|
|
cancelButtonText: "取消",
|
|
type: "warning",
|
|
})
|
|
.then(() => {
|
|
rulesParserApi.deleteByIds({id: id}, (response) => {
|
|
if (response.data.code === 200) {
|
|
Message.success("删除成功")
|
|
this.refreshPage()
|
|
} else {
|
|
this.refreshPage()
|
|
}
|
|
})
|
|
})
|
|
.catch(() => {
|
|
});
|
|
},
|
|
closeDialog() {
|
|
this.saveLoading = false;
|
|
this.dialogVisible = false;
|
|
this.refreshPage();
|
|
},
|
|
submit() {
|
|
this.saveLoading = true;
|
|
let dataBody = {}
|
|
for (let formField in this.form) {
|
|
if (formField !== 'itemPropMap') {
|
|
dataBody[formField] = this.form[formField];
|
|
} else {
|
|
let propMap = {}
|
|
const propMapList = this.form[formField];
|
|
for (let i = 0; i < propMapList.length; ++i) {
|
|
propMap[propMapList[i].field] = propMapList[i].value
|
|
}
|
|
dataBody[formField] = JSON.stringify(propMap).replaceAll("\\\\", "\\")
|
|
// 注意,这里必须在 stringfy 之后二次将双反斜杠转换成单个,以适应日期正则表达式的情况
|
|
}
|
|
}
|
|
if (this.form.id === undefined) {
|
|
rulesParserApi.add(dataBody, (response) => {
|
|
if (response.data.code === 200) {
|
|
this.closeDialog();
|
|
Message.success("添加成功")
|
|
} else {
|
|
this.closeDialog();
|
|
Message.error("添加失败")
|
|
}
|
|
})
|
|
} else {
|
|
rulesParserApi.update(dataBody, (response) => {
|
|
if (response.data.code === 200) {
|
|
this.closeDialog();
|
|
Message.success("更新成功")
|
|
} else {
|
|
this.closeDialog();
|
|
Message.error("更新失败")
|
|
}
|
|
})
|
|
}
|
|
this.saveLoading = false;
|
|
},
|
|
query() {
|
|
this.pageIndex = 1;
|
|
let dataBody = this.setDataBody();
|
|
this.queryPageable({
|
|
index: 0,
|
|
size: 10,
|
|
data: dataBody,
|
|
});
|
|
},
|
|
buildDictTree(data, columnString) {
|
|
let siteOptions = []
|
|
let nodeList = []
|
|
data.forEach(e => {
|
|
if (e.columnName === columnString)
|
|
nodeList.push({
|
|
value: e.dictCode,
|
|
label: e.dictValue,
|
|
children: undefined,
|
|
parentDictCode: e.parentDictCode
|
|
});
|
|
})
|
|
nodeList.forEach(e1 => {
|
|
if (e1.parentDictCode !== undefined) {
|
|
nodeList.forEach(e2 => {
|
|
if (e1.parentDictCode === e2.value) {
|
|
if (e2.children === undefined) {
|
|
e2.children = []
|
|
}
|
|
e2.children.push(e1);
|
|
}
|
|
})
|
|
} else {
|
|
siteOptions.push(e1);
|
|
}
|
|
})
|
|
return siteOptions;
|
|
},
|
|
resetSearch() {
|
|
this.pageIndex = 1;
|
|
this.queryUseSelenium = ""
|
|
this.queryCrawlMethod = ""
|
|
this.queryName = "";
|
|
this.queryPageable({
|
|
index: 0,
|
|
size: 10,
|
|
data: {},
|
|
});
|
|
},
|
|
resetFormData() {
|
|
this.form = {
|
|
parserName: "",
|
|
startUrl: "",
|
|
allowDomain: "",
|
|
crawlMethod: 0,
|
|
detailPageReg: "",
|
|
itemPropMap: [],
|
|
timeZone: 8,
|
|
depth: 2,
|
|
cron: "0 0 0/6 * * ?",
|
|
lengthLimit: "",
|
|
useSelenium: 0,
|
|
useProxy: 0,
|
|
}
|
|
},
|
|
refreshPage() {
|
|
this.saveLoading = false;
|
|
let dataBody = this.setDataBody();
|
|
this.queryPageable({
|
|
index: this.pageIndex - 1,
|
|
size: 10,
|
|
data: dataBody,
|
|
});
|
|
},
|
|
addFieldRule() {
|
|
this.form.itemPropMap.push({value: '', field: 'es_urltitle'});
|
|
this.form.itemPropMap.push({value: '', field: 'es_urlcontent'});
|
|
this.form.itemPropMap.push({value: '', field: 'es_urltime'});
|
|
this.form.itemPropMap.push({value: '', field: 'es_lasttime'});
|
|
},
|
|
...
|
|
mapMutations({}),
|
|
...
|
|
mapActions({
|
|
queryPageable: 'rulesParser/queryPageable',
|
|
})
|
|
}
|
|
,
|
|
computed: {
|
|
metadata: function () {
|
|
const temp = [];
|
|
this.$store.state.metadata.metadata.forEach((element) => {
|
|
if (element.tableName === "rules_parser") {
|
|
let tempElement = {...element};
|
|
tempElement.minWidth = tempElement.columnWidth + "%";
|
|
tempElement.columnWidth = 0;
|
|
temp.push(tempElement);
|
|
}
|
|
});
|
|
return temp;
|
|
},
|
|
esFields: function () {
|
|
let temp = [];
|
|
this.$store.state.metadata.metadata.forEach(element => {
|
|
if (element.tableName === 'es_public_info') {
|
|
temp.push(element);
|
|
}
|
|
});
|
|
return temp;
|
|
},
|
|
...
|
|
mapState({
|
|
tableData: (state) => state.rulesParser.table,
|
|
total: (state) => state.rulesParser.count,
|
|
isLoading: (state) => state.rulesParser.isLoading
|
|
})
|
|
},
|
|
mounted() {
|
|
this.refreshPage();
|
|
metaDictApi.queryByTableName('rules_parser', (response) => {
|
|
if (response.data.code === 200) {
|
|
this.useSeleniumOptions = this.buildDictTree(response.data.content, 'useSelenium');
|
|
this.crawlMethodOptions = this.buildDictTree(response.data.content, 'crawlMethod');
|
|
this.cronList = this.buildDictTree(response.data.content, 'cronList');
|
|
}
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.height {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.table {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.dialogForm {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.demo-form {
|
|
width: 400px;
|
|
}
|
|
|
|
.box-card .text_label {
|
|
text-align: right;
|
|
vertical-align: middle;
|
|
float: left;
|
|
font-size: 14px;
|
|
color: #606266;
|
|
padding: 0 12px 0 0;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
line-height: 28px;
|
|
}
|
|
|
|
.box-card {
|
|
background-color: #f8f8f8;
|
|
margin-bottom: 10px;
|
|
padding-bottom: 15px;
|
|
}
|
|
|
|
.box-card .title {
|
|
text-align: center;
|
|
font-size: 18px;
|
|
padding: 10px;
|
|
color: #666;
|
|
}
|
|
|
|
.box-card .dot {
|
|
display: inline-block;
|
|
vertical-align: middle;
|
|
width: 6px;
|
|
height: 6px;
|
|
margin: 0 15px;
|
|
background-color: #0080e5;
|
|
}
|
|
|
|
.el-input__inner {
|
|
height: 28px !important;
|
|
line-height: 28px !important;
|
|
}
|
|
|
|
.card-item {
|
|
height: 41px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.el-cascader--mini {
|
|
width: 100%;
|
|
}
|
|
|
|
.logintype_content {
|
|
margin: 5px;
|
|
border: 1px solid rgb(221, 221, 221);
|
|
background-color: rgb(242, 242, 242);
|
|
padding: 10px 10px 0 10px;
|
|
}
|
|
|
|
.el-page-header, .el-page-header__content {
|
|
color: #666 !important;
|
|
}
|
|
|
|
.parse-button-icon {
|
|
text-align: center;
|
|
color: #409EFF;
|
|
}
|
|
|
|
.el-radio {
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.el-button:focus, .el-button:hover {
|
|
margin-right: 0px;
|
|
}
|
|
|
|
.el-pagination {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
</style> |