|
@@ -0,0 +1,542 @@
|
|
|
+<template>
|
|
|
+ <el-scrollbar ref="scrollbar" class="analysis" @scroll="scroll">
|
|
|
+ <div class="head">
|
|
|
+ <div class="title">分类舆情</div>
|
|
|
+ <div class="searchRow">
|
|
|
+ <div class="searchCol searchTitle">省:</div>
|
|
|
+ <div
|
|
|
+ :class="{
|
|
|
+ searchCol: true,
|
|
|
+ searchActive: searchActive.area === optionindex,
|
|
|
+ }"
|
|
|
+ v-for="(optionitem, optionindex) in areaification"
|
|
|
+ :key="optionindex + 'option'"
|
|
|
+ v-text="optionitem.name"
|
|
|
+ @click="() => clickSelect('area', optionindex)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="searchRow"
|
|
|
+ v-if="
|
|
|
+ areaification[searchActive.area] &&
|
|
|
+ areaification[searchActive.area].child
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <div class="searchCol searchTitle">市:</div>
|
|
|
+ <div
|
|
|
+ :class="{
|
|
|
+ searchCol: true,
|
|
|
+ searchActive: searchActive.areaSon === optionindex,
|
|
|
+ }"
|
|
|
+ v-for="(optionitem, optionindex) in areaification[searchActive.area]
|
|
|
+ .child"
|
|
|
+ :key="optionindex + 'optionSon'"
|
|
|
+ v-text="optionitem.name"
|
|
|
+ @click="() => clickSelect('areaSon', optionindex)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="searchRow">
|
|
|
+ <div class="searchCol searchTitle">分类:</div>
|
|
|
+ <div
|
|
|
+ :class="{
|
|
|
+ searchCol: true,
|
|
|
+ searchActive: searchActive.classification === optionindex,
|
|
|
+ }"
|
|
|
+ v-for="(optionitem, optionindex) in classification"
|
|
|
+ :key="optionindex + 'option'"
|
|
|
+ v-text="optionitem.name"
|
|
|
+ @click="() => clickSelect('classification', optionindex)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="searchRow">
|
|
|
+ <div class="searchCol searchTitle">时间:</div>
|
|
|
+ <div
|
|
|
+ :class="{
|
|
|
+ searchCol: true,
|
|
|
+ searchActive: searchActive.time === optionindex,
|
|
|
+ }"
|
|
|
+ v-for="(optionitem, optionindex) in time"
|
|
|
+ :key="optionindex + 'option'"
|
|
|
+ v-text="optionitem.name"
|
|
|
+ @click="() => clickSelect('time', optionindex)"
|
|
|
+ ></div>
|
|
|
+ <div
|
|
|
+ :class="{
|
|
|
+ searchCol: true,
|
|
|
+ searchActive: searchActive.time === -1,
|
|
|
+ }"
|
|
|
+ @click="() => clickSelect('time', -1)"
|
|
|
+ >
|
|
|
+ 自定义
|
|
|
+ </div>
|
|
|
+ <div class="searchCol">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="date"
|
|
|
+ :disabled="searchActive.time !== -1"
|
|
|
+ @change="change"
|
|
|
+ type="daterange"
|
|
|
+ :clearable="false"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ range-separator="-"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="searchRow">
|
|
|
+ <div class="searchCol searchTitle">搜索:</div>
|
|
|
+ <div class="searchCol">
|
|
|
+ <el-input v-model="searchText" placeholder="搜索文章">
|
|
|
+ <template #suffix>
|
|
|
+ <el-icon @click="search"><Search /></el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="body">
|
|
|
+ <div class="mainTitle">
|
|
|
+ <div class="mainTitleTool" v-if="selectlist.length">
|
|
|
+ <el-select
|
|
|
+ v-model="selectValue"
|
|
|
+ class="m-2"
|
|
|
+ placeholder="Select"
|
|
|
+ @change="changeSelect"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in selectlist"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ 文章列表
|
|
|
+ </div>
|
|
|
+ <br />
|
|
|
+ <div class="lists">
|
|
|
+ <div
|
|
|
+ class="list"
|
|
|
+ v-for="item in listTable"
|
|
|
+ :key="item.title"
|
|
|
+ @click="() => toDetail(item)"
|
|
|
+ >
|
|
|
+ <div class="listHead" v-text="item ? item.title || '' : ''"></div>
|
|
|
+ <div
|
|
|
+ class="listSubtitle"
|
|
|
+ v-html="item ? item.summary || '' : ''"
|
|
|
+ ></div>
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="12" style="color: #9aa8c4">
|
|
|
+ <el-icon><Clock /></el-icon>
|
|
|
+ <span v-text="item.publishTime"></span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12" style="text-align: right">
|
|
|
+ 来源:
|
|
|
+ <span class="source" v-text="item.sourceWebsite"></span>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="hotList">
|
|
|
+ <div class="title">
|
|
|
+ <img :src="hotIcon" style="width: 18px; height: 18px" />
|
|
|
+ 热度排行
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="hotItem"
|
|
|
+ v-for="(item, index) in listHot"
|
|
|
+ :key="item.rk"
|
|
|
+ @click="() => searchHot(item.content)"
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ :style="{ color: colorRanking[index] || '#999', 'font-size': '14px' }"
|
|
|
+ >{{ index + 1 }}</span
|
|
|
+ >
|
|
|
+ {{ item.content }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-icon :size="45" class="upload" v-show="showUpload" @click="upload"
|
|
|
+ ><Upload
|
|
|
+ /></el-icon>
|
|
|
+ </el-scrollbar>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import dayjs from 'dayjs';
|
|
|
+import { ref } from 'vue';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { getEconomize, hotRank, searchData, getClass } from '../../api/index';
|
|
|
+
|
|
|
+import hotIcon from '../../assets/img/hot.png';
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
+const colorRanking = ['#FE2D46', '#F60', '#FAA90E'];
|
|
|
+const nowTime = Date.now();
|
|
|
+const scrollbar = ref();
|
|
|
+const showUpload = ref(false);
|
|
|
+const searchActive = ref({
|
|
|
+ classification: 0,
|
|
|
+ time: 0,
|
|
|
+ area: 0,
|
|
|
+ areaSon: 0,
|
|
|
+});
|
|
|
+const defaultOption = {
|
|
|
+ type: 'option',
|
|
|
+ name: '全部',
|
|
|
+ id: 0,
|
|
|
+};
|
|
|
+const classification = ref([defaultOption]);
|
|
|
+const areaification = ref([defaultOption]);
|
|
|
+const time = [
|
|
|
+ {
|
|
|
+ type: 'option',
|
|
|
+ time: nowTime - 86400000,
|
|
|
+ name: '24小时',
|
|
|
+ id: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'option',
|
|
|
+ time: nowTime - 172800000,
|
|
|
+ name: '48小时',
|
|
|
+ id: 2,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'option',
|
|
|
+ time: nowTime - 604800000,
|
|
|
+ name: '近7天',
|
|
|
+ id: 3,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'option',
|
|
|
+ time: nowTime - 2592000000,
|
|
|
+ name: '近30天',
|
|
|
+ id: 4,
|
|
|
+ },
|
|
|
+];
|
|
|
+const searchText = ref('');
|
|
|
+const selectlist = [
|
|
|
+ // {
|
|
|
+ // label: '按发布时间降序',
|
|
|
+ // value: 0,
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // label: '按发布时间升序',
|
|
|
+ // value: 1,
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // label: '按发情感值升序',
|
|
|
+ // value: 2,
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // label: '按发情感值降序',
|
|
|
+ // value: 3,
|
|
|
+ // },
|
|
|
+];
|
|
|
+const selectValue = ref(0);
|
|
|
+const date = ref([]);
|
|
|
+
|
|
|
+const listTable = ref([]);
|
|
|
+const listHot = ref([]);
|
|
|
+
|
|
|
+const pageSize = 10;
|
|
|
+let page = 1;
|
|
|
+let total = -1;
|
|
|
+let T = undefined;
|
|
|
+
|
|
|
+if (localStorage.getItem('Economize')) {
|
|
|
+ areaification.value = JSON.parse(localStorage.getItem('Economize'));
|
|
|
+} else
|
|
|
+ getEconomize({
|
|
|
+ data: {
|
|
|
+ key: 'b264662362c25150a1e6612013a7b921',
|
|
|
+ keywords: '中国',
|
|
|
+ subdistrict: 2,
|
|
|
+ },
|
|
|
+ }).then(r => {
|
|
|
+ const list = r.districts[0].districts || [];
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ const v = list[i];
|
|
|
+ areaification.value.push({
|
|
|
+ type: 'option',
|
|
|
+ name: v.name,
|
|
|
+ child: v.districts.map((item, index) => {
|
|
|
+ return {
|
|
|
+ type: 'option',
|
|
|
+ name: item.name,
|
|
|
+ id: index,
|
|
|
+ };
|
|
|
+ }),
|
|
|
+ id: i,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ localStorage.setItem('Economize', JSON.stringify(areaification.value));
|
|
|
+ console.log(areaification.value);
|
|
|
+ });
|
|
|
+
|
|
|
+getClass({}).then(res => {
|
|
|
+ const li = res || [];
|
|
|
+ const l = [];
|
|
|
+ for (let i = 0; i < li.length; i++) {
|
|
|
+ const v = li[i];
|
|
|
+ l.push({
|
|
|
+ type: 'option',
|
|
|
+ name: v,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ classification.value.push(...l);
|
|
|
+});
|
|
|
+hotRank({}).then(res => {
|
|
|
+ listHot.value = res || [];
|
|
|
+});
|
|
|
+const toDetail = item => {
|
|
|
+ router.push({
|
|
|
+ path: '/analysis_detail',
|
|
|
+ query: {
|
|
|
+ detail: JSON.stringify(item),
|
|
|
+ },
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const getList = () => {
|
|
|
+ const search = {
|
|
|
+ category:
|
|
|
+ classification.value[searchActive.value.classification].name || undefined,
|
|
|
+ keywords: searchText.value,
|
|
|
+ page: page++,
|
|
|
+ pageSize,
|
|
|
+ };
|
|
|
+
|
|
|
+ const sheng = areaification.value[searchActive.value.area] || {};
|
|
|
+ if (areaification.value.length && sheng.name) {
|
|
|
+ const city = [sheng.name];
|
|
|
+ sheng.child && sheng.child.length && city.push(sheng.child[searchActive.value.areaSon].name);
|
|
|
+ search.city = city.join(",");
|
|
|
+ }
|
|
|
+ search.category === '全部' ? (search.category = '') : '';
|
|
|
+ // 时间区间
|
|
|
+ if (searchActive.value['time'] === -1) {
|
|
|
+ search.start = date.value[0];
|
|
|
+ search.end = date.value[1];
|
|
|
+ } else {
|
|
|
+ search.start = dayjs(time[searchActive.value['time']].time).format(
|
|
|
+ 'YYYY-MM-DD HH:mm:ss'
|
|
|
+ );
|
|
|
+ search.end = dayjs(nowTime).format('YYYY-MM-DD HH:mm:ss');
|
|
|
+ }
|
|
|
+ searchData({
|
|
|
+ data: search,
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ const li = res.records || [];
|
|
|
+ listTable.value.push(...li);
|
|
|
+ total = res.total || 0;
|
|
|
+ if (T) T = window.clearTimeout(T);
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ if (T) T = window.clearTimeout(T);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const clickSelect = (select, index) => {
|
|
|
+ searchActive.value[select] = index;
|
|
|
+ if (index === -1) return;
|
|
|
+ page = 1;
|
|
|
+ total = -1;
|
|
|
+ listTable.value = [];
|
|
|
+ changeInput();
|
|
|
+};
|
|
|
+const changeSelect = () => {
|
|
|
+ // 更改排序
|
|
|
+ console.log(selectValue.value);
|
|
|
+};
|
|
|
+
|
|
|
+const change = () => {
|
|
|
+ date.value = [date.value[0] + ' 00:00:00', date.value[1] + ' 23:59:59'];
|
|
|
+ page = 1;
|
|
|
+ total = -1;
|
|
|
+ listTable.value = [];
|
|
|
+ changeInput();
|
|
|
+};
|
|
|
+
|
|
|
+const changeInput = () => {
|
|
|
+ if (page !== 1 && total < page * pageSize) return;
|
|
|
+ if (T) T = window.clearTimeout(T);
|
|
|
+ T = window.setTimeout(() => {
|
|
|
+ getList();
|
|
|
+ }, 200);
|
|
|
+};
|
|
|
+
|
|
|
+const search = () => {
|
|
|
+ page = 1;
|
|
|
+ total = -1;
|
|
|
+ listTable.value = [];
|
|
|
+ changeInput();
|
|
|
+};
|
|
|
+
|
|
|
+const searchHot = text => {
|
|
|
+ searchText.value = text;
|
|
|
+ page = 1;
|
|
|
+ total = -1;
|
|
|
+ listTable.value = [];
|
|
|
+ changeInput();
|
|
|
+};
|
|
|
+
|
|
|
+const scroll = e => {
|
|
|
+ const height =
|
|
|
+ document.querySelector('.analysis .head').offsetHeight +
|
|
|
+ document.querySelector('.analysis .body').offsetHeight -
|
|
|
+ document.querySelector('.analysis').offsetHeight;
|
|
|
+ const scrollNum = e.scrollTop.toFixed(2) - 0;
|
|
|
+ if (!showUpload.value && scrollNum > 180) showUpload.value = true;
|
|
|
+ else if (scrollNum <= 180) showUpload.value = false;
|
|
|
+ if (height - scrollNum > 0 || T) return;
|
|
|
+ changeInput();
|
|
|
+};
|
|
|
+
|
|
|
+const upload = () => {
|
|
|
+ scrollbar.value.setScrollTop(0);
|
|
|
+};
|
|
|
+
|
|
|
+getList();
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.analysis {
|
|
|
+ height: 100%;
|
|
|
+ min-width: 1305px;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+.analysis .head,
|
|
|
+.analysis .body {
|
|
|
+ margin: 0 1em;
|
|
|
+ width: calc(100% - 400px);
|
|
|
+ min-width: 855px;
|
|
|
+}
|
|
|
+
|
|
|
+.analysis .body {
|
|
|
+ border: 1px solid #f3f3f3;
|
|
|
+ margin: 1em;
|
|
|
+}
|
|
|
+
|
|
|
+.analysis .hotList {
|
|
|
+ position: absolute;
|
|
|
+ width: 300px;
|
|
|
+ right: 50px;
|
|
|
+ top: 100px;
|
|
|
+}
|
|
|
+
|
|
|
+.title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ height: 49px;
|
|
|
+ line-height: 49px;
|
|
|
+ padding-left: 8px;
|
|
|
+ border-bottom: 1px solid #f5f5f5;
|
|
|
+}
|
|
|
+
|
|
|
+.searchRow {
|
|
|
+ margin: 0.5em 0;
|
|
|
+}
|
|
|
+
|
|
|
+.searchCol {
|
|
|
+ display: inline-block;
|
|
|
+ margin: 0 0.5em;
|
|
|
+ padding: 0 0.5em;
|
|
|
+ height: 35px;
|
|
|
+ line-height: 35px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+.searchCol:hover {
|
|
|
+ color: rgb(64, 158, 255);
|
|
|
+}
|
|
|
+
|
|
|
+.searchActive,
|
|
|
+.hotItem:hover {
|
|
|
+ font-weight: 600;
|
|
|
+ color: rgb(64, 158, 255);
|
|
|
+ border-radius: 5px;
|
|
|
+ background-color: rgba(64, 158, 255, 0.1);
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.searchRow .searchTitle {
|
|
|
+ color: #b9c0d3;
|
|
|
+}
|
|
|
+
|
|
|
+.body .mainTitle {
|
|
|
+ padding: 0 20px;
|
|
|
+ position: relative;
|
|
|
+ line-height: 60px;
|
|
|
+ border-bottom: 1px solid #f5f5f5;
|
|
|
+}
|
|
|
+.body .mainTitleTool {
|
|
|
+ position: absolute;
|
|
|
+ right: 20px;
|
|
|
+ line-height: 60px;
|
|
|
+ top: 0;
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+.body .el-checkbox {
|
|
|
+ margin-right: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.listHead,
|
|
|
+.listSubtitle {
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.lists {
|
|
|
+ padding: 0.5em;
|
|
|
+}
|
|
|
+
|
|
|
+.list {
|
|
|
+ border-radius: 5px;
|
|
|
+ padding: 0.5em;
|
|
|
+ cursor: pointer;
|
|
|
+ line-height: 1.8em;
|
|
|
+ font-size: 16px;
|
|
|
+}
|
|
|
+.list:hover {
|
|
|
+ background-color: rgba(64, 158, 255, 0.1);
|
|
|
+}
|
|
|
+.list:not(:last-child) {
|
|
|
+ border-bottom: 1px dashed #b9c0d3;
|
|
|
+}
|
|
|
+
|
|
|
+.listSubtitle {
|
|
|
+ color: #b9c0d3;
|
|
|
+}
|
|
|
+
|
|
|
+.source {
|
|
|
+ color: #22ac38;
|
|
|
+}
|
|
|
+
|
|
|
+.hotItem {
|
|
|
+ line-height: 1em;
|
|
|
+ font-size: 16px;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 0.6em;
|
|
|
+}
|
|
|
+
|
|
|
+.hotItem:not(:last-child) {
|
|
|
+ border-bottom: 1px dashed #e9e9e9;
|
|
|
+}
|
|
|
+
|
|
|
+.upload {
|
|
|
+ position: absolute;
|
|
|
+ right: 25px;
|
|
|
+ bottom: 25px;
|
|
|
+ background-color: #e9e9e990;
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: 5px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+</style>
|