master
fuxiaochun 2023-08-16 23:48:18 +08:00
parent 53227c80cd
commit 88001e059e
4 changed files with 989 additions and 0 deletions

View File

@ -144,6 +144,24 @@ const router = createRouter({
},
component: () => import("@/views/business/legal/countryInfo.vue"),
},
{
path: 'business/legal/search',
name: 'search',
meta: {
title: 'AI商情-法律法规-境外法规检索',
group: 'business'
},
component: () => import("@/views/business/legal/search.vue"),
},
{
path: 'business/legal/searchResult',
name: 'searchResult',
meta: {
title: 'AI商情-法律法规-境外法规检索',
group: 'business'
},
component: () => import("@/views/business/legal/searchResult.vue"),
},
]
}
],

View File

@ -0,0 +1,315 @@
<template>
<div class="pageContainer">
<div class="title">境外法规检索</div>
<div class="formBox">
<div class="item">
<div class="label">关键字</div>
<div class="val">
<van-field :clearable="true" :center="true" class="txt" v-model="keywords" placeholder="请输入关键字"></van-field>
</div>
</div>
<div class="item">
<div class="label">地区</div>
<div class="val">
<van-field class="areaInput" v-model="area.name" is-link readonly placeholder="地区" @click="onPick('area')" />
</div>
</div>
<div class="item">
<div class="label">国家</div>
<div class="val">
<van-field class="areaInput" v-model="nation.name" is-link readonly placeholder="国家" @click="onPick('nation')" />
</div>
</div>
<div class="item">
<div class="label">生效区域</div>
<div class="val">
<van-field class="areaInput" v-model="effectArea.name" is-link readonly placeholder="生效区域" @click="onPick('effectArea')" />
</div>
</div>
<van-popup v-model:show="showAreaPicker" round position="bottom">
<van-picker
v-model="curSelectedAreaValue"
:title="areaPickerTitle"
:columns="areaColumns"
:columns-field-names="{ text: 'name', value: 'id', children: null }"
@cancel="showAreaPicker = false"
@confirm="onAreaPickerConfirm"
/>
</van-popup>
<div class="item" v-if="legalCategories.length">
<div class="label">法规分类</div>
<div class="val">
<van-field class="areaInput" v-model="curCategory.name" is-link readonly placeholder="请选择法规分类" @click="showCategoryPicker = true" />
<div class="legalSubTypes" v-if="curCategory.children">
<van-checkbox-group v-model="checkedCategoryType">
<van-checkbox :key="item.id" :name="item.name" v-for="item in curCategory.children" shape="square" >{{ item.name }}</van-checkbox>
</van-checkbox-group>
</div>
</div>
</div>
<van-popup v-model:show="showCategoryPicker" round position="bottom">
<van-picker
title="法规分类"
:columns="legalCategories"
:columns-field-names="{ text: 'name', value: 'id', children: null }"
@cancel="showCategoryPicker = false"
@confirm="onCategoryPickerConfirm"
/>
</van-popup>
</div>
<div class="btns">
<button class="primary" @click="onSearch"></button>
<button @click="onReset"></button>
</div>
<div class="tips">
<p>友情提示本网站仅只提供境外法规标题参考翻译及法规原文地址境外网站访问不稳定访问时请耐心等待如原文地址打不开请稍后再尝试</p>
</div>
</div>
</template>
<script setup>
import { ref, onBeforeMount, onMounted, onUpdated, watch } from 'vue';
import http from '@/io/http';
import { showToast } from 'vant';
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const keywords = ref(''); //
const areas = ref([]);
const nations = ref([]);
const effectAreas = ref([]);
const area = ref({});
const nation = ref({});
const effectArea = ref({});
const showAreaPicker = ref(false);
const curAreaPickerType = ref();
const areaPickerTitle = ref();
const areaColumns = ref([]);
const selectedAreaValues = ref({});
const curSelectedAreaValue = ref();
const showCategoryPicker = ref(false);
const legalCategories = ref([]);
const curCategory = ref({});
const checkedTypeData = ref({});
const checkedCategoryType = ref([]);
onMounted(()=>{
getAreas();
getLegalCategory();
});
//
const getAreas = ()=>{
let params = { parent_key: 'continent' };
http('/api/keywords', params, 'get').then(res => {
areas.value = res.data || [];
}).catch(err => {
showToast(err.message);
});
};
//
const getLegalCategory = (id) => {
let params = { parent_key: 'law_category' };
http('/api/keywords', params, 'get').then(res => {
legalCategories.value = res.data || [];
// curCategory.value = res.data[0];
}).catch(err => {
showToast(err.message);
});
};
const areaPickerTitleOptions = {
area: '请选择地区',
nation: '请选择国家',
effectArea: '请选择生效地区'
};
const onPick = (type)=>{
showAreaPicker.value = true;
curAreaPickerType.value = type;
curSelectedAreaValue.value = selectedAreaValues.value[type];
areaPickerTitle.value = areaPickerTitleOptions[type];
if(type == 'area'){
areaColumns.value = areas.value;
}else if(type == 'nation'){
areaColumns.value = nations.value;
}else if (type == 'effectArea') {
areaColumns.value = effectAreas.value;
}
};
const onAreaPickerConfirm = (val)=>{
showAreaPicker.value = false;
let _temp = val.selectedOptions[0];
let _typeVal = {...selectedAreaValues.value};
_typeVal[curAreaPickerType.value] = _temp.id;
selectedAreaValues.value = _typeVal;
switch (curAreaPickerType.value){
case 'area':
nations.value = _temp.children;
area.value = _temp;
break;
case 'nation':
effectAreas.value = _temp.children;
nation.value = _temp;
break;
case 'effectArea':
effectArea.value = _temp;
break;
}
};
const onCategoryPickerConfirm = (val)=>{
showCategoryPicker.value = false;
let _temp = val.selectedOptions[0];
curCategory.value = _temp;
checkedCategoryType.value = [];
};
const onSearch = ()=>{
let continent = area.value.name;
let params = {
title: keywords.value,
continent: continent, //
country: nation.value.id,
area: effectArea.value.id,
categories: checkedCategoryType.value.join(',')
};
router.push({
path: `/business/legal/searchResult`,
query: params
});
};
const onReset = ()=>{
keywords.value = '';
area.value = '';
nation.value = '';
effectArea.value = '';
nations.value = [];
effectAreas.value = [];
curCategory.value = {};
checkedTypeData.value = {};
};
</script>
<style lang="scss" scoped>
.pageContainer{
padding: 40px;
color: #FFF;
.title{
font-size: 36px;
padding: 20px 0;
text-align: center;
font-weight: bold;
}
.formBox{
width: 100%;
.item{
padding: 8px 0;
.label{
width: 100%;
height: 66px;
line-height: 66px;
font-size: 27px;
font-weight: bold;
}
.val{
flex: 1;
line-height: 72px;
display: flex;
flex-direction: column;
.txt,
.areaInput{
flex: 1;
height: 72px;
border: 2px solid rgba($color: #414548, $alpha: 0.4);
border-radius: 4px;
background: none;
color: #FFF;
padding: 10px 20px;
box-sizing: border-box;
:deep(input) {
background: none;
color: #FFF;
font-size: 27px;
line-height: 52px;
&::placeholder {
color: #999;
}
}
}
}
:deep(.van-cell:after){
display: none;
}
.legalSubTypes{
flex: 1;
margin-top: 20px;
border: 2px solid #A6A8AF;
border-radius: 4px;
padding: 20px;
font-size: 28px;
color: #FFF;
li{
width: 100%;
line-height: 36px;
padding: 15px 10px;
margin-bottom: 10px;
}
}
:deep(.van-checkbox__label){
color: #FFF;
}
:deep(.van-checkbox){
padding: 15px 0;
}
:deep(.van-checkbox__icon--checked ~ span){
color: #3662FE;
}
}
}
.btns{
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 50px 0;
button{
width: 200px;
height: 72px;
background: #414548;
border-radius: 3px;
font-size: 26px;
color: #FFF;
line-height: 72px;
text-align: center;
&.primary{
background: #3662FE;
}
}
}
.tips{
width: 100%;
font-size: 22px;
line-height: 36px;
color: #A6A8AF;
margin-top: 30px;
}
}
</style>

View File

@ -0,0 +1,239 @@
<template>
<div class="pageContainer">
<div class="title">境外法规检索</div>
<div class="formBox">
<div class="item">
<div class="label">关键字</div>
<div class="val">
<van-field :clearable="true" :center="true" class="txt" v-model="keywords"
placeholder="请输入关键字"></van-field>
</div>
</div>
<div class="item">
<div class="label">地区</div>
<div class="val">
<van-field class="areaInput" v-model="area.name" is-link readonly placeholder="地区"
@click="onPick('area')" />
</div>
</div>
<div class="item">
<div class="label">国家</div>
<div class="val">
<van-field class="areaInput" v-model="nation.name" is-link readonly placeholder="国家"
@click="onPick('nation')" />
</div>
</div>
<div class="item">
<div class="label">生效区域</div>
<div class="val">
<van-field class="areaInput" v-model="effectArea.name" is-link readonly placeholder="生效区域"
@click="onPick('effectArea')" />
</div>
</div>
<van-popup v-model:show="showAreaPicker" round position="bottom">
<van-picker v-model="curSelectedAreaValue" :title="areaPickerTitle" :columns="areaColumns"
:columns-field-names="{ text: 'name', value: 'id', children: null }" @cancel="showAreaPicker = false"
@confirm="onAreaPickerConfirm" />
</van-popup>
</div>
<div class="btns">
<button class="primary" @click="onSearch"></button>
<button @click="onReset"></button>
</div>
</div>
</template>
<script setup>
import { ref, onBeforeMount, onMounted, onUpdated, watch } from 'vue';
import http from '@/io/http';
import { showToast } from 'vant';
import { useRouter, useRoute } from 'vue-router';
const props = defineProps({
onClose: Function
});
const router = useRouter();
const keywords = ref(''); //
const areas = ref([]);
const nations = ref([]);
const effectAreas = ref([]);
const area = ref({});
const nation = ref({});
const effectArea = ref({});
const showAreaPicker = ref(false);
const curAreaPickerType = ref();
const areaPickerTitle = ref();
const areaColumns = ref([]);
const selectedAreaValues = ref({});
const curSelectedAreaValue = ref();
onMounted(() => {
getAreas();
});
//
const getAreas = () => {
let params = { parent_key: 'continent' };
http('/api/keywords', params, 'get').then(res => {
areas.value = res.data || [];
}).catch(err => {
showToast(err.message);
});
};
const areaPickerTitleOptions = {
area: '请选择地区',
nation: '请选择国家',
effectArea: '请选择生效地区'
};
const onPick = (type) => {
showAreaPicker.value = true;
curAreaPickerType.value = type;
curSelectedAreaValue.value = selectedAreaValues.value[type];
areaPickerTitle.value = areaPickerTitleOptions[type];
if (type == 'area') {
areaColumns.value = areas.value;
} else if (type == 'nation') {
areaColumns.value = nations.value;
} else if (type == 'effectArea') {
areaColumns.value = effectAreas.value;
}
};
const onAreaPickerConfirm = (val) => {
showAreaPicker.value = false;
let _temp = val.selectedOptions[0];
let _typeVal = { ...selectedAreaValues.value };
_typeVal[curAreaPickerType.value] = _temp.id;
selectedAreaValues.value = _typeVal;
switch (curAreaPickerType.value) {
case 'area':
nations.value = _temp.children;
area.value = _temp;
break;
case 'nation':
effectAreas.value = _temp.children;
nation.value = _temp;
break;
case 'effectArea':
effectArea.value = _temp;
break;
}
};
const onSearch = () => {
let continent = area.value.name;
let params = {
title: keywords.value,
continent: continent, //
country: nation.value.id,
area: effectArea.value.id,
};
props.onClose && props.onClose();
router.push({
path: `/business/legal/searchResult`,
query: params
});
};
const onReset = () => {
keywords.value = '';
area.value = '';
nation.value = '';
effectArea.value = '';
nations.value = [];
effectAreas.value = [];
};
</script>
<style lang="scss" scoped>
.pageContainer {
color: #FFF;
.title {
font-size: 36px;
padding: 20px 0;
text-align: center;
font-weight: bold;
}
.formBox {
width: 100%;
.item {
padding: 8px 0;
.label {
width: 100%;
height: 66px;
line-height: 66px;
font-size: 27px;
font-weight: bold;
}
.val {
flex: 1;
line-height: 72px;
display: flex;
flex-direction: column;
.txt,
.areaInput {
flex: 1;
height: 72px;
border: 2px solid rgba($color: #414548, $alpha: 0.4);
border-radius: 4px;
background: none;
color: #FFF;
padding: 10px 20px;
box-sizing: border-box;
:deep(input) {
background: none;
color: #FFF;
font-size: 27px;
line-height: 52px;
&::placeholder {
color: #999;
}
}
}
}
:deep(.van-cell:after) {
display: none;
}
}
}
.btns {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 50px 0;
button {
width: 200px;
height: 72px;
background: #414548;
border-radius: 3px;
font-size: 26px;
color: #FFF;
line-height: 72px;
text-align: center;
&.primary {
background: #3662FE;
}
}
}
}
</style>

View File

@ -0,0 +1,417 @@
<template>
<div class="pageContainer">
<div class="breadNav">
<router-link to="/">首页</router-link><span>&gt;</span>
<router-link to="/business">AI商情</router-link><span>&gt;</span>
<router-link to="/business/legal">法律法规</router-link><span>&gt;</span>
<router-link to="/business/legal/search">境外法规检索</router-link>
</div>
<div class="main">
<div class="results" v-if="Array.isArray(result.data)">
<h1>检索结果</h1>
<ul v-if="result.data.length > 0">
<li :key="index+item.title1" v-for="item,index in result.data">
<div class="area">{{item.continent }}/{{ item.country }}/{{ item.area }}</div>
<p><a :href="item.url" target="_blank" rel="noopener noreferrer"><strong>{{item.title1}}</strong></a></p>
<p><a :href="item.url" target="_blank" rel="noopener noreferrer">{{item.title2}}</a></p>
<div class="category">分类
<span :key="category" v-for="category in item.categories">{{category}}</span>
</div>
</li>
</ul>
<div class="plageholder" v-else></div>
<div class="pagesBox">
<div class="pageInfo">
<span>{{result.total}}</span>
<span>每页显示<strong>{{pageSize}}</strong></span>
<span>{{result.last_page }}</span>
</div>
<div class="pageAction">
<a href="javascript:;" @click="goFirstPage"></a>
<a href="javascript:;" @click="goPrePage" v-if="pageNum > 1"></a>
<a href="javascript:;" @click="goNextPage" v-if="pageNum < result.last_page"></a>
<a href="javascript:;" @click="goLastPage"></a>
<div class="pageForm">
<van-field type="digit" class="txt" v-model="inputPage" />
<van-button type="primary" class="btn" @click="goInputPage">GO</van-button>
</div>
</div>
</div>
</div>
<div class="plageholder" v-else></div>
</div>
</div>
<div class="footerTips">
<p>友情提示本网站仅只提供境外法规标题参考翻译及法规原文地址境外网站访问不稳定访问时请耐心等待如原文地址打不开请稍后再尝试</p>
</div>
<div class="searchBar" @click="showSearchForm = true;"><van-icon name="search" /></div>
<div class="searchFormBox" v-if="showSearchForm">
<div class="closeBtn" @click="showSearchForm = false"><i class="icon-quxiao"></i></div>
<SearchFormModal :onClose="onSearchFormClose" />
</div>
</template>
<script setup>
import { ref, onBeforeMount, onMounted, onUpdated, watch } from 'vue';
import http from '@/io/http';
import { showToast } from 'vant';
import { useRouter, useRoute } from 'vue-router';
import SearchFormModal from './searchFormModal.vue';
const router = useRouter();
const route = useRoute();
const pageSize = ref(15);
const pageNum = ref(1);
const result = ref({});
const inputPage = ref(1);
const showSearchForm = ref(false);
watch(pageNum, (newPage, oldPage)=>{
if(newPage !== oldPage){
inputPage.value = newPage;
}
});
watch(route, ()=>{
getResult();
});
onMounted(() => {
getResult();
});
const onSearchFormClose = ()=>{
showSearchForm.value = false;
};
const getResult = ()=>{
let params = {
...route.query,
per_page: pageSize.value,
page: pageNum.value
};
http('/api/law', params, 'get').then(res => {
result.value = res.data;
}).catch(err => {
message.error(err.message);
});
};
//
const goFirstPage = ()=>{
pageNum.value = 1;
getResult();
};
//
const goPrePage = ()=>{
let _num = pageNum.value;
pageNum.value = _num <= 1 ? 1 : _num - 1;
getResult();
};
//
const goNextPage = ()=>{
let _num = pageNum.value;
pageNum.value = _num >= result.value.last_page ? result.value.last_page : _num + 1;
getResult();
};
//
const goLastPage = ()=>{
pageNum.value = result.value.last_page;
getResult();
};
const goInputPage = ()=>{
pageNum.value = inputPage.value;
getResult();
};
</script>
<style lang="scss" scoped>
.pageContainer{
color: #FFF;
padding: 25px;
:deep(.van-cell:after){
display: none;
}
.breadNav{
padding-bottom: 20px;
line-height: 30px;
color: #999;
font-size: 22px;
span{
padding: 0 5px;
}
a{
color: #999;
&:hover{
color: #FFF;
}
}
}
.searchForm{
flex: 1;
padding: 20px 30px;
background: #161718;
border: 2px solid #414548;
border-radius: 3px;
.title{
font-size: 23px;
line-height: 30px;
margin-bottom: 30px;
text-align: center;
font-weight: bold;
}
.item{
padding: 8px 0;
display: flex;
justify-content: flex-start;
align-items: flex-start;
line-height: 32px;
.label{
width: 80px;
height: 32px;
line-height: 32px;
text-align: right;
padding-right: 15px;
font-size: 14px;
font-weight: bold;
}
.val{
flex: 1;
line-height: 32px;
display: flex;
flex-direction: column;
.txt,
.areaInput{
flex: 1;
border: 2px solid #A6A8AF;
border-radius: 4px;
background: none;
color: #FFF;
&::placeholder{
color: #999;
}
:deep(input){
background: none;
color: #FFF;
&::placeholder{
color: #999;
}
}
:deep(.ant-input-clear-icon){
color: #999;
}
:deep(.ant-select-selector){
color: #999;
border: none;
background: none;
height: 36px;
line-height: 36px;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
:deep(.ant-select-selection-item){
border: none;
line-height: 51px;
}
}
:deep(.ant-select-arrow){
color: #FFF;
}
}
}
}
.btns{
width: 100%;
height: 40px;
display: flex;
justify-content: flex-start;
align-items: center;
button{
margin-right: 20px;
width: 86px;
height: 30px;
background: #414548;
border-radius: 3px;
color: #FFF;
line-height: 30px;
text-align: center;
&.primary{
background: #3662FE;
}
}
}
}
.plageholder{
width: 100%;
height: 50vh;
font-size: 28px;
color: #ccc;
display: flex;
justify-content: center;
align-items: center;
}
.results{
width: 100%;
min-height: 70vh;
h1{
font-weight: bold;
font-size: 36px;
text-align: center;
height: 60px;
line-height: 60px;
margin-bottom: 10px;
}
ul{
width: 100%;
li{
width: 100%;
padding: 20px 0;
border-bottom: 1px solid #666;
font-weight: bold;
font-size: 15px;
.area{
font-size: 26px;
font-weight: bold;
line-height: 32px;
padding-bottom: 30px;
}
p{
line-height: 32px;
font-size: 28px;
padding: 10px 0;
a{
color: #FFF;
&:hover{
text-decoration: underline;
}
}
strong{
color: #3662FE;
}
}
.category{
font-size: 22px;
line-height: 30px;
margin-top: 30px;
span{
margin-right: 20px;
}
}
}
}
.pagesBox{
width: 100%;
font-size: 22px;
.pageInfo{
width: 100%;
padding: 10px 0;
color: #ccc;
display: flex;
justify-content: space-between;
align-items: center;
}
.pageAction{
width: 100%;
height: 70px;
display: flex;
justify-content: space-between;
align-items: center;
}
.pageForm{
display: flex;
height: 70px;
align-items: center;
.txt{
width: 80px;
height: 50px;
line-height: 46px;
text-align: center;
border: 2px solid #A6A8AF;
border-radius: 4px;
background: none;
color: #FFF;
margin: 0 5px;
padding: 0;
:deep(input){
background: none;
color: #FFF;
text-align: center;
&::placeholder{
color: #999;
}
}
}
.btn{
margin-left: 10px;
width: 80px;
height: 50px;
}
}
}
}
}
.footerTips{
width: 100%;
padding: 50px 20px;
display: flex;
justify-content: center;
align-items: center;
p{
font-size: 22px;
color: #999;
line-height: 30px;
}
}
.searchBar{
width: 94px;
height: 94px;
background: #3662FE;
box-shadow: 0px 12px 26px 0px rgba(32,69,201,0.86);
border-radius: 50%;
position: fixed;
top: 150px;
right: 10px;
z-index: 3;
display: flex;
justify-content: center;
align-items: center;
}
.searchFormBox{
width: 100%;
height: calc(100vh - 90px);
margin-top: 90px;
padding: 40px;
background: #161718;
position: fixed;
top: 0;
left: 0;
z-index: 5;
.closeBtn{
width: 80px;
height: 80px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 20px;
right: 10px;
color: #FFF;
font-size: 38px;
}
}
</style>