index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <template>
  2. <ax-body hide-indicator-area :style="[StyleSheet]">
  3. <!-- 标题栏 -->
  4. <template #title>
  5. <view class="titlebar">
  6. <image src="@/static/img/title.png" class="page-title"></image>
  7. <!-- <text class="page-subtitle">“特惠充电享不停”</text> -->
  8. </view>
  9. </template>
  10. <view class="base">
  11. <!-- 搜索块 -->
  12. <view id="search" class="app-flex search-view">
  13. <view class="locate-city">
  14. <image src="@/static/img/locate.svg" class="_icon"></image>
  15. <text class="__name">{{city.data[city.index].text}}</text>
  16. </view>
  17. <label class="search-bar" @click="$app.url.goto('/pages/search/search')">
  18. <input placeholder-class="app-placeholder" placeholder="输入目的地/电站名" />
  19. <image src="@/static/img/search.svg" class="_icon-search"></image>
  20. </label>
  21. </view>
  22. </view>
  23. <!-- 主滚动 -->
  24. <view class="main-scroll-wrap">
  25. <scroll-view class="root-scroll app-hide-scrollbar" @scrolltolower="scrollLock=true" @scrolltoupper="scrollLock=false" scroll-y>
  26. <view class="contet-root">
  27. <!-- 内容顶部 -->
  28. <view id="roller" class="base">
  29. <!-- 快捷栏 -->
  30. <view class="shortcut-bar">
  31. <view class="_item" @click="$app.url.goto('/pages/order/order')">
  32. <image src="@/static/img/shortcut1.png" class="_icon"></image>
  33. <view class="_name">订单中心</view>
  34. </view>
  35. <view class="_item" @click="$app.url.goto('/pages/coupon-buy/coupon-buy')">
  36. <image src="@/static/img/shortcut2.png" class="_icon"></image>
  37. <view class="_name">购充电券</view>
  38. </view>
  39. <view class="_item" @click="customerService()">
  40. <image src="@/static/img/shortcut3.png" class="_icon"></image>
  41. <view class="_name">在线客服</view>
  42. </view>
  43. <view class="_item" @click="$app.url.goto('/pages/feedback/feedback')">
  44. <image src="@/static/img/shortcut4.png" class="_icon"></image>
  45. <view class="_name">意见反馈</view>
  46. </view>
  47. </view>
  48. <!-- 版头广告 -->
  49. <swiper class="banner" v-if="banners.length>0" autoplay="true" >
  50. <swiper-item v-for="(item,index) in banners" :key="index">
  51. <view class="swiper-item">
  52. <image @load="bannerLoadCompleted()" :src="showImg(item.pic)" class="swiper-item-image" mode="widthFix"></image>
  53. </view>
  54. </swiper-item>
  55. </swiper>
  56. </view>
  57. <view id="fixed" class="base">
  58. <!-- 选项条 -->
  59. <view class="app-flex options-bar">
  60. <view class="app-flex middle option-wrap">
  61. <view v-for="(item,index) in sorts.data" :key="index" @click="changeSort(index)"
  62. class="option-item" :class="{active:sorts.index==index}">{{item.name}}</view>
  63. </view>
  64. <view class="app-flex c-center separ"></view>
  65. <view @click="$app.url.goto('/pages/map/map',false)" class="app-flex middle map-mode">
  66. <image src="@/static/img/map.svg" class="_icon"></image>
  67. <text>地图模式</text>
  68. </view>
  69. </view>
  70. </view>
  71. <!-- 电站列表 -->
  72. <view id="list-box" class="list-scroll-wrap">
  73. <scroll-view class="list-scroll" :scroll-y="scrollLock">
  74. <view class="list">
  75. <view v-for="(item,index) in list.data" :key="index" @click="gotoSiteDetail(item)" class="item">
  76. <view class="contet">
  77. <view class="name">
  78. <view class="icon"><image src="@/static/img/plug.svg" mode="widthFix"></image></view>
  79. <view class="txt">{{item.name}}</view>
  80. </view>
  81. <view class="parkade">
  82. <view class="icon"><image src="@/static/img/parkade.svg" mode="widthFix"></image></view>
  83. <!-- <view class="txt"><rich-text :nodes="item.parkTips"></rich-text></view> -->
  84. <view class="txt">充电减免2小时停车费,超出时长部分计时收费</view>
  85. </view>
  86. <view class="app-flex c-between info">
  87. <view class="app-flex middle">
  88. <view class="charge"><text class="icon">快</text><text class="value">{{item.params.emptyFast}}</text><text class="max">{{item.params.totalFast}}</text></view>
  89. <view class="charge"><text class="icon blue">慢</text><text class="value">{{item.params.emptySlow}}</text><text class="max">{{item.params.totalSlow}}</text></view>
  90. </view>
  91. <view class="discount" v-if="discountInfo">
  92. <view>
  93. {{discountInfo.temp2}}
  94. </view>
  95. </view>
  96. <view class="distance">
  97. <view class="icon"><image src="@/static/img/distance.svg" mode="widthFix"></image></view>
  98. <text>{{item.params.rangeShow}}</text>
  99. </view>
  100. </view>
  101. </view>
  102. <view class="price">
  103. <view class="app-flex middle" style="color: #FF5D50;">
  104. <text class="value">{{item.params.nowPrice?item.params.nowPrice.toFixed(4):"0.0000"}}</text>
  105. <text class="unit">元/度</text>
  106. </view>
  107. <view>{{item.params.priceShow}}</view>
  108. </view>
  109. </view>
  110. </view>
  111. </scroll-view>
  112. </view>
  113. <!-- <view style="height: 158px;"></view> -->
  114. </view>
  115. </scroll-view>
  116. </view>
  117. <app-navigation id="app-navigation" active="home"></app-navigation>
  118. </ax-body>
  119. </template>
  120. <script>
  121. var bmap = require('static/js/bmap-wx.js');
  122. export default {
  123. async onLoad(options) {
  124. const permit = await this.queryPermit();
  125. if(permit.privacy){
  126. // 没有通过隐私协议
  127. this.privacy.visible = true;
  128. }else{
  129. // 已通过隐私协议
  130. if(permit.location===true){
  131. // 可以调用定位能力
  132. this.updateLocation();
  133. }else if(permit.location===false){
  134. // 通过了隐私协议,但是定位被拒绝
  135. this.location.visible = true;
  136. this.updateLocation();
  137. }else if(permit.location===undefined){
  138. // 没有申请过定位能力
  139. this.updateLocation();
  140. }
  141. }
  142. if (options.hasOwnProperty('q') && options.q) {
  143. // 通过下面这步解码,可以拿到url的值
  144. const url = decodeURIComponent(options.q)
  145. // 对url中携带的参数提取处理
  146. console.log("url:"+url)
  147. var device_no = this.getQueryParams(url,"connectorCode")
  148. console.log("device_no:"+device_no)
  149. if(device_no){
  150. this.getDeviceInfo(device_no)
  151. }
  152. }
  153. },
  154. mounted() {
  155. this.setListHeight();
  156. this.setAppNavigationHeight();
  157. },
  158. data() {
  159. return {
  160. // 导航栏高度
  161. appNavigationHeight: 0,
  162. // 页面滚动锁
  163. scrollLock: true,
  164. sorts:{
  165. index: 0,
  166. data: [{name:'离我最近',code:"range"},{name:'空闲最多',code:"device"},{name:'电费最低',code:"price"}]
  167. },
  168. list:{
  169. height: 0,
  170. data: []
  171. },
  172. banners:[],
  173. location:{
  174. visible: false,
  175. value: '',
  176. },
  177. privacy:{
  178. visible: false,
  179. },
  180. city:{
  181. index: 0,
  182. data:[
  183. {text:'贵阳',areaCode:"5201"},
  184. {text:'六盘水',areaCode:"5202"},
  185. {text:'遵义',areaCode:"5203"},
  186. {text:'安顺',areaCode:"5204"},
  187. {text:'毕节',areaCode:"5205"},
  188. {text:'铜仁',areaCode:"5206"},
  189. {text:'黔东南',areaCode:"5226"},
  190. {text:'黔南',areaCode:"5227"},
  191. {text:'黔西南',areaCode:"5223"},
  192. ]
  193. },
  194. discountInfo:null
  195. }
  196. },
  197. onShow() {
  198. this.getBanners()
  199. },
  200. computed:{
  201. StyleSheet(){
  202. return {
  203. '--app-navigation-heiht': `${this.appNavigationHeight}px`,
  204. '--list-heiht': `${this.list.height}px`
  205. }
  206. }
  207. },
  208. onShareAppMessage(res) {
  209. if (res.from === 'button') {
  210. // 来自页面内分享按钮
  211. console.log(res.target);
  212. }
  213. return {
  214. title: "用券充天天都享会员价", // 标题
  215. path: "/pages/index/index", // 分享路径
  216. imageUrl: '../../static/img/share.jpg', // 分享图
  217. desc: '用券充天天都享会员价'
  218. };
  219. },
  220. onShareTimeline() {
  221. return {
  222. title: "用券充天天都享会员价", // 标题
  223. path: "/pages/index/index", // 分享路径
  224. imageUrl: '../../static/img/share.jpg'// 分享图
  225. };
  226. },
  227. methods: {
  228. getDeviceInfo(sn){
  229. this.$api.base("post","/chargeApi/checkDevicesBySn",{"sn":sn},{}).then(res=>{
  230. console.log("设备信息:",res)
  231. var item = res.device;
  232. //设备状态 0:离网1:空闲2:占用(未充电)3:占用(充电中)4:占用(预约锁定)255:故障
  233. if(item.deviceStatus == 0 || item.deviceStatus == 255 ){
  234. return;
  235. }
  236. this.$app.url.goto('/pages/terminal/terminal?deviceId='+item.id+"&deviceStatus="+item.deviceStatus);
  237. })
  238. },
  239. getQueryParams(url,key) {
  240. const queryString = url.split('?')[1] || '';
  241. const params = {};
  242. const pairs = queryString.split('&');
  243. pairs.forEach(pair => {
  244. const [key, value] = pair.split('=');
  245. params[decodeURIComponent(key)] = decodeURIComponent(value || '');
  246. });
  247. return params[key];
  248. },
  249. showImg(img){
  250. return this.$config.url.request+img
  251. },
  252. // 打开客服
  253. customerService(){
  254. const cs = this.$config.customerService;
  255. this.$app.act.customerService(cs.id,cs.url).catch(err=>{
  256. console.log(err);
  257. this.$app.popup.alert('客服中心失联啦,请联系管理员!');
  258. });
  259. },
  260. // 查询许可
  261. queryPermit(){
  262. return new Promise((resolve,reject)=>{
  263. const data = {};
  264. const check = ()=>{
  265. if( Object.keys(data).length == 2 ) resolve(data);
  266. }
  267. if(uni.getPrivacySetting){
  268. uni.getPrivacySetting({
  269. success: res => {
  270. data.privacy = res.needAuthorization;
  271. },
  272. complete: () => {
  273. if(typeof data.privacy !='boolean' && typeof data.privacy !='undefined') data.privacy = null;
  274. check();
  275. },
  276. });
  277. }else{
  278. data.privacy = false;
  279. }
  280. uni.getSetting({
  281. success: res => {
  282. data.location = uni.getLocation ? res.authSetting['scope.userLocation'] : undefined;
  283. },
  284. complete: () => {
  285. if(typeof data.location !='boolean' && typeof data.location !='undefined') data.location = null;
  286. check();
  287. },
  288. })
  289. });
  290. },
  291. // 更新位置
  292. updateLocation(){
  293. this.getLocation().then(res=>{
  294. this.location.value = [res.longitude,res.latitude].join(',');
  295. this.getStations(res.longitude,res.latitude)
  296. this.$app.storage.set('USER_LOCATION',this.location.value);
  297. return this.reverseGeocoder([res.latitude,res.longitude].join(','))
  298. });
  299. },
  300. // 获取定位
  301. getLocation(){
  302. return new Promise((resolve,reject)=>{
  303. // this.loading.visible = true;
  304. if(uni.getLocation){
  305. uni.getLocation({
  306. success: res => resolve(res),
  307. fail: err => {
  308. console.log(err)
  309. console.log('定位失败');
  310. // this.location.visible = true;
  311. this.getStations("","")
  312. },
  313. complete: () => {
  314. }
  315. })
  316. }else{
  317. console.log('微信版本太低,无定位接口可用');
  318. resolve({longitude:'',latitude:''});
  319. }
  320. });
  321. },
  322. reverseGeocoder(latlon){
  323. console.log(latlon)
  324. let lat = latlon.split(",")[0]
  325. let lng = latlon.split(",")[1]
  326. let baiduLoc = this.convertGcj02ToBd09(lng,lat)
  327. latlon = baiduLoc.lat+","+baiduLoc.lng
  328. console.log(latlon)
  329. return new Promise((resolve,reject)=>{
  330. var BMap = new bmap.BMapWX({
  331. ak: 'vtQgaPzonb3H4qeUOWGr53ePcNCsmdMj'
  332. });
  333. BMap.regeocoding({
  334. location:latlon,
  335. success:res=>{
  336. let code = res.originalData.result.addressComponent.adcode.substr(0,4)
  337. for (var i = 0; i < this.city.data.length; i++) {
  338. if(this.city.data[i].areaCode==code){
  339. this.city.index = i
  340. break;
  341. }
  342. }
  343. console.log(res)
  344. },
  345. fail:err=>{
  346. console.log(err)
  347. }
  348. })
  349. });
  350. },
  351. getBanners(){
  352. this.$api.base("post","/userApi/getBanners",{},{}).then(res=>{
  353. this.banners = res.banners
  354. })
  355. },
  356. convertBdToTx(lng, lat) {
  357. // 百度坐标系(BD09)转火星坐标系(GCJ-02,即腾讯地图使用的坐标系)
  358. // 这里的转换公式是基于经验公式,可能存在一定的误差
  359. let x_pi = 3.14159265358979324 * 3000.0 / 180.0;
  360. let x = lng - 0.0065;
  361. let y = lat - 0.006;
  362. let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
  363. let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
  364. let lngs = z * Math.cos(theta);
  365. let lats = z * Math.sin(theta);
  366. return { lng: lngs, lat: lats };
  367. },
  368. getStations(lng,lat){
  369. this.$api.base("post","/chargeApi/getStations",{order:this.sorts.index,lat,lng},{}).then(res=>{
  370. res.stationList.forEach(i=>{
  371. var txPoint = this.convertBdToTx(i.lng,i.lat)
  372. i.lng = txPoint.lng
  373. i.lat = txPoint.lat
  374. })
  375. this.list.data = res.stationList
  376. if(res.discountInfo){
  377. this.discountInfo = res.discountInfo
  378. }
  379. })
  380. },
  381. // 设定导航栏高度
  382. setAppNavigationHeight(){
  383. this.$nextTick(()=>{
  384. uni.createSelectorQuery().in(this).select("#app-navigation").boundingClientRect(data=>{
  385. this.appNavigationHeight = data.height;
  386. }).exec();
  387. });
  388. },
  389. // 设置列表高度
  390. setListHeight(){
  391. this.$app.act.selectorQuery(this,"#list-box,#roller",true).then(res=>{
  392. const win = uni.getWindowInfo();
  393. const roller = res.find(i=>i.id=='roller');
  394. const list = res.find(i=>i.id=='list-box');
  395. this.list.height = win.windowHeight - list.top - this.appNavigationHeight + roller.height;
  396. });
  397. },
  398. // 版头加载完成
  399. bannerLoadCompleted(){
  400. this.setListHeight();
  401. },
  402. changeSort(index){
  403. this.sorts.index = index;
  404. switch(index){
  405. case 0:
  406. this.list.data.sort((a,b)=>a.params.range-b.params.range)
  407. break;
  408. case 1:
  409. this.list.data.sort((a,b)=>b.params.totalEmpty-a.params.totalEmpty)
  410. break;
  411. case 2:
  412. this.list.data.sort((a,b)=>a.params.nowPrice-b.params.nowPrice)
  413. break;
  414. }
  415. },
  416. scrolltolower(){
  417. console.log('到底')
  418. },
  419. gotoSiteDetail(item){
  420. this.$app.url.goto('/pages/site/site?item='+JSON.stringify(item));
  421. },
  422. // 确认隐私协议
  423. agreePrivacyAuthorization(){
  424. this.privacy.visible = false;
  425. this.updateLocation();
  426. },
  427. // 打开隐私协议
  428. openPrivacyContract(){
  429. uni.openPrivacyContract();
  430. },
  431. // 拒绝隐私协议
  432. refusePrivacy(){
  433. this.privacy.visible = false;
  434. },
  435. convertGcj02ToBd09(lng, lat) {
  436. const x_pi = 3.14159265358979324 * 3000.0 / 180.0;
  437. const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);
  438. const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);
  439. const bd_lng = z * Math.cos(theta) + 0.0065;
  440. const bd_lat = z * Math.sin(theta) + 0.006;
  441. return {
  442. lng: bd_lng,
  443. lat: bd_lat
  444. };
  445. }
  446. }
  447. }
  448. </script>
  449. <style>
  450. @import url('index.css');
  451. .discount{
  452. flex: 1;
  453. text-align: right;
  454. padding-right: 5px;
  455. }
  456. .discount view{
  457. display: inline-flex;
  458. align-items: center;
  459. height: 22px;
  460. border: 1px solid #ccc;
  461. border-radius: 5px;
  462. font-size: 12px;
  463. color: #F59C79;
  464. padding: 0 7px;
  465. overflow: hidden;
  466. }
  467. </style>