factoryindex5.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <template>
  2. <view class="page">
  3. <!-- 1. 顶部标题 -->
  4. <view class="header">
  5. <text class="title">{{ title }}</text>
  6. </view>
  7. <!-- 2. 轮播图 -->
  8. <swiper
  9. class="swiper"
  10. circular
  11. :indicator-dots="true"
  12. :autoplay="true"
  13. :interval="3000"
  14. :duration="800"
  15. >
  16. <swiper-item v-for="(item, index) in picture" :key="index">
  17. <image :src="item" class="swiper-item" mode="aspectFill" />
  18. </swiper-item>
  19. </swiper>
  20. <!-- 3. 全屏地图 -->
  21. <view class="map-box">
  22. <view class="map-wrap">
  23. <canvas canvas-id="ydChart" id="ydChart" class="map-canvas" />
  24. </view>
  25. </view>
  26. <!-- 4. 悬浮按钮 -->
  27. <view class="fab" @tap="openDrawer">
  28. <text class="fab-txt">园区</text>
  29. </view>
  30. <!-- 5. 侧边抽屉 -->
  31. <view class="drawer-mask" :class="{show: drawerOpen}" @tap="closeDrawer" />
  32. <view class="drawer" :class="{show: drawerOpen}">
  33. <view class="drawer-header">
  34. <input
  35. v-model="keyword"
  36. class="search-input"
  37. placeholder="搜索园区"
  38. @input="onSearch"
  39. />
  40. <view class="mode-toggle" @tap="toggleMode">
  41. <text :class="{active: mode==='grid'}">宫格</text>
  42. <text :class="{active: mode==='list'}">列表</text>
  43. </view>
  44. </view>
  45. <!-- 宫格模式 -->
  46. <scroll-view v-if="mode==='grid'" scroll-y class="drawer-body">
  47. <view class="grid-box">
  48. <view
  49. v-for="(item,index) in filteredList"
  50. :key="index"
  51. class="grid-card"
  52. @tap="onSelectPark(item)"
  53. >
  54. <text class="grid-label">{{ item.label }}</text>
  55. </view>
  56. </view>
  57. </scroll-view>
  58. <!-- 列表模式 -->
  59. <scroll-view v-else scroll-y class="drawer-body">
  60. <view
  61. v-for="(item,index) in filteredList"
  62. :key="index"
  63. class="list-item"
  64. @tap="onSelectPark(item)"
  65. >
  66. <text class="list-label">{{ item.label }}</text>
  67. <text class="list-count">{{ item.count||0 }} 处</text>
  68. </view>
  69. </scroll-view>
  70. </view>
  71. </view>
  72. </template>
  73. <script>
  74. import * as echarts from 'echarts'
  75. export default {
  76. data() {
  77. return {
  78. title: '盐都区闲置厂房分布图',
  79. picture: [
  80. 'https://ydwqfw.com.cn/yd_qycpfbH5/bg1.jpg',
  81. 'https://ydwqfw.com.cn/yd_qycpfbH5/bg2.jpg',
  82. 'https://ydwqfw.com.cn/yd_qycpfbH5/bg3.jpg',
  83. 'https://ydwqfw.com.cn/yd_qycpfbH5/bg4.jpg'
  84. ],
  85. chart: null,
  86. drawerOpen: false,
  87. mode: 'grid', // grid | list
  88. keyword: '',
  89. parkList2: [
  90. { label: '全部', value: '', count: 99 },
  91. { label: '台创园', value: '1', count: 12 },
  92. { label: '大冈', value: '2', count: 8 },
  93. { label: '大纵湖', value: '3', count: 15 },
  94. { label: '学富', value: '4', count: 6 },
  95. { label: '尚庄', value: '5', count: 9 },
  96. { label: '张庄', value: '6', count: 11 },
  97. { label: '楼王', value: '7', count: 7 },
  98. { label: '潘黄', value: '8', count: 13 },
  99. { label: '盐渎', value: '9', count: 18 },
  100. { label: '秦南', value: '10', count: 5 },
  101. { label: '郭猛', value: '11', count: 10 },
  102. { label: '高新区', value: '12', count: 22 },
  103. { label: '龙冈', value: '13', count: 14 }
  104. ]
  105. }
  106. },
  107. computed: {
  108. filteredList() {
  109. if (!this.keyword) return this.parkList2
  110. const kw = this.keyword.toLowerCase()
  111. return this.parkList2.filter(i => i.label.toLowerCase().includes(kw))
  112. }
  113. },
  114. async onReady() {
  115. const json = await this.loadGeoJSON()
  116. echarts.registerMap('yanduqu', json)
  117. let canvasNode
  118. // #ifdef H5
  119. canvasNode = document.getElementById('ydChart')
  120. // #endif
  121. // #ifndef H5
  122. canvasNode = await new Promise(resolve => {
  123. uni.createSelectorQuery()
  124. .in(this)
  125. .select('#ydChart')
  126. .node(res => resolve(res.node))
  127. .exec()
  128. })
  129. // #endif
  130. this.chart = echarts.init(canvasNode, null, {
  131. width: uni.getSystemInfoSync().windowWidth,
  132. height: uni.getSystemInfoSync().windowHeight - uni.upx2px(300) // 扣掉轮播图高度
  133. })
  134. this.renderMap()
  135. },
  136. methods: {
  137. loadGeoJSON() {
  138. return new Promise((resolve, reject) => {
  139. uni.request({
  140. url: './static/yandu.json',
  141. method: 'GET',
  142. success: res => resolve(res.data),
  143. fail: reject
  144. })
  145. })
  146. },
  147. renderMap() {
  148. const option = {
  149. geo: [{
  150. map: 'yanduqu',
  151. roam: true,
  152. aspectScale: 1.2,
  153. zoom: 1.3,
  154. itemStyle: {
  155. areaColor: {
  156. type: 'linear-gradient',
  157. x: 0, y: 400, x2: 0, y2: 0,
  158. colorStops: [
  159. { offset: 0, color: 'rgba(37,108,190,0.8)' },
  160. { offset: 1, color: 'rgba(15,169,195,0.8)' }
  161. ],
  162. global: true
  163. },
  164. borderColor: '#4ecee6',
  165. borderWidth: 1
  166. },
  167. emphasis: {
  168. areaColor: 'rgba(255,202,40,.9)'
  169. },
  170. label: { show: true, color: '#fff', fontSize: 12, fontWeight: 'bold' }
  171. }],
  172. series: [{ type: 'map', geoIndex: 0, data: [] }]
  173. }
  174. this.chart.setOption(option)
  175. this.chart.on('click', params => {
  176. uni.navigateTo({
  177. url: `/pages/factoryBuildings/factoryBuildingsList?parkid=${params.name}`
  178. })
  179. })
  180. },
  181. // 抽屉交互
  182. openDrawer() {
  183. this.drawerOpen = true
  184. },
  185. closeDrawer() {
  186. this.drawerOpen = false
  187. },
  188. toggleMode() {
  189. this.mode = this.mode === 'grid' ? 'list' : 'grid'
  190. },
  191. onSearch() {}, // 已用 computed 实时过滤
  192. // 选中园区
  193. onSelectPark(item) {
  194. this.closeDrawer()
  195. // 1. 地图高亮
  196. if (this.chart && item.value) {
  197. this.chart.dispatchAction({
  198. type: 'highlight',
  199. seriesIndex: 0,
  200. name: item.label
  201. })
  202. setTimeout(() => {
  203. this.chart.dispatchAction({
  204. type: 'downplay',
  205. seriesIndex: 0,
  206. name: item.label
  207. })
  208. }, 1200)
  209. }
  210. // 2. 跳转列表
  211. uni.navigateTo({
  212. url: `/pages/factoryBuildings/factoryBuildingsList?parkid=${item.value}`
  213. })
  214. }
  215. }
  216. }
  217. </script>
  218. <style scoped>
  219. /* CSS 变量,一键换色 */
  220. :root {
  221. --theme: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  222. --card-bg: #f7f9fc;
  223. --text: #333;
  224. --shadow: 0 6rpx 24rpx rgba(0,0,0,.08);
  225. }
  226. .page {
  227. display: flex;
  228. flex-direction: column;
  229. height: 100vh;
  230. background: #f5f5f5;
  231. }
  232. .header {
  233. padding: 20rpx 0;
  234. text-align: center;
  235. background: #fff;
  236. }
  237. .title {
  238. font-size: 36rpx;
  239. font-weight: bold;
  240. color: var(--text);
  241. }
  242. .swiper {
  243. width: 100%;
  244. height: 300rpx;
  245. }
  246. .swiper-item {
  247. width: 100%;
  248. height: 100%;
  249. }
  250. .map-box {
  251. flex: 1;
  252. width: 100%;
  253. position: relative;
  254. }
  255. .map-canvas {
  256. width: 100%;
  257. height: 100%;
  258. }
  259. /* 悬浮按钮 */
  260. .fab {
  261. position: fixed;
  262. right: 40rpx;
  263. bottom: 80rpx;
  264. width: 100rpx;
  265. height: 100rpx;
  266. border-radius: 50%;
  267. background: var(--theme);
  268. box-shadow: var(--shadow);
  269. display: flex;
  270. align-items: center;
  271. justify-content: center;
  272. z-index: 9;
  273. }
  274. .fab-txt {
  275. font-size: 28rpx;
  276. color: #fff;
  277. font-weight: 600;
  278. }
  279. /* 抽屉 */
  280. .drawer-mask {
  281. position: fixed;
  282. left: 0;
  283. top: 0;
  284. right: 0;
  285. bottom: 0;
  286. background: rgba(0,0,0,.45);
  287. opacity: 0;
  288. visibility: hidden;
  289. transition: all .3s;
  290. z-index: 10;
  291. }
  292. .drawer-mask.show {
  293. opacity: 1;
  294. visibility: visible;
  295. }
  296. .drawer {
  297. position: fixed;
  298. right: 0;
  299. top: 0;
  300. bottom: 0;
  301. width: 75vw;
  302. background: #fff;
  303. transform: translateX(100%);
  304. transition: transform .3s;
  305. display: flex;
  306. flex-direction: column;
  307. z-index: 11;
  308. }
  309. .drawer.show {
  310. transform: translateX(0);
  311. }
  312. .drawer-header {
  313. padding: 30rpx;
  314. border-bottom: 1rpx solid #eee;
  315. }
  316. .search-input {
  317. height: 64rpx;
  318. padding: 0 20rpx;
  319. border: 1rpx solid #ddd;
  320. border-radius: 8rpx;
  321. font-size: 28rpx;
  322. }
  323. .mode-toggle {
  324. display: flex;
  325. margin-top: 20rpx;
  326. justify-content: space-around;
  327. }
  328. .mode-toggle text {
  329. padding: 8rpx 24rpx;
  330. border-radius: 8rpx;
  331. font-size: 26rpx;
  332. color: #666;
  333. background: var(--card-bg);
  334. }
  335. .mode-toggle text.active {
  336. background: var(--theme);
  337. color: #fff;
  338. }
  339. .drawer-body {
  340. flex: 1;
  341. padding: 20rpx 30rpx;
  342. }
  343. /* 宫格 */
  344. .grid-box {
  345. display: grid;
  346. grid-template-columns: repeat(3, 1fr);
  347. gap: 20rpx;
  348. }
  349. .grid-card {
  350. display: flex;
  351. align-items: center;
  352. justify-content: center;
  353. height: 140rpx;
  354. border-radius: 12rpx;
  355. background: var(--card-bg);
  356. box-shadow: var(--shadow);
  357. font-size: 28rpx;
  358. font-weight: 600;
  359. color: var(--text);
  360. }
  361. /* 列表 */
  362. .list-item {
  363. display: flex;
  364. justify-content: space-between;
  365. align-items: center;
  366. padding: 24rpx 0;
  367. border-bottom: 1rpx solid #f0f0f0;
  368. }
  369. .list-label {
  370. font-size: 30rpx;
  371. color: var(--text);
  372. }
  373. .list-count {
  374. font-size: 26rpx;
  375. color: #999;
  376. }
  377. </style>