index.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. <template>
  2. <div class="base-container no-events">
  3. <div class="content">
  4. <!-- <navigation @handleTab="handleTab"></navigation> -->
  5. <div class="left yes-events">
  6. <component :is="components[currentComponent]" @backHome="backHome" />
  7. </div>
  8. <div class="right yes-events">
  9. <div class="list adopt-list-wrap">
  10. <chart-box name="果树管理" arrow="" color="yellow">
  11. <el-tabs v-model="activeName" class="demo-tabs">
  12. <el-tab-pane label="果树列表" name="果树列表">
  13. <adopt-list></adopt-list>
  14. </el-tab-pane>
  15. <el-tab-pane label="待分配" name="待分配">
  16. <template #label>
  17. <span class="custom-tabs-label">
  18. <span>待分配</span>
  19. <span class="count-text" v-if="listCount">{{ listCount }}</span>
  20. </span>
  21. </template>
  22. <client-list
  23. :checkDistributeShow="checkDistributeShow"
  24. :startReloadList="startReloadList"
  25. @update:checkDistributeShow="hanldeDistributeCheck"
  26. @update:checkData="handlCheckChange"
  27. @update:listCount="updateListCount"
  28. ></client-list>
  29. </el-tab-pane>
  30. <el-tab-pane label="已分配" name="已分配">
  31. <apply-list
  32. :startReloadList="startReloadList"
  33. @update:userList="updateUser"
  34. ></apply-list>
  35. </el-tab-pane>
  36. <!-- <el-tab-pane label="确认地址" name="确认地址">
  37. <address-list></address-list>
  38. </el-tab-pane> -->
  39. </el-tabs>
  40. </chart-box>
  41. </div>
  42. </div>
  43. <!-- 图例 -->
  44. <div v-if="legendArr && legendArr.length" class="map-bg map-legend yes-events">
  45. <div class="item" v-for="(legend, legendI) in legendArr" :key="legendI">
  46. <span class="legend-block" :style="{ background: legend.color }"></span>
  47. {{ legend.name }}
  48. </div>
  49. </div>
  50. <div v-else class="map-bg map-legend yes-events">
  51. <div class="item" v-show="!checkDistributeShow">
  52. <img src="@/assets/images/map/status/wry.png" alt="" />
  53. 未开放
  54. </div>
  55. <div class="item" v-show="!checkShow">
  56. <img src="@/assets/images/map/status/dry.png" alt="" />
  57. 待守护
  58. </div>
  59. <!-- <div class="item" v-show="!checkShow">
  60. <img src="@/assets/images/map/status/yry.png" alt="" />
  61. 已守护
  62. </div> -->
  63. <div class="item selected-item" v-show="checkShow || checkDistributeShow">
  64. <img src="@/assets/images/map/status/selected.png" alt="" />
  65. 已选择
  66. </div>
  67. </div>
  68. <div class="tips" v-show="checkShow">
  69. <div class="text">
  70. <span>提示:</span>请在底图上点选 <span>未开放的果树</span>,或按住 Ctrl 框选
  71. <span>开放守护的区域</span>
  72. </div>
  73. </div>
  74. <div class="tips" v-show="checkDistributeShow">
  75. <div class="text">
  76. <span>提示:</span>请在底图上点选 <span>待守护的果树</span>,或按住 Ctrl 框选
  77. <span>分配的区域</span>
  78. </div>
  79. </div>
  80. <div v-show="!checkShow && !checkDistributeShow" class="right-button yes-events" @click="hanldeCheck">
  81. 新增守护区域
  82. </div>
  83. <div v-show="checkShow" class="center-button">
  84. <div class="cancel yes-events" @click="handleCancel">取消</div>
  85. <div class="yes-events" @click="saveEdit">确认开放守护权限</div>
  86. </div>
  87. <div v-show="checkDistributeShow" class="center-button">
  88. <div class="cancel yes-events" @click="handleCancel">取消分配</div>
  89. <div class="yes-events" @click="saveDistributeEdit" :class="{'disabled': (!selectedTree.length || !selectedTels.length) || selectedTree.length < selectedTels.length}">
  90. 确认分配<span class="select-text">(已选{{selectedTree.length}}/<span class="select-total">{{ selectedTels.length }}</span>)</span>
  91. </div>
  92. </div>
  93. </div>
  94. </div>
  95. <custom-map class="bottom-map" ref="mapRef" @update:selectedTree="handleSelectedTree"></custom-map>
  96. <!-- 图片弹窗 -->
  97. <PicturePreview :imageUrl="urls" :curIndex="urlsIndex"></PicturePreview>
  98. <PdfDialog title="果园报告"></PdfDialog>
  99. </template>
  100. <script setup>
  101. import { nextTick, onMounted, onUnmounted, ref } from "vue";
  102. import config from "@/api/config.js";
  103. import timeLine from "@/components/timeLine.vue";
  104. import PicturePreview from "@/components/PicturePreview.vue";
  105. import navigation from "@/components/navigation.vue";
  106. import chartBox from "@/components/chartBox.vue";
  107. import leftTabs from "./components/leftTabs/index.vue";
  108. import treeDetail from "./components/treeDetail.vue";
  109. import adoptList from "./components/adoptList.vue";
  110. import clientList from "./components/clientList.vue";
  111. import customMap from "./homeMap.vue";
  112. import applyList from "./components/applyList.vue";
  113. import addressList from "./components/addressList.vue";
  114. import { convertPointToArray } from "@/utils/index";
  115. import { useRoute } from "vue-router";
  116. import { useStore } from "vuex";
  117. import eventBus from "@/api/eventBus";
  118. import PdfDialog from "../../components/PdfDialog";
  119. import { ElMessage } from "element-plus";
  120. let store = useStore();
  121. const components = {
  122. leftTabs,
  123. treeDetail,
  124. };
  125. const route = useRoute();
  126. const mapRef = ref(null);
  127. const activeName = ref("果树列表");
  128. const currentFarmId = store.getters.userinfo.curFarmId
  129. // const currentFarmId = sessionStorage.getItem('userinfo').curFarmId
  130. onMounted(() => {
  131. //区域切换监听事件
  132. eventBus.on("area:id", areaId);
  133. areaId({ areaId: 0, farmId: currentFarmId });
  134. // 修改单棵树信息后刷新地图数据
  135. eventBus.off("refreshMapData", getPointList);
  136. eventBus.on("refreshMapData", getPointList);
  137. if (route.query.acitveName) {
  138. activeName.value = route.query.acitveName
  139. }
  140. });
  141. onUnmounted(() => {
  142. if (mapRef.value) {
  143. mapRef.value.destroy(); // 确保在自定义地图组件中实现了destroy方法
  144. }
  145. eventBus.off("area:id", areaId);
  146. eventBus.off("changePointLegend", toggleLegend);
  147. eventBus.off("clickMapPoint", toggleLeftComponet);
  148. });
  149. const getPointList = () => {
  150. VE_API.manage_interface.fetchSampleList({ farmId: organId.value }).then(({ data }) => {
  151. if (mapRef.value) {
  152. nextTick(() => {
  153. mapRef.value.addCluster(data);
  154. // mapRef.value.initData(arr)
  155. // mapRef.value.initMap(arr, () => mapRef.value.getRegionList(organId.value))
  156. });
  157. }
  158. });
  159. };
  160. const regionData = ref([]);
  161. function getBlueRegionList() {
  162. VE_API.manage_interface.fetchRegionList({ farmId: organId.value }).then(({ data }) => {
  163. regionData.value = data;
  164. mapRef.value.initAreaMap(data);
  165. });
  166. }
  167. const checkShow = ref(false);
  168. const hanldeCheck = () => {
  169. checkShow.value = true;
  170. mapRef.value.enableBoxSelect();
  171. };
  172. // 分配果树
  173. const checkDistributeShow = ref(false);
  174. const startReloadList = ref(false);
  175. const hanldeDistributeCheck = () => {
  176. checkDistributeShow.value = true;
  177. mapRef.value.enableDistributeBoxSelect();
  178. };
  179. // 分配果树勾选值变化事件
  180. const selectedTels = ref([]);
  181. const handlCheckChange = (val) => {
  182. selectedTels.value = val;
  183. };
  184. const listCount = ref(null)
  185. function updateListCount(count) {
  186. listCount.value = count
  187. }
  188. // 地图--选树时抛出的事件
  189. const selectedTree = ref([]);
  190. const handleSelectedTree = (data) => {
  191. selectedTree.value = data;
  192. }
  193. const handleCancel = () => {
  194. checkShow.value = false;
  195. checkDistributeShow.value = false;
  196. selectedTree.value = []
  197. selectedTels.value = []
  198. // mapRef.value.clearSelection()
  199. mapRef.value.stopBoxSelect();
  200. };
  201. const saveEdit = () => {
  202. mapRef.value.saveSelect(() => {
  203. getPointList();
  204. });
  205. checkShow.value = false;
  206. checkDistributeShow.value = false;
  207. };
  208. const saveDistributeEdit = () => {
  209. if (!selectedTree.value.length || !selectedTels.value.length || selectedTree.value.length < selectedTels.value.length) {
  210. return
  211. }
  212. // mapRef.value.saveDistributeSelect(() => {
  213. // getPointList();
  214. // });
  215. const params = {
  216. farmId: currentFarmId,
  217. sampleIds: selectedTree.value,
  218. tels: selectedTels.value,
  219. };
  220. VE_API.manage_interface.saveTreeUser(params).then(({code}) => {
  221. if (code === 0) {
  222. ElMessage.success("分配成功");
  223. startReloadList.value = !startReloadList.value // 触发重新加载列表
  224. activeName.value = "已分配"
  225. handleCancel()
  226. return;
  227. }
  228. })
  229. };
  230. // 图例
  231. const legendArr = ref([]);
  232. const organId = ref("");
  233. const regionId = ref(null);
  234. eventBus.off("changePointLegend", toggleLegend);
  235. eventBus.on("changePointLegend", toggleLegend);
  236. function toggleLegend({ colorObj }) {
  237. legendArr.value = colorObj?.list;
  238. }
  239. //区域切换监听事件
  240. function areaId({ areaId, farmId }) {
  241. organId.value = farmId;
  242. regionId.value = areaId;
  243. // 树点位
  244. getPointList();
  245. // 分区
  246. if (!regionData.value.length) {
  247. // 全部区域
  248. getBlueRegionList();
  249. }
  250. }
  251. const urls = ref([]);
  252. const urlsIndex = ref(0);
  253. const currentComponent = ref("leftTabs");
  254. eventBus.on("clickMapPoint", toggleLeftComponet);
  255. function toggleLeftComponet() {
  256. currentComponent.value = "treeDetail";
  257. }
  258. function backHome() {
  259. currentComponent.value = "leftTabs";
  260. mapRef.value.resetCurrentTree();
  261. }
  262. function setReload() {
  263. startReloadList.value = !startReloadList.value
  264. }
  265. function updateUser() {
  266. activeName.value = "待分配"
  267. setReload()
  268. }
  269. </script>
  270. <style lang="scss" scoped>
  271. .base-container {
  272. width: 100%;
  273. height: 100%;
  274. position: absolute;
  275. box-sizing: border-box;
  276. z-index: 1;
  277. color: #ffffff;
  278. .content {
  279. width: 100%;
  280. height: calc(100% - 36px);
  281. display: flex;
  282. justify-content: space-between;
  283. box-sizing: border-box;
  284. .left,
  285. .right {
  286. width: 376px;
  287. height: calc(100% - 10px);
  288. margin-top: 10px;
  289. box-sizing: border-box;
  290. display: flex;
  291. position: relative;
  292. }
  293. .left {
  294. background: #fff;
  295. border-radius: 8px;
  296. margin-left: 25px;
  297. padding: 0 10px 10px;
  298. .arrow {
  299. position: absolute;
  300. right: -16px;
  301. top: calc(50% - 40px);
  302. background: #fff;
  303. width: 16px;
  304. height: 80px;
  305. line-height: 80px;
  306. border-radius: 0 5px 5px 0;
  307. text-align: center;
  308. cursor: pointer;
  309. }
  310. ::v-deep {
  311. .el-tabs__item {
  312. outline: none;
  313. }
  314. }
  315. }
  316. .right {
  317. width: 376px;
  318. margin-right: 25px;
  319. .adopt-list-wrap {
  320. ::v-deep {
  321. .el-tabs {
  322. height: 100%;
  323. }
  324. .el-tabs__content {
  325. height: calc(100% - 40px - 15px);
  326. position: static;
  327. }
  328. .el-tabs__item {
  329. color: #727272;
  330. width: 100px;
  331. }
  332. .el-tabs__active-bar {
  333. background-color: #2199f8;
  334. height: 1px;
  335. }
  336. .el-tabs__item.is-active {
  337. color: #2199f8;
  338. }
  339. .el-tabs__nav-wrap:after {
  340. height: 1px;
  341. background-color: rgba(127, 127, 127, 0.1);
  342. }
  343. .el-tabs__nav {
  344. left: 50%;
  345. transform: translateX(-50%) !important;
  346. }
  347. .el-tab-pane {
  348. height: 100%;
  349. }
  350. }
  351. .custom-tabs-label {
  352. position: relative;
  353. .count-text {
  354. position: absolute;
  355. top: -7px;
  356. right: -20px;
  357. width: 16px;
  358. height: 16px;
  359. background: #E04C4C;
  360. color: #fff;
  361. text-align: center;
  362. line-height: 16px;
  363. border-radius: 50%;
  364. font-size: 10px;
  365. }
  366. }
  367. }
  368. .list {
  369. width: 100%;
  370. height: 100%;
  371. overflow: auto;
  372. .btn-wrap {
  373. width: 100%;
  374. height: 25px;
  375. line-height: 25px;
  376. margin: 10px 0;
  377. display: flex;
  378. align-items: center;
  379. justify-content: space-between;
  380. div {
  381. width: 48%;
  382. height: 100%;
  383. color: #ffd489;
  384. border: 1px solid rgba(255, 213, 137, 0.6);
  385. border-radius: 2px;
  386. text-align: center;
  387. font-size: 12px;
  388. cursor: pointer;
  389. &.active {
  390. background: #ffd489;
  391. color: #000;
  392. }
  393. }
  394. }
  395. .img-box {
  396. width: 100%;
  397. height: calc(100% - 35px);
  398. overflow: auto;
  399. }
  400. .img-box1 {
  401. width: 100%;
  402. height: calc(100% - 10px);
  403. overflow: auto;
  404. margin-top: 10px;
  405. }
  406. .img-box2 {
  407. width: 100%;
  408. height: calc(100% - 45px);
  409. overflow: auto;
  410. margin-top: 10px;
  411. }
  412. img {
  413. width: 100%;
  414. height: auto;
  415. object-fit: cover;
  416. margin-bottom: 12px;
  417. cursor: pointer;
  418. }
  419. .mt {
  420. margin-top: -12px;
  421. }
  422. }
  423. }
  424. .overflow {
  425. overflow: auto;
  426. }
  427. .legend {
  428. position: fixed;
  429. bottom: 8px;
  430. right: 64px;
  431. height: 20px;
  432. }
  433. .map-bg {
  434. position: fixed;
  435. z-index: 2;
  436. background: rgba(35, 35, 35, 0.8);
  437. border-radius: 18px;
  438. padding: 7px 16px;
  439. left: 460px;
  440. }
  441. .tips {
  442. position: fixed;
  443. z-index: 2;
  444. top: 105px;
  445. width: 100%;
  446. display: flex;
  447. justify-content: center;
  448. .text {
  449. font-size: 20px;
  450. padding: 6px 16px;
  451. font-family: "PangMenZhengDao";
  452. background: rgba(4, 3, 0, 0.6);
  453. border-radius: 20px;
  454. span {
  455. color: #ffd489;
  456. }
  457. }
  458. }
  459. .right-button {
  460. position: fixed;
  461. z-index: 2;
  462. right: 460px;
  463. bottom: 36px;
  464. font-size: 24px;
  465. font-family: "PangMenZhengDao";
  466. padding: 16px 32px 20px 32px;
  467. cursor: pointer;
  468. background: url("@/assets/images/foster-home/btn-bg.png") no-repeat center center / 100% 100%;
  469. }
  470. .center-button {
  471. position: fixed;
  472. z-index: 2;
  473. width: 100%;
  474. display: flex;
  475. align-items: center;
  476. justify-content: center;
  477. bottom: 116px;
  478. div {
  479. cursor: pointer;
  480. width: 248px;
  481. text-align: center;
  482. padding: 15px;
  483. font-family: "PangMenZhengDao";
  484. color: #000;
  485. font-size: 20px;
  486. background: linear-gradient(0deg, #ffd887, #ed9e1e);
  487. border: 2px solid rgba(255, 255, 255, 0.61);
  488. border-radius: 12px;
  489. }
  490. .cancel {
  491. margin-right: 20px;
  492. background: #eeeeee;
  493. }
  494. .disabled {
  495. opacity: 0.6;
  496. cursor: not-allowed;
  497. }
  498. }
  499. .select-text {
  500. padding-left: 4px;
  501. font-size: 16px;
  502. .select-total {
  503. color: rgba(29, 29, 29, 0.5);
  504. }
  505. }
  506. .map-btn {
  507. top: 19px;
  508. cursor: pointer;
  509. }
  510. .map-legend {
  511. bottom: 36px;
  512. display: flex;
  513. align-items: center;
  514. .item {
  515. display: flex;
  516. align-items: center;
  517. font-size: 14px;
  518. img {
  519. width: 16px;
  520. margin-right: 6px;
  521. }
  522. .legend-block {
  523. width: 16px;
  524. height: 16px;
  525. box-sizing: border-box;
  526. border-radius: 50%;
  527. border: 2px solid #fff;
  528. margin-right: 6px;
  529. }
  530. }
  531. .selected-item {
  532. img {
  533. width: 24px;
  534. }
  535. }
  536. .legend-title {
  537. border-bottom: 1px solid rgba(102, 102, 102, 0.35);
  538. }
  539. .item + .item {
  540. padding-left: 12px;
  541. }
  542. }
  543. }
  544. }
  545. .bottom-map {
  546. width: 100%;
  547. height: 100%;
  548. position: absolute;
  549. z-index: 0;
  550. }
  551. .compare-start-btn {
  552. position: absolute;
  553. z-index: 2;
  554. left: 50%;
  555. transform: translateX(-50%);
  556. cursor: pointer;
  557. bottom: 106px;
  558. // right: 445px;
  559. img {
  560. height: 55px;
  561. }
  562. }
  563. </style>
  564. <style lang="less">
  565. .file-wrap {
  566. &.map-file {
  567. width: 367px;
  568. position: relative;
  569. background: url("@/assets/images/home/file-bg.png") no-repeat top center / 100% 100%;
  570. margin-left: 12px;
  571. padding: 12px;
  572. .file-title {
  573. font-size: 20px;
  574. color: #ffd489;
  575. .tag {
  576. border: 1px solid #ffd489;
  577. border-radius: 4px;
  578. font-size: 12px;
  579. display: inline-block;
  580. width: 44px;
  581. height: 20px;
  582. text-align: center;
  583. line-height: 18px;
  584. margin-left: 8px;
  585. padding: 1px 4px;
  586. }
  587. }
  588. .overview-file {
  589. padding-top: 20px;
  590. .box-title {
  591. font-size: 16px;
  592. padding-left: 13px;
  593. margin-bottom: 16px;
  594. position: relative;
  595. display: flex;
  596. justify-content: space-between;
  597. color: #fff;
  598. &::before {
  599. content: "";
  600. position: absolute;
  601. left: 0;
  602. top: 3px;
  603. width: 3px;
  604. height: 16px;
  605. background: #fff;
  606. border-radius: 11px;
  607. }
  608. }
  609. .title {
  610. color: #f3c11d;
  611. font-size: 16px;
  612. font-family: "PangMenZhengDao";
  613. margin-bottom: 20px;
  614. .big {
  615. width: 13px;
  616. height: 13px;
  617. margin: -10px 0 0 4px;
  618. }
  619. .small {
  620. width: 7px;
  621. height: 7px;
  622. margin-left: -3px;
  623. }
  624. }
  625. .base-data {
  626. background: rgba(207, 207, 207, 0.1);
  627. border-radius: 4px;
  628. padding: 6px 0;
  629. display: flex;
  630. .base-item {
  631. flex: 1;
  632. text-align: center;
  633. .label {
  634. font-size: 12px;
  635. color: #666666;
  636. }
  637. .value {
  638. padding-top: 2px;
  639. font-size: 16px;
  640. color: #ffffff;
  641. }
  642. }
  643. .base-item + .base-item {
  644. border-left: 1px solid rgba(102, 102, 102, 0.42);
  645. }
  646. }
  647. .list {
  648. margin-top: 15px;
  649. width: max-content;
  650. font-size: 14px;
  651. .list-item {
  652. color: #bbbbbb;
  653. display: flex;
  654. margin-bottom: 8px;
  655. .list-name {
  656. color: #f3c11d;
  657. margin-right: 6px;
  658. img {
  659. width: 17px;
  660. height: 13px;
  661. }
  662. }
  663. }
  664. }
  665. }
  666. .overview-file + .overview-file {
  667. margin-top: 8px;
  668. }
  669. .box-wrap {
  670. display: flex;
  671. .box-item {
  672. // flex: 1;
  673. min-width: 109px;
  674. display: flex;
  675. flex-direction: column;
  676. justify-content: center;
  677. align-items: center;
  678. padding: 6px;
  679. box-sizing: border-box;
  680. background: rgba(207, 207, 207, 0.1);
  681. border-radius: 4px;
  682. border: 1px solid rgba(207, 207, 207, 0.1);
  683. cursor: pointer;
  684. .item-name {
  685. font-size: 12px;
  686. color: #666666;
  687. width: max-content;
  688. }
  689. .item-val {
  690. font-size: 18px;
  691. color: #fff;
  692. width: max-content;
  693. padding-top: 3px;
  694. }
  695. &.active {
  696. background: rgba(255, 212, 137, 0.16);
  697. border: 1px solid #ffd489;
  698. .item-name {
  699. color: #bbbbbb;
  700. }
  701. }
  702. }
  703. .box-item + .box-item {
  704. margin-left: 8px;
  705. }
  706. }
  707. }
  708. }
  709. </style>