| @@ -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]", | |||
| }, | |||
| }, | |||
| }, | |||
| }; | |||
| }); | |||