@@ -0,0 +1 @@ | |||
VITE_BASE_URL=http://parkshuyuan.test.hhrchina.com |
@@ -0,0 +1,2 @@ | |||
VITE_NODE_ENV=development | |||
VITE_BASE_URL=http://parkshuyuan.test.hhrchina.com |
@@ -0,0 +1,2 @@ | |||
VITE_NODE_ENV=production | |||
VITE_BASE_URL=http://parkshuyuan.test.hhrchina.com |
@@ -0,0 +1,24 @@ | |||
# Logs | |||
logs | |||
*.log | |||
npm-debug.log* | |||
yarn-debug.log* | |||
yarn-error.log* | |||
pnpm-debug.log* | |||
lerna-debug.log* | |||
node_modules | |||
dist | |||
dist-ssr | |||
*.local | |||
# Editor directories and files | |||
.vscode/* | |||
!.vscode/extensions.json | |||
.idea | |||
.DS_Store | |||
*.suo | |||
*.ntvs* | |||
*.njsproj | |||
*.sln | |||
*.sw? |
@@ -0,0 +1,3 @@ | |||
{ | |||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] | |||
} |
@@ -0,0 +1,7 @@ | |||
# Vue 3 + Vite | |||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more. | |||
## Recommended IDE Setup | |||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). |
@@ -0,0 +1,16 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8" /> | |||
<link rel="icon" type="image/svg+xml" href="/logo.png" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<title>书院</title> | |||
</head> | |||
<body> | |||
<div id="app"></div> | |||
<script type="module" src="/src/main.js"></script> | |||
</body> | |||
</html> |
@@ -0,0 +1,35 @@ | |||
{ | |||
"name": "SHUYUAN-TOWN", | |||
"private": true, | |||
"version": "0.0.0", | |||
"type": "module", | |||
"scripts": { | |||
"dev": "vite", | |||
"build": "vite build", | |||
"preview": "vite preview" | |||
}, | |||
"dependencies": { | |||
"amfe-flexible": "^2.2.1", | |||
"axios": "^1.4.0", | |||
"codemirror": "^5.65.14", | |||
"element-plus": "^2.3.4", | |||
"marked": "^3.0.8", | |||
"pinia": "^2.0.35", | |||
"pinia-plugin-persist": "^1.0.0", | |||
"postcss-pxtorem": "^6.0.0", | |||
"qs": "^6.11.2", | |||
"simplemde": "^1.11.2", | |||
"vant": "^4.6.2", | |||
"vue": "^3.2.47", | |||
"vue-router": "^4.1.6", | |||
"vue-scrollto": "^2.20.0" | |||
}, | |||
"devDependencies": { | |||
"@vitejs/plugin-vue": "^4.1.0", | |||
"sass": "^1.62.1", | |||
"unplugin-auto-import": "^0.15.3", | |||
"unplugin-vue-components": "^0.24.1", | |||
"vite": "^4.3.2", | |||
"vite-plugin-compression": "^0.5.1" | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
module.exports = { | |||
plugins: { | |||
// autoprefixer: { | |||
// overrideBrowserslist: [ | |||
// "Android 4.1", | |||
// "iOS 7.1", | |||
// "Chrome > 31", | |||
// "ff > 31", | |||
// "ie >= 8", | |||
// "last 10 versions", // 所有主流浏览器最近10版本用 | |||
// ], | |||
// grid: true, | |||
// }, | |||
// "postcss-pxtorem": { | |||
// rootValue: 192, | |||
// exclude: /node_modules\/vant|mobile/i, // 排除mobile和vant库 | |||
// propList: ["*"], | |||
// selectorBlackList: [".van-"], // 排除移动端使用了vant库 | |||
// }, | |||
"postcss-pxtorem": { | |||
rootValue: 37.5, | |||
exclude: /node_modules\/element-plus|pc/i, // 排除pc | |||
propList: ["*"], | |||
}, | |||
}, | |||
}; |
@@ -0,0 +1,57 @@ | |||
<template> | |||
<template v-if="!isMobile"> | |||
<StickBlock /> | |||
<template v-if="!loading"> | |||
<TopNav :logo="homeData.logo" /> | |||
<div class="main_content"> | |||
<Banner :banner="homeData.topBanner"></Banner> | |||
<router-view></router-view> | |||
</div> | |||
<FooterInfo /> | |||
</template> | |||
</template> | |||
<template v-else> | |||
<PageBack /> | |||
<MobileStickBlock></MobileStickBlock> | |||
<template v-if="!loading"> | |||
<MobileTopNav :logo="homeData.logo" /> | |||
<MobileBanner :banner="homeData.topBanner" /> | |||
<MobileTipDialog ref="tipRef"></MobileTipDialog> | |||
<router-view></router-view> | |||
</template> | |||
</template> | |||
</template> | |||
<script setup> | |||
import TopNav from "@/components/pc/TopNav.vue"; | |||
import Banner from "@/components/pc/Banner.vue"; | |||
import FooterInfo from "@/components/pc/FooterInfo.vue"; | |||
import StickBlock from "@/components/pc/StickBlock.vue"; | |||
import MobileBanner from "@/components/mobile/MBanner.vue"; | |||
import MobileTopNav from "@/components/mobile/MTopNav.vue"; | |||
import MobileTipDialog from "@/components/mobile/TipDialog.vue"; | |||
import MobileStickBlock from "@/components/mobile/MStickBlock.vue"; | |||
import PageBack from "@/components/mobile/PageBack.vue"; | |||
const isMobile = /Android|webOS|iPhone|iPod|BlackBerry|iPad/i.test(navigator.userAgent); | |||
let homeData = ref({}); | |||
const loading = ref(true); | |||
import { getHomeData } from "./views/pc/home/home"; | |||
import { onMounted, ref } from "vue"; | |||
import useHomeStore from "@/store/module/home"; | |||
const homeStore = useHomeStore(); | |||
const { getData } = getHomeData(); | |||
onMounted(async () => { | |||
loading.value = true; | |||
homeData = await getData(); | |||
homeStore.homeData.value = homeData; | |||
loading.value = false; | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.main_content { | |||
min-height: 200px; | |||
} | |||
</style> |
@@ -0,0 +1,45 @@ | |||
/** | |||
* api接口统一管理 | |||
*/ | |||
import {get, post } from "../utils/request"; | |||
// 首页-获取详情 | |||
export const getAreaData = () => get("/areaInfoManagement/getAreaData"); | |||
// 增加官网浏览量 | |||
export const updateAreaInfoManagementPageView = p => | |||
post(`/areaInfoManagement/updateAreaInfoManagementPageView/${p}`); | |||
// 获取最近那一个编辑或者上架的招商载体 | |||
export const getRegistrationPackageByFirst = () => | |||
get("/registrationPackage/getRegistrationPackageByFirst"); | |||
// 获取全部产业聚集 | |||
export const getAreaIndustrialAllocationList = () => | |||
get("/areaIndustrialAllocation/getAreaIndustrialAllocationList"); | |||
// 获取全部招商载体 | |||
export const getRegistrationPackageList = () => | |||
get("/registrationPackage/getRegistrationPackageList"); | |||
// 获取全部企业链家 | |||
export const getEnterpriseHomeLinkList = () => get("/enterpriseHomeLink/getEnterpriseHomeLinkList"); | |||
// 获取全部招商项目 | |||
export const getAttractInvestmentProjectList = () => | |||
get("/attractInvestmentProject/getAttractInvestmentProjectList"); | |||
// 申请入驻接口 | |||
export const addEnter = p => | |||
post(`/enter/addEnter`, p, { | |||
headers: { | |||
"Content-Type": "application/json;charset=UTF-8", | |||
}, | |||
}); | |||
// 获取全部政策 | |||
export const getReleasePolicyList = p => get(`/releasePolicy/getReleasePolicyList`, p); | |||
export const getReleasePolicyById = p => get(`/releasePolicy/getReleasePolicyById`, p); | |||
// 获取某个实体属性状态列表 | |||
export const getPickListByKey = p => get(`/common/getPickListByKey`, p); | |||
// 增加浏览量 | |||
export const updateReleasePolicyPageView = p => | |||
post(`/releasePolicy/updateReleasePolicyPageView/${p}`); | |||
export const getContactInfoConfig = p => get(`/contactInfoConfig/getContactInfoConfigByOne`, p); |
@@ -0,0 +1,25 @@ | |||
<template> | |||
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white"> | |||
<van-swipe-item v-for="(item, index) in propsData.banner" :key="index"> | |||
<img :src="formatImg(item)" alt="banner图片" /> | |||
</van-swipe-item> | |||
</van-swipe> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
banner: { | |||
type: Array, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.my-swipe .van-swipe-item { | |||
height: 150px; | |||
img { | |||
@include font(12px, $color-black); | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,49 @@ | |||
<template> | |||
<div class="block" @click="showTipInfo"> | |||
<div class="block-item"> | |||
<img src="@/assets/phone.png" alt="电话咨询" /> | |||
<span>电话咨询</span> | |||
</div> | |||
<div class="block-item"> | |||
<img src="@/assets/code.png" alt="扫码关注" /> | |||
<span>扫码关注</span> | |||
</div> | |||
</div> | |||
<TipDialog ref="tipRef"></TipDialog> | |||
</template> | |||
<script setup> | |||
import TipDialog from "@/components/mobile/TipDialog.vue"; | |||
const tipRef = ref(null); | |||
const showTipInfo = () => { | |||
tipRef.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.block { | |||
position: fixed; | |||
right: 0; | |||
bottom: 100px; | |||
background: rgba(0, 153, 255, 1); | |||
z-index: 1008; | |||
border-radius: 24px; | |||
padding: 20px 0; | |||
@include border-box; | |||
&-item { | |||
@include size(52px, auto); | |||
@include font(10px, $color-white); | |||
@include flex(column, center, center, nowrap); | |||
border-top: 1px solid rgba(255, 255, 255, 1); | |||
padding: 10px 0; | |||
@include border-box; | |||
cursor: pointer; | |||
img { | |||
height: 25px; | |||
margin-bottom: 4px; | |||
} | |||
} | |||
&-item:last-child { | |||
border-bottom: 1px solid rgba(255, 255, 255, 1); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,124 @@ | |||
<!-- 导航 --> | |||
<template> | |||
<div class="header"> | |||
<van-sticky> | |||
<div class="wrap"> | |||
<section class="wrap_left"> | |||
<img v-if="propsData.logo" :src="formatImg(logoArr[0])" alt="logo" /> | |||
<div class="title"> | |||
<span class="ch">浦东新区书院镇</span> | |||
<span class="en">SHUYUAN TOWN</span> | |||
</div> | |||
</section> | |||
<van-dropdown-menu class="wrap_right" ref="menuRef"> | |||
<van-dropdown-item> | |||
<template #title> | |||
<van-icon name="wap-nav" size="26px" /> | |||
</template> | |||
<router-link | |||
v-for="item in nav" | |||
:key="item.name" | |||
:to="item.path" | |||
class="routers" | |||
@click="goPath(item.name)" | |||
> | |||
{{ item.meta.title }} | |||
</router-link> | |||
</van-dropdown-item> | |||
</van-dropdown-menu> | |||
</div> | |||
</van-sticky> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { useRouter } from "vue-router"; //1.引入路由 | |||
const router = useRouter(); //2.实例化路由 | |||
const nav = router.getRoutes().filter(item => item.meta.isNav && item.meta.type === "mobile"); | |||
const menuRef = ref(null); | |||
const goPath = name => { | |||
router.push({ | |||
name, | |||
}); | |||
menuRef.value.close(); | |||
}; | |||
import { formatImg } from "@/utils/common.js"; | |||
let logoArr = ref([]); | |||
const propsData = defineProps({ | |||
logo: { | |||
type: String, | |||
}, | |||
}); | |||
logoArr.value = propsData.logo ? JSON.parse(propsData.logo) : []; | |||
</script> | |||
<style lang="scss" scoped> | |||
//@import url(); 引入公共css类 | |||
.header { | |||
// @include size(100%, 48px); | |||
// margin: 0 auto; | |||
// color: $color-black; | |||
// background: #fff; | |||
// @include border-box; | |||
} | |||
.wrap { | |||
padding: 10px 12px; | |||
@include size(100%, 48px); | |||
background: #fff; | |||
@include flex(row, space-between, center, wrap); | |||
@include border-box; | |||
.wrap_left { | |||
@include flex(row, center, center, wrap); | |||
img { | |||
@include size(auto, 30px); | |||
margin-right: 10px; | |||
} | |||
.title { | |||
@include size(auto, 30px); | |||
@include flex(column, space-around, flex-start, nowrap); | |||
} | |||
& .ch { | |||
@include font(14px, $color-black); | |||
font-weight: bold; | |||
} | |||
& .en { | |||
@include font(12px, $color-black); | |||
} | |||
} | |||
.wrap_right { | |||
@include size(auto, 100%); | |||
@include flex(row, flex-end, center, wrap); | |||
.routers { | |||
@include size(90%, auto); | |||
margin: auto; | |||
padding: 10px 0; | |||
@include font(14px, $color-black); | |||
@include flex(row, center, center, nowrap); | |||
text-align: center; | |||
// border-bottom: 1px solid rgba(179, 179, 179, 1); | |||
@include border-box; | |||
} | |||
.router-link-active { | |||
color: rgba(0, 153, 255, 1); | |||
} | |||
} | |||
.van-dropdown-item__content > .routers + .routers { | |||
border-top: 1px solid rgba(179, 179, 179, 1); | |||
@include border-box; | |||
} | |||
} | |||
:deep() { | |||
.van-dropdown-menu__bar { | |||
height: 100%; | |||
box-shadow: none; | |||
} | |||
.van-dropdown-menu__title:after { | |||
display: none; | |||
} | |||
.van-dropdown-item { | |||
top: 48px; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,35 @@ | |||
<template> | |||
<div class="page-back" v-if="$route.path !== '/m_home'"> | |||
<div class="icon-back" @click="goBack"> | |||
<img src="@/assets/page-back.png" /> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { useRoute, useRouter } from "vue-router"; //1.引入路由 | |||
const $router = useRouter(); //2.实例化路由 | |||
const $route = useRoute(); //2.实例化路由 | |||
const goBack = () => { | |||
if ($route.path == "/m_preferential-policy-detail") { | |||
$router.go(-1); | |||
} else { | |||
$router.push({ | |||
path: "/", | |||
}); | |||
} | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.icon-back { | |||
width: 38px; | |||
height: 38px; | |||
position: fixed; | |||
left: 10px; | |||
top: 48px; | |||
z-index: 999; | |||
img { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,95 @@ | |||
<template> | |||
<van-dialog v-model:show="dialogVisible" :showConfirmButton="false"> | |||
<template #title> | |||
<div class="head"> | |||
<van-icon name="cross" size="20px" @click="dialogVisible = false" /> | |||
</div> | |||
<div>立即咨询</div> | |||
</template> | |||
<div class="dialog"> | |||
<div class="dialog-item"> | |||
<span class="dialog-item-title">电话咨询</span> | |||
<div class="phone"> | |||
<p v-if="contract.mobileOne">{{ contract.mobileOne }}</p> | |||
<p v-if="contract.mobileTwo">{{ contract.mobileTwo }}</p> | |||
<p v-if="contract.mobileThree">{{ contract.mobileThree }}</p> | |||
</div> | |||
</div> | |||
<div class="dialog-item" v-if="contract.qrCode.length > 0"> | |||
<span class="dialog-item-title">扫码咨询</span> | |||
<img :src="formatImg(contract.qrCode[0])" alt="" /> | |||
</div> | |||
<div class="dialog-item" v-if="contract.email"> | |||
<span class="dialog-item-title">邮箱</span> | |||
<span class="email">{{ contract.email }}</span> | |||
</div> | |||
</div> | |||
</van-dialog> | |||
</template> | |||
<script setup> | |||
import { getContactInfoConfig } from "@/apis/index"; | |||
import { toast, formatImg } from "@/utils/common.js"; | |||
import { onMounted, ref } from "vue"; | |||
const dialogVisible = ref(false); | |||
const handleClose = () => { | |||
dialogVisible.value = false; | |||
}; | |||
const showTip = () => { | |||
dialogVisible.value = true; | |||
}; | |||
defineExpose({ showTip, handleClose }); | |||
const contract = ref(); | |||
const getData = () => { | |||
getContactInfoConfig() | |||
.then(res => { | |||
if (res.data.status == 0) { | |||
contract.value = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
}) | |||
.catch(error => { | |||
console.log(error); | |||
}); | |||
}; | |||
onMounted(() => { | |||
getData(); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.head { | |||
text-align: right; | |||
padding: 0 10px; | |||
@include border-box; | |||
} | |||
.dialog { | |||
@include flex(column, flex-start, center, nowrap); | |||
padding-bottom: 20px; | |||
@include border-box; | |||
&-item { | |||
@include flex(column, flex-start, center, nowrap); | |||
&-title { | |||
@include font(14px, $color-black); | |||
font-weight: 400; | |||
margin: 12px 0; | |||
} | |||
.phone { | |||
@include font(14px, rgba(0, 153, 255, 1)); | |||
} | |||
p { | |||
margin-bottom: 10px; | |||
} | |||
img { | |||
@include size(150px, 150px); | |||
} | |||
.email { | |||
@include font(14px, $color-black); | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,31 @@ | |||
<template> | |||
<el-carousel :interval="5000" arrow="always"> | |||
<el-carousel-item v-for="(item, index) in propsData.banner" :key="index"> | |||
<img :src="formatImg(item)" alt="banner图片" /> | |||
</el-carousel-item> | |||
</el-carousel> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
banner: { | |||
type: Array, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.el-carousel--horizontal { | |||
height: 700px; | |||
min-width: $wrapWidth; | |||
// background: yellowgreen; | |||
} | |||
:deep() { | |||
.el-carousel__container { | |||
height: 100%; | |||
img { | |||
// background: pink; | |||
@include size(100%, 100%); | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,127 @@ | |||
<!-- 底部导航 --> | |||
<template> | |||
<el-row class="footer_box"> | |||
<div class="wrap_box"> | |||
<div class="nav_info"> | |||
<section class="nav"> | |||
<div class="title">浦东新区书院镇</div> | |||
<div class="navs"> | |||
<router-link v-for="item in nav" :key="item.name" :to="item.path"> | |||
{{ item.meta.title }} | |||
</router-link> | |||
</div> | |||
</section> | |||
<section class="footer_info"> | |||
<img v-if="qrCode" :src="formatImg(qrCode)" alt="" /> | |||
<div class="info"> | |||
<span>地址:{{ addressConfig }}</span> | |||
<span>电话:{{ mobile }}</span> | |||
<span>传真:{{ faxConfig }}</span> | |||
<span>邮箱:{{ email }}</span> | |||
</div> | |||
</section> | |||
</div> | |||
<div class="line"></div> | |||
<div class="bottom_info"> | |||
<span>版权所有 ©2023-现在</span> | |||
<span>{{ addressConfig }}</span> | |||
<span>沪CP畜2022025118号</span> | |||
<span>沪B2-20200177</span> | |||
<span>沪公网安备31010402001838号</span> | |||
</div> | |||
</div> | |||
</el-row> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
import { useRouter } from "vue-router"; //1.引入路由 | |||
const router = useRouter(); //2.实例化路由 | |||
const nav = router.getRoutes().filter(item => item.meta.isNav && item.meta.type === "pc"); | |||
import useHomeStore from "@/store/module/home"; | |||
const homeStore = useHomeStore(); | |||
const qrCode = homeStore.contract.value.qrCode[0]; | |||
const email = homeStore.contract.value.email; | |||
const mobile = homeStore.contract.value.mobileOne; | |||
const faxConfig = homeStore.contract.value.faxConfig; | |||
const addressConfig = homeStore.contract.value.addressConfig; | |||
</script> | |||
<style lang="scss" scoped> | |||
//@import url(); 引入公共css类 | |||
.footer_box { | |||
@include size(100%, 305Px); | |||
background: url(https://img.js.design/assets/img/64a77155c194a91b49b37dcc.jpg) no-repeat; | |||
background-size: 100% 100%; | |||
box-sizing: border-box; | |||
@include flex(row, center, center, wrap); | |||
} | |||
.wrap_box { | |||
width: 1200Px; | |||
} | |||
.nav_info { | |||
@include size(1200Px, 250Px); | |||
@include flex(row, space-between, center, nowrap); | |||
// padding: 0 50Px; | |||
box-sizing: border-box; | |||
} | |||
.nav { | |||
@include border-box; | |||
.title { | |||
@include font(36Px, $color-white); | |||
margin-bottom: 54Px; | |||
} | |||
.navs { | |||
@include flex(row, flex-start, center, nowrap); | |||
margin: 0 auto; | |||
@include font(16Px, $color-white); | |||
a { | |||
@include font(16Px, $color-white); | |||
margin-right: 20Px; | |||
} | |||
// a:hover { | |||
// color: #99a9b8; | |||
// } | |||
// .router-link-active { | |||
// color: #99a9b8; | |||
// } | |||
} | |||
.navs > a + a { | |||
border-left: 1Px solid $color-white; | |||
padding-left: 20Px; | |||
} | |||
} | |||
.footer_info { | |||
@include size(auto, 170Px); | |||
@include flex(row, space-between, center, nowrap); | |||
img { | |||
@include size(170Px, 170Px); | |||
margin-right: 10Px; | |||
} | |||
.info { | |||
@include size(auto, 170Px); | |||
@include font(16Px, $color-white); | |||
@include flex(column, space-between, flex-start, nowrap); | |||
> div { | |||
flex: 1; | |||
@include flex(column, center, flex-start, nowrap); | |||
span { | |||
margin-bottom: 4Px; | |||
} | |||
} | |||
} | |||
} | |||
.line { | |||
width: 100%; | |||
height: 1Px; | |||
background: $color-white; | |||
} | |||
.bottom_info { | |||
@include size(1200Px, 55Px); | |||
@include font(14Px, $color-white); | |||
font-weight: 500; | |||
// padding: 0 50Px; | |||
box-sizing: border-box; | |||
@include flex(row, space-between, center, nowrap); | |||
} | |||
</style> |
@@ -0,0 +1,49 @@ | |||
<template> | |||
<div class="block" @click="showTipInfo"> | |||
<div class="block-item"> | |||
<img src="@/assets/phone.png" alt="电话咨询" /> | |||
<span>电话咨询</span> | |||
</div> | |||
<div class="block-item"> | |||
<img src="@/assets/code.png" alt="扫码关注" /> | |||
<span>扫码关注</span> | |||
</div> | |||
</div> | |||
<el-backtop :bottom="100"></el-backtop> | |||
<TipDialog ref="tipRefPC"></TipDialog> | |||
</template> | |||
<script setup> | |||
import TipDialog from "@/components/pc/TipDialogPC.vue"; | |||
const tipRefPC = ref(null); | |||
const showTipInfo = () => { | |||
tipRefPC.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.block { | |||
position: fixed; | |||
right: 0; | |||
top: 200px; | |||
background: rgba(0, 153, 255, 1); | |||
z-index: 1008; | |||
border-radius: 45px; | |||
padding: 50px 0; | |||
@include border-box; | |||
&-item { | |||
@include size(100px, auto); | |||
@include font(16px, $color-white); | |||
@include flex(column, center, center, nowrap); | |||
border-top: 1px solid rgba(255, 255, 255, 1); | |||
padding: 10px 0; | |||
@include border-box; | |||
cursor: pointer; | |||
img { | |||
height: 60px; | |||
margin-bottom: 4px; | |||
} | |||
} | |||
&-item:last-child { | |||
border-bottom: 1px solid rgba(255, 255, 255, 1); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,89 @@ | |||
<template> | |||
<el-dialog | |||
v-model="dialogVisible" | |||
title="立即咨询" | |||
:before-close="handleClose" | |||
center | |||
style="border-radius: 20px" | |||
> | |||
<div class="dialog" v-if="contract"> | |||
<div class="dialog-item"> | |||
<span class="dialog-item-title">电话咨询</span> | |||
<div class="phone"> | |||
<p v-if="contract.mobileOne">{{ contract.mobileOne }}</p> | |||
<p v-if="contract.mobileTwo">{{ contract.mobileTwo }}</p> | |||
<p v-if="contract.mobileThree">{{ contract.mobileThree }}</p> | |||
</div> | |||
</div> | |||
<div class="dialog-item" v-if="contract.qrCode.length > 0"> | |||
<span class="dialog-item-title">扫码咨询</span> | |||
<img :src="formatImg(contract.qrCode[0])" alt="" /> | |||
</div> | |||
<div class="dialog-item" v-if="contract.email"> | |||
<span class="dialog-item-title">邮箱</span> | |||
<span class="email">{{ contract.email }}</span> | |||
</div> | |||
</div> | |||
</el-dialog> | |||
</template> | |||
<script setup> | |||
import { getContactInfoConfig } from "@/apis/index"; | |||
import { toast, formatImg } from "@/utils/common.js"; | |||
import { onMounted, ref } from "vue"; | |||
import useHomeStore from "@/store/module/home"; | |||
const homeStore = useHomeStore(); | |||
const dialogVisible = ref(false); | |||
const handleClose = () => { | |||
dialogVisible.value = false; | |||
}; | |||
const showTip = () => { | |||
dialogVisible.value = true; | |||
}; | |||
defineExpose({ showTip, handleClose }); | |||
const contract = ref(); | |||
const getData = () => { | |||
getContactInfoConfig() | |||
.then(res => { | |||
if (res.data.status == 0) { | |||
contract.value = res.data.data; | |||
homeStore.contract.value = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
}) | |||
.catch(error => { | |||
console.log(error); | |||
}); | |||
}; | |||
onMounted(() => { | |||
getData(); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.dialog { | |||
@include flex(row, space-between, flex-start, nowrap); | |||
&-item { | |||
width: 30%; | |||
@include flex(column, flex-start, center, nowrap); | |||
&-title { | |||
@include font(20px, $color-black); | |||
font-weight: 400; | |||
margin-bottom: 50px; | |||
} | |||
.phone { | |||
@include font(24px, rgba(0, 153, 255, 1)); | |||
} | |||
p { | |||
margin-bottom: 30px; | |||
} | |||
img { | |||
@include size(150px, 150px); | |||
} | |||
.email { | |||
@include font(24px, $color-black); | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,117 @@ | |||
<!-- 导航 --> | |||
<template> | |||
<div class="header"> | |||
<div class="wrap"> | |||
<section class="wrap_left"> | |||
<img v-if="propsData.logo" :src="formatImg(logoArr[0])" alt="logo" /> | |||
<div class="title"> | |||
<span class="ch">浦东新区书院镇</span> | |||
<span class="en">SHUYUAN TOWN</span> | |||
</div> | |||
</section> | |||
<section class="wrap_right"> | |||
<div> | |||
<router-link | |||
v-for="item in nav" | |||
:key="item.name" | |||
:to="item.path" | |||
:class="[item.name === 'P_ApplyForEntry' ? 'apply' : '']" | |||
> | |||
{{ item.meta.title }} | |||
</router-link> | |||
</div> | |||
</section> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { useRouter } from "vue-router"; //1.引入路由 | |||
const router = useRouter(); //2.实例化路由 | |||
const nav = router.getRoutes().filter(item => item.meta.isNav && item.meta.type === "pc"); | |||
import { formatImg } from "@/utils/common.js"; | |||
let logoArr = ref([]); | |||
const propsData = defineProps({ | |||
logo: { | |||
type: String, | |||
}, | |||
}); | |||
logoArr.value = propsData.logo ? JSON.parse(propsData.logo) : []; | |||
</script> | |||
<style lang="scss" scoped> | |||
//@import url(); 引入公共css类 | |||
.header { | |||
@include size(100%, 100px); | |||
margin: 0 auto; | |||
color: $color-black; | |||
background: #fff; | |||
@include flex(row, center, center, wrap); | |||
@include border-box; | |||
} | |||
.wrap { | |||
min-width: $wrapWidth; | |||
@include size($wrapWidth, 100%); | |||
@include flex(row, space-between, center, wrap); | |||
.wrap_left { | |||
@include flex(row, center, center, wrap); | |||
img { | |||
@include size(auto, 50px); | |||
margin-right: 20px; | |||
} | |||
.title { | |||
@include size(auto, 50px); | |||
@include flex(column, space-around, flex-start, nowrap); | |||
} | |||
& .ch { | |||
@include font(16px, $color-black); | |||
font-weight: bold; | |||
} | |||
& .en { | |||
@include font(12px, $color-black); | |||
} | |||
} | |||
.wrap_right { | |||
height: 100%; | |||
@include flex(row, flex-end, center, wrap); | |||
& > div { | |||
height: 100%; | |||
@include flex(row, space-between, center, wrap); | |||
a { | |||
margin-right: 60px; | |||
@include font(20px, $color-black); | |||
font-weight: bold; | |||
@include flex(row, center, center, wrap); | |||
} | |||
a.apply { | |||
border-radius: 50px; | |||
background: rgba(0, 145, 255, 1); | |||
font-weight: normal; | |||
padding: 2px 12px; | |||
@include font(18px, $color-white); | |||
box-sizing: border-box; | |||
margin-right: 0; | |||
} | |||
:hover { | |||
color: rgba(0, 145, 255, 1); | |||
} | |||
.router-link-active { | |||
color: rgba(0, 145, 255, 1); | |||
} | |||
} | |||
} | |||
.enterprise { | |||
@include size(160px, 40px); | |||
border: solid 1px rgba(255, 255, 255, 0.5); | |||
border-radius: 20px; | |||
@include border-box; | |||
@include flex(row, center, center, wrap); | |||
img { | |||
@include size(20px, 20px); | |||
margin-right: 10px; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,21 @@ | |||
import { createApp } from "vue"; | |||
import App from "./App.vue"; | |||
import router from "./router"; | |||
import store from "./store"; | |||
import "@/styles/reset.scss"; /*引入公共样式*/ | |||
import "amfe-flexible"; | |||
// main.js | |||
if (/Android|webOS|iPhone|iPod|BlackBerry|iPad/i.test(navigator.userAgent)) { | |||
// Vue.use(VueTouch, { name: "v-touch" }); | |||
// VueTouch.config.swipe = { | |||
// threshold: 50, | |||
// }; | |||
var oMeta = document.createElement("meta"); | |||
oMeta.content = | |||
"width=device-width, initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0, user-scalable=0"; | |||
oMeta.name = "viewport"; | |||
document.getElementsByTagName("head")[0].appendChild(oMeta); | |||
} | |||
createApp(App).use(store).use(router).mount("#app"); |
@@ -0,0 +1,200 @@ | |||
import { createRouter, createWebHistory } from "vue-router"; | |||
const isMobile = /Android|webOS|iPhone|iPod|BlackBerry|iPad/i.test(navigator.userAgent); | |||
const redirectPath = isMobile ? "/m_home" : "/p_home"; | |||
const routes = [{ | |||
path: "/", | |||
redirect: redirectPath, | |||
}, | |||
{ | |||
path: "/m_home", | |||
name: "M_Home", | |||
component: () => | |||
import ("@/views/mobile/home/Home.vue"), | |||
meta: { | |||
title: "首页", | |||
isNav: true, | |||
type: "mobile", | |||
}, | |||
}, | |||
{ | |||
path: "/m_industry-park", | |||
name: "M_IndustryPark", | |||
component: () => | |||
import ("@/views/mobile/industry/IndustryPark.vue"), | |||
meta: { | |||
title: "产业园区", | |||
isNav: true, | |||
type: "mobile", | |||
}, | |||
}, | |||
{ | |||
path: "/m_investment-projects", | |||
name: "M_InvestmentProjects", | |||
component: () => | |||
import ("@/views/mobile/project/InvestmentProjects.vue"), | |||
meta: { | |||
title: "招商项目 ", | |||
isNav: true, | |||
type: "mobile", | |||
}, | |||
}, | |||
{ | |||
path: "/m_preferential-policy", | |||
name: "M_PreferentialPolicy", | |||
component: () => | |||
import ("@/views/mobile/policy/PreferentialPolicy.vue"), | |||
meta: { | |||
title: "优惠政策", | |||
isNav: true, | |||
type: "mobile", | |||
}, | |||
}, | |||
{ | |||
path: "/m_preferential-policy-detail", | |||
name: "M_PreferentialPolicyDetail", | |||
component: () => | |||
import ("@/views/mobile/policy/PreferentialPolicyDetail.vue"), | |||
meta: { | |||
title: "优惠政策", | |||
type: "mobile", | |||
}, | |||
}, | |||
{ | |||
path: "/m_about-me", | |||
name: "M_AboutMe", | |||
component: () => | |||
import ("@/views/mobile/aboutMe/AboutMe.vue"), | |||
meta: { | |||
title: "关于书院", | |||
isNav: true, | |||
type: "mobile", | |||
}, | |||
}, | |||
{ | |||
path: "/m_apply-for-entry", | |||
name: "M_ApplyForEntry", | |||
component: () => | |||
import ("@/views/mobile/ApplyForEntry.vue"), | |||
meta: { | |||
title: "申请入驻", | |||
isNav: true, | |||
type: "mobile", | |||
}, | |||
}, | |||
{ | |||
path: "/p_home", | |||
name: "P_Home", | |||
component: () => | |||
import ("@/views/pc/home/Home.vue"), | |||
meta: { | |||
title: "首页", | |||
isNav: true, | |||
type: "pc", | |||
}, | |||
}, | |||
{ | |||
path: "/p_industry-park", | |||
name: "P_IndustryPark", | |||
component: () => | |||
import ("@/views/pc/industry/IndustryPark.vue"), | |||
meta: { | |||
title: "产业园区", | |||
isNav: true, | |||
type: "pc", | |||
}, | |||
}, | |||
{ | |||
path: "/p_investment-projects", | |||
name: "P_InvestmentProjects", | |||
component: () => | |||
import ("@/views/pc/project/InvestmentProjects.vue"), | |||
meta: { | |||
title: "招商项目 ", | |||
isNav: true, | |||
type: "pc", | |||
}, | |||
}, | |||
{ | |||
path: "/p_preferential-policy", | |||
name: "P_PreferentialPolicy", | |||
component: () => | |||
import ("@/views/pc/policy/PreferentialPolicy.vue"), | |||
meta: { | |||
title: "优惠政策", | |||
isNav: true, | |||
type: "pc", | |||
}, | |||
}, | |||
{ | |||
path: "/p_preferential-policy-detail", | |||
name: "P_PreferentialPolicyDetail", | |||
component: () => | |||
import ("@/views/pc/policy/PreferentialPolicyDetail.vue"), | |||
meta: { | |||
title: "优惠政策", | |||
type: "pc", | |||
}, | |||
}, | |||
{ | |||
path: "/p_about-me", | |||
name: "P_AboutMe", | |||
component: () => | |||
import ("@/views/pc/aboutMe/AboutMe.vue"), | |||
meta: { | |||
title: "关于书院", | |||
isNav: true, | |||
type: "pc", | |||
}, | |||
}, | |||
{ | |||
path: "/p_apply-for-entry", | |||
name: "P_ApplyForEntry", | |||
component: () => | |||
import ("@/views/pc/apply/ApplyForEntry.vue"), | |||
meta: { | |||
title: "申请入驻", | |||
isNav: true, | |||
type: "pc", | |||
}, | |||
}, | |||
]; | |||
const router = createRouter({ | |||
history: createWebHistory("/public"), | |||
routes, | |||
scrollBehavior() { | |||
return { | |||
left: 0, | |||
top: 0, | |||
}; | |||
}, | |||
}); | |||
router.beforeEach((to, from, next) => { | |||
// 移动端访问PC | |||
if (isMobile && to.meta.type !== "mobile") { | |||
let routers = router.options.routes.filter(v => v.path != "/" && v.meta.type === "mobile"); | |||
let path = null; | |||
routers.forEach(v => { | |||
if (v.name.split("_")[1] == to.name.split("_")[1]) { | |||
path = v.path; | |||
} | |||
}); | |||
if (path) next(path); | |||
next("/"); | |||
} | |||
// pc 访问 移动 | |||
if (!isMobile && to.meta.type !== "pc") { | |||
const routers = router.options.routes.filter(v => v.path != "/" && v.meta.type === "pc"); | |||
let path = null; | |||
routers.forEach(v => { | |||
if (v.name.split("_")[1] == to.name.split("_")[1]) { | |||
path = v.path; | |||
} | |||
}); | |||
if (path) next(path); | |||
next("/"); | |||
} | |||
next(); | |||
}); | |||
export default router; |
@@ -0,0 +1,10 @@ | |||
import { createPinia } from "pinia"; | |||
// 引入持久化插件 | |||
import piniaPluginPersist from "pinia-plugin-persist"; | |||
const store = createPinia(); | |||
// 使用该插件 | |||
store.use(piniaPluginPersist); | |||
//导出 | |||
export default store; |
@@ -0,0 +1,27 @@ | |||
import { defineStore } from "pinia"; | |||
import { ref } from "vue"; | |||
const useHomeStore = defineStore("Home", { | |||
state: () => { | |||
return { | |||
homeData: ref({}), | |||
policyInfo: ref({}), | |||
contract: ref({}), | |||
}; | |||
}, | |||
actions: {}, | |||
getters: {}, | |||
persist: { | |||
//这里存储默认使用的是session | |||
enabled: true, | |||
strategies: [{ | |||
//key的名称 | |||
key: "home", | |||
//更改默认存储,我更改为localStorage | |||
storage: localStorage, | |||
// 可以选择哪些进入local存储,这样就不用全部都进去存储了 | |||
// 默认是全部进去存储 | |||
// paths: [""], | |||
}, ], | |||
}, | |||
}); | |||
export default useHomeStore; |
@@ -0,0 +1,55 @@ | |||
* { | |||
padding: 0; | |||
margin: 0; | |||
} | |||
html { | |||
height: 100%; | |||
} | |||
body { | |||
height: 100%; | |||
} | |||
#app { | |||
width: 100%; | |||
height: 100%; | |||
max-width: auto; | |||
min-height: 100%; | |||
} | |||
ul, | |||
ol, | |||
li { | |||
list-style: none; | |||
} | |||
a { | |||
text-decoration: none; | |||
} | |||
.pointer { | |||
cursor: pointer; | |||
} | |||
.fl { | |||
float: left; | |||
} | |||
.fr { | |||
float: right; | |||
} | |||
.tip { | |||
color: #fff; | |||
font-size: 16px; | |||
text-align: center; | |||
background: rgba(0, 0, 0, 0.7); | |||
border-radius: 8px; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
justify-content: center; | |||
padding: 14px; | |||
box-sizing: content-box; | |||
} |
@@ -0,0 +1,69 @@ | |||
$bg-color: red; | |||
$color-white: #fff; | |||
$color-black: rgba(0, 0, 0, 1); | |||
$wrapWidth: 1200px; | |||
$contentWidth: 1180px; | |||
// 尺寸 | |||
@mixin size($w, $h) { | |||
width: $w; | |||
height: $h; | |||
} | |||
// 字体大小、颜色 | |||
@mixin font($font-size, $font-color) { | |||
font-size: $font-size; | |||
color: $font-color; | |||
} | |||
// 背景图片 | |||
@mixin bg-image($url) { | |||
background-image: url($url); | |||
} | |||
// 文本溢出省略显示 | |||
// 单行文本溢出省略显示 | |||
@mixin text-ellipsis() { | |||
white-space: nowrap; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
} | |||
// 多行文本溢出省略显示,支持 WebKit浏览器或移动端的页面 | |||
// $row - 行数 | |||
@mixin text-ellipsis-multiple($row) { | |||
overflow: hidden; | |||
word-break: break-all; | |||
text-overflow: ellipsis; | |||
display: -webkit-box; | |||
-webkit-line-clamp: $row; | |||
-webkit-box-orient: vertical; | |||
} | |||
// flex布局 | |||
@mixin flex( $direction: row, $justify-content: flex-start, $align-items: flex-start, $flex-wrap: nowrap) { | |||
display: flex; | |||
flex-direction: $direction; | |||
justify-content: $justify-content; | |||
align-items: $align-items; | |||
flex-wrap: $flex-wrap; | |||
} | |||
@mixin hoverLine($height, $color: $color-text-primary) { | |||
position: relative; | |||
&:hover::after { | |||
content: ""; | |||
position: absolute; | |||
height: $height; | |||
width: 100%; | |||
background-color: $color; | |||
bottom: 0; | |||
left: 0; | |||
} | |||
} | |||
// IE盒模型 | |||
@mixin border-box { | |||
-webkit-box-sizing: border-box; | |||
-moz-box-sizing: border-box; | |||
box-sizing: border-box; | |||
} |
@@ -0,0 +1,103 @@ | |||
import router from "../router/index"; | |||
import dayjs from "dayjs"; // 时间格式化组件 | |||
import { showToast } from "vant"; | |||
const { VITE_BASE_URL } = | |||
import.meta.env; | |||
// 在新的页面打开路由 | |||
export const routerOpenInNewWindow = routerPath => { | |||
let routeData = router.resolve(routerPath); | |||
window.open(routeData.href, "_blank"); | |||
}; | |||
/** | |||
* | |||
* @param {*} path 图片路径 | |||
* @returns | |||
*/ | |||
export const formatImg = path => { | |||
return path ? `${VITE_BASE_URL}/common/getImg?path=${path}` : ""; | |||
// return path ? `http://shuyuan.test.hhrchina.com/filex/img/${path}` : ""; | |||
}; | |||
/** | |||
* | |||
* @param {*} path 图片路径 | |||
* @returns | |||
*/ | |||
export const formatVideo = path => { | |||
return path ? `${VITE_BASE_URL}/common/getVideo?path=${path}` : ""; | |||
}; | |||
/** | |||
* | |||
* @param {*} time 时间 | |||
* @param {*} fmtString 转换的格式 | |||
* @returns | |||
*/ | |||
export const formatDate = (time, fmtString) => { | |||
time = new Date(time); | |||
// 使用dayjs这个日期格式化类库实现日期的格式化功能 | |||
if (time == "" || !time) { | |||
return "——"; | |||
} else { | |||
return dayjs(time).format(fmtString); | |||
} | |||
}; | |||
/** | |||
* | |||
* @param {*} ary 数组 | |||
* @param {*} key 根据key值排序 | |||
* @returns | |||
*/ | |||
export const sortByKey = (ary, key) => { | |||
return ary.sort(function(a, b) { | |||
let x = a[key]; | |||
let y = b[key]; | |||
return x < y ? -1 : x > y ? 1 : 0; | |||
}); | |||
}; | |||
/** | |||
* | |||
* @param {*} val string | |||
* @returns | |||
*/ | |||
export const toast = (val, type) => { | |||
const isMobile = /Android|webOS|iPhone|iPod|BlackBerry|iPad/i.test(navigator.userAgent); | |||
if (isMobile) { | |||
showToast({ | |||
message: val, | |||
className: "tip", | |||
}); | |||
} else { | |||
ElMessage({ | |||
message: val, | |||
type, | |||
}); | |||
} | |||
}; | |||
export const debounce = (func, delay) => { | |||
let timer = null; | |||
return function(...args) { | |||
clearTimeout(timer); | |||
timer = setTimeout(() => { | |||
func.apply(this, args); | |||
}, delay); | |||
}; | |||
}; | |||
export const throttle = (func, wait) => { | |||
let timer; | |||
return () => { | |||
if (timer) { | |||
return; | |||
} | |||
timer = setTimeout(() => { | |||
func(); | |||
timer = null; | |||
}, wait); | |||
}; | |||
}; |
@@ -0,0 +1,151 @@ | |||
/**axios封装 | |||
* 请求拦截、相应拦截、错误统一处理 | |||
*/ | |||
import axios from "axios"; | |||
// import QS from 'qs'; | |||
// 环境的切换 | |||
axios.defaults.baseURL = | |||
import.meta.env.VITE_BASE_URL; | |||
// 请求超时时间 | |||
axios.defaults.timeout = 25000; | |||
// post请求头 | |||
axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8"; | |||
// axios.defaults.headers.post["Content-Type"] = "application/json; charset=UTF-8"; | |||
let pending = []; //声明一个数组用于存储每个请求的取消函数和axios标识 | |||
let cancelRepeatUrl = []; // 园区服务、物业租售 | |||
let cancelToken = axios.CancelToken; | |||
let removePending = config => { | |||
// console.log(config); | |||
for (let i in pending) { | |||
if (pending[i].url === axios.defaults.baseURL + config.url) { | |||
//在当前请求在数组中存在时执行取消函数 | |||
pending[i].f(); //执行取消操作 | |||
// pending.splice(i, 1); // 根据具体情况决定是否在这里就把pending去掉 | |||
// console.log("重复的:" + pending[i].url); | |||
} | |||
} | |||
}; | |||
// 请求拦截器 | |||
axios.interceptors.request.use( | |||
config => { | |||
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了 | |||
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 | |||
// const token = store.state.token; | |||
// token && (config.headers.Authorization = token); | |||
// return config; | |||
removePending(config); //在一个axios发送前执行一下判定操作,在removePending中执行取消操作 | |||
// console.log(config.url); | |||
config.cancelToken = new cancelToken(function executor(c) { | |||
//本次axios请求的配置添加cancelToken | |||
if (cancelRepeatUrl.indexOf(config.url) > -1) { | |||
pending.push({ | |||
// url: config.url, | |||
url: axios.defaults.baseURL + config.url, | |||
f: c, | |||
}); | |||
// console.log(axios.defaults.baseURL + config.url); | |||
//将本次的url添加到pending中,因此对于某个url第一次发起的请求不会被取消,因为还没有配置取消函数 | |||
} | |||
}); | |||
return Promise.resolve(config); | |||
}, | |||
error => { | |||
return Promise.error(error); | |||
} | |||
); | |||
// 响应拦截器 | |||
axios.interceptors.response.use( | |||
response => { | |||
if (response.status === 200) { | |||
// removePending(response.config); //在一个axios响应后再执行一下取消操作,把已经完成的请求从pending中移除 | |||
return Promise.resolve(response); | |||
} else { | |||
return Promise.reject(response); | |||
} | |||
}, | |||
// 服务器状态码不是200的情况 | |||
error => { | |||
if (error.response) { | |||
return Promise.reject(error.response); | |||
} | |||
} | |||
); | |||
/** | |||
* get方法,对应get请求 | |||
* @param {String} url [请求的url地址] | |||
* @param {Object} params [请求时携带的参数] | |||
*/ | |||
export function get(url, params) { | |||
return new Promise((resolve, reject) => { | |||
axios | |||
.get(url, { | |||
params: params, | |||
}) | |||
.then(res => { | |||
resolve(res); | |||
}) | |||
.catch(err => { | |||
reject(err); | |||
}); | |||
}); | |||
} | |||
/** | |||
* post方法,对应post请求 | |||
* @param {String} url [请求的url地址] | |||
* @param {Object} params [请求时携带的参数] | |||
*/ | |||
export function post(url, params, options) { | |||
return new Promise((resolve, reject) => { | |||
axios | |||
.post(url, params, options) | |||
.then(res => { | |||
resolve(res); | |||
}) | |||
.catch(err => { | |||
reject(err); | |||
}); | |||
}); | |||
} | |||
/** | |||
* put方法,对应put请求 | |||
* @param {String} url [请求的url地址] | |||
* @param {Object} params [请求时携带的参数] | |||
*/ | |||
export function put(url, params) { | |||
return new Promise((resolve, reject) => { | |||
axios | |||
.put(url, params) | |||
.then(res => { | |||
resolve(res); | |||
}) | |||
.catch(err => { | |||
reject(err); | |||
}); | |||
}); | |||
} | |||
/** | |||
* delete方法,对应delete请求,delete是保留字,使用del代替 | |||
* @param {String} url [请求的url地址] | |||
*/ | |||
export function del(url, params) { | |||
return new Promise((resolve, reject) => { | |||
axios | |||
.delete(url, { | |||
params: params, | |||
}) | |||
.then(res => { | |||
resolve(res); | |||
}) | |||
.catch(err => { | |||
reject(err); | |||
}); | |||
}); | |||
} |
@@ -0,0 +1,217 @@ | |||
<template> | |||
<section> | |||
<div class="form-tab"> | |||
<span | |||
v-for="(item, index) in tabArr" | |||
:class="[currentTb == index ? 'active' : '']" | |||
@click="changeTab(item.value, index)" | |||
:key="item.value" | |||
> | |||
{{ item.label }} | |||
</span> | |||
</div> | |||
<div class="form"> | |||
<el-form | |||
v-if="currentTb == 0" | |||
:label-position="labelPosition" | |||
label-width="100px" | |||
:model="params" | |||
> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
企业名称 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.companyName" | |||
placeholder="请输入企业名称" | |||
maxlength="50" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系人 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.name" | |||
placeholder="请输入联系人名称" | |||
maxlength="10" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系电话 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.mobile" | |||
placeholder="请输入联系电话" | |||
maxlength="11" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label">预计产税</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.estimatedPropertyTax" | |||
placeholder="请输入预计产税" | |||
maxlength="20" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label><div class="form-item-label">需求内容</div></template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.text" | |||
type="textarea" | |||
rows="10" | |||
placeholder="请输入需求内容" | |||
resize="none" | |||
maxlength="200" | |||
/> | |||
</el-form-item> | |||
</el-form> | |||
<el-form | |||
v-else | |||
:label-position="labelPosition" | |||
label-width="100px" | |||
:model="formLabelAlign" | |||
> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
企业名称 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.companyName" | |||
placeholder="请输入企业名称" | |||
maxlength="50" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系人 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.name" | |||
placeholder="请输入联系人名称" | |||
maxlength="10" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系电话 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.mobile" | |||
placeholder="请输入联系电话" | |||
maxlength="11" | |||
/> | |||
</el-form-item> | |||
</el-form> | |||
<van-button | |||
class="submit" | |||
round | |||
block | |||
type="primary" | |||
native-type="submit" | |||
@click="submitForm" | |||
> | |||
提交申请 | |||
</van-button> | |||
<div class="text-tip">提交后我们将在1-7个工作日内联系您</div> | |||
</div> | |||
</section> | |||
</template> | |||
<script setup> | |||
import { apply } from "@/views/pc/apply/apply"; | |||
const { labelPosition, currentTb, tabArr, params, applyForEntry, changeTab, submitForm } = apply(); | |||
</script> | |||
<style lang="scss" scoped> | |||
.form-tab { | |||
padding: 10px 22px; | |||
@include border-box; | |||
@include flex(row, space-between, center, nowrap); | |||
span { | |||
width: 50%; | |||
height: 30px; | |||
@include font(12px, $color-black); | |||
font-weight: 500; | |||
@include flex(row, center, center, nowrap); | |||
border-radius: 20px; | |||
background: rgba(245, 245, 245, 1); | |||
cursor: pointer; | |||
@include border-box; | |||
} | |||
.active { | |||
@include font(12px, $color-white); | |||
background: rgba(0, 153, 255, 1); | |||
} | |||
} | |||
.form { | |||
margin: 10px 22px; | |||
padding: 18px; | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 20px; | |||
@include border-box; | |||
text-align: center; | |||
&-item { | |||
border-radius: 50px; | |||
background: rgba(242, 244, 249, 1); | |||
padding: 5px 10px 5px 0; | |||
@include border-box; | |||
&-label { | |||
@include font(12px, $color-black); | |||
border-right: 3px solid rgba(145, 145, 145, 1); | |||
padding-right: 10px; | |||
@include border-box; | |||
} | |||
} | |||
.submit { | |||
@include size(186px, 40px); | |||
@include font(12px, $color-white); | |||
border-radius: 50px; | |||
@include border-box; | |||
margin: 20px auto; | |||
} | |||
.text-tip { | |||
@include font(12px, $color-black); | |||
} | |||
} | |||
.required { | |||
color: rgba(255, 0, 0, 1); | |||
} | |||
:deep() { | |||
.el-input__wrapper, | |||
.el-textarea__inner { | |||
background: none; | |||
box-shadow: none; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,59 @@ | |||
<template> | |||
<div class="block"> | |||
<div class="title">{{ propsData.aboutItem.label }}</div> | |||
<div class="content" v-html="formatHtml(propsData.aboutItem.value)"></div> | |||
<!-- <div class="content"> | |||
<div> | |||
书院镇位于上海市浦东新区的东南部、自贸区临港新片区北门户位置,在浦东国际机场和洋山深水港之间,东临大海,南接泥城镇,西至万祥镇,北与老港镇隔大治河相望。 | |||
</div> | |||
<img | |||
src="https://img.js.design/assets/img/64ab9e9276b0da9863e5bba0.png#1b5fe6bef0090a0d2c77afb77c9b7873" | |||
alt="" | |||
/> | |||
</div> --> | |||
</div> | |||
</template> | |||
<script setup> | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
aboutItem: { | |||
type: Object, | |||
}, | |||
}); | |||
const formatHtml = text => { | |||
let html = SimpleMDE.prototype.markdown(text); | |||
console.log(html); | |||
let imgUrl = `${import.meta.env.VITE_BASE_URL}/common/getImg?path=`; | |||
html = html.replace( | |||
/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, | |||
"<img src='" + imgUrl + "$1' width='100%'/>" | |||
); | |||
return html; | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.block { | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 20px; | |||
padding: 10px; | |||
@include border-box; | |||
@include flex(column, space-between, center, nowrap); | |||
margin: 10px 0; | |||
.title { | |||
@include font(14px, $color-black); | |||
font-weight: 500; | |||
margin-bottom: 10px; | |||
} | |||
.content { | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
word-break: break-word; | |||
} | |||
img { | |||
@include size(100%, auto); | |||
margin-top: 10px; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,75 @@ | |||
<template> | |||
<div class="about" v-if="!loading"> | |||
<!-- <div> | |||
<span v-for="item in tabArr" :key="item.label">{{ item.label }}</span> | |||
</div> --> | |||
<template v-for="(item, index) in tabArr" :key="item.label"> | |||
<About | |||
:aboutItem="{ | |||
label: tabArr[index].label, | |||
value: homeData[tabArr[index].key], | |||
}" | |||
></About> | |||
</template> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { onMounted, ref } from "vue"; | |||
import About from "./About.vue"; | |||
import useHomeStore from "@/store/module/home"; | |||
const homeStore = useHomeStore(); | |||
let homeData = ref({}); | |||
const tab = [ | |||
{ | |||
label: "区位优势", | |||
value: 0, | |||
key: "regionalAdvantages", | |||
}, | |||
{ | |||
label: "发展机遇", | |||
value: 1, | |||
key: "developmentOpportunity", | |||
}, | |||
{ | |||
label: "配套建设", | |||
value: 2, | |||
key: "supportingConstruction", | |||
}, | |||
{ | |||
label: "发展趋势", | |||
value: 3, | |||
key: "developmentTendency", | |||
}, | |||
{ | |||
label: "交通优势", | |||
value: 4, | |||
key: "transportationAdvantage", | |||
}, | |||
{ | |||
label: "镇域概况", | |||
value: 5, | |||
key: "townProfile", | |||
}, | |||
{ | |||
label: "书院介绍", | |||
value: 6, | |||
key: "academyHistory", | |||
}, | |||
]; | |||
const tabArr = ref(tab); | |||
const loading = ref(true); | |||
onMounted(() => { | |||
loading.value = true; | |||
homeData.value = homeStore.homeData.value; | |||
loading.value = false; | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.about { | |||
padding: 0 15px; | |||
overflow: hidden; | |||
background: rgba(245, 245, 245, 1); | |||
@include border-box; | |||
} | |||
</style> |
@@ -0,0 +1,73 @@ | |||
<template> | |||
<div v-if="!loading"> | |||
<div class="wrap-box"> | |||
<Introduce :homeData="homeData"></Introduce> | |||
<IndustrialPark :projectItem="registrationPackage"></IndustrialPark> | |||
<IndustrialCluster :industrialAllocation="industrialAllocation"></IndustrialCluster> | |||
</div> | |||
<div class="wrap-box"> | |||
<div class="title">政策优势</div> | |||
<Policy v-for="(item, index) in policy" :key="index" :policyItem="item"></Policy> | |||
<PicturePolicy | |||
:policySupportKeyIndustries="homeData.policySupportKeyIndustries" | |||
:domesticTalentIntroduction="homeData.domesticTalentIntroduction" | |||
></PicturePolicy> | |||
<DetailPolicy | |||
v-for="(item, index) in detailPolicy" | |||
:key="index" | |||
:detailPolicyItem="item" | |||
></DetailPolicy> | |||
<OtherPolicy></OtherPolicy> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import Introduce from "./project/Introduce.vue"; | |||
import IndustrialPark from "./project/IndustrialPark.vue"; | |||
import IndustrialCluster from "./project/IndustrialCluster.vue"; | |||
import Policy from "./policy/Policy.vue"; | |||
import PicturePolicy from "./policy/PicturePolicy.vue"; | |||
import DetailPolicy from "./policy/DetailPolicy.vue"; | |||
import OtherPolicy from "./policy/OtherPolicy.vue"; | |||
import { getHomeData } from "@/views/pc/home/home"; | |||
import { onBeforeMount } from "vue"; | |||
const { | |||
getAreaIndustrialAllocation, | |||
setDetailPolicy, | |||
setPolicy, | |||
updatePageView, | |||
getRegistrationPackage, | |||
} = getHomeData(); | |||
let homeData = ref({}); | |||
let industrialAllocation = ref([]); | |||
let policy = ref([]); | |||
let detailPolicy = ref([]); | |||
let registrationPackage = ref({}); | |||
const loading = ref(true); | |||
import useHomeStore from "@/store/module/home"; | |||
const homeStore = useHomeStore(); | |||
onBeforeMount(async () => { | |||
loading.value = true; | |||
homeData.value = homeStore.homeData.value; | |||
policy = setPolicy(homeData.value); | |||
detailPolicy = setDetailPolicy(homeData.value); | |||
industrialAllocation.value = await getAreaIndustrialAllocation(); | |||
registrationPackage.value = await getRegistrationPackage(); | |||
loading.value = false; | |||
updatePageView(homeData.value.areaInfoManagementId); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.wrap-box { | |||
padding: 10px 15px; | |||
background: #fff; | |||
@include border-box; | |||
} | |||
.title { | |||
@include font(16px, $color-black); | |||
font-weight: bold; | |||
margin-bottom: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,43 @@ | |||
<template> | |||
<div class="detail-title">{{ propsData.detailPolicyItem.title }}</div> | |||
<div class="detail-policy"> | |||
<div | |||
class="detail-policy-content" | |||
v-html="SimpleMDE.prototype.markdown(propsData.detailPolicyItem.content)" | |||
></div> | |||
<!-- <div class="detail-policy-content"> | |||
按当年销售金额最高 10% 给予奖励,单款芯片产品年度奖励总额不超过 500 万元。 | |||
</div> --> | |||
</div> | |||
</template> | |||
<script setup> | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
detailPolicyItem: { | |||
type: Object, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.detail-title { | |||
@include font(14px, rgba(42, 130, 228, 1)); | |||
border-bottom: 2px solid rgba(42, 130, 228, 1); | |||
padding: 10px 0; | |||
@include border-box; | |||
} | |||
.detail-policy { | |||
padding: 10px 0; | |||
@include border-box; | |||
&-title { | |||
@include font(14px, $color-black); | |||
margin-bottom: 10px; | |||
font-weight: bold; | |||
} | |||
&-content { | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
word-break: break-word; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,65 @@ | |||
<template> | |||
<div class="other-policy" v-if="dataList && dataList.length > 0"> | |||
<div class="other-policy-title">其他政策</div> | |||
<div class="other-policy-content"> | |||
<div | |||
v-for="item in dataList" | |||
:key="item.releasePolicyId" | |||
:title="item.policyName" | |||
@click="goRoute(item)" | |||
> | |||
{{ item.policyName }} | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { onMounted } from "vue"; | |||
import { getReleasePolicyData } from "@/views/pc/policy/policy"; | |||
import { useRouter } from "vue-router"; | |||
const $router = useRouter(); | |||
const { params, getList } = getReleasePolicyData(); | |||
let dataList = ref([]); | |||
const loading = ref(true); | |||
const getListData = async () => { | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
}; | |||
const goRoute = val => { | |||
localStorage.setItem("policyInfo", JSON.stringify(val)); | |||
$router.push({ | |||
path: "/m_preferential-policy-detail", | |||
query: { | |||
releasePolicyId: val.releasePolicyId, | |||
}, | |||
}); | |||
}; | |||
onMounted(async () => { | |||
loading.value = true; | |||
getListData(); | |||
loading.value = false; | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.other-policy { | |||
// padding: 16px 42px; | |||
@include border-box; | |||
margin-bottom: 28px; | |||
&-title { | |||
@include font(16px, $color-black); | |||
font-weight: bold; | |||
margin-bottom: 20px; | |||
} | |||
&-content { | |||
// @include flex(row, space-between, flex-start, wrap); | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
div { | |||
margin-bottom: 8px; | |||
@include text-ellipsis; | |||
@include border-box; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,63 @@ | |||
<template> | |||
<div class="picture-box"> | |||
<div | |||
class="picture-policy" | |||
v-if=" | |||
propsData.policySupportKeyIndustries && | |||
propsData.policySupportKeyIndustries.length > 0 | |||
" | |||
> | |||
<div class="picture-policy-title">重点产业扶持政策</div> | |||
<img | |||
class="picture-policy-content" | |||
:src="formatImg(propsData.policySupportKeyIndustries[0])" | |||
alt="重点产业扶持政策" | |||
/> | |||
</div> | |||
<div | |||
class="picture-policy" | |||
v-if=" | |||
propsData.domesticTalentIntroduction && | |||
propsData.domesticTalentIntroduction.length > 0 | |||
" | |||
> | |||
<div class="picture-policy-title">国内人才引进</div> | |||
<img | |||
class="picture-policy-content" | |||
:src="formatImg(propsData.domesticTalentIntroduction[0])" | |||
alt="国内人才引进" | |||
/> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
policySupportKeyIndustries: { | |||
type: Array, | |||
}, | |||
domesticTalentIntroduction: { | |||
type: Array, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.picture-box { | |||
// @include flex(row, space-between, flex-start, nowrap); | |||
} | |||
.picture-policy { | |||
// padding: 10px 22px 0; | |||
@include border-box; | |||
margin-bottom: 10px; | |||
&-title { | |||
@include font(14px, $color-black); | |||
text-align: center; | |||
font-weight: bold; | |||
margin-bottom: 10px; | |||
} | |||
&-content { | |||
@include size(100%, 100%); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,44 @@ | |||
<template> | |||
<div | |||
class="policy" | |||
:style="{ color: propsData.policyItem.color, background: propsData.policyItem.bgColor }" | |||
> | |||
<div class="policy-title" :style="{ color: propsData.policyItem.color }"> | |||
{{ propsData.policyItem.title }} | |||
</div> | |||
<div | |||
class="policy-content" | |||
:style="{ color: propsData.policyItem.color }" | |||
v-html="SimpleMDE.prototype.markdown(propsData.policyItem.content)" | |||
></div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
policyItem: { | |||
type: Object, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.policy { | |||
background: rgba(255, 242, 197, 1); | |||
border-radius: 20px; | |||
padding: 10px 12px; | |||
@include border-box; | |||
margin-bottom: 12px; | |||
&-title { | |||
@include font(14px, $color-black); | |||
text-align: center; | |||
font-weight: bold; | |||
margin-bottom: 10px; | |||
} | |||
&-content { | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
word-break: break-word; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,51 @@ | |||
<template> | |||
<div class="industrial-park"> | |||
<div class="industrial-park-title">产业聚集</div> | |||
<div class="industrial-park-content"> | |||
<van-swipe class="my-swipe" :autoplay="5000" indicator-color="white"> | |||
<van-swipe-item | |||
v-for="item in propsData.industrialAllocation" | |||
:key="item.areaIndustrialAllocationId" | |||
> | |||
<Cluster :data="item" :height="200" /> | |||
</van-swipe-item> | |||
</van-swipe> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import Cluster from "@/views/mobile/industry/Cluster.vue"; | |||
const propsData = defineProps({ | |||
industrialAllocation: { | |||
type: Array, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.industrial-park { | |||
// padding: 16px 42px; | |||
@include border-box; | |||
margin-top: 10px; | |||
&-title { | |||
@include font(16px, $color-black); | |||
font-weight: bold; | |||
margin-bottom: 10px; | |||
} | |||
&-content { | |||
@include flex(row, space-between, flex-start, wrap); | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
span { | |||
margin-bottom: 10px; | |||
@include text-ellipsis; | |||
@include border-box; | |||
} | |||
} | |||
} | |||
:deep() { | |||
// .van-swipe__indicator { | |||
// color: rgba(204, 204, 204, 1); | |||
// } | |||
} | |||
</style> |
@@ -0,0 +1,51 @@ | |||
<template> | |||
<div class="industrial-park"> | |||
<div class="industrial-park-title">产业园区</div> | |||
<div class="industrial-park-content"> | |||
<!-- <van-swipe class="my-swipe" :autoplay="5000" indicator-color="white"> --> | |||
<!-- <van-swipe-item v-for="item in 3"> --> | |||
<Project :projectItem="propsData.projectItem" @doSth="showTipInfo" /> | |||
<!-- </van-swipce-item> --> | |||
<!-- </van-swipe> --> | |||
</div> | |||
</div> | |||
<TipDialog ref="tipRef"></TipDialog> | |||
</template> | |||
<script setup> | |||
import TipDialog from "@/components/mobile/TipDialog.vue"; | |||
import Project from "@/views/mobile/project/Project.vue"; | |||
const propsData = defineProps({ | |||
projectItem: { | |||
type: Object, | |||
}, | |||
}); | |||
const tipRef = ref(null); | |||
const showTipInfo = () => { | |||
tipRef.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.industrial-park { | |||
// padding: 16px 42px; | |||
@include border-box; | |||
margin-top: 20px; | |||
&-title { | |||
@include font(16px, $color-black); | |||
font-weight: bold; | |||
margin-bottom: 10px; | |||
} | |||
&-content { | |||
@include flex(row, space-between, flex-start, wrap); | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
span { | |||
margin-bottom: 10px; | |||
@include text-ellipsis; | |||
@include border-box; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,92 @@ | |||
<template> | |||
<div class="introduce"> | |||
<div class="introduce-box"> | |||
<div class="introduce-box-title">区位优势</div> | |||
<div> | |||
{{ propsData.homeData.academyHistory }} | |||
</div> | |||
</div> | |||
<img | |||
class="introduce-img" | |||
v-if="propsData.homeData.parkPicture && propsData.homeData.parkPicture.length > 0" | |||
:src="formatImg(propsData.homeData.parkPicture[0])" | |||
alt="园区图片" | |||
/> | |||
<div class="introduce-box"> | |||
<div class="introduce-box-title">镇域概括</div> | |||
<ul class="introduce-ul"> | |||
<li> | |||
<div class="subtitle">行政区域面积</div> | |||
<div> | |||
<span class="blue-text">{{ propsData.homeData.administrativeArea }}</span> | |||
平方公里 | |||
</div> | |||
</li> | |||
<li> | |||
<div>实有入口</div> | |||
<div> | |||
<span class="blue-text">{{ propsData.homeData.actualEntry }}</span> | |||
万 | |||
</div> | |||
</li> | |||
<li> | |||
<div>下辖</div> | |||
<div> | |||
<span class="blue-text">{{ propsData.homeData.haveJurisdictionOver }}</span> | |||
个村 | |||
</div> | |||
</li> | |||
<li> | |||
<div>居民区</div> | |||
<div> | |||
<span class="blue-text">{{ propsData.homeData.residentialArea }}</span> | |||
个 | |||
</div> | |||
</li> | |||
</ul> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
homeData: { | |||
type: Object, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.introduce { | |||
padding-top: 5px; | |||
@include border-box; | |||
&-img { | |||
@include size(100%, 325px); | |||
border-radius: 20px; | |||
@include border-box; | |||
margin: 10px 0; | |||
} | |||
&-box { | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
&-title { | |||
@include font(16px, $color-black); | |||
margin-bottom: 10px; | |||
} | |||
} | |||
} | |||
.introduce-ul { | |||
@include flex(row, space-between, flex-start, nowrap); | |||
li { | |||
// width: 25%; | |||
// margin-top: 10px; | |||
} | |||
.subtitle { | |||
@include font(12px, $color-black); | |||
} | |||
.blue-text { | |||
@include font(20px, rgba(0, 153, 255, 1)); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,70 @@ | |||
<template> | |||
<div | |||
:class="['cluster', propsData.height ? 'height_200' : '']" | |||
:style="{ backgroundImage: 'url(' + formatImg(propsData.data.productUrl[0]) + ')' }" | |||
> | |||
<div class="cluster-title">{{ propsData.data.leadingIndustryName }}</div> | |||
<div | |||
class="cluster-content" | |||
v-html="SimpleMDE.prototype.markdown(propsData.data.leadingIndustryDetail)" | |||
></div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import SimpleMDE from "simplemde"; | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
data: { | |||
type: Object, | |||
}, | |||
height: { | |||
type: Number, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.cluster { | |||
position: relative; | |||
border-radius: 10px; | |||
padding: 10px 20px; | |||
@include border-box; | |||
@include flex(column, flex-start, flex-start, nowrap); | |||
margin-bottom: 10px; | |||
// @include bg-image("https://img.js.design/assets/img/64abb2f1eec7205596b5791f.jpg"); | |||
background-size: 100% 100%; | |||
background-repeat: no-repeat; | |||
&-title { | |||
@include font(14px, $color-white); | |||
font-weight: bold; | |||
margin-bottom: 10px; | |||
z-index: 100; | |||
} | |||
&-content { | |||
@include font(12px, $color-white); | |||
line-height: 20px; | |||
word-break: break-word; | |||
z-index: 100; | |||
} | |||
} | |||
.cluster:before { | |||
content: ""; | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
background-color: rgba(0, 0, 0, 0.5); | |||
border-radius: 10px; | |||
@include border-box; | |||
} | |||
.height_200 { | |||
height: 200px; | |||
.cluster-content { | |||
@include text-ellipsis-multiple(7); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,67 @@ | |||
<template> | |||
<div class="enterprise_item_box"> | |||
<div class="block-title">{{ propsData.projectItem.element.text }}</div> | |||
<div | |||
class="enterprise" | |||
v-for="item in propsData.projectItem.enterpriseData" | |||
:key="item.enterpriseHomeLinkId" | |||
> | |||
<img :src="formatImg(item.url[0])" alt="图片" /> | |||
<div class="content" v-html="SimpleMDE.prototype.markdown(item.detail)"></div> | |||
<!-- <div class="content">占地面积381亩</div> | |||
<div class="content">建筑面积337515平方米</div> --> | |||
<div class="consult" @click="showTipInfo">立 即 咨 询</div> | |||
</div> | |||
</div> | |||
<TipDialog ref="tipRef"></TipDialog> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
projectItem: { | |||
type: Object, | |||
}, | |||
}); | |||
import TipDialog from "@/components/mobile/TipDialog.vue"; | |||
const tipRef = ref(null); | |||
const showTipInfo = () => { | |||
tipRef.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.enterprise_item_box { | |||
margin-bottom: 20px; | |||
} | |||
.block-title { | |||
@include flex(row, center, center, nowrap); | |||
@include font(14px, $color-black); | |||
font-weight: 500; | |||
} | |||
.enterprise { | |||
img { | |||
@include font(12px, $color-black); | |||
display: inline-block; | |||
@include size(100%, 220px); | |||
border-radius: 20px; | |||
@include border-box; | |||
margin: 12px 0; | |||
} | |||
.content { | |||
@include font(12px, $color-black); | |||
font-weight: 400; | |||
margin-bottom: 12px; | |||
line-height: 20px; | |||
word-break: break-word; | |||
} | |||
.consult { | |||
@include size(290px, 32px); | |||
border-radius: 30px; | |||
background: rgba(0, 153, 255, 1); | |||
@include flex(row, center, center, nowrap); | |||
@include font(14px, $color-white); | |||
margin: 0 auto; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,95 @@ | |||
<template> | |||
<div class="industry"> | |||
<div class="form-tab"> | |||
<span | |||
v-for="item in tabArr" | |||
:key="item.value" | |||
:class="[currentTab == item.value ? 'active' : '']" | |||
@click="changeTab(item.value)" | |||
> | |||
{{ item.label }} | |||
</span> | |||
</div> | |||
<template v-if="!loading"> | |||
<template v-if="currentTab == 0"> | |||
<InvestmentVehicle | |||
v-for="item in registrationPackageList" | |||
:key="item.registrationPackageId" | |||
:projectItem="item" | |||
@doSth="showTipInfo" | |||
></InvestmentVehicle> | |||
</template> | |||
<template v-if="currentTab == 1"> | |||
<Enterprise | |||
v-for="item in enterpriseData" | |||
:key="item.element.itemId" | |||
:projectItem="item" | |||
></Enterprise> | |||
</template> | |||
<template v-if="currentTab == 2"> | |||
<Cluster | |||
v-for="item in industrialAllocation" | |||
:key="item.areaIndustrialAllocationId" | |||
:data="item" | |||
></Cluster> | |||
</template> | |||
</template> | |||
</div> | |||
<TipDialog ref="tipRef"></TipDialog> | |||
</template> | |||
<script setup> | |||
import Cluster from "./Cluster.vue"; | |||
import Enterprise from "./Enterprise.vue"; | |||
import InvestmentVehicle from "../project/Project.vue"; | |||
import TipDialog from "@/components/mobile/TipDialog.vue"; | |||
import { getIndustryData } from "@/views/pc/industry/getRegistrationPackageList"; | |||
import { onMounted } from "vue"; | |||
let { | |||
loading, | |||
tabArr, | |||
currentTab, | |||
changeTab, | |||
registrationPackageList, | |||
enterpriseData, | |||
industrialAllocation, | |||
} = getIndustryData(); | |||
onMounted(() => { | |||
loading.value = true; | |||
changeTab(0); | |||
loading.value = false; | |||
}); | |||
const tipRef = ref(null); | |||
const showTipInfo = () => { | |||
tipRef.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.industry { | |||
padding: 10px 15px; | |||
overflow: hidden; | |||
background: #fff; | |||
@include border-box; | |||
min-height: 100%; | |||
} | |||
.form-tab { | |||
@include size(100%, auto); | |||
margin: 10px auto 20px; | |||
@include flex(row, space-between, center, nowrap); | |||
span { | |||
width: 30%; | |||
height: 25px; | |||
@include font(12px, $color-black); | |||
font-weight: 500; | |||
@include flex(row, center, center, nowrap); | |||
border-radius: 20px; | |||
background: rgba(245, 245, 245, 1); | |||
@include border-box; | |||
} | |||
.active { | |||
@include font(12px, $color-white); | |||
background: rgba(0, 153, 255, 1); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,192 @@ | |||
<template> | |||
<div class="policy" v-if="!loading"> | |||
<div class="policy-search"> | |||
<div class="search-left" @click="show = true"> | |||
{{ selectText }} | |||
<van-icon name="arrow-down" /> | |||
</div> | |||
<van-field | |||
class="search-input" | |||
v-model="params.search" | |||
@clear="toSearch" | |||
@keyup.enter="toSearch" | |||
/> | |||
<div class="search-right"> | |||
<van-icon name="search" @click="toSearch" /> | |||
搜索 | |||
</div> | |||
</div> | |||
<div class="list_box"> | |||
<van-list | |||
v-model:loading="pageLoading" | |||
:finished="finished" | |||
finished-text="没有更多了" | |||
@load="onLoad" | |||
ref="checkbox" | |||
> | |||
<ul class="policy-ul"> | |||
<li | |||
class="policy-item" | |||
v-for="item in dataList" | |||
:key="item.releasePolicyId" | |||
@click="goRoute(item)" | |||
> | |||
<div class="policy-item-left">{{ item.policyName }}</div> | |||
<div class="policy-item-right"> | |||
<span>政策类型:{{ item.policyType.text }}</span> | |||
<span>发布时间:{{ formatDate(item.modifiedOn, "YYYY.MM.DD") }}</span> | |||
</div> | |||
</li> | |||
</ul> | |||
</van-list> | |||
</div> | |||
</div> | |||
<van-popup v-model:show="show" round position="bottom"> | |||
<van-picker :columns="selectList" @confirm="changeSelect" @cancel="onCancel" /> | |||
</van-popup> | |||
</template> | |||
<script setup> | |||
import { getReleasePolicyData } from "@/views/pc/policy/policy"; | |||
import { formatDate, sortByKey } from "@/utils/common.js"; | |||
import { onMounted } from "vue"; | |||
import useHomeStore from "@/store/module/home"; | |||
import { useRouter } from "vue-router"; | |||
const homeStore = useHomeStore(); | |||
const $router = useRouter(); | |||
const { params, getList, getPickList } = getReleasePolicyData(); | |||
let dataList = ref([]); | |||
let selectList = ref([]); | |||
let total = ref(0); | |||
const loading = ref(true); | |||
let selectText = ref("全部"); | |||
const pageLoading = ref(false); | |||
const finished = ref(false); | |||
const onCancel = () => { | |||
show.value = false; | |||
}; | |||
const onLoad = () => { | |||
if (params.value.page !== 1) { | |||
getListData(); | |||
} | |||
}; | |||
const changeSelect = async ({ selectedOptions }) => { | |||
selectText.value = selectedOptions[0].text; | |||
console.log(selectedOptions[0]); | |||
params.value.policyTypeId = selectedOptions[0].text == "全部" ? "" : selectedOptions[0].itemId; | |||
params.value.page = 1; | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
show.value = false; | |||
}; | |||
const toSearch = async () => { | |||
params.value.page = 1; | |||
params.value.pageSize = 10; | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
}; | |||
const goRoute = val => { | |||
$router.push({ | |||
path: "/m_preferential-policy-detail", | |||
query: { | |||
releasePolicyId: val.releasePolicyId, | |||
}, | |||
}); | |||
}; | |||
const getListData = async () => { | |||
pageLoading.value = true; | |||
let dataObj = await getList(params); | |||
dataList.value = dataList.value.concat(dataObj.list); | |||
total.value = dataObj.total; | |||
params.value.page = dataObj.page_no; | |||
pageLoading.value = false; | |||
if (dataList.value.length >= total.value) { | |||
finished.value = true; | |||
} else { | |||
finished.value = false; | |||
} | |||
params.value.page += 1; | |||
}; | |||
onMounted(async () => { | |||
loading.value = true; | |||
let pickArr = await getPickList(); | |||
selectList.value = sortByKey(pickArr, "seq"); | |||
selectList.value.unshift({ | |||
text: "全部", | |||
itemId: "000", | |||
}); | |||
selectList.value.forEach(element => { | |||
element.value = element.itemId; | |||
}); | |||
// let dataObj = await getList(params); | |||
// dataList.value = dataObj.list; | |||
// total.value = dataObj.total; | |||
getListData(); | |||
loading.value = false; | |||
}); | |||
const input3 = ref(""); | |||
const show = ref(false); | |||
</script> | |||
<style lang="scss" scoped> | |||
.policy { | |||
height: calc(100vh - 48px - 150px); | |||
padding: 20px 15px; | |||
overflow: hidden; | |||
background: rgba(245, 245, 245, 1); | |||
@include border-box; | |||
&-search { | |||
@include size(100%, 34px); | |||
border-radius: 25px; | |||
border: 2px solid rgba(29, 112, 217, 1); | |||
@include flex(row, space-between, center, nowrap); | |||
@include font(12px, $color-black); | |||
margin-bottom: 20px; | |||
padding: 0 16px; | |||
@include border-box; | |||
.search-left { | |||
color: rgba(29, 112, 217, 1); | |||
} | |||
.search-right { | |||
color: rgba(153, 153, 153, 1); | |||
} | |||
.search-input { | |||
flex: 1; | |||
height: 100%; | |||
background: transparent; | |||
} | |||
} | |||
.list_box { | |||
overflow-y: scroll; | |||
height: calc(100vh - 48px - 150px - 84px); | |||
// height: calc(100vh - 84px); | |||
} | |||
&-item { | |||
@include size(100%, auto); | |||
@include font(12px, $color-black); | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 20px; | |||
padding: 10px 15px; | |||
@include border-box; | |||
margin-bottom: 15px; | |||
&-left { | |||
@include text-ellipsis-multiple(2); | |||
font-weight: bold; | |||
} | |||
&-right { | |||
height: 100%; | |||
font-weight: normal; | |||
@include flex(row, space-between, center, nowrap); | |||
margin-top: 10px; | |||
} | |||
} | |||
} | |||
:deep() { | |||
.van-cell__value, | |||
.van-field__body { | |||
height: 100%; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,70 @@ | |||
<template> | |||
<section class="detail" v-if="!loading"> | |||
<div class="box"> | |||
<div class="title">{{ policyInfo.policyName }}</div> | |||
<div class="subtitle"> | |||
<span>发布日期:{{ formatDate(policyInfo.modifiedOn, "YYYY年MM月DD日") }}</span> | |||
<span>政策类型:{{ policyInfo.policyType.text }}</span> | |||
<span>浏览量:{{ policyInfo.pageView || 0 }}</span> | |||
</div> | |||
<div class="content" v-html="formatHtml(policyInfo.policyText)"></div> | |||
</div> | |||
</section> | |||
</template> | |||
<script setup> | |||
import { onMounted } from "vue"; | |||
import { getReleasePolicyData } from "@/views/pc/policy/policy"; | |||
import { formatDate } from "@/utils/common.js"; | |||
import { useRoute } from "vue-router"; | |||
import SimpleMDE from "simplemde"; | |||
const formatHtml = text => { | |||
let html = SimpleMDE.prototype.markdown(text); | |||
let imgUrl = `${import.meta.env.VITE_BASE_URL}/common/getImg?path=`; | |||
html = html.replace( | |||
/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, | |||
"<img src='" + imgUrl + "$1' width='100%'/>" | |||
); | |||
return html; | |||
}; | |||
const { updatePageView, getReleasePolicyDetail } = getReleasePolicyData(); | |||
const loading = ref(true); | |||
let policyInfo = ref({}); | |||
const $route = useRoute(); | |||
onMounted(async () => { | |||
loading.value = true; | |||
policyInfo.value = await getReleasePolicyDetail($route.query.releasePolicyId); | |||
loading.value = false; | |||
updatePageView($route.query.releasePolicyId); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.detail { | |||
min-height: calc(100vh - 48px - 150px); | |||
padding: 10px 15px; | |||
overflow: hidden; | |||
background: rgba(245, 245, 245, 1); | |||
@include border-box; | |||
.box { | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 10px; | |||
padding: 12px 0 30px 10px; | |||
@include border-box; | |||
} | |||
.title { | |||
@include font(14px, $color-black); | |||
font-weight: 500; | |||
} | |||
.subtitle { | |||
@include font(12px, rgba(97, 96, 96, 1)); | |||
margin: 20px 0; | |||
span { | |||
margin-right: 15px; | |||
} | |||
} | |||
.content { | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
white-space: pre-wrap; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,62 @@ | |||
<template> | |||
<div class="container" v-if="!loading"> | |||
<van-tabs v-model:active="active" swipeable type="card" :ellipsis="false"> | |||
<van-tab | |||
v-for="item in dataList" | |||
:key="item.attractInvestmentProjectId" | |||
:title="item.productName" | |||
> | |||
<Project :projectItem="item" @doSth="showTipInfo"></Project> | |||
</van-tab> | |||
</van-tabs> | |||
</div> | |||
<TipDialog ref="tipRef"></TipDialog> | |||
</template> | |||
<script setup> | |||
import TipDialog from "@/components/mobile/TipDialog.vue"; | |||
import Project from "./Project.vue"; | |||
import { getReleasePolicyData } from "@/views/pc/project/project"; | |||
import { onMounted } from "vue"; | |||
const { getList } = getReleasePolicyData(); | |||
let dataList = ref([]); | |||
const loading = ref(true); | |||
onMounted(async () => { | |||
loading.value = true; | |||
dataList = await getList(); | |||
loading.value = false; | |||
}); | |||
const active = ref(0); | |||
const tipRef = ref(null); | |||
const showTipInfo = () => { | |||
tipRef.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.container { | |||
padding: 10px 15px; | |||
overflow: hidden; | |||
background: #fff; | |||
@include border-box; | |||
min-height: 100%; | |||
:deep() { | |||
.van-tabs__wrap { | |||
margin-bottom: 20px; | |||
} | |||
.van-tabs__nav--card { | |||
border: none; | |||
margin: 0; | |||
} | |||
.van-tab--card { | |||
border-right: none; | |||
border-radius: 25px; | |||
// margin-right: 8px; | |||
color: rgba(0, 0, 0, 1); | |||
} | |||
.van-tab--card.van-tab--active { | |||
color: #fff; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,60 @@ | |||
<template> | |||
<div class="project"> | |||
<img :src="formatImg(propsData.projectItem.productUrl[0])" alt="招商项目图片" /> | |||
<div class="project-title">{{ propsData.projectItem.productName }}</div> | |||
<div class="project-content"> | |||
{{ propsData.projectItem.productText }} | |||
</div> | |||
<span class="project-consult" @click="doSth">立 即 咨 询</span> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
projectItem: { | |||
type: Object, | |||
}, | |||
}); | |||
const emit = defineEmits(["doSth"]); | |||
const doSth = () => { | |||
emit("doSth"); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.project { | |||
// background: rgba(255, 255, 255, 1); | |||
// padding: 24px 12px; | |||
@include border-box; | |||
@include flex(column, flex-start, center, nowrap); | |||
margin-bottom: 24px; | |||
img { | |||
@include font(12px, $color-black); | |||
@include size(100%, 238px); | |||
border-radius: 20px; | |||
margin-bottom: 10px; | |||
@include border-box; | |||
} | |||
&-title { | |||
width: 100%; | |||
@include font(14px, $color-black); | |||
font-weight: bold; | |||
} | |||
&-content { | |||
@include font(12px, $color-black); | |||
line-height: 20px; | |||
margin: 10px 0; | |||
word-break: break-word; | |||
white-space: pre-wrap; | |||
} | |||
&-consult { | |||
@include size(290px, 32px); | |||
border-radius: 30px; | |||
background: rgba(0, 153, 255, 1); | |||
@include flex(row, center, center, nowrap); | |||
@include font(14px, $color-white); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,49 @@ | |||
<template> | |||
<div class="block"> | |||
<div class="title">{{ propsData.aboutItem.label }}</div> | |||
<div class="content" v-html="formatHtml(propsData.aboutItem.value)"></div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
aboutItem: { | |||
type: Object, | |||
}, | |||
}); | |||
const formatHtml = text => { | |||
let html = SimpleMDE.prototype.markdown(text); | |||
let imgUrl = `${import.meta.env.VITE_BASE_URL}/common/getImg?path=`; | |||
html = html.replace( | |||
/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, | |||
"<img src='" + imgUrl + "$1' width='100%'/>" | |||
); | |||
return html; | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.block { | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 10px; | |||
padding: 36px; | |||
@include border-box; | |||
@include flex(column, space-between, center, nowrap); | |||
margin-bottom: 24px; | |||
.title { | |||
@include font(36px, $color-black); | |||
font-weight: bold; | |||
margin-bottom: 50px; | |||
} | |||
.content { | |||
@include font(20px, $color-black); | |||
line-height: 30px; | |||
word-break: break-word; | |||
} | |||
img { | |||
@include size(100%, auto); | |||
margin-top: 20px; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,137 @@ | |||
<template> | |||
<div class="about"> | |||
<div class="about-box"> | |||
<van-sticky> | |||
<div class="form-tab"> | |||
<span | |||
v-for="item in tabArr" | |||
:key="item.value" | |||
:class="[currentTab == item.value ? 'active' : '']" | |||
@click="changeTab(item.value)" | |||
> | |||
{{ item.label }} | |||
</span> | |||
</div> | |||
</van-sticky> | |||
<template v-if="!loading"> | |||
<About | |||
:id="'tab' + index" | |||
class="scroll-item" | |||
v-for="(item, index) in tabArr" | |||
:key="item.value" | |||
:aboutItem="{ | |||
label: item.label, | |||
value: homeData[tab[index].key], | |||
}" | |||
></About> | |||
</template> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { onMounted, onUnmounted, ref } from "vue"; | |||
import About from "./About.vue"; | |||
import useHomeStore from "@/store/module/home"; | |||
import VueScrollTo from "vue-scrollto"; | |||
import { debounce } from "@/utils/common.js"; | |||
const homeStore = useHomeStore(); | |||
let homeData = ref({}); | |||
const tab = [ | |||
{ | |||
label: "区位优势", | |||
value: 0, | |||
key: "regionalAdvantages", | |||
}, | |||
{ | |||
label: "发展机遇", | |||
value: 1, | |||
key: "developmentOpportunity", | |||
}, | |||
{ | |||
label: "配套建设", | |||
value: 2, | |||
key: "supportingConstruction", | |||
}, | |||
{ | |||
label: "发展趋势", | |||
value: 3, | |||
key: "developmentTendency", | |||
}, | |||
{ | |||
label: "交通优势", | |||
value: 4, | |||
key: "transportationAdvantage", | |||
}, | |||
{ | |||
label: "镇域概况", | |||
value: 5, | |||
key: "townProfile", | |||
}, | |||
{ | |||
label: "书院介绍", | |||
value: 6, | |||
key: "academyHistory", | |||
}, | |||
]; | |||
const tabArr = ref(tab); | |||
const currentTab = ref(0); | |||
const loading = ref(true); | |||
const changeTab = val => { | |||
currentTab.value = val; | |||
VueScrollTo.scrollTo(`#tab${val}`, 500, { offset: -132 }); | |||
}; | |||
const scrollListenerHandler = debounce(() => { | |||
const scrollTop = | |||
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; // 滚动条偏移量 | |||
document.querySelectorAll(".scroll-item").forEach((item, index) => { | |||
if (item.offsetTop - 132 <= scrollTop) { | |||
currentTab.value = index; | |||
} | |||
}); | |||
}, 100); | |||
onMounted(() => { | |||
loading.value = true; | |||
homeData.value = homeStore.homeData.value; | |||
loading.value = false; | |||
window.addEventListener("scroll", scrollListenerHandler); | |||
}); | |||
onUnmounted(() => { | |||
window.removeEventListener("scroll", scrollListenerHandler); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.about { | |||
background: rgba(245, 245, 245, 1); | |||
&-box { | |||
@include size($contentWidth, auto); | |||
margin: 0 auto; | |||
padding: 20px 0 58px; | |||
@include border-box; | |||
} | |||
.form-tab { | |||
background: rgba(245, 245, 245, 1); | |||
@include size($contentWidth, auto); | |||
padding: 25px 0; | |||
@include flex(row, space-between, center, nowrap); | |||
@include border-box; | |||
span { | |||
width: 200px; | |||
height: 82px; | |||
@include font(24px, $color-black); | |||
font-weight: 500; | |||
@include flex(row, center, center, nowrap); | |||
border-radius: 20px; | |||
cursor: pointer; | |||
@include border-box; | |||
} | |||
.active { | |||
@include font(24px, $color-white); | |||
background: rgba(0, 153, 255, 1); | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,215 @@ | |||
<template> | |||
<section class="apply-box"> | |||
<div class="form-tab"> | |||
<span | |||
v-for="(item, index) in tabArr" | |||
:class="[currentTb == index ? 'active' : '']" | |||
@click="changeTab(item.value, index)" | |||
:key="item.value" | |||
> | |||
{{ item.label }} | |||
</span> | |||
</div> | |||
<div class="form"> | |||
<el-form | |||
v-if="currentTb == 0" | |||
:label-position="labelPosition" | |||
label-width="170px" | |||
:model="params" | |||
style="max-width: 800px" | |||
> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
企业名称 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.companyName" | |||
placeholder="请输入企业名称" | |||
maxlength="50" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系人 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.name" | |||
placeholder="请输入联系人名称" | |||
maxlength="10" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系电话 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.mobile" | |||
placeholder="请输入联系电话" | |||
maxlength="11" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label">预计产税</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.estimatedPropertyTax" | |||
placeholder="请输入预计产税" | |||
maxlength="20" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label><div class="form-item-label">需求内容</div></template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.text" | |||
type="textarea" | |||
rows="10" | |||
placeholder="请输入需求内容" | |||
resize="none" | |||
maxlength="200" | |||
/> | |||
</el-form-item> | |||
</el-form> | |||
<el-form | |||
v-else | |||
:label-position="labelPosition" | |||
label-width="170px" | |||
:model="params" | |||
style="max-width: 800px" | |||
> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
企业名称 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.companyName" | |||
placeholder="请输入企业名称" | |||
maxlength="50" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系人 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.name" | |||
placeholder="请输入联系人名称" | |||
maxlength="10" | |||
/> | |||
</el-form-item> | |||
<el-form-item class="form-item"> | |||
<template v-slot:label> | |||
<div class="form-item-label"> | |||
<span class="required">*</span> | |||
联系电话 | |||
</div> | |||
</template> | |||
<el-input | |||
class="form-item-content" | |||
v-model="params.mobile" | |||
placeholder="请输入联系电话" | |||
maxlength="11" | |||
/> | |||
</el-form-item> | |||
</el-form> | |||
<el-button class="submit" round type="primary" @click="submitForm">提交申请</el-button> | |||
<div class="submit-tip">提交后我们将在1-7个工作日内联系您</div> | |||
</div> | |||
</section> | |||
</template> | |||
<script setup> | |||
import { apply } from "@/views/pc/apply/apply"; | |||
const { labelPosition, currentTb, tabArr, params, applyForEntry, changeTab, submitForm } = apply(); | |||
</script> | |||
<style lang="scss" scoped> | |||
.apply-box { | |||
overflow: hidden; | |||
background: url(https://img.js.design/assets/img/64acb3a790162375e01752b3.jpg) no-repeat; | |||
background-size: 100% 100%; | |||
} | |||
.form-tab { | |||
@include size($contentWidth, auto); | |||
margin: 25px auto; | |||
@include flex(row, space-between, center, nowrap); | |||
span { | |||
width: 50%; | |||
height: 82px; | |||
@include font(24px, $color-black); | |||
font-weight: 500; | |||
@include flex(row, center, center, nowrap); | |||
border-radius: 20px; | |||
background: rgba(245, 245, 245, 1); | |||
cursor: pointer; | |||
@include border-box; | |||
} | |||
.active { | |||
@include font(24px, $color-white); | |||
background: rgba(0, 153, 255, 1); | |||
} | |||
} | |||
.form { | |||
@include size($contentWidth, auto); | |||
margin: 0 auto 60px; | |||
padding: 45px 220px 30px 178px; | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 20px; | |||
@include border-box; | |||
text-align: center; | |||
&-item { | |||
border-radius: 50px; | |||
background: rgba(242, 244, 249, 1); | |||
padding: 10px 25px 10px 0; | |||
@include border-box; | |||
&-label { | |||
@include font(20px, $color-black); | |||
border-right: 3px solid rgba(145, 145, 145, 1); | |||
padding-right: 25px; | |||
@include border-box; | |||
} | |||
} | |||
.submit { | |||
@include size(240px, 68px); | |||
@include font(24px, $color-white); | |||
border-radius: 34px; | |||
@include border-box; | |||
margin: 100px 0 52px; | |||
} | |||
.submit-tip { | |||
@include font(20px, $color-black); | |||
} | |||
} | |||
.required { | |||
color: rgba(255, 0, 0, 1); | |||
} | |||
:deep() { | |||
.el-input__wrapper, | |||
.el-textarea__inner { | |||
background: none; | |||
box-shadow: none; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,74 @@ | |||
import { addEnter } from "@/apis/index"; | |||
import { ref } from "vue"; | |||
import { toast, throttle } from "@/utils/common.js"; | |||
export function apply() { | |||
// tab相关 | |||
const currentTb = ref(0); | |||
const tabArr = ref([{ | |||
label: "我是企业,我要申请入驻", | |||
value: "012-01894315f4680185", | |||
}, | |||
{ | |||
label: "我是中介,我要合作加入", | |||
value: "012-01894315f4690186", | |||
}, | |||
]); | |||
const changeTab = (val, index) => { | |||
params.value = { | |||
enterId: "", | |||
mobile: "", | |||
name: "", | |||
companyName: "", | |||
type: { | |||
text: "012-01894315f4680185", //企业入驻 012-01894315f4680185 中介加入 012-01894315f4690186 | |||
}, | |||
estimatedPropertyTax: "", | |||
}; | |||
params.value.type.text = val; | |||
currentTb.value = index; | |||
}; | |||
// 表单相关 | |||
const labelPosition = ref("right"); | |||
const params = ref({ | |||
enterId: "", | |||
mobile: "", | |||
name: "", | |||
companyName: "", | |||
type: { | |||
text: "012-01894315f4680185", //企业入驻 012-01894315f4680185 中介加入 012-01894315f4690186 | |||
}, | |||
estimatedPropertyTax: "", | |||
}); | |||
const applyForEntry = async() => { | |||
let data; | |||
let res = await addEnter(params); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} | |||
return data; | |||
}; | |||
const submitForm = throttle(async() => { | |||
let { companyName, name, mobile } = params.value; | |||
if (!companyName.trim()) { | |||
toast("请输入企业名称!", "error"); | |||
return; | |||
} | |||
if (!name.trim()) { | |||
toast("请输入联系人名称!", "error"); | |||
return; | |||
} | |||
if (!mobile.trim()) { | |||
toast("请输入联系电话!", "error"); | |||
return; | |||
} | |||
let res = await addEnter(params.value); | |||
if (res.data.status == 0) { | |||
toast("提交成功!", "success"); | |||
} | |||
}, 500); | |||
return { labelPosition, currentTb, tabArr, params, applyForEntry, changeTab, submitForm }; | |||
} |
@@ -0,0 +1,68 @@ | |||
<template> | |||
<div v-if="!loading"> | |||
<div class="wrap-box"> | |||
<Introduce :homeData="homeData"></Introduce> | |||
</div> | |||
<div class="project-container"> | |||
<div class="wrap-box"><Project></Project></div> | |||
</div> | |||
<IndustrialCluster :industrialAllocation="industrialAllocation"></IndustrialCluster> | |||
<div class="wrap-box"> | |||
<Policy v-for="(item, index) in policy" :key="index" :policyItem="item"></Policy> | |||
<PicturePolicy | |||
:policySupportKeyIndustries="homeData.policySupportKeyIndustries" | |||
:domesticTalentIntroduction="homeData.domesticTalentIntroduction" | |||
></PicturePolicy> | |||
<template v-if="detailPolicy.length > 0"> | |||
<DetailPolicy | |||
v-for="(item, index) in detailPolicy" | |||
:key="index" | |||
:detailPolicyItem="item" | |||
></DetailPolicy> | |||
</template> | |||
<OtherPolicy></OtherPolicy> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import Introduce from "./project/Introduce.vue"; | |||
import Project from "./project/Project.vue"; | |||
import IndustrialCluster from "./project/IndustrialCluster.vue"; | |||
import Policy from "./policy/Policy.vue"; | |||
import PicturePolicy from "./policy/PicturePolicy.vue"; | |||
import DetailPolicy from "./policy/DetailPolicy.vue"; | |||
import OtherPolicy from "./policy/OtherPolicy.vue"; | |||
import { getHomeData } from "@/views/pc/home/home"; | |||
import { onMounted } from "vue"; | |||
const { getAreaIndustrialAllocation, setDetailPolicy, setPolicy, updatePageView } = getHomeData(); | |||
let homeData = ref({}); | |||
let industrialAllocation = ref([]); | |||
let policy = ref([]); | |||
let detailPolicy = ref([]); | |||
const loading = ref(true); | |||
import useHomeStore from "@/store/module/home"; | |||
const homeStore = useHomeStore(); | |||
onMounted(async () => { | |||
loading.value = true; | |||
homeData.value = homeStore.homeData.value; | |||
policy = setPolicy(homeData.value); | |||
detailPolicy = setDetailPolicy(homeData.value); | |||
industrialAllocation.value = await getAreaIndustrialAllocation(); | |||
loading.value = false; | |||
updatePageView(homeData.value.areaInfoManagementId); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.project-container { | |||
background: rgba(248, 250, 253, 1); | |||
padding: 12px 0; | |||
@include border-box; | |||
} | |||
.wrap-box { | |||
@include size($contentWidth, auto); | |||
margin: 0 auto; | |||
} | |||
</style> |
@@ -0,0 +1,130 @@ | |||
import { | |||
getAreaData, | |||
getAreaIndustrialAllocationList, | |||
updateAreaInfoManagementPageView, | |||
getRegistrationPackageByFirst, | |||
} from "@/apis/index"; | |||
import { toast } from "@/utils/common.js"; | |||
export function getHomeData() { | |||
// 获取数据 | |||
const getData = async() => { | |||
let data; | |||
try { | |||
let res = await getAreaData(); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
toast("获取数据失败,请刷新重试!", "error"); | |||
} | |||
return data; | |||
}; | |||
// 获取主导产业 | |||
const getAreaIndustrialAllocation = async() => { | |||
let data; | |||
try { | |||
let res = await getAreaIndustrialAllocationList(); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
toast("获取主导产业失败,请刷新重试!", "error"); | |||
} | |||
return data; | |||
}; | |||
// 获取最近那一个编辑或者上架的招商载体 | |||
const getRegistrationPackage = async() => { | |||
let data; | |||
let res = await getRegistrationPackageByFirst(); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} | |||
return data; | |||
}; | |||
const setDetailPolicy = homeData => { | |||
const detailPolicy = ref([]); | |||
if (homeData) { | |||
const { | |||
policyTitle, | |||
policyText, | |||
policySubtitleI, | |||
policyContentI, | |||
policySubtitleII, | |||
policyContentII, | |||
} = homeData; | |||
detailPolicy.value = [{ | |||
title: policyTitle, | |||
content: policyText, | |||
}, | |||
{ | |||
title: policySubtitleI, | |||
content: policyContentI, | |||
}, | |||
{ | |||
title: policySubtitleII, | |||
content: policyContentII, | |||
}, | |||
]; | |||
} | |||
return detailPolicy; | |||
}; | |||
const setPolicy = homeData => { | |||
const policy = ref([]); | |||
if (homeData) { | |||
const { fiscalTaxationPolicy, talentRelatedPolicy, industryRelatedPolicy } = homeData; | |||
policy.value = [{ | |||
title: "财税政策", | |||
content: fiscalTaxationPolicy, | |||
color: "#333", | |||
bgColor: "rgb(255, 242, 197)", | |||
}, | |||
{ | |||
title: "人才相关政策", | |||
content: talentRelatedPolicy, | |||
color: "#333", | |||
bgColor: "rgba(172, 217, 255, 1)", | |||
}, | |||
{ | |||
title: "产业相关政策", | |||
content: industryRelatedPolicy, | |||
color: "#fff", | |||
bgColor: "rgba(28, 54, 127, 1)", | |||
}, | |||
]; | |||
} | |||
return policy; | |||
}; | |||
const updatePageView = async p => { | |||
let data; | |||
try { | |||
let res = await updateAreaInfoManagementPageView(p); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
console.log(error); | |||
} | |||
return data; | |||
}; | |||
return { | |||
getData, | |||
getAreaIndustrialAllocation, | |||
setDetailPolicy, | |||
setPolicy, | |||
updatePageView, | |||
getRegistrationPackage, | |||
}; | |||
} |
@@ -0,0 +1,45 @@ | |||
<template> | |||
<div class="detail-title">{{ propsData.detailPolicyItem.title }}</div> | |||
<div class="detail-policy"> | |||
<div | |||
class="detail-policy-content" | |||
v-html="SimpleMDE.prototype.markdown(propsData.detailPolicyItem.content)" | |||
></div> | |||
<!-- <div class="detail-policy-content"> | |||
按当年销售金额最高 10% 给予奖励,单款芯片产品年度奖励总额不超过 500 万元。 | |||
</div> --> | |||
</div> | |||
</template> | |||
<script setup> | |||
// 安装 "simplemde": "^1.11.2", | |||
// 安装 "codemirror": "^5.65.14", | |||
// 安装 "marked": "^3.0.8", | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
detailPolicyItem: { | |||
type: Object, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.detail-title { | |||
@include font(24px, rgba(42, 130, 228, 1)); | |||
border-bottom: 3px solid rgba(42, 130, 228, 1); | |||
padding: 10px 0 18px; | |||
@include border-box; | |||
} | |||
.detail-policy { | |||
padding: 20px 0 14px; | |||
@include border-box; | |||
&-title { | |||
@include font(20px, $color-black); | |||
margin-bottom: 10px; | |||
} | |||
&-content { | |||
@include font(18px, $color-black); | |||
line-height: 30px; | |||
word-break: break-word; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,68 @@ | |||
<template> | |||
<div class="other-policy" v-if="dataList && dataList.length > 0"> | |||
<div class="other-policy-title">其他政策</div> | |||
<div class="other-policy-content"> | |||
<span | |||
v-for="item in dataList" | |||
:key="item.releasePolicyId" | |||
:title="item.policyName" | |||
@click="goRoute(item)" | |||
> | |||
{{ item.policyName }} | |||
</span> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { onMounted } from "vue"; | |||
import { getReleasePolicyData } from "@/views/pc/policy/policy"; | |||
import { routerOpenInNewWindow } from "@/utils/common.js"; | |||
const { params, getList } = getReleasePolicyData(); | |||
let dataList = ref([]); | |||
const loading = ref(true); | |||
const getListData = async () => { | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
}; | |||
const goRoute = val => { | |||
localStorage.setItem("policyInfo", JSON.stringify(val)); | |||
routerOpenInNewWindow({ | |||
path: "/p_preferential-policy-detail", | |||
query: { | |||
releasePolicyId: val.releasePolicyId, | |||
}, | |||
}); | |||
}; | |||
onMounted(async () => { | |||
loading.value = true; | |||
getListData(); | |||
loading.value = false; | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.other-policy { | |||
padding: 16px; | |||
@include border-box; | |||
margin-bottom: 28px; | |||
&-title { | |||
@include font(24px, $color-black); | |||
text-align: center; | |||
font-weight: bold; | |||
margin-bottom: 20px; | |||
} | |||
&-content { | |||
@include flex(row, space-between, flex-start, wrap); | |||
@include font(18px, $color-black); | |||
line-height: 20px; | |||
span { | |||
width: 50%; | |||
margin-bottom: 20px; | |||
@include text-ellipsis; | |||
padding-right: 30px; | |||
@include border-box; | |||
cursor: pointer; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,76 @@ | |||
<template> | |||
<div | |||
:class="[ | |||
'picture-box', | |||
propsData.policySupportKeyIndustries && | |||
propsData.policySupportKeyIndustries.length > 0 && | |||
propsData.domesticTalentIntroduction && | |||
propsData.domesticTalentIntroduction.length > 0 | |||
? '' | |||
: 'box-center', | |||
]" | |||
> | |||
<div | |||
class="picture-policy" | |||
v-if=" | |||
propsData.policySupportKeyIndustries && | |||
propsData.policySupportKeyIndustries.length > 0 | |||
" | |||
> | |||
<div class="picture-policy-title">重点产业扶持政策</div> | |||
<img | |||
class="picture-policy-content" | |||
:src="formatImg(propsData.policySupportKeyIndustries[0])" | |||
alt="重点产业扶持政策" | |||
/> | |||
</div> | |||
<div | |||
class="picture-policy" | |||
v-if=" | |||
propsData.domesticTalentIntroduction && | |||
propsData.domesticTalentIntroduction.length > 0 | |||
" | |||
> | |||
<div class="picture-policy-title">国内人才引进</div> | |||
<img | |||
class="picture-policy-content" | |||
:src="formatImg(propsData.domesticTalentIntroduction[0])" | |||
alt="国内人才引进" | |||
/> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
policySupportKeyIndustries: { | |||
type: Array, | |||
}, | |||
domesticTalentIntroduction: { | |||
type: Array, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.picture-box { | |||
@include flex(row, space-between, flex-start, nowrap); | |||
} | |||
.box-center { | |||
justify-content: center; | |||
} | |||
.picture-policy { | |||
padding: 10px 22px 0; | |||
@include border-box; | |||
margin-bottom: 24px; | |||
&-title { | |||
@include font(20px, $color-black); | |||
text-align: center; | |||
font-weight: bold; | |||
margin-bottom: 10px; | |||
} | |||
&-content { | |||
@include size(100%, 100%); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,47 @@ | |||
<template> | |||
<div | |||
class="policy" | |||
:style="{ color: propsData.policyItem.color, background: propsData.policyItem.bgColor }" | |||
> | |||
<div class="policy-title" :style="{ color: propsData.policyItem.color }"> | |||
{{ propsData.policyItem.title }} | |||
</div> | |||
<div | |||
class="policy-content" | |||
:style="{ color: propsData.policyItem.color }" | |||
v-html="SimpleMDE.prototype.markdown(propsData.policyItem.content)" | |||
></div> | |||
</div> | |||
</template> | |||
<script setup> | |||
// 安装 "simplemde": "^1.11.2", | |||
// 安装 "codemirror": "^5.65.14", | |||
// 安装 "marked": "^3.0.8", | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
policyItem: { | |||
type: Object, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.policy { | |||
background: rgba(255, 242, 197, 1); | |||
border-radius: 20px; | |||
padding: 16px 12px; | |||
@include border-box; | |||
margin-bottom: 24px; | |||
&-title { | |||
font-size: 20px; | |||
text-align: center; | |||
font-weight: bold; | |||
margin-bottom: 20px; | |||
} | |||
&-content { | |||
font-size: 18px; | |||
line-height: 30px; | |||
word-break: break-word; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,40 @@ | |||
<template> | |||
<div class="industrial-park"> | |||
<el-carousel :interval="4000" type="card" indicator-position="none"> | |||
<el-carousel-item | |||
v-for="item in propsData.industrialAllocation" | |||
:key="item.areaIndustrialAllocationId" | |||
> | |||
<!-- <h3 text="2xl" justify="center">{{ item }}</h3> --> | |||
<Cluster :data="item"></Cluster> | |||
</el-carousel-item> | |||
</el-carousel> | |||
</div> | |||
</template> | |||
<script setup> | |||
import Cluster from "@/views/pc/industry/Cluster.vue"; | |||
const propsData = defineProps({ | |||
industrialAllocation: { | |||
type: Array, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.industrial-park { | |||
padding: 46px 0; | |||
@include border-box; | |||
margin-top: 10px; | |||
} | |||
:deep() { | |||
.el-carousel__container { | |||
height: auto; | |||
min-height: 400px; | |||
} | |||
.el-carousel__item { | |||
border-radius: 10px; | |||
} | |||
// .van-swipe__indicator { | |||
// color: rgba(204, 204, 204, 1); | |||
// } | |||
} | |||
</style> |
@@ -0,0 +1,104 @@ | |||
<template> | |||
<div class="introduce"> | |||
<div class="info"> | |||
<div class="info-box"> | |||
<div class="info-box-title">区位优势</div> | |||
<div> | |||
{{ propsData.homeData.academyHistory }} | |||
</div> | |||
</div> | |||
<div class="info-box"> | |||
<div class="info-box-title margin-title">镇域概括</div> | |||
<ul class="info-ul"> | |||
<li> | |||
<div class="subtitle">行政区域面积</div> | |||
<div> | |||
<span class="blue-text"> | |||
{{ propsData.homeData.administrativeArea }} | |||
</span> | |||
平方公里 | |||
</div> | |||
</li> | |||
<li> | |||
<div>实有入口</div> | |||
<div> | |||
<span class="blue-text">{{ propsData.homeData.actualEntry }}</span> | |||
万 | |||
</div> | |||
</li> | |||
<li> | |||
<div>下辖</div> | |||
<div> | |||
<span class="blue-text"> | |||
{{ propsData.homeData.haveJurisdictionOver }} | |||
</span> | |||
个村 | |||
</div> | |||
</li> | |||
<li> | |||
<div>居民区</div> | |||
<div> | |||
<span class="blue-text">{{ propsData.homeData.residentialArea }}</span> | |||
个 | |||
</div> | |||
</li> | |||
</ul> | |||
</div> | |||
</div> | |||
<img | |||
v-if="propsData.homeData.parkPicture && propsData.homeData.parkPicture.length > 0" | |||
class="introduce-img" | |||
:src="formatImg(propsData.homeData.parkPicture[0])" | |||
alt="园区图片" | |||
/> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
homeData: { | |||
type: Object, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.introduce { | |||
margin: 56px auto; | |||
@include flex(row, space-between, stretch, nowrap); | |||
&-img { | |||
@include size(550px, auto); | |||
border-radius: 20px; | |||
@include border-box; | |||
} | |||
} | |||
.info { | |||
flex: 1; | |||
padding-right: 52px; | |||
@include border-box; | |||
&-box { | |||
@include font(20px, $color-black); | |||
&-title { | |||
@include font(24px, $color-black); | |||
margin-bottom: 36px; | |||
} | |||
} | |||
} | |||
.margin-title { | |||
margin-top: 48px; | |||
} | |||
.info-ul { | |||
@include flex(row, space-between, flex-start, wrap); | |||
li { | |||
width: 50%; | |||
margin-top: 24px; | |||
} | |||
.subtitle { | |||
@include font(20px, $color-black); | |||
} | |||
.blue-text { | |||
@include font(48px, rgba(0, 153, 255, 1)); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,79 @@ | |||
<template> | |||
<div class="project"> | |||
<ul class="project-left"> | |||
<li class="active">招商载体</li> | |||
<li @click="toPath('/p_industry-park', 1)">企业链家</li> | |||
<li @click="toPath('/p_apply-for-entry')"> | |||
<div> | |||
我是企业 | |||
<br /> | |||
我要入驻 | |||
</div> | |||
<el-icon size="30px"><Right /></el-icon> | |||
</li> | |||
</ul> | |||
<div class="project-right" v-if="!loading"> | |||
<ProjectItem :projectItem="registrationPackage"></ProjectItem> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { Right } from "@element-plus/icons-vue"; // 引入 Edit 这个 svg组件 | |||
import ProjectItem from "./ProjectItem.vue"; | |||
import { getHomeData } from "../home"; | |||
import { onBeforeMount } from "vue"; | |||
import { useRoute, useRouter } from "vue-router"; //1.引入路由 | |||
const $router = useRouter(); //2.实例化路由 | |||
const { getRegistrationPackage } = getHomeData(); | |||
let registrationPackage = ref({}); | |||
const loading = ref(true); | |||
onBeforeMount(async () => { | |||
loading.value = true; | |||
registrationPackage.value = await getRegistrationPackage(); | |||
loading.value = false; | |||
}); | |||
const toPath = (path, tab) => { | |||
$router.push({ | |||
path, | |||
query: { | |||
tab, | |||
}, | |||
}); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.project { | |||
@include flex(row, space-between, flex-start, nowrap); | |||
&-left { | |||
width: 282px; | |||
li { | |||
@include size(100%, 130px); | |||
@include font(24px, $color-black); | |||
font-weight: 500; | |||
background: linear-gradient( | |||
90deg, | |||
rgba(0, 134, 231, 0.2) 0%, | |||
rgba(0, 136, 230, 0) 100% | |||
); | |||
margin-bottom: 5px; | |||
border-radius: 20px; | |||
@include flex(row, center, center, nowrap); | |||
@include border-box; | |||
cursor: pointer; | |||
} | |||
.active { | |||
background: linear-gradient(90deg, rgba(0, 134, 231, 1) 0%, rgba(0, 136, 230, 0) 100%); | |||
@include font(24px, $color-white); | |||
} | |||
} | |||
&-right { | |||
flex: 1; | |||
} | |||
.el-icon { | |||
margin-left: 20px; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,75 @@ | |||
<template> | |||
<div class="project"> | |||
<img :src="formatImg(propsData.projectItem.productUrl[0])" alt="招商项目图片" /> | |||
<div class="project-right"> | |||
<div class="content"> | |||
<span class="content-title">{{ propsData.projectItem.productName }}</span> | |||
<div class="content-content" :title="propsData.projectItem.productText"> | |||
{{ propsData.projectItem.productText }} | |||
</div> | |||
</div> | |||
<span class="project-right-consult" @click="showTipInfo">立 即 咨 询</span> | |||
</div> | |||
</div> | |||
<TipDialog ref="tipRefPC"></TipDialog> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
projectItem: { | |||
type: Object, | |||
}, | |||
}); | |||
import TipDialog from "@/components/pc/TipDialogPC.vue"; | |||
const tipRefPC = ref(null); | |||
const showTipInfo = () => { | |||
tipRefPC.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.project { | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 10px; | |||
padding: 24px 20px; | |||
@include border-box; | |||
@include flex(row, space-between, center, nowrap); | |||
img { | |||
@include size(496px, 352px); | |||
margin-right: 25px; | |||
border-radius: 20px; | |||
@include border-box; | |||
} | |||
&-right { | |||
flex: 1; | |||
@include size(auto, 352px); | |||
@include flex(column, space-between, flex-start, nowrap); | |||
.content { | |||
flex: 1; | |||
padding-bottom: 20px; | |||
@include flex(column, flex-start, flex-start, nowrap); | |||
&-title { | |||
@include font(24px, $color-black); | |||
font-weight: 400; | |||
margin-bottom: 10px; | |||
} | |||
&-content { | |||
@include font(18px, $color-black); | |||
line-height: 26px; | |||
@include text-ellipsis-multiple(10); | |||
} | |||
} | |||
&-consult { | |||
@include size(242px, 42px); | |||
margin: 0 auto; | |||
border-radius: 50px; | |||
background: rgba(0, 153, 255, 1); | |||
@include flex(row, center, center, nowrap); | |||
@include font(20px, $color-white); | |||
cursor: pointer; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,65 @@ | |||
<template> | |||
<div | |||
class="cluster" | |||
:style="{ | |||
backgroundImage: 'url(' + formatImg(propsData.data.productUrl[0]) + ')', | |||
minHeight: $route.path === '/p_industry-park' ? auto : '500px', | |||
}" | |||
> | |||
<div class="cluster-title">{{ propsData.data.leadingIndustryName }}</div> | |||
<div | |||
class="cluster-content" | |||
v-html="SimpleMDE.prototype.markdown(propsData.data.leadingIndustryDetail)" | |||
></div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import SimpleMDE from "simplemde"; | |||
import { formatImg } from "@/utils/common.js"; | |||
import { useRoute } from "vue-router"; //1.引入路由 | |||
const $route = useRoute(); //2.实例化路由 | |||
const propsData = defineProps({ | |||
data: { | |||
type: Object, | |||
}, | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.cluster { | |||
position: relative; | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 10px; | |||
padding: 35px 40px 35px 32px; | |||
@include border-box; | |||
@include flex(column, flex-start, flex-start, nowrap); | |||
margin-bottom: 24px; | |||
// @include bg-image("https://img.js.design/assets/img/64abb2f1eec7205596b5791f.jpg"); | |||
background-size: 100% 100%; | |||
background-repeat: no-repeat; | |||
&-title { | |||
@include font(36px, $color-white); | |||
font-weight: bold; | |||
margin-bottom: 20px; | |||
z-index: 100; | |||
} | |||
&-content { | |||
@include font(20px, $color-white); | |||
line-height: 30px; | |||
@include text-ellipsis-multiple(8); | |||
word-break: break-word; | |||
z-index: 100; | |||
} | |||
} | |||
.cluster:before { | |||
content: ""; | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
border-radius: 10px; | |||
background-color: rgba(0, 0, 0, 0.5); | |||
@include border-box; | |||
} | |||
</style> |
@@ -0,0 +1,100 @@ | |||
<template> | |||
<div class="enterprise_item_box"> | |||
<div class="block-title"> | |||
<span>{{ propsData.projectItem.element.text }}</span> | |||
</div> | |||
<div class="enterprise_item"> | |||
<div | |||
class="enterprise" | |||
v-for="item in propsData.projectItem.enterpriseData" | |||
:key="item.enterpriseHomeLinkId" | |||
> | |||
<img :src="formatImg(item.url[0])" alt="图片" /> | |||
<div class="enterprise-right"> | |||
<div class="content"> | |||
<div | |||
class="content-title" | |||
v-html="SimpleMDE.prototype.markdown(item.detail)" | |||
></div> | |||
</div> | |||
<span class="enterprise-right-consult" @click="showTipInfo">立 即 咨 询</span> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<TipDialog ref="tipRefPC"></TipDialog> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
import SimpleMDE from "simplemde"; | |||
const propsData = defineProps({ | |||
projectItem: { | |||
type: Object, | |||
}, | |||
}); | |||
import TipDialog from "@/components/pc/TipDialogPC.vue"; | |||
const tipRefPC = ref(null); | |||
const showTipInfo = () => { | |||
tipRefPC.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.enterprise_item_box { | |||
margin-bottom: 40px; | |||
} | |||
.block-title { | |||
@include flex(row, center, center, nowrap); | |||
span { | |||
@include font(24px, $color-black); | |||
font-weight: 500; | |||
border-top-left-radius: 50px; | |||
border-top-right-radius: 50px; | |||
background: rgba(255, 255, 255, 1); | |||
padding: 10px 70px; | |||
max-width: $contentWidth; | |||
} | |||
} | |||
.enterprise_item { | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 20px; | |||
@include border-box; | |||
} | |||
.enterprise { | |||
padding: 24px 12px; | |||
@include border-box; | |||
@include flex(row, space-between, center, nowrap); | |||
img { | |||
@include size(766px, 460px); | |||
margin-right: 32px; | |||
} | |||
&-right { | |||
flex: 1; | |||
@include size(auto, 460px); | |||
@include flex(column, space-between, flex-start, nowrap); | |||
.content { | |||
flex: 1; | |||
padding-bottom: 20px; | |||
@include flex(column, flex-start, flex-start, nowrap); | |||
&-title { | |||
// p { | |||
@include font(20px, $color-black); | |||
font-weight: 400; | |||
margin-bottom: 25px; | |||
word-break: break-word; | |||
// } | |||
} | |||
} | |||
&-consult { | |||
@include size(100%, 50px); | |||
border-radius: 50px; | |||
background: rgba(0, 153, 255, 1); | |||
@include flex(row, center, center, nowrap); | |||
@include font(20px, $color-white); | |||
cursor: pointer; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,100 @@ | |||
<template> | |||
<div class="industry"> | |||
<div class="box"> | |||
<div class="form-tab"> | |||
<span | |||
v-for="item in tabArr" | |||
:key="item.value" | |||
:class="[currentTab == item.value ? 'active' : '']" | |||
@click="changeTab(item.value)" | |||
> | |||
{{ item.label }} | |||
</span> | |||
</div> | |||
<div v-loading="loading" class="tab_content"> | |||
<template v-if="!loading"> | |||
<template v-if="currentTab == 0"> | |||
<InvestmentVehicle | |||
v-for="item in registrationPackageList" | |||
:key="item.registrationPackageId" | |||
:projectItem="item" | |||
></InvestmentVehicle> | |||
</template> | |||
<template v-if="currentTab == 1"> | |||
<Enterprise | |||
v-for="item in enterpriseData" | |||
:key="item.element.itemId" | |||
:projectItem="item" | |||
></Enterprise> | |||
</template> | |||
<template v-if="currentTab == 2"> | |||
<Cluster | |||
v-for="item in industrialAllocation" | |||
:key="item.areaIndustrialAllocationId" | |||
:data="item" | |||
></Cluster> | |||
</template> | |||
</template> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import Cluster from "./Cluster.vue"; | |||
import Enterprise from "./Enterprise.vue"; | |||
import InvestmentVehicle from "../project/Project.vue"; | |||
import { getIndustryData } from "@/views/pc/industry/getRegistrationPackageList"; | |||
import { useRoute } from "vue-router"; //1.引入路由 | |||
import { onMounted } from "vue"; | |||
const $route = useRoute(); //2.实例化路由 | |||
let { | |||
loading, | |||
tabArr, | |||
currentTab, | |||
changeTab, | |||
registrationPackageList, | |||
enterpriseData, | |||
industrialAllocation, | |||
} = getIndustryData(); | |||
onMounted(() => { | |||
loading.value = true; | |||
if ($route.query.tab) { | |||
changeTab($route.query.tab); | |||
} else { | |||
changeTab(0); | |||
} | |||
loading.value = false; | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.industry { | |||
padding: 30px 0 58px; | |||
background: rgba(245, 245, 245, 1); | |||
.box { | |||
@include size($contentWidth, auto); | |||
margin: 0 auto; | |||
} | |||
.form-tab { | |||
@include size($contentWidth, auto); | |||
margin: 25px auto; | |||
@include flex(row, flex-start, center, nowrap); | |||
span { | |||
width: 200px; | |||
height: 82px; | |||
@include font(24px, $color-black); | |||
font-weight: 500; | |||
@include flex(row, center, center, nowrap); | |||
border-radius: 20px; | |||
cursor: pointer; | |||
@include border-box; | |||
} | |||
.active { | |||
@include font(24px, $color-white); | |||
background: rgba(0, 153, 255, 1); | |||
} | |||
} | |||
.tab_content { | |||
min-height: 600px; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,109 @@ | |||
import { | |||
getPickListByKey, | |||
getRegistrationPackageList, | |||
getAreaIndustrialAllocationList, | |||
getEnterpriseHomeLinkList, | |||
} from "@/apis/index"; | |||
import { ref } from "vue"; | |||
import { toast, sortByKey } from "@/utils/common.js"; | |||
const loading = ref(true); | |||
const tabArr = ref([{ | |||
label: "招商载体", | |||
value: 0, | |||
}, | |||
{ | |||
label: '企业"链家"', | |||
value: 1, | |||
}, | |||
{ | |||
label: "产业聚集", | |||
value: 2, | |||
}, | |||
]); | |||
const currentTab = ref(0); | |||
let registrationPackageList = ref([]); | |||
let enterpriseData = ref([]); | |||
let industrialAllocation = ref([]); | |||
const pickList = []; | |||
export function getIndustryData() { | |||
const changeTab = async val => { | |||
currentTab.value = val; | |||
getData(val); | |||
}; | |||
const getPickList = temp => { | |||
const params = { | |||
key: "shitiwuyeshangjia_lianjialeixing", | |||
}; | |||
let data; | |||
getPickListByKey(params) | |||
.then(res => { | |||
if (res.data.status == 0) { | |||
data = sortByKey(res.data.data, "seq"); | |||
pickList.value = data; | |||
enterpriseData.value = setEnterpriseData(pickList.value, temp); | |||
console.log(enterpriseData.value); | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
}) | |||
.catch(error => { | |||
console.log(error); | |||
}); | |||
}; | |||
const getData = async tab => { | |||
registrationPackageList.value = []; | |||
enterpriseData.value = []; | |||
industrialAllocation.value = []; | |||
loading.value = true; | |||
let data; | |||
let res; | |||
if (tab == 0) { | |||
res = await getRegistrationPackageList(); | |||
} else if (tab == 1) { | |||
res = await getEnterpriseHomeLinkList(); | |||
} else { | |||
res = await getAreaIndustrialAllocationList(); | |||
} | |||
try { | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
if (tab == 0) { | |||
registrationPackageList.value = data; | |||
} else if (tab == 1) { | |||
getPickList(data); | |||
} else { | |||
industrialAllocation.value = data; | |||
} | |||
loading.value = false; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
console.log(error); | |||
toast("获取数据失败,请刷新重试!", "error"); | |||
} | |||
}; | |||
const setEnterpriseData = (typeArr, data) => { | |||
let arr = []; | |||
typeArr.forEach(element => { | |||
arr.push({ | |||
element, | |||
enterpriseData: data.filter(item => item.homeLinkType.id === element.itemId), | |||
}); | |||
}); | |||
return arr; | |||
}; | |||
return { | |||
loading, | |||
tabArr, | |||
currentTab, | |||
changeTab, | |||
getData, | |||
registrationPackageList, | |||
enterpriseData, | |||
industrialAllocation, | |||
}; | |||
} |
@@ -0,0 +1,215 @@ | |||
<template> | |||
<div class="policy" v-if="!loading"> | |||
<div class="policy-search"> | |||
<el-input | |||
v-model="params.search" | |||
placeholder="" | |||
class="input-with-select" | |||
clearable | |||
@clear="toSearch" | |||
@keyup.enter="toSearch" | |||
> | |||
<template #prepend> | |||
<el-select | |||
v-model="params.policyTypeId" | |||
placeholder="全部" | |||
@change="changeSelect" | |||
> | |||
<el-option label="全部" value="全部"></el-option> | |||
<el-option | |||
v-for="item in selectList" | |||
:key="item.itemId" | |||
:label="item.text" | |||
:value="item.itemId" | |||
/> | |||
</el-select> | |||
</template> | |||
<template #append> | |||
<el-button :icon="Search" @click="toSearch">搜索</el-button> | |||
</template> | |||
</el-input> | |||
</div> | |||
<section v-loading="dataLoading"> | |||
<ul class="policy-ul" v-if="dataList && dataList.length > 0"> | |||
<li | |||
class="policy-item" | |||
v-for="item in dataList" | |||
:key="item.releasePolicyId" | |||
@click="goRoute(item)" | |||
> | |||
<div class="policy-item-left">{{ item.policyName }}</div> | |||
<div class="policy-item-right"> | |||
<span>政策类型:{{ item.policyType.text }}</span> | |||
<span>发布时间:{{ formatDate(item.modifiedOn, "YYYY.MM.DD") }}</span> | |||
</div> | |||
</li> | |||
</ul> | |||
<section class="listNull" v-else> | |||
<img src="@/assets/icon-no-data.png" alt="暂无数据" /> | |||
</section> | |||
</section> | |||
<div class="pagination" v-if="dataList && dataList.length > 0"> | |||
<el-pagination | |||
v-model:current-page="params.page" | |||
background | |||
layout="prev, pager, next" | |||
:page-size="params.pageSize" | |||
:total="total" | |||
@current-change="handleCurrentChange" | |||
/> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { Search } from "@element-plus/icons-vue"; | |||
import { getReleasePolicyData } from "@/views/pc/policy/policy"; | |||
import { formatDate, sortByKey, routerOpenInNewWindow } from "@/utils/common.js"; | |||
import { onMounted } from "vue"; | |||
import useHomeStore from "@/store/module/home"; | |||
const homeStore = useHomeStore(); | |||
const { params, getList, getPickList } = getReleasePolicyData(); | |||
let dataList = ref([]); | |||
let selectList = ref([]); | |||
let total = ref(0); | |||
const loading = ref(true); | |||
const dataLoading = ref(false); | |||
const changeSelect = async val => { | |||
params.value.policyTypeId = val == "全部" ? "" : val; | |||
params.value.page = 1; | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
}; | |||
const toSearch = async () => { | |||
dataLoading.value = true; | |||
params.value.page = 1; | |||
params.value.pageSize = 10; | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
dataLoading.value = false; | |||
}; | |||
const handleCurrentChange = async val => { | |||
params.value.page = val; | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
}; | |||
const goRoute = val => { | |||
routerOpenInNewWindow({ | |||
path: "/p_preferential-policy-detail", | |||
query: { | |||
releasePolicyId: val.releasePolicyId, | |||
}, | |||
}); | |||
}; | |||
onMounted(async () => { | |||
loading.value = true; | |||
let pickArr = await getPickList(); | |||
selectList.value = sortByKey(pickArr, "seq"); | |||
let dataObj = await getList(params); | |||
dataList.value = dataObj.list; | |||
total.value = dataObj.total; | |||
loading.value = false; | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.policy { | |||
// width: 1180px; | |||
padding: 30px 0 68px; | |||
background: rgba(245, 245, 245, 1); | |||
@include border-box; | |||
&-search { | |||
@include size(726px, 50px); | |||
margin: 0 auto 25px; | |||
border-radius: 25px; | |||
border: 2px solid rgba(29, 112, 217, 1); | |||
@include border-box; | |||
background: #fff; | |||
} | |||
.listNull { | |||
@include size($contentWidth, auto); | |||
margin: 20px auto 40px; | |||
@include flex(row, center, center, nowrap); | |||
} | |||
&-item { | |||
@include size($contentWidth, 100px); | |||
margin: 0 auto; | |||
@include font(18px, $color-black); | |||
background: rgba(255, 255, 255, 1); | |||
@include flex(row, space-between, center, nowrap); | |||
padding: 20px 18px 24px 34px; | |||
border-radius: 25px; | |||
@include border-box; | |||
margin-bottom: 30px; | |||
cursor: pointer; | |||
&-left { | |||
flex: 1; | |||
@include text-ellipsis-multiple(2); | |||
} | |||
&-right { | |||
height: 100%; | |||
padding-left: 94px; | |||
@include flex(column, space-between, flex-end, nowrap); | |||
} | |||
} | |||
} | |||
.pagination { | |||
@include flex(row, center, center, nowrap); | |||
} | |||
:deep() { | |||
.el-input-group, | |||
.el-input, | |||
.el-select, | |||
.select-trigger, | |||
.el-input__wrapper { | |||
height: 100%; | |||
background: transparent; | |||
border: none !important; | |||
box-shadow: none !important; | |||
} | |||
.el-select { | |||
width: 160px; | |||
border: none !important; | |||
box-shadow: none !important; | |||
} | |||
.el-input-group--prepend | |||
.el-input-group__prepend | |||
.el-select | |||
.el-input.is-focus | |||
.el-input__wrapper { | |||
box-shadow: none !important; | |||
border: none; | |||
} | |||
.el-input__wrapper { | |||
box-shadow: none !important; | |||
} | |||
.el-input-group__append { | |||
border-top-right-radius: 25px; | |||
border-bottom-right-radius: 25px; | |||
background: transparent; | |||
} | |||
.el-input-group__prepend { | |||
border-top-left-radius: 25px; | |||
border-bottom-left-radius: 25px; | |||
background: transparent; | |||
box-shadow: none; | |||
.el-input__wrapper { | |||
padding-left: 25px; | |||
} | |||
} | |||
.el-input-group--prepend .el-input-group__prepend .el-select:hover .el-input__wrapper { | |||
box-shadow: none !important; | |||
} | |||
.el-button { | |||
display: inline-flex; | |||
} | |||
.el-select .el-input__wrapper.is-focus { | |||
box-shadow: none !important; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,60 @@ | |||
<template> | |||
<section class="detail" v-if="!loading"> | |||
<div class="title">{{ policyInfo.policyName }}</div> | |||
<div class="subtitle"> | |||
<span>发布日期:{{ formatDate(policyInfo.modifiedOn, "YYYY年MM月DD日") }}</span> | |||
<span>政策类型:{{ policyInfo.policyType.text }}</span> | |||
<span>浏览量:{{ policyInfo.pageView || 0 }}</span> | |||
</div> | |||
<div class="content" v-html="formatHtml(policyInfo.policyText)"></div> | |||
</section> | |||
</template> | |||
<script setup> | |||
import { onMounted } from "vue"; | |||
import { getReleasePolicyData } from "@/views/pc/policy/policy"; | |||
import { formatDate } from "@/utils/common.js"; | |||
import { useRoute } from "vue-router"; | |||
import SimpleMDE from "simplemde"; | |||
const formatHtml = text => { | |||
let html = SimpleMDE.prototype.markdown(text); | |||
let imgUrl = `${import.meta.env.VITE_BASE_URL}/common/getImg?path=`; | |||
html = html.replace( | |||
/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, | |||
"<img src='" + imgUrl + "$1' width='100%'/>" | |||
); | |||
return html; | |||
}; | |||
const { updatePageView, getReleasePolicyDetail } = getReleasePolicyData(); | |||
const loading = ref(true); | |||
let policyInfo = ref({}); | |||
const $route = useRoute(); | |||
onMounted(async () => { | |||
loading.value = true; | |||
policyInfo.value = await getReleasePolicyDetail($route.query.releasePolicyId); | |||
loading.value = false; | |||
updatePageView($route.query.releasePolicyId); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.detail { | |||
@include size($contentWidth, auto); | |||
margin: 0 auto; | |||
padding: 60px 0 58px; | |||
.title { | |||
@include font(24px, $color-black); | |||
font-weight: bold; | |||
} | |||
.subtitle { | |||
@include font(18px, rgba(97, 96, 96, 1)); | |||
margin: 20px 0 30px; | |||
span { | |||
margin-right: 100px; | |||
} | |||
} | |||
.content { | |||
@include font(16px, $color-black); | |||
line-height: 34px; | |||
white-space: pre-wrap; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,83 @@ | |||
import { | |||
getReleasePolicyList, | |||
getPickListByKey, | |||
updateReleasePolicyPageView, | |||
getReleasePolicyById, | |||
} from "@/apis/index"; | |||
import { ref } from "vue"; | |||
import { toast } from "@/utils/common.js"; | |||
export function getReleasePolicyData() { | |||
const params = ref({ | |||
policyTypeId: "", | |||
search: "", | |||
page: 1, | |||
pageSize: 10, | |||
}); | |||
const getList = async params => { | |||
let data; | |||
try { | |||
let res = await getReleasePolicyList(params.value); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
toast("获取数据失败,请刷新重试!", "error"); | |||
} | |||
return data; | |||
}; | |||
const getPickList = async() => { | |||
const params = { | |||
key: "zhengcefabuguanli_zhengceleixing", | |||
}; | |||
let data; | |||
try { | |||
let res = await getPickListByKey(params); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
toast("获取数据失败,请刷新重试!", "error"); | |||
} | |||
return data; | |||
}; | |||
const getReleasePolicyDetail = async releasePolicyId => { | |||
let data; | |||
try { | |||
let res = await getReleasePolicyById({ releasePolicyId }); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
console.log(error); | |||
} | |||
return data; | |||
}; | |||
const updatePageView = async p => { | |||
let data; | |||
try { | |||
let res = await updateReleasePolicyPageView(p); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
console.log(error); | |||
} | |||
return data; | |||
}; | |||
return { params, getList, getPickList, updatePageView, getReleasePolicyDetail }; | |||
} |
@@ -0,0 +1,121 @@ | |||
<template> | |||
<div class="investment"> | |||
<div class="container" v-if="!loading"> | |||
<van-sticky class="form-tab"> | |||
<el-tabs | |||
class="wd-tabs" | |||
type="card" | |||
:stretch="true" | |||
v-model="currentTab" | |||
@tab-click="changeTab" | |||
> | |||
<el-tab-pane | |||
v-for="(item, index) in dataList" | |||
:key="item.attractInvestmentProjectId" | |||
:label="item.productName" | |||
:name="index" | |||
></el-tab-pane> | |||
</el-tabs> | |||
</van-sticky> | |||
<div | |||
v-for="(item, index) in dataList" | |||
:key="item.attractInvestmentProjectId" | |||
class="form-right scroll-item" | |||
scroll-item | |||
:id="'tab' + index" | |||
> | |||
<Project :projectItem="item"></Project> | |||
</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup> | |||
import Project from "./Project.vue"; | |||
import { getReleasePolicyData } from "@/views/pc/project/project"; | |||
import { onMounted, onUnmounted, ref } from "vue"; | |||
import VueScrollTo from "vue-scrollto"; | |||
import { debounce } from "@/utils/common.js"; | |||
const { getList } = getReleasePolicyData(); | |||
let dataList = ref([]); | |||
const currentTab = ref(0); | |||
const loading = ref(false); | |||
const changeTab = targetName => { | |||
currentTab.value = targetName.paneName; | |||
VueScrollTo.scrollTo(`#tab${targetName.paneName}`, 500, { offset: -84 }); | |||
}; | |||
const scrollListenerHandler = debounce(() => { | |||
const scrollTop = | |||
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; // 滚动条偏移量 | |||
document.querySelectorAll(".scroll-item").forEach((item, index) => { | |||
if (item.offsetTop - 84 <= scrollTop) { | |||
currentTab.value = index; | |||
} | |||
}); | |||
}, 100); | |||
onMounted(async () => { | |||
loading.value = true; | |||
dataList = await getList(); | |||
loading.value = false; | |||
window.addEventListener("scroll", scrollListenerHandler); | |||
}); | |||
onUnmounted(() => { | |||
window.removeEventListener("scroll", scrollListenerHandler); | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
.investment { | |||
padding: 30px 0 58px; | |||
background: rgba(245, 245, 245, 1); | |||
} | |||
.container { | |||
@include size($contentWidth, auto); | |||
margin: 0 auto; | |||
// @include flex(row, space-between, flex-start, nowrap); | |||
.form-tab { | |||
margin: 24px auto; | |||
} | |||
.form-right { | |||
width: 100%; | |||
} | |||
:deep() { | |||
.el-tabs--card > .el-tabs__header { | |||
background: rgba(245, 245, 245, 1); | |||
height: 82px; | |||
border: none; | |||
} | |||
.el-tabs__nav-next, | |||
.el-tabs__nav-prev { | |||
line-height: 82px; | |||
} | |||
.el-tabs--card > .el-tabs__header .el-tabs__nav { | |||
border: none; | |||
} | |||
.el-tabs--card > .el-tabs__header .el-tabs__item { | |||
width: auto; | |||
height: 82px; | |||
border-radius: 20px; | |||
border: none; | |||
@include font(24px, $color-black); | |||
font-weight: 500; | |||
} | |||
.el-tabs--card > .el-tabs__header .el-tabs__item.is-active { | |||
@include font(24px, $color-white); | |||
background: rgba(0, 153, 255, 1); | |||
} | |||
.el-tabs__item { | |||
// width: 200px; | |||
// height: 82px; | |||
// @include font(24px, $color-black); | |||
// font-weight: 500; | |||
// @include flex(row, center, center, nowrap); | |||
// border-radius: 20px; | |||
// cursor: pointer; | |||
// @include border-box; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,74 @@ | |||
<template> | |||
<div class="project"> | |||
<img :src="formatImg(propsData.projectItem.productUrl[0])" alt="招商项目图片" /> | |||
<div class="project-right"> | |||
<div class="content"> | |||
<span class="content-title">{{ propsData.projectItem.productName }}</span> | |||
<div class="content-content" :title="propsData.projectItem.productText"> | |||
{{ propsData.projectItem.productText }} | |||
</div> | |||
</div> | |||
<span class="project-right-consult" @click="showTipInfo">立 即 咨 询</span> | |||
</div> | |||
</div> | |||
<TipDialog ref="tipRefPC"></TipDialog> | |||
</template> | |||
<script setup> | |||
import { formatImg } from "@/utils/common.js"; | |||
const propsData = defineProps({ | |||
projectItem: { | |||
type: Object, | |||
}, | |||
}); | |||
import TipDialog from "@/components/pc/TipDialogPC.vue"; | |||
const tipRefPC = ref(null); | |||
const showTipInfo = () => { | |||
tipRefPC.value.showTip(); | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.project { | |||
background: rgba(255, 255, 255, 1); | |||
border-radius: 20px; | |||
padding: 24px 12px; | |||
@include border-box; | |||
@include flex(row, space-between, center, nowrap); | |||
margin-bottom: 24px; | |||
img { | |||
@include size(526px, 460px); | |||
margin-right: 32px; | |||
} | |||
&-right { | |||
flex: 1; | |||
@include size(auto, 460px); | |||
@include flex(column, space-between, flex-start, nowrap); | |||
.content { | |||
flex: 1; | |||
padding-bottom: 20px; | |||
@include flex(column, flex-start, flex-start, nowrap); | |||
&-title { | |||
@include font(24px, $color-black); | |||
font-weight: bold; | |||
margin-bottom: 25px; | |||
} | |||
&-content { | |||
@include font(18px, $color-black); | |||
line-height: 30px; | |||
@include text-ellipsis-multiple(11); | |||
white-space: pre-wrap; | |||
} | |||
} | |||
&-consult { | |||
@include size(100%, 50px); | |||
border-radius: 50px; | |||
background: rgba(0, 153, 255, 1); | |||
@include flex(row, center, center, nowrap); | |||
@include font(20px, $color-white); | |||
cursor: pointer; | |||
} | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,23 @@ | |||
import { getAttractInvestmentProjectList } from "@/apis/index"; | |||
import { ref } from "vue"; | |||
import { toast } from "@/utils/common.js"; | |||
export function getReleasePolicyData() { | |||
const getList = async() => { | |||
let data; | |||
try { | |||
let res = await getAttractInvestmentProjectList(); | |||
if (res.data.status == 0) { | |||
data = res.data.data; | |||
} else { | |||
toast(res.data.msg, "error"); | |||
} | |||
} catch (error) { | |||
toast("获取数据失败,请刷新重试!", "error"); | |||
} | |||
return ref(data); | |||
}; | |||
return { getList }; | |||
} |
@@ -0,0 +1,77 @@ | |||
import { defineConfig, loadEnv } from "vite"; | |||
import vue from "@vitejs/plugin-vue"; | |||
import { resolve } from "path"; | |||
import AutoImport from "unplugin-auto-import/vite"; | |||
import Components from "unplugin-vue-components/vite"; | |||
// 按需引入 ElementPlus vant | |||
import { ElementPlusResolver } from "unplugin-vue-components/resolvers"; | |||
import { VantResolver } from "unplugin-vue-components/resolvers"; | |||
// 生成.gz文件 | |||
import viteCompression from "vite-plugin-compression"; | |||
// https://vitejs.dev/config/ | |||
export default defineConfig(({ mode }) => { | |||
let env = loadEnv(mode, process.cwd()); | |||
return { | |||
plugins: [ | |||
vue(), | |||
AutoImport({ | |||
// 自动导入vue相关的Api | |||
imports: ["vue", "vue-router"], | |||
resolvers: [ElementPlusResolver()], | |||
}), | |||
Components({ | |||
resolvers: [ElementPlusResolver(), VantResolver()], | |||
}), | |||
{ | |||
...viteCompression(), | |||
apply: "build", | |||
}, | |||
], | |||
resolve: { | |||
// 配置路径别名 | |||
alias: { | |||
"@": resolve(__dirname, "./src"), | |||
}, | |||
}, | |||
css: { | |||
// CSS 预处理器 | |||
preprocessorOptions: { | |||
//define global scss variable | |||
scss: { | |||
javascriptEnabled: true, | |||
additionalData: `@import "@/styles/variables.scss";`, | |||
}, | |||
}, | |||
}, | |||
server: { | |||
host: "0.0.0.0", | |||
port: 8080, | |||
open: true, | |||
https: false, | |||
proxy: { | |||
"/api": { | |||
target: env.VITE_BASE_URL, | |||
changeOrigin: true, | |||
ws: true, | |||
rewrite: path => path.replace(/^\/api/, ""), | |||
}, | |||
}, | |||
}, | |||
build: { | |||
rollupOptions: { | |||
output: { | |||
manualChunks: { | |||
vue: ["vue", "pinia", "vue-router"], | |||
elementIcons: ["@element-plus/icons-vue"], | |||
}, | |||
chunkFileNames: "wap/js/[name]-[hash].js", | |||
entryFileNames: "wap/js/[name]-[hash].js", | |||
assetFileNames: "wap/[ext]/[name]-[hash].[ext]", | |||
}, | |||
}, | |||
}, | |||
}; | |||
}); |