house.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. <template>
  2. <view class="house-detail">
  3. <view class="house-swiper">
  4. <swiper class="swiper" circular :indicator-dots="true">
  5. <swiper-item v-for="item in responsibility" :key="item.id">
  6. <view class="swiper-item-box">
  7. <image class="image" :src="item.url" mode="aspectFill">
  8. </image>
  9. <view class="open-video-icon" v-if="item.fileType === 'video'" @click="openVideo(item)">
  10. <uni-icons type="icon-bofang" custom-prefix="iconfont" color="rgba(255, 255, 255, 0.6)"
  11. size="26">
  12. </uni-icons>
  13. </view>
  14. </view>
  15. </swiper-item>
  16. </swiper>
  17. <uni-icons class="btn" @click="roomCollection" :type="detail.roomCollection ? 'heart-filled' : 'heart'"
  18. color="#ff4d4f" size="28">
  19. </uni-icons>
  20. </view>
  21. <view class="house-title">
  22. <view class="house-name">{{detail.name}}</view>
  23. <view class="tag">
  24. <text style="margin-right: 6rpx;">{{detail.area}}m²</text>
  25. <text v-for="(tag,index) in detail.tagList" :key="index"> | {{tag.name}}</text>
  26. </view>
  27. <view class="pirce">
  28. <text class="number">{{detail.price}}</text>
  29. <text class="label">/月</text>
  30. </view>
  31. <view class="project-label">距离您{{detail.distance || '-'}}km</view>
  32. <button type="default" class="wx-icon" open-type="share">
  33. <uni-icons type="weixin" open-type="share" size="38" color="#43b156">
  34. </uni-icons>
  35. </button>
  36. </view>
  37. <view class="house-label house-article">{{detail.comment}}</view>
  38. <view class="house-content">
  39. <view class="content-title">基本信息</view>
  40. <view class="information-list">
  41. <view class="information-box">
  42. <text class="information-title">所属楼宇:</text>
  43. <text class="information-text">{{detail.projectItemName}}</text>
  44. </view>
  45. <view class="information-box">
  46. <text class="information-title">所属楼层:</text>
  47. <text class="information-text">{{detail.projectItemTargetName}}</text>
  48. </view>
  49. <view class="information-box">
  50. <text class="information-title">房源类型:</text>
  51. <text class="information-text">{{$field.findTypeName('houseType',detail.roomTypeId)}}</text>
  52. </view>
  53. <view class="information-box">
  54. <text class="information-title">房间号码:</text>
  55. <text class="information-text">{{detail.roomNumber}}</text>
  56. </view>
  57. <view class="information-box">
  58. <text class="information-title">房源面积:</text>
  59. <text class="information-text">{{detail.area}}</text>
  60. </view>
  61. <view class="information-box">
  62. <text class="information-title">付款方式:</text>
  63. <text class="information-text">{{$field.findTypeName('payWay',detail.payWay)}}</text>
  64. </view>
  65. <view class="information-box" style="margin-bottom: 0;">
  66. <text class="information-title">产权证书:</text>
  67. <text class="information-text">{{detail.propertyCertificateNumber}}</text>
  68. </view>
  69. <view class="information-box" style="margin-bottom: 0;">
  70. <text class="information-title">是否装修:</text>
  71. <text class="information-text">{{detail.decoration === 1 ? '已装修':'未装修'}}</text>
  72. </view>
  73. </view>
  74. <view class="content-title">房源简介</view>
  75. <view class="desc-text" v-html="detail.introduce"></view>
  76. <view class="people-tell">
  77. <image class="people-avatar" :src="detail.chargePersonPortrait" mode="aspectFill"
  78. @click="$navigateTo('/pages/person/person?userId='+detail.chargePersonId)"></image>
  79. <view class="people-text" @click="$navigateTo('/pages/person/person?userId='+detail.chargePersonId)">
  80. <view class="people-name">{{detail.chargePersonName}}</view>
  81. <view class="people-organization">
  82. 组织名称
  83. </view>
  84. </view>
  85. <view class="people-icon" @click="tell">
  86. <uni-icons type="phone-filled" color="#fff"></uni-icons>
  87. </view>
  88. </view>
  89. <view class="content-title">地理位置</view>
  90. <view class="content-map">
  91. <map id="map" class="map" :show-location="true" :latitude="latitude" :longitude="longitude"></map>
  92. </view>
  93. <view class="content-title">配套设施</view>
  94. <view class="content-device">
  95. <view class="device-item"
  96. v-for="item in $field.findTypeNameByList('supportingFacilities',detail.supportingFacilities)"
  97. :key="item.id">
  98. <uni-icons class="device-icon" custom-prefix="iconfont" :type="item.icon" size="18"></uni-icons>
  99. <text class="device-label">{{item.name}}</text>
  100. </view>
  101. </view>
  102. <view class="content-title">
  103. <text>访客记录</text>
  104. <text class="more" @click="$navigateTo('/pages/visitor/visitor?houseId=' + houseId)">查看更多>></text>
  105. </view>
  106. <view class="visitor">
  107. <view class="visitor-item" v-for="(item,index) in visitorList" :key="index">
  108. <image class="visitor-avatar" :src="item.userPortrait" mode="aspectFill">
  109. </image>
  110. <text class="visitor-label">{{item.userName}}访问了房源</text>
  111. <uni-dateformat class="visitor-time" :date="item.date" :threshold="[60000,3600000 * 24 * 365]">
  112. </uni-dateformat>
  113. </view>
  114. </view>
  115. <view class="content-title">
  116. <text>房源评价</text>
  117. </view>
  118. <view class="evaluate">
  119. <hb-comment ref="hbComment" @add="addEvaluate" @del="del" @like="like" :deleteTip="'确认删除?'"
  120. :cmData="commentData" v-if="commentData">
  121. </hb-comment>
  122. </view>
  123. </view>
  124. <view class="hui-button-box">
  125. <view class="hui-button hui-button-light"
  126. @click="$navigateTo('/pages/reservation/reservation?houseId=' + houseId)">
  127. <uni-icons class="hui-button-icon" type="calendar" color="#08979c" size="22"></uni-icons> 预约看房
  128. </view>
  129. <view class="hui-button"
  130. @click="$navigateTo('/pages/chat/chat?userId='+detail.chargePersonId+'&userName='+detail.chargePersonName)">
  131. <uni-icons class="hui-button-icon" type="chat" color="#fff" size="22"></uni-icons>
  132. 在线联系
  133. </view>
  134. </view>
  135. <uni-popup ref="popup" type="bottom" mask-background-color="rgba(0,0,0,0.8)">
  136. <video id="myVideo" v-if="videoUrl" :src="videoUrl" controls autoplay>
  137. </video>
  138. </uni-popup>
  139. </view>
  140. </template>
  141. <script>
  142. import {
  143. dayjs
  144. } from '../../uni_modules/iRainna-dayjs/js_sdk/dayjs.min';
  145. import {
  146. getHouseDetailById,
  147. insertVisitor,
  148. getVisitorByQuery,
  149. collection,
  150. disableCollection,
  151. getHouseEvaluateListByPage,
  152. insertEvaluate
  153. } from '@/request/api/house.js'
  154. export default {
  155. data() {
  156. return {
  157. detail: {
  158. area: 0
  159. },
  160. houseId: 15,
  161. responsibility: [],
  162. coordinates: [],
  163. latitude: 39.90923,
  164. longitude: 116.397428,
  165. mapContext: {},
  166. visitorList: [],
  167. startTime: '',
  168. commentData: {
  169. readNumer: 0,
  170. commentSize: 0,
  171. comment: []
  172. },
  173. shareButton: false,
  174. coordinates: '',
  175. videoUrl: ''
  176. }
  177. },
  178. onLoad(body) {
  179. if (body.houseId) this.houseId = body.houseId;
  180. this.coordinates = this.$store.getters.coordinates;
  181. this.startTime = new Date().getTime();
  182. },
  183. onReady() {
  184. this.mapContext = uni.createMapContext("map", this);
  185. this.init();
  186. },
  187. onUnload() {
  188. let endTime = new Date().getTime();
  189. let time = endTime - this.startTime;
  190. if (time > 10000) this.createVisitor();
  191. },
  192. onShareAppMessage(res) {
  193. return {
  194. title: this.detail.name || '',
  195. }
  196. },
  197. methods: {
  198. init() {
  199. getHouseDetailById(this.houseId + '?coordinates=' + this.coordinates).then(res => {
  200. if (res.code === 200) {
  201. this.detail = res.data;
  202. let imageBox = [],
  203. videoBox = [];
  204. if (this.detail.picture) {
  205. imageBox = JSON.parse(this.detail.picture).map(node => {
  206. node['fileType'] = 'image';
  207. return node;
  208. });
  209. }
  210. if (this.detail.video) {
  211. videoBox = JSON.parse(this.detail.video).map(node => {
  212. node['fileType'] = 'video';
  213. node['videoUrl'] = node.url;
  214. node['url'] = node.url + '?x-oss-process=video/snapshot,t_0,f_jpg'
  215. return node;
  216. });
  217. }
  218. this.responsibility = videoBox.concat(imageBox);
  219. if (this.detail.coordinates) {
  220. this.coordinates = this.detail.coordinates.split(',');
  221. this.latitude = this.coordinates[1];
  222. this.longitude = this.coordinates[0];
  223. this.addMarkers();
  224. }
  225. this.getVisitor();
  226. this.getHouseEvaluate();
  227. }
  228. })
  229. },
  230. getHouseEvaluate() {
  231. getHouseEvaluateListByPage({
  232. currPage: 1,
  233. pageSize: 100,
  234. projectItemTargetRoomId: this.houseId
  235. }).then(res => {
  236. if (res.code == 200) {
  237. this.commentData.commentSize = res.data.totalCount;
  238. this.commentData.comment = this.getTree(res.data.dataList.map(node => {
  239. return {
  240. "id": node.id, // 唯一主键
  241. "owner": true, // 是否是拥有者,为true则可以删除,管理员全部为true
  242. "hasLike": false, // 是否点赞
  243. "likeNum": 2, // 点赞数量
  244. "avatarUrl": node.userPortrait, // 评论者头像地址
  245. "nickName": node.userName, // 评论者昵称,昵称过长请在后端截断
  246. "content": node.content, // 评论内容
  247. "parentId": node.parentId, // 所属评论的唯一主键
  248. "createTime": node.date // 创建时间
  249. }
  250. }))
  251. }
  252. })
  253. },
  254. openVideo(item) {
  255. this.videoUrl = item.videoUrl;
  256. this.$refs.popup.open('center');
  257. },
  258. getTree(data) {
  259. let result = [];
  260. let map = {};
  261. data.forEach(item => {
  262. map[item.id] = item;
  263. });
  264. data.forEach(item => {
  265. let parent = map[item.parentId];
  266. if (parent) {
  267. (parent.children || (parent.children = [])).push(item);
  268. } else {
  269. result.push(item);
  270. }
  271. });
  272. return result;
  273. },
  274. addEvaluate(data) {
  275. insertEvaluate({
  276. userId: this.$store.getters.user.userId,
  277. projectItemTargetRoomId: this.houseId,
  278. content: data.content,
  279. parentId: data.pId || 0
  280. }).then(res => {
  281. if (res.code == 200) {
  282. this.$toast('评价成功');
  283. this.getHouseEvaluate();
  284. this.$refs.hbComment.addComplete();
  285. }
  286. })
  287. },
  288. mp4Preview() {
  289. },
  290. del() {
  291. },
  292. like() {
  293. },
  294. tell() {
  295. uni.makePhoneCall({
  296. phoneNumber: '17601274604' //仅为示例
  297. });
  298. },
  299. roomCollection() {
  300. this.detail.roomCollection ? disableCollection(this.houseId).then(res => this.successCollection(res,
  301. '取消收藏')) : collection(this.houseId).then(res => this.successCollection(res, '收藏成功'));
  302. },
  303. successCollection(res, msg) {
  304. if (res.code === 200) {
  305. this.init();
  306. this.$toast(msg);
  307. }
  308. },
  309. addMarkers() {
  310. const positions = [{
  311. latitude: this.coordinates[1],
  312. longitude: this.coordinates[0],
  313. }]
  314. const markers = []
  315. positions.forEach((p, i) => {
  316. markers.push(
  317. Object.assign({}, {
  318. id: i + 1,
  319. width: 50,
  320. height: 50,
  321. joinCluster: true, // 指定了该参数才会参与聚合
  322. }, p)
  323. )
  324. })
  325. this.mapContext.addMarkers({
  326. markers,
  327. clear: false,
  328. complete(res) {}
  329. })
  330. },
  331. getVisitor() {
  332. getVisitorByQuery({
  333. currPage: 1,
  334. pageSize: 2,
  335. projectItemTargetRoomId: this.houseId
  336. }).then(res => {
  337. if (res.code === 200) {
  338. this.visitorList = res.data.dataList;
  339. }
  340. })
  341. },
  342. createVisitor() {
  343. insertVisitor({
  344. userId: this.$store.getters.user.userId,
  345. projectItemTargetRoomId: this.houseId
  346. })
  347. },
  348. share() {
  349. //根据id获取
  350. uni.createSelectorQuery().select('#shareButton').node().exec(res => {
  351. //res[0].node未获取到的指定的dom元素对象
  352. console.log("res", res[0].node)
  353. })
  354. }
  355. },
  356. }
  357. </script>
  358. <style lang="scss">
  359. .house-detail {
  360. background: #fff;
  361. padding-bottom: 160rpx;
  362. .house-swiper {
  363. position: relative;
  364. .btn {
  365. position: absolute;
  366. top: 20rpx;
  367. right: 30rpx;
  368. }
  369. }
  370. .swiper {
  371. height: 400rpx;
  372. .image,
  373. .swiper-item-box {
  374. width: 100%;
  375. height: 100%;
  376. position: relative;
  377. }
  378. .open-video-icon {
  379. position: absolute;
  380. top: 50%;
  381. left: 50%;
  382. width: 80rpx;
  383. height: 80rpx;
  384. border-radius: 80rpx;
  385. background: rgba(0, 0, 0, 0.6);
  386. display: flex;
  387. align-items: center;
  388. justify-content: center;
  389. margin-left: -40rpx;
  390. margin-top: -40rpx;
  391. padding-left: 6rpx;
  392. box-sizing: border-box;
  393. }
  394. }
  395. .house-label {
  396. font-size: 24rpx;
  397. color: $uni-secondary-color;
  398. font-weight: 300;
  399. }
  400. .house-article {
  401. font-size: 28rpx;
  402. padding: 0 30rpx;
  403. }
  404. .house-title {
  405. padding: 40rpx 130rpx 30rpx 30rpx;
  406. position: relative;
  407. .house-name {
  408. font-size: 36rpx;
  409. font-weight: 500;
  410. }
  411. .project-label {
  412. font-size: 24rpx;
  413. color: $uni-secondary-color;
  414. font-weight: 300;
  415. position: absolute;
  416. bottom: 50rpx;
  417. right: 30rpx;
  418. }
  419. .tag {
  420. font-size: 24rpx;
  421. font-weight: 300;
  422. color: $uni-secondary-color;
  423. padding: 12rpx 0;
  424. }
  425. .pirce {
  426. color: $uni-primary;
  427. .number {
  428. font-weight: 600;
  429. font-size: 48rpx;
  430. }
  431. .label {
  432. font-size: 24rpx;
  433. }
  434. }
  435. .wx-icon {
  436. position: absolute;
  437. top: 0;
  438. right: 30rpx;
  439. transform: translateY(50%);
  440. line-height: 1;
  441. background: transparent;
  442. &::after {
  443. display: none;
  444. }
  445. }
  446. }
  447. .content-map {
  448. width: 100%;
  449. height: 360rpx;
  450. border-radius: 16rpx;
  451. overflow: hidden;
  452. display: flex;
  453. .map {
  454. flex: 1;
  455. height: 100%;
  456. border-radius: 16rpx;
  457. }
  458. }
  459. .house-content {
  460. padding: 0 30rpx;
  461. box-sizing: border-box;
  462. .content-title {
  463. font-size: 32rpx;
  464. font-weight: 500;
  465. margin: 30rpx 0;
  466. display: flex;
  467. align-items: center;
  468. justify-content: space-between;
  469. .more {
  470. color: $uni-secondary-color;
  471. font-weight: 300;
  472. font-size: 24rpx;
  473. }
  474. }
  475. .people-tell {
  476. margin: 40rpx 0;
  477. height: 140rpx;
  478. border-radius: 20rpx;
  479. box-shadow: 0px 1px 12px rgba(3, 3, 3, 0.08);
  480. display: flex;
  481. align-items: center;
  482. padding: 0 30rpx;
  483. .people-avatar {
  484. width: 90rpx;
  485. height: 90rpx;
  486. border-radius: 50%;
  487. margin-right: 30rpx;
  488. }
  489. .people-text {
  490. flex: 1;
  491. width: 0;
  492. overflow: hidden;
  493. }
  494. .people-name {
  495. font-size: 16px;
  496. font-weight: 500;
  497. margin-bottom: 8rpx;
  498. }
  499. .people-organization {
  500. font-weight: 300;
  501. font-size: 24rpx;
  502. color: $uni-secondary-color;
  503. }
  504. .people-icon {
  505. width: 60rpx;
  506. height: 60rpx;
  507. border-radius: 50%;
  508. background: $uni-primary;
  509. text-align: center;
  510. line-height: 60rpx;
  511. }
  512. }
  513. .visitor {
  514. .visitor-item {
  515. margin-bottom: 20rpx;
  516. display: flex;
  517. align-items: center;
  518. font-weight: 300;
  519. .visitor-avatar {
  520. width: 80rpx;
  521. height: 80rpx;
  522. border-radius: 80rpx;
  523. }
  524. .visitor-label {
  525. flex: 1;
  526. width: 0;
  527. overflow: hidden;
  528. white-space: nowrap;
  529. text-overflow: ellipsis;
  530. margin: 0 20rpx;
  531. }
  532. .visitor-time {
  533. font-size: 24rpx;
  534. color: $uni-secondary-color;
  535. }
  536. &:last-child {
  537. margin-bottom: 0;
  538. }
  539. }
  540. }
  541. .desc-text {
  542. font-weight: 300;
  543. }
  544. .information-list {
  545. display: flex;
  546. flex-wrap: wrap;
  547. .information-box {
  548. width: 50%;
  549. display: flex;
  550. margin-bottom: 30rpx;
  551. }
  552. .information-title {
  553. color: $uni-secondary-color;
  554. font-weight: 500;
  555. }
  556. .information-text {
  557. flex: 1;
  558. width: 0;
  559. overflow: hidden;
  560. white-space: nowrap;
  561. font-weight: 300;
  562. }
  563. }
  564. .content-device {
  565. display: flex;
  566. flex-wrap: wrap;
  567. .device-item {
  568. width: 50%;
  569. display: flex;
  570. align-items: center;
  571. margin-bottom: 20rpx;
  572. .device-label {
  573. flex: 1;
  574. width: 0;
  575. color: $uni-secondary-color;
  576. font-weight: 200;
  577. margin-left: 10rpx;
  578. }
  579. .device-icon {
  580. color: $uni-base-color;
  581. }
  582. }
  583. }
  584. }
  585. .uni-popup__wrapper {
  586. width: 100%;
  587. height: 600rpx;
  588. #myVideo {
  589. width: 100%;
  590. height: 100%;
  591. }
  592. }
  593. }
  594. </style>