首页搜索

This commit is contained in:
cxc
2022-10-13 17:42:32 +08:00
parent e8fb69f0e3
commit e2da1fd45b
46 changed files with 7598 additions and 456 deletions

View File

@ -0,0 +1,107 @@
<template>
<div class="collectAndVisit">
<!-- <div>{{ visit_count }}浏览</div> -->
<div class="heart_w">
<div
class="heart"
@click="change()"
:class="{ in: state.isCollect }"
></div>
<span>{{ state.collect }}</span>
</div>
</div>
</template>
<script setup>
import request from "@/utils/request";
import { reactive } from "vue";
function launch(id) {
return request({
url: "/v1/user/collect/launch",
method: "post",
data: { kind: props.kind, object_id: props.object_id },
});
}
let flag = true;
const props = defineProps({
is_collect: {
type: Boolean,
required: true,
},
collect_count: {
type: Number,
required: true,
},
// visit_count: {
// type: Number,
// required: true,
// },
kind: {
type: Number,
required: true,
},
object_id: {
type: [String, Number],
required: true,
},
});
const state = reactive({
collect: props.collect_count,
isCollect: props.is_collect,
});
async function postData() {
if (flag) {
flag = false;
await launch()
.then((res) => {
if (200 == res.code) {
state.isCollect = res.data;
if (res.data) {
state.collect++;
} else {
state.collect--;
}
}
flag = true;
})
.catch(() => {
flag = false;
});
}
}
function change() {
postData();
}
</script>
<style lang="scss" scoped>
.collectAndVisit {
display: flex;
justify-content: space-around;
.heart_w {
vertical-align: text-bottom;
span {
vertical-align: text-bottom;
position: relative;
top: 1px;
}
}
.heart {
cursor: pointer;
user-select: none;
vertical-align: text-bottom;
display: inline-block;
width: 18.3px;
height: 16.4px;
margin-right: 5px;
background-size: cover;
background-image: url(./img/heart0.png);
}
.in {
background-image: url(./img/heart1.png);
}
}
</style>

View File

@ -0,0 +1,187 @@
<template>
<div class="box">
<div class="wrap">
<div class="img">
<!-- <img :src="data.image" alt /> -->
<el-image :src="data.image" fit="fill"></el-image>
</div>
<div class="content">
<div class="tit">
<span style="cursor: pointer" @click="handleDetail(data.id)"
>姓名{{ data.name }}</span
>
<!-- <div>
<span style="vertical-align: middle">匹配度</span>
<el-rate
style="display: inline-block"
v-model="state.val"
disabled
></el-rate>
</div> -->
</div>
<!-- <div class="labelList">
<div v-for="item in data.industrys">{{ item }}</div>
</div>-->
<div v-if="data.industrys" class="line" style="overflow: hidden">
<el-col>所属领域</el-col>
<el-col
:title="data.industrys[data.industrys.length - 1]"
style="display: inline-block; width: calc(100% - 86px)"
>
<span
style="
display: inline-block;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
>{{ data.industrys[data.industrys.length - 1] }}</span
>
</el-col>
</div>
<div class="line">
所在机构
<span>{{ data.research_name }}</span>
</div>
<div
v-if="data.patent_title"
class="line"
v-for="(patent_title, index) in data.patent_title"
:key="index"
>
{{ patent_title }}
</div>
</div>
<div class="keywordsWrap">
<div class="keywords">
<wordcloud
v-if="data.keywords"
:data="createdData(data.keywords)"
></wordcloud>
</div>
</div>
</div>
<slot></slot>
</div>
</template>
<script setup>
import wordcloud from "./wordcloud.vue";
import collectAndVisit from "./collectAndVisit.vue";
import { useRouter } from "vue-router";
const router = useRouter();
const props = defineProps({
data: {
type: Object,
required: true,
},
});
function handleDetail(id) {
let routeData = router.resolve({ path: `/searchList/4/detail/${id}` });
window.open(routeData.href, "_blank");
}
const state = reactive({
val: 3,
});
function createdData(arr) {
let l = [];
let snap = JSON.parse(JSON.stringify(arr));
snap.map((e) => {
l.push({ name: e, value: 30 });
return { name: e, value: 30 };
});
return l;
}
</script>
<style lang="scss" scoped>
.box {
width: 100%;
background: #ffffff;
padding: 20px;
box-sizing: border-box;
.wrap {
display: flex;
flex-direction: row;
.img {
width: 90px;
margin-right: 12px;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
.el-image {
width: 90px;
height: 90px;
border-radius: 50%;
}
}
.keywordsWrap {
display: flex;
align-items: center;
justify-content: center;
}
.keywords {
width: 129px;
height: 129px;
}
.content {
flex: 1;
overflow: hidden;
.labelList {
overflow: hidden;
div {
padding: 2px 4px;
float: left;
background: #0054ff;
border-radius: 4px;
margin-right: 5px;
margin-bottom: 5px;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #ffffff;
}
}
.line {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #666666;
margin: 10px 0;
span {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 500;
color: #333;
}
}
.tit {
overflow: hidden;
display: flex;
& > span {
flex: 1;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 20px;
font-family: Source Han Sans CN;
font-weight: bold;
color: #333333;
overflow: hidden;
}
& > div {
width: 175px;
vertical-align: middle;
}
}
}
}
}
</style>

View File

@ -0,0 +1,145 @@
<template>
<div class="box">
<div class="wrap">
<div class="img">
<!-- <img :src="data.image" alt /> -->
<img src="./img/achieve.png" />
</div>
<div class="content">
<div class="tit" @click="handleDetail(data.id)">{{ data.title }}</div>
<slot name="des" />
<div v-if="data.industrys" class="line">
所属领域
<span>{{ data.industrys[data.industrys.length - 1] }}</span>
</div>
<div class="line">
合作模式
<span>{{ data.mode_title }}</span>
</div>
<div class="line">
技术成熟度
<span>{{ data.maturity_title }}</span>
</div>
</div>
<div>
<div class="keywords">
<wordcloud
v-if="data.keywords"
:data="createdData(data.customers)"
></wordcloud>
</div>
<collectAndVisit
:is_collect="data.is_collect"
:collect_count="data.collect_count"
:visit_count="data.visit_count"
:object_id="data.id"
:kind="1005"
/>
</div>
</div>
</div>
</template>
<script setup>
import { maturityOptions } from "@/utils/parameter";
import wordcloud from "./wordcloud.vue";
import collectAndVisit from "./collectAndVisit.vue";
const router = useRouter();
const props = defineProps({
data: {
type: Object,
required: true,
},
});
function handleDetail(id) {
let routeData = router.resolve({ path: `/searchList/1/detail/${id}` });
window.open(routeData.href, "_blank");
}
function createdData(arr) {
let l = [];
let snap = JSON.parse(JSON.stringify(arr));
snap.map((e) => {
l.push({ name: e, value: 30 });
return { name: e, value: 30 };
});
return l;
}
</script>
<style lang="scss" scoped>
.box {
width: 100%;
height: 190px;
background: #ffffff;
padding: 20px;
box-sizing: border-box;
.wrap {
display: flex;
flex-direction: row;
.img {
width: 200px;
height: 150px;
margin-right: 10px;
img {
display: block;
margin: 0;
width: 100%;
height: 100%;
}
}
.keywords {
width: 129px;
height: 129px;
}
.content {
flex: 1;
overflow: hidden;
.labelList {
overflow: hidden;
div {
padding: 2px 4px;
float: left;
background: #0054ff;
border-radius: 4px;
margin-right: 5px;
margin-bottom: 5px;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #ffffff;
}
}
.line {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #666666;
margin: 10px 0;
span {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 500;
color: #333;
}
}
.tit {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
font-size: 20px;
font-family: Source Han Sans CN;
font-weight: bold;
color: #333333;
overflow: hidden;
cursor: pointer;
}
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

View File

@ -0,0 +1,116 @@
<template>
<div>
<el-row type="flex">
<div style="display: inline-block;line-height: 32px;">所属领域</div>
<div style="flex:1">
<el-row :gutter="10" style="margin-bottom: 10px;">
<el-col :span="8">
<el-select
style="width: 100%;"
placeholder="请选择"
v-model="state.id1"
@change="state.id2 = '';change()"
clearable
>
<el-option
v-for="option in state.industryList"
:key="option.id"
:value="option.id"
:label="option.name"
/>
</el-select>
</el-col>
<el-col :span="8">
<el-select
style="width: 100%;"
placeholder="请选择"
v-model="state.id2"
@change="state.id3 = '';change()"
clearable
>
<el-option
v-for="option in searchIndustryData(state.id1)['children']"
:key="option.id"
:value="option.id"
:label="option.name"
/>
</el-select>
</el-col>
<el-col :span="8">
<el-select
style="width: 100%;"
placeholder="请选择"
v-model="state.id3"
@change="change()"
clearable
>
<el-option
v-for="option in searchIndustryData(state.id2)['children']"
:key="option.id"
:value="option.id"
:label="option.name"
/>
</el-select>
</el-col>
</el-row>
</div>
</el-row>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
import { industry } from "@/api/website/home/index";
const state = reactive({
industryList: [],
id1: '',
id2: '',
id3: '',
});
let treeData = [];
const emit = defineEmits();
function change() {
emit("industryChange", {
id1: state.id1,
id2: state.id2,
id3: state.id3,
});
}
function searchIndustryData (code) {
return deepFindTree(code, treeData);
}
function deepFindTree (code, arr) {
let snap = false;
for (let index = 0; index < arr.length; index++) {
const ele = arr[index];
if (code == ele.id) {
snap = ele;
break;
} else if (ele['children'] != null) {
snap = deepFindTree(code, ele['children']);
if (snap) {
break;
}
} else {
snap = false;
}
}
return snap;
}
function init () {
industry().then(res => {
if (200 == res.code) {
state.industryList = res.data;
treeData = res.data;
}
})
}
onMounted(() => {
init();
});
</script>

View File

@ -0,0 +1,141 @@
<!-- to do 实验室后台模块未开发 2022/02/17 -->
<template>
<div class="box">
<div class="wrap">
<div class="img">
<!-- <img :src="data.image" alt /> -->
<img src="https://t7.baidu.com/it/u=1951548898,3927145&fm=193&f=GIF" />
</div>
<div class="content">
<div class="tit" @click="handleDetail(data.id)">{{ data.title }}</div>
<slot name="des" />
<div v-if="data.industrys" class="line">
所属领域
<span>{{ data.industrys[data.industrys.length - 1] }}</span>
</div>
<div class="line">
依托管理部门
<span>{{ data.mode_title }}</span>
</div>
<div class="line">
合作模式
<span>{{ data.mode_title }}</span>
</div>
<div class="line">
技术成熟度
<span>{{ data.maturity_title }}</span>
</div>
</div>
<div>
<div class="keywords">
<wordcloud v-if="data.keywords" :data="createdData(data.customers)"></wordcloud>
</div>
<collectAndVisit :is_collect="data.is_collect" :collect_count="data.collect_count" :visit_count="data.visit_count" :object_id="data.id" :kind="1005"/>
</div>
</div>
</div>
</template>
<script setup>
import wordcloud from './wordcloud.vue'
import collectAndVisit from './collectAndVisit.vue'
const router = useRouter();
const props = defineProps({
data: {
type: Object,
required: true,
},
});
function handleDetail (id) {
let routeData = router.resolve({ path: `/searchList/1/detail/${id}` });
window.open(routeData.href, '_blank');
}
function createdData (arr) {
let l = [];
let snap = JSON.parse(JSON.stringify(arr))
snap.map(e => {
l.push({ name: e, value: 30 })
return { name: e, value: 30 }
})
return l;
}
</script>
<style lang="scss" scoped>
.box {
width: 100%;
height: 190px;
background: #ffffff;
padding: 20px;
box-sizing: border-box;
.wrap {
display: flex;
flex-direction: row;
.img {
width: 200px;
height: 150px;
margin-right: 10px;
img {
display: block;
margin: 0;
width: 100%;
height: 100%;
}
}
.keywords {
width: 129px;
height: 129px;
}
.content {
flex: 1;
overflow: hidden;
.labelList {
overflow: hidden;
div {
padding: 2px 4px;
float: left;
background: #0054ff;
border-radius: 4px;
margin-right: 5px;
margin-bottom: 5px;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #ffffff;
}
}
.line {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #666666;
margin: 10px 0;
span {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 500;
color: #333;
}
}
.tit {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
font-size: 20px;
font-family: Source Han Sans CN;
font-weight: bold;
color: #333333;
overflow: hidden;
cursor: pointer;
}
}
}
}
</style>

View File

@ -0,0 +1,76 @@
<template>
<div v-loading="loading">
<section v-for="item in state.list">
<slot :item="item"></slot>
</section>
<div class="loadMore" v-if="isShowLoadMore" @click="getDataList()">加载更多</div>
</div>
</template>
<script setup>
import { onMounted } from 'vue';
const loading = ref(true);
const isShowLoadMore = computed({
get() {
return state.total > state.list.length;
},
});
const props = defineProps({
getData: {
type: Function,
required: true,
},
params: {
type: Object,
required: true,
default: {}
},
pageNum: {
type: Number,
default: 1,
},
pageSize: {
type: Number,
default: 5,
},
});
const state = reactive({
list: [],
pageNum: props.pageNum,
pageSize: props.pageSize,
total: 0,
});
async function getDataList () {
loading.value = true;
let snap = {
page_num: state.pageNum,
page_size: state.pageSize,
};
let _data = Object.assign({}, snap, props.params);
console.log('_data',_data)
let { code, data, msg } = await props.getData(_data);
if (200 == code) {
state.list.push.apply(state.list,data.data);
state.total = data.count;
state.pageNum++;
}
loading.value = false;
}
onMounted(() => {
getDataList()
});
</script>
<style scoped>
.loadMore {
width: 100%;
height: 30px;
line-height: 30px;
text-align: center;
background-color: #fff;
cursor: pointer;
user-select: none;
}
</style>

View File

@ -0,0 +1,126 @@
<template>
<div class="box">
<div class="wrap">
<div class="img">
<!-- <img :src="data.image" alt /> -->
<img src="https://t7.baidu.com/it/u=1951548898,3927145&fm=193&f=GIF" />
</div>
<div class="content">
<div class="tit">{{ data.title }}</div>
<div class="line">
技术成熟度
<span>{{ data.maturity_title }}</span>
</div>
<div class="line">
合作模式
<span>{{ data.cooperation_mode_title }}</span>
</div>
<div class="labelList">
<div v-for="item in data.industrys">{{ item }}</div>
</div>
</div>
<div>
<div class="keywords">
<wordcloud v-if="data.keywords" :data="createdData(data.keywords)"></wordcloud>
</div>
<collectAndVisit :is_collect="data.is_collect" :collect_count="data.collect_count" :visit_count="data.visit_count" :object_id="data.id" :kind="1005"/>
</div>
</div>
</div>
</template>
<script setup>
import wordcloud from './wordcloud.vue'
import collectAndVisit from './collectAndVisit.vue'
const props = defineProps({
data: {
type: Object,
required: true,
},
});
function createdData (arr) {
let l = [];
let snap = JSON.parse(JSON.stringify(arr))
snap.map(e => {
l.push({ name: e, value: 30 })
return { name: e, value: 30 }
})
return l;
}
</script>
<style lang="scss" scoped>
.box {
width: 100%;
height: 190px;
background: #ffffff;
padding: 20px;
box-sizing: border-box;
.wrap {
display: flex;
flex-direction: row;
.img {
width: 200px;
height: 150px;
margin-right: 10px;
img {
display: block;
margin: 0;
width: 100%;
height: 100%;
}
}
.keywords {
width: 129px;
height: 129px;
}
.content {
flex: 1;
overflow: hidden;
.labelList {
overflow: hidden;
div {
padding: 2px 4px;
float: left;
background: #0054ff;
border-radius: 4px;
margin-right: 5px;
margin-bottom: 5px;
font-size: 14px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #ffffff;
}
}
.line {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 400;
color: #666666;
margin: 10px 0;
span {
font-size: 16px;
font-family: Source Han Sans CN;
font-weight: 500;
color: #333;
}
}
.tit {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
font-size: 20px;
font-family: Source Han Sans CN;
font-weight: bold;
color: #333333;
overflow: hidden;
}
}
}
}
</style>

View File

@ -0,0 +1,114 @@
<template>
<div class="searchContainer" v-loading="loading">
<div class="wrap">
<div
class="banner"
style="height: 394px; background-size: cover; background-color: #ccc"
:style="{ backgroundImage: state.banner ? `url(${state.banner})` : '' }"
>
<div class="conter1000">
<div class="tit">{{ title }}</div>
<el-input
v-model.trim="state.currentKeyword"
placeholder="请输入检索词"
>
<template #append>
<el-button icon="Search" @click="handleQuery">搜索</el-button>
</template>
</el-input>
</div>
</div>
<div class="conter1000">
<slot></slot>
</div>
</div>
<webFooter></webFooter>
</div>
</template>
<script setup>
import webFooter from "@/components/webFooter/index.vue";
import { banner } from "@/api/website/home/index";
import { useRoute, useRouter } from "vue-router";
import { reactive, ref } from "vue";
const route = useRoute();
const loading = ref(true);
const state = reactive({
currentKeyword: "",
});
const props = defineProps({
title: {
type: String,
required: true,
},
bannerKey: {
required: true,
type: String,
default: "",
},
});
const emit = defineEmits();
function handleQuery() {
emit("handleQuery", state.currentKeyword);
}
onMounted(() => {
state.currentKeyword = route.query.keyword;
handleQuery();
banner({ locals: props.bannerKey })
.then((resp) => {
state.banner = resp.data[0].images;
loading.value = false;
})
.catch(() => {
loading.value = false;
});
});
</script>
<style lang="scss" scoped>
.searchContainer {
width: 100%;
min-height: 100%;
display: flex;
flex-direction: column;
.wrap {
flex: 1;
background: #f2f6ff;
}
.banner {
display: flex;
align-items: center;
justify-content: center;
.tit {
text-align: center;
font-size: 34px;
line-height: 80px;
font-family: Source Han Sans CN;
font-weight: bold;
color: #ffffff;
text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
}
.el-input {
::v-deep(.el-input__inner) {
border-right: 0;
}
::v-deep(.el-input-group__append) {
overflow: hidden;
font-size: 16px;
color: #fff;
border-top-color: #0054ff;
border-right-color: #0054ff;
border-bottom-color: #0054ff;
background-color: #0054ff;
}
}
::v-deep(.el-input__inner),
::v-deep(.el-input-group__append) {
height: 50px !important;
border-radius: 0;
}
}
}
</style>

View File

@ -0,0 +1,209 @@
<template>
<div class="wordcloud">
<div
class="wordCloud__tagBall"
:style="{width:`${this.width}px`,height:`${this.height}px`}"
@mouseenter="stop"
@mouseleave="start"
>
<span
class="wordCloud__tag"
v-for="(item, index) of data"
:key="index"
:style="{color:color[index % color.length],...contentEle[index].style}"
:title="item.name+item.value"
>{{item.name}}</span>
</div>
</div>
</template>
<script>
export default {
name: 'cloudWork',
props: {
width: {
type: Number,
default: 129
},
height: {
type: Number,
default: 129
},
// 测试数据
data: {
type: Array,
default: () => [
{
name: '测试一类',
value: 30
},
{
name: '测试二类',
value: 30
},
{
name: '测试三类',
value: 30
},
{
name: '测试四类',
value: 30
},
]
}
},
data: () => ({
color: ['#0054FF', '#B9B9B9', '#D1AF07', '#E27914', '#CB4A4D', '#B02690'],
contentEle: [],
direction: '-1',
speed: 400,
animateID: null
}),
created() {
this.contentEle = this.data.map(() => ({
x: 0,
y: 0,
z: 0,
style: {}
}));
},
mounted() {
this.innit();
},
methods: {
innit() {
const RADIUSX = (this.width - 50) / 2;
const RADIUSY = (this.height - 50) / 2;
this.contentEle = [];
for (let i = 0; i < this.data.length; i += 1) {
const k = -1 + (2 * (i + 1) - 1) / this.data.length;
const a = Math.acos(k);
const b = a * Math.sqrt(this.data.length * Math.PI);
const x = RADIUSX * Math.sin(a) * Math.cos(b);
const y = RADIUSY * Math.sin(a) * Math.sin(b);
const z = RADIUSX * Math.cos(a);
const singleEle = {
x,
y,
z,
style: {}
};
this.contentEle.push(singleEle);
}
this.animate();
},
animate() {
this.rotateX();
this.rotateY();
this.move();
this.animateID = window.requestAnimationFrame(this.animate);
},
rotateX() {
const angleX = ['-1', '1'].includes(this.direction)
? Math.PI / Infinity
: Math.PI / ((Number(this.direction) / 2) * Number(this.speed));
const cos = Math.cos(angleX);
const sin = Math.sin(angleX);
this.contentEle = this.contentEle.map((t) => {
const y1 = t.y * cos - t.z * sin;
const z1 = t.z * cos + t.y * sin;
return {
...t,
y: y1,
z: z1
};
});
},
rotateY() {
const angleY = ['-2', '2'].includes(this.direction)
? Math.PI / Infinity
: Math.PI / (Number(this.direction) * Number(this.speed));
const cos = Math.cos(angleY);
const sin = Math.sin(angleY);
this.contentEle = this.contentEle.map((t) => {
const x1 = t.x * cos - t.z * sin;
const z1 = t.z * cos + t.x * sin;
return {
...t,
x: x1,
z: z1
};
});
},
move() {
const CX = this.width / 2;
const CY = this.height / 2;
this.contentEle = this.contentEle.map((singleEle) => {
const { x, y, z } = singleEle;
const fallLength = 500;
const RADIUS = (this.width - 50) / 2;
const scale = fallLength / (fallLength - z);
const alpha = (z + RADIUS) / (2 * RADIUS);
const left = `${x + CX - 15}px`;
const top = `${y + CY - 15}px`;
const transform = `translate(${left}, ${top}) scale(${scale})`;
const style = {
...singleEle.style,
opacity: alpha + 0.5,
zIndex: parseInt(scale * 100, 10),
transform
};
return {
x,
y,
z,
style
};
});
},
// 鼠标移入暂停
stop() {
window.cancelAnimationFrame(this.animateID);
},
// 鼠标离开恢复
start() {
this.animate();
}
}
};
</script>
<style scoped>
button {
margin: 20px;
}
.wordcloud {
width: 100%;
height: 100%;
overflow: hidden;
}
.wordCloud__tagBall {
width: 100%;
height: 100%;
position: relative;
}
.wordCloud__tag {
display: block;
position: absolute;
left: 0px;
top: 0px;
color: green;
text-decoration: none;
font-size: 12px;
font-family: '微软雅黑';
font-weight: bold;
}
.wordCloud__tag :hover {
color: red;
}
.wordCloud__home {
display: flex;
justify-content: center;
}
</style>