Files
tougao_web/src/components/page/scholarCrawlers.vue

435 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="scholar-db-container">
<div class="crumbs">
<el-breadcrumb separator="/">
<el-breadcrumb-item> <i class="el-icon-user"></i> Scholar Database </el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="toolbar">
<div class="filters">
<el-form :inline="true" :model="query" size="small">
<el-form-item label="">
<el-cascader
ref="cascader"
@change="handleChange(index)"
v-model="major_id"
:placeholder="'Please select field'"
:options="options"
:props="getProps()"
style="width: 260px"
></el-cascader>
</el-form-item>
<el-form-item label="">
<el-input v-model="query.keyword" placeholder="Name / Email / Affiliation" clearable style="width: 260px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" :loading="loading" @click="handleSearch"> Search </el-button>
<el-button @click="handleReset">Reset</el-button>
</el-form-item>
</el-form>
</div>
<div class="actions">
<el-button type="success" icon="el-icon-download" @click="handleExport" :loading="exportLoading">
Download Excel
</el-button>
</div>
</div>
<el-card class="table-card" shadow="never">
<div class="source-tag">Data sourced from PubMed</div>
<el-table :data="list" border stripe v-loading="loading" header-row-class-name="dark-table-header">
<el-table-column prop="name" label="Base Information" min-width="220">
<template slot-scope="scope">
<div>
<p class="info-row">
<span class="label">Name: </span><span class="value bold">{{ scope.row.name }}</span>
</p>
<p class="info-row">
<span class="label">Email: </span><span class="value link">{{ scope.row.email }}</span>
</p>
<p class="info-row" style="margin-top: 10px; font-size: 12px">
<span class="label">Acquisition Time:</span>
<span class="value time">{{ scope.row.ctime_text ? scope.row.ctime_text : '-' }}</span>
</p>
<span class="custom-tag">{{ scope.row.state_text }}</span>
</div>
</template></el-table-column
>
<el-table-column prop="affiliation" label="Affiliation" min-width="260" />
<el-table-column prop="fieldDisplay" label="Research areas" min-width="200">
<template slot-scope="scope">
<div v-for="(field, index) in scope.row.fields" :key="index">
<span
><span style="color: #006699">{{ index + 1 }}.</span> {{ field.field }}</span
>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:current-page="query.pageIndex"
:page-size="query.pageSize"
:page-sizes="[10, 20, 50]"
:total="total"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div>
</el-card>
</div>
</template>
<script>
import Common from '@/components/common/common';
export default {
name: 'scholarCrawlers',
data() {
return {
mediaUrl: Common.mediaUrl,
major_id: [],
majors: [],
query: {
major_id: null,
keyword: '',
pageIndex: 1,
pageSize: 10
},
list: [],
total: 0,
loading: false,
exportLoading: false
};
},
created() {
this.loadFields();
this.fetchList();
},
methods: {
loadFields() {
this.$api.post('api/Major/getMajorList', {}).then((res) => {
const transformData = (data) => {
return data.map((item) => {
const transformedItem = {
...item,
value: item.major_id,
label: `${item.major_title}`
};
// 如果存在 children递归处理
if (item.children && item.children.length > 0) {
transformedItem.children = transformData(item.children);
}
return transformedItem;
});
};
// 执行递归,获取选项数据
const data = transformData(res.data.majors.find((item) => item.major_id == 1).children);
this.options = [...data]; // 将选项数据赋给 options
});
},
handleChange(i) {
this.$nextTick(() => {
this.$refs[`cascader`].dropDownVisible = false;
this.query.major_id = this.major_id[this.major_id.length - 1];
this.query.pageIndex = 1;
this.fetchList();
this.$forceUpdate();
});
},
getProps() {
return {
value: 'value',
label: 'label',
children: 'children',
checkStrictly: true, // 允许任意选择一级
expandTrigger: 'hover' // 使用 hover 触发展开
};
},
async fetchMajors() {
try {
const res = await this.$api.post('api/Ucenter/getMajor', { major_id: 1 });
if (res && res.code === 0 && res.data && res.data.major) {
this.majors = res.data.major.children || [];
}
} catch (e) {
// ignore
}
},
async fetchList() {
this.loading = true;
try {
const params = {
major_id: this.query.major_id,
keyword: this.query.keyword,
pageIndex: this.query.pageIndex,
pageSize: this.query.pageSize
};
const res = await this.$api.post('api/expert_manage/getList', params);
if (res && res.code === 0 && res.data) {
const rawList = res.data.list || [];
// 后端已提供单个 field 字段,直接用于列表展示
this.list = rawList.map((item) => {
// 1. 获取后端返回的原始 field 数组(看截图应该是 item.field
const fieldArray = item.fields || [];
// 2. 提取出数组中每个对象里的 'field' 属性字符串
// 如果只需要展示文本,可以用 join 连起来
const fieldNames = fieldArray.map((f) => f.field).join(', ');
return {
...item,
fieldDisplay: fieldNames
};
});
this.total = res.data.total || 0;
} else {
this.list = [];
this.total = 0;
}
} catch (e) {
this.list = [];
this.total = 0;
} finally {
this.loading = false;
}
},
handleSearch() {
this.query.pageIndex = 1;
this.fetchList();
},
handleReset() {
this.major_id = [];
this.query = {
major_id: null,
keyword: '',
pageIndex: 1,
pageSize: 10
};
this.fetchList();
},
handleSizeChange(size) {
this.query.pageSize = size;
this.query.pageIndex = 1;
this.fetchList();
},
handlePageChange(page) {
this.query.pageIndex = page;
this.fetchList();
},
async handleExport() {
// 导出前必须至少选择领域或填写关键字
if (!this.query.major_id && !this.query.keyword) {
this.$message.warning('Please select a research area or enter a keyword before exporting.');
return;
}
this.exportLoading = true;
try {
const params = {
major_id: this.query.major_id,
keyword: this.query.keyword
};
const res = await this.$api.post('api/expert_manage/exportExcel', params);
if (res && res.code === 0 && res.data && res.data.file_url) {
window.open(this.mediaUrl + res.data.file_url, '_blank');
} else {
this.$message.error(res.msg || 'Export failed');
}
} catch (e) {
this.$message.error(e.msg || 'Export failed');
} finally {
this.exportLoading = false;
}
}
}
};
</script>
<style scoped>
.scholar-db-container {
padding: 0 10px;
}
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15px;
margin-top: 15px;
}
.title {
font-size: 18px;
font-weight: 600;
display: flex;
align-items: center;
}
.title i {
margin-right: 8px;
}
.filters {
flex: 1;
margin: 0 20px;
margin-left: 0;
}
.actions {
white-space: nowrap;
}
.table-card {
margin-top: 10px;
}
.pagination {
margin-top: 15px;
text-align: right;
}
/deep/ .dark-table-header th {
background-color: #f5f7fa;
font-weight: 600;
}
/deep/ .el-form-item--mini.el-form-item,
.el-form-item--small.el-form-item {
margin-bottom: 0;
}
.custom-tag {
/* 基础布局 */
display: inline-block;
position: absolute;
right: 6px;
top: 0px;
/* 颜色与背景 */
color: #ce4f15; /* 深灰色文字 */
/* 形状 */
/* font-weight: bold; */
/* 字体 */
font-size: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* 每一行的布局 */
.info-row {
margin-bottom: 4px;
font-size: 14px;
display: flex;
align-items: center;
line-height: 1.2;
font-family: Arial, sans-serif;
}
/* 左侧 Label 样式 */
.label {
color: #999; /* 浅灰色 */
margin-right: 8px;
flex-shrink: 0;
}
/* 右侧 Value 样式 */
.value {
color: #333;
}
.value.bold {
font-weight: bold;
font-size: 15px;
}
.value.link {
color: #0066a1;
} /* 邮箱颜色 */
.value.time {
color: #888;
} /* 邮箱颜色 */
.value.italic {
font-style: italic;
color: #666;
}
.blue-text {
color: #0066a1;
}
/* H-指数特有颜色 */
.h-score {
font-weight: bold;
margin-right: 15px;
}
.green {
color: #28a745;
}
.red {
color: #dc3545;
}
/* 星星评分 */
.stars {
color: #ffb400;
font-size: 16px;
}
.stars .half {
font-size: 14px;
}
/* 状态标签 (参考你之前的要求) */
.status-tag {
background-color: #fffbe6; /* 浅黄背景 */
color: #d48806; /* 橙黄色文字 */
border: 1px solid #ffe58f;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.source-tag {
/* 布局 */
float: right;
display: inline-flex;
align-items: center;
padding: 6px 16px;
margin-top: -10px;
margin-bottom: 10px;
/* 颜色与背景 */
background-color: #f0f7ff; /* 极浅蓝背景 */
color: #0077cc; /* 品牌蓝文字 */
border: 1px solid #e1f0ff; /* 浅蓝色边框 */
/* 形状与阴影 */
border-radius: 20px;
box-shadow: 0 2px 6px rgba(0, 119, 204, 0.1); /* 淡淡的蓝色投影 */
/* 字体 */
font-size: 11px;
font-weight: bold;
letter-spacing: 0.5px; /* 字间距增加 */
}
/* 前置小圆点 */
.source-tag::before {
content: '';
width: 8px;
height: 8px;
background-color: #0095ff; /* 亮蓝色圆点 */
border-radius: 50%;
margin-right: 10px;
}
</style>