Compare commits

...

16 Commits

  1. 92
      src/components/filter/filter_modal.vue
  2. 109
      src/components/filter/header.vue
  3. 139
      src/components/filter/period_modal.vue
  4. 107
      src/components/filter/stadium_select.vue
  5. 16
      src/components/kv_line.vue
  6. 0
      src/components/kvs_line.vue
  7. 4
      src/components/order_refund/fixed.vue
  8. 47
      src/pages.json
  9. 7
      src/pages/index/index.vue
  10. BIN
      src/static/images/icon/index/tab_13.png
  11. 3
      src/store/index.js
  12. 23
      src/subpackage/common/pages/pay_type_select.vue
  13. 39
      src/subpackage/device/pages/switch_manage/switch_manage.vue
  14. 5
      src/subpackage/order/pages/order_manage/order_manage.vue
  15. 5
      src/subpackage/order/pages/timekeeping/modules/order_list/deposit.vue
  16. 5
      src/subpackage/order/pages/timekeeping/modules/order_list/person.vue
  17. 5
      src/subpackage/order/pages/timekeeping/modules/order_list/site.vue
  18. 16
      src/subpackage/order/pages/timekeeping/order_detail/deposit.vue
  19. 12
      src/subpackage/order/pages/timekeeping/order_detail/person.vue
  20. 12
      src/subpackage/order/pages/timekeeping/order_detail/site.vue
  21. 8
      src/subpackage/order/pages/ym_card/detail/detail.vue
  22. 6
      src/subpackage/party/pages/login/regist.vue
  23. 103
      src/subpackage/shower/components/number_edit.vue
  24. 276
      src/subpackage/shower/components/popup/deduction.vue
  25. 146
      src/subpackage/shower/components/popup/device_deduction.vue
  26. 111
      src/subpackage/shower/components/popup/recharge.vue
  27. 72
      src/subpackage/shower/components/popup/template.vue
  28. 100
      src/subpackage/shower/components/time_range.vue
  29. 17
      src/subpackage/shower/js/api.js
  30. 10
      src/subpackage/shower/js/server.js
  31. 274
      src/subpackage/shower/pages/card/detail.vue
  32. 192
      src/subpackage/shower/pages/card/manage.vue
  33. 172
      src/subpackage/shower/pages/card/use_record.vue
  34. 203
      src/subpackage/shower/pages/device/use_detail.vue
  35. 263
      src/subpackage/shower/pages/device/use_record.vue
  36. 289
      src/subpackage/shower/pages/order/detail.vue
  37. 252
      src/subpackage/shower/pages/order/list.vue
  38. BIN
      src/subpackage/shower/static/images/arrow.png
  39. BIN
      src/subpackage/shower/static/images/calendar.png
  40. BIN
      src/subpackage/shower/static/images/close.png
  41. BIN
      src/subpackage/shower/static/images/locate.png

92
src/components/filter/filter_modal.vue

@ -0,0 +1,92 @@
<template>
<view class="filter-modal" v-show="visibled" @click="hide">
<scroll-view class="fm-container" scroll-y @click.stop="_=>false">
<view class="fc-title">{{ title }}</view>
<view class="fc-list">
<view
class="fl-item"
:class="{ active: curValue === e.value }"
v-for="(e, i) in list"
:key="i"
@click="itemClick(e)"
>{{ e.label }}</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data(){
return {
visibled: false,
title: '',
list: [],
curValue: '',
initOption: {
// success,
}
}
},
methods: {
show(){
this.visibled = true;
},
hide(){
this.visibled = false;
},
init(e){
this.title = e.title;
this.list = e.list;
this.curValue = e.curValue;
this.initOption = e;
this.show();
},
itemClick(e){
this.initOption?.success?.(e);
this.hide();
}
}
}
</script>
<style lang="scss">
.filter-modal{
position: fixed;
left: 0;
top: var(--window-top);
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .5);
z-index: 10;
.fm-container{
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 500upx;
height: 100%;
padding-left: 42upx;
background-color: #fff;
padding-top: 26upx;
padding-left: 42upx;
padding-bottom: 40upx;
.fc-title{
margin-bottom: 58upx;
padding: 50upx 0;
border-bottom: 2upx solid #D8D8D8;
@include flcw(24upx, 34upx, #9c9c9f);
}
.fc-list{
.fl-item{
margin-bottom: 50upx;
@include tHide;
@include flcw(32upx, 44upx, #1a1a1a);
&.active{
color: $mColor;
}
}
}
}
}
</style>

109
src/components/filter/header.vue

@ -0,0 +1,109 @@
<template>
<view class="order-list-header">
<!-- 门店选择 -->
<stadium-select
ref="stadiumSelect"
:visibled="isStadium"
@change:stadium="$emit('change:stadium', $event)"
></stadium-select>
<!-- 时间段 -->
<view class="olh-period">
<view class="op-txt" @click="$emit('click:time')">{{ startTime || '' }} ~ {{ endTime || '' }}</view>
<view class="op-icon" @click="$emit('click:time')"></view>
</view>
<!-- 交易明细&按钮 -->
<view class="olh-bot">
<view class="ob-desc">
<text v-if="isCount">成功交易{{ successCount || 0 }}退款{{ refundCount || 0 }}</text>
</view>
<view class="ob-btns">
<image v-if="isSearch" @click="$emit('click:search')" class="ob-icon" src="/static/images/icon/search.png"></image>
<image @click="$emit('click:filter')" class="ob-icon" src="/static/images/icon/filter.png"></image>
</view>
</view>
</view>
</template>
<script>
import stadiumSelect from './stadium_select.vue';
export default {
components: {
'stadium-select': stadiumSelect,
},
props: {
startTime: {
default: ''
},
endTime: {
default: ''
},
successCount: {
default: 0
},
refundCount: {
default: 0
},
isCount: Boolean,
isSearch: Boolean
},
data(){
return {
isStadium: false
}
},
methods: {
initStadiumSelect({ brand_id, stadium_id }){
this.isStadium = !!brand_id;
if(!brand_id) return;
return this.$refs.stadiumSelect.initStadiums({ brand_id, stadium_id });
},
showPeriodModal(e){
this.$refs.periodModal.show(e);
},
periodConfirm(e){
console.log('periodConfirm:', e)
}
}
}
</script>
<style lang="scss">
.order-list-header{
.olh-period{
padding: 24upx 24upx 0upx;
@include ctf(flex-start);
.op-txt{
@include flcw(32upx, 44upx, #1a1a1a, 500);
@include tHide(1);
}
.op-icon{
flex-shrink: 0;
margin-left: 14upx;
width: 0;
height: 0;
border-left: 10upx solid transparent;
border-right: 10upx solid transparent;
border-top: 10upx solid #1a1a1a;
}
}
.olh-bot{
padding: 16upx 24upx;
@include ctf(space-between);
.ob-desc{
flex-grow: 1;
@include flcw(24upx, 34upx, #9c9c9f);
@include tHide(1);
}
.ob-btns{
flex-shrink: 0;
@include ctf(flex-end);
.ob-icon{
flex-shrink: 0;
margin-left: 40upx;
width: 40upx;
height: 40upx;
}
}
}
}
</style>

139
src/components/filter/period_modal.vue

@ -0,0 +1,139 @@
<template>
<view class="period-modal" v-show="visibled" @click="hide">
<view class="pm-period" @click.stop="()=>false">
<view class="pp-tit">日期范围</view>
<picker class="pp-picker" mode="date" @change="startTimeChange" :value="start">
<view class="pp-time">
<text class="pt-txt">开始时间</text>
<view class="pt-right">
<input class="pr-ipt" placeholder="选择时间" disabled :value="start" />
<image class="pr-img" mode="aspectFit" src="/static/images/icon/arrow_b2.png"></image>
</view>
</view>
</picker>
<picker class="pp-picker" mode="date" @change="endTimeChange" :value="end">
<view class="pp-time">
<text class="pt-txt">截止时间</text>
<view class="pt-right">
<input class="pr-ipt" placeholder="选择时间" disabled :value="end" />
<image class="pr-img" mode="aspectFit" src="/static/images/icon/arrow_b2.png"></image>
</view>
</view>
</picker>
<view class="pl-btn active" @click="confirmPeriod">确定</view>
</view>
</view>
</template>
<script>
import { showNone } from '@/utils/util';
export default {
data(){
return {
start: '',
end: '',
visibled: false,
initOption: {
// success,
}
}
},
methods: {
init(e){
this.start = e.start;
this.end = e.end;
this.initOption = e;
this.show();
},
show(){
this.visibled = true;
},
hide(){
this.visibled = false;
},
startTimeChange(e){
this.start = e.detail.value;
},
endTimeChange(e){
this.end = e.detail.value;
},
confirmPeriod(){
let { start, end, initOption } = this;
if(!start || !end)return showNone('请选择日期范围!');
if(
new Date(start.replace(/\-/g,'/')).getTime()
> new Date(end.replace(/\-/g,'/')).getTime()
)return showNone('请选择合理时间!');
this.hide();
initOption?.success?.({ start, end });
this.$emit('click:cofirm', { start, end });
}
}
}
</script>
<style lang="scss">
.period-modal{
position: fixed;
left: 0;
top: var(--window-top);
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .5);
z-index: 10;
.pm-period{
position: absolute;
left: 0;
top: 0;
padding-left: 38upx;
padding-top: 66upx;
height: 472upx;
width: 100%;
background-color: #fff;
.pp-tit{
@include flcw(24upx, 34upx, #9c9c9f);
}
.pp-picker{
width: 100%;
}
.pp-time{
padding-right: 40upx;
height: 124upx;
border-bottom: 2upx solid #D8D8D8;
@include ctf(space-between);
.pt-txt{
flex-shrink: 0;
@include flcw(32upx, 44upx, #1a1a1a);
}
.pt-right{
flex-grow: 1;
@include ctf(flex-end);
.pr-ipt{
flex-grow: 1;
height: 100%;
text-align: right;
@include flcw(32upx, 44upx, #1a1a1a);
}
.pr-img{
flex-shrink: 0;
margin-left: 36upx;
width: 28upx;
height: 28upx;
}
}
}
.pl-btn{
text-align: center;
@include flcw(32upx, 124upx, rgba(0, 152, 116, .2), 500);
color: rgba(0, 152, 116, .2);
&.active{
color: $mColor;
}
}
}
}
</style>

107
src/components/filter/stadium_select.vue

@ -0,0 +1,107 @@
<template>
<view class="stadium-select" v-show="visibled">
<text class="ss-txt">当前门店</text>
<picker mode="selector" class="ss-picker" :range="stadiumList" range-key="name" @change="pickerChange">
<view class="ss-frame">
<input type="text" disabled class="sf-ipt" :value="curStadium.name || ''" placeholder="选择门店"/>
<image class="sf-img" mode="aspectFit" src="/static/images/icon/arrow_b2.png"></image>
</view>
</picker>
</view>
</template>
<script>
import { API } from '../../js/api';
import { servers } from '../../js/server';
export default {
props: {
visibled: {
type: Boolean,
default: false
}
},
data(){
return {
stadiumList: [
{ id: '', name: '全部' }
],
curStadium: {}
}
},
methods: {
pickerChange(e){
let { value } = e.detail;
let { stadiumList } = this;
this.curStadium = stadiumList[value];
this.$emit('change:stadium', this.curStadium);
},
async initStadiums({ brand_id, stadium_id }){
if(!brand_id) return;
let _ls = await this.getStadiumList(brand_id);
let _curStadium = _ls.find(e=>+e.id === +stadium_id);
if(_curStadium)this.curStadium = _curStadium;
},
//
getStadiumList(brand_id){
return servers.get({
url: API.stadiumList,
data: { brand_id },
isDefaultGet: false
})
.then(res=>{
let _data = res?.data || {};
if(_data.code === 0){
let _list = _data?.data?.list || [];
return this.stadiumList = [ ...this.stadiumList, ..._list ];
}else{
console.warn('getStoreList err:', err);
return this.stadiumList;
}
})
.catch(err=>{
console.warn('getStoreList err:', err);
return this.stadiumList;
})
},
}
}
</script>
<style lang="scss">
.stadium-select{
padding: 0 24upx;
height: 144upx;
background-color: #fff;
@include ctf(space-between);
.ss-txt{
margin-right: 20upx;
flex-shrink: 0;
@include flcw(28upx, 40upx, #9c9c9f);
}
.ss-picker{
flex-grow: 1;
.ss-frame{
padding: 0 20upx;
height: 92upx;
border-radius: 10upx;
background-color: #F2F2F7;
@include ctf(space-between);
.sf-ipt{
flex-grow: 1;
@include flcw(28upx, 40upx, #1a1a1a);
@include tHide(1);
}
.sf-img{
margin-left: 10upx;
flex-shrink: 0;
width: 28upx;
height: 28upx;
transform: rotateZ(90deg);
}
}
}
.ocs-store{
}
}
</style>

16
src/subpackage/order/components/kv_line.vue → src/components/kv_line.vue

@ -1,8 +1,10 @@
<template>
<view class="kv-line">
<view class="kl-name">{{ label || '' }}</view>
<view class="kl-val"><slot name="default">value</slot></view>
<slot name="right"></slot>
<view class="kl-name" :style="labelStyle">{{ label || '' }}</view>
<view class="kl-val"><slot name="default"></slot></view>
<view class="kl-right">
<slot name="right"></slot>
</view>
</view>
</template>
@ -13,6 +15,10 @@ export default {
type: String,
default: ''
},
labelStyle: {
type: String,
default: ''
}
}
}
</script>
@ -28,5 +34,9 @@ export default {
@include flcw(28upx, 48upx, #1A1A1A);
@include tHide;
}
.kl-right{
flex-grow: 1;
flex-shrink: 0;
}
}
</style>

0
src/subpackage/order/components/kvs_line.vue → src/components/kvs_line.vue

4
src/components/order_refund/fixed.vue

@ -33,10 +33,10 @@ export default {
return +refund_amount || 0
},
isShow(){
console.log('brandInfo', this.brandInfo)
let { payAmount, refundAmount, refund_times, brandInfo } = this;
console.log('payAmount:', payAmount, 'refundAmount:', refundAmount, 'refund_times:', refund_times)
if(
payAmount - refundAmount > 0
+payAmount - +refundAmount > 0
&&[0, 1].includes(refund_times)
&&brandInfo?.permission?.['1018'] // 退
){

47
src/pages.json

@ -995,6 +995,53 @@
}
}
]
},
{
"root": "subpackage/shower",
"pages": [
{
"path": "pages/card/manage",
"style" : {
"navigationBarTitleText": "水阀卡管理"
}
},
{
"path": "pages/card/detail",
"style" : {
"navigationBarTitleText": "水阀卡详情"
}
},
{
"path": "pages/card/use_record",
"style" : {
"navigationBarTitleText": "使用记录"
}
},
{
"path": "pages/device/use_record",
"style" : {
"navigationBarTitleText": "水阀使用记录"
}
},
{
"path": "pages/device/use_detail",
"style" : {
"navigationBarTitleText": "水阀使用详情"
}
},
{
"path": "pages/order/list",
"style" : {
"navigationBarTitleText": "浴室订单列表"
}
},
{
"path": "pages/order/detail",
"style" : {
"navigationBarTitleText": "浴室订单详情"
}
}
]
}
],
"globalStyle": {

7
src/pages/index/index.vue

@ -168,6 +168,12 @@
// path: '/subpackage/blacklist/pages/abnormal_list/abnormal_list',
// serverKey: 1015 // 1015
// },
{
id: 13,
name: '水阀卡管理',
path: '/subpackage/shower/pages/card/manage',
serverKey: 1019 // 1019
},
];
@ -236,7 +242,6 @@
if(tabInfo.id === 12){
let url = tabInfo.path + `?appid=${indexData.brand.mini_wechat_appid}`
console.log(222,url)
return util.routeTo(url,'nT');
}

BIN
src/static/images/icon/index/tab_13.png

Before

Width: 52  |  Height: 52  |  Size: 1.2 KiB

After

Width: 138  |  Height: 100  |  Size: 573 B

3
src/store/index.js

@ -46,7 +46,8 @@ export default new Vuex.Store({
'1016': '小程序管理',
'1017': '钱包&提现',
'1018': '退款权限',
'1022': '工单'
'1022': '工单',
'1019': '水阀卡管理',
},
// 场地占用提交页面信息
occupyInfo: {

23
src/subpackage/common/pages/pay_type_select.vue

@ -5,7 +5,7 @@
<image mode="aspectFill" :src="stadiumInfo.logo"></image>
<view>{{ stadiumInfo.name || '-' }}</view>
</view>
<view class="ph-type-text">订场支付金额</view>
<view class="ph-type-text">支付金额</view>
<view class="ph-price"><text>¥</text>{{ payAmount || 0 }}</view>
<view class="ph-discount-tip">
<block v-if="payMethodsID == 3&&selectedCard&&selectedCard.discount&&selectedCard.discount<10">
@ -71,7 +71,7 @@
<script>
import server from '../js/server.js';
import API from '../js/api.js';
import util from '../../../utils/util.js';
import util, { routeTo } from '../../../utils/util.js';
import cardSearch from '../components/card_search';
export default {
components: {
@ -111,7 +111,8 @@ export default {
},
/**
* @param {Object} _options = JSON.parse(decodeURIComponent(option.query))
* @param {String} _options.type 1 -> 客户订场 2-> 散客 3-> 锁场, 4 -> 挂账
* type: 场地面板(1 -> 客户订场 2-> 散客 3-> 锁场, 4 -> 挂账) 水阀卡(5 -> 充值)
* @param {String} _options.type
* @param {String} _options.brand_id
* @param {String} _options.amount
* @param {Object} _options.stadiumInfo
@ -136,9 +137,23 @@ export default {
submitBtn: util.debounce(function(){
let _type = this.optionsQuery?.type;
if(+_type === 4)return this.takeUpBbocPay();
if(_type !== 4)return this.takeUpSubmit();
if([1,2,3].includes(+_type))return this.takeUpSubmit();
if(+_type === 5)return this.rechargeSubmit();
}, 300, true),
//
rechargeSubmit(){
let { payMethodsID, otherTypeRemark, selectedCard, payAmount } = this;
let _data = {
payTypeName: this.getPayMethodsName(payMethodsID) || '',
amount: +payAmount,
remark: otherTypeRemark || '其它'
}
if(payMethodsID === 3)_data['selectedCardNo'] = selectedCard?.card_no || '';
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit('payConfirm', _data);
routeTo();
},
payMethodsChange(e){
if(e.id == 3)return this.$refs.cardSearch.show();
this.payMethodsID = e.id;

39
src/subpackage/device/pages/switch_manage/switch_manage.vue

@ -5,7 +5,6 @@
<view class="sm-tip" v-if="pageInfo.longOpenName"><text>*</text>长开表示不再被系统智能控制只有手动关闭后才会被智能控制请谨慎操作</view>
<view class="sm-list">
<view v-for="(e, i) in deviceList" :key="i" :class="[ 'sl-item', pageInfo.iconNum<=2?'sl-limit':'' ]">
<view class="si-top" >
<image mode="aspectFit" :src="getIcon()" ></image>
<view class="st-right">
@ -60,6 +59,13 @@
</view>
</view>
</view>
<!-- md0726 新增水阀使用记录按钮 -->
<block v-if="pageInfo.id === 11">
<view class="sm-fixed-space"></view>
<view class="sm-fixed">
<view class="sf-btn" @click="toWaterRecord">水阀使用记录</view>
</view>
</block>
</view>
</template>
@ -72,7 +78,7 @@ import deviceServer from '../../js/device_server';
import deviceApi from '../../js/device_api';
import { mapState } from 'vuex';
import util from '../../../../utils/util';
import util, { routeTo } from '../../../../utils/util';
import DEVICE_FUN from '../../js/device_fun.js';
export default {
@ -139,6 +145,13 @@ export default {
}, 1000)
},
methods: {
// 使
toWaterRecord(){
let { curStoreInfo, pageInfo } = this;
console.log(curStoreInfo)
let _qryStr = `brand_id=${curStoreInfo?.brand_id || ''}&stadium_id=${curStoreInfo?.id || ''}`;
routeTo(`/subpackage/shower/pages/device/use_record?${_qryStr}`, 'nT')
},
isDetailBtn(e){
if(e&&e.toString() === "[object Object]"){
return e.hardware_model == 'XiaoHuiXiong' && e.hardware_type === 'Air';
@ -558,12 +571,11 @@ function getShowArr(key){
openName: '开', closeName: '关', hardware_type: 'Router4G',iconNum:1
},
's11': {
id: 11, name: '水阀管理', isOpen: true, isClose: true,isLongOpen:true, isTiming:true,
id: 11, name: '水阀管理', isOpen: true, isClose: true, isTiming:true,
openIcon: '/subpackage/device/static/images/open.png',
closeIcon: '/subpackage/device/static/images/close.png',
longOpenIcon: '/subpackage/device/static/images/longOpen.png',
timingIcon: '/subpackage/device/static/images/timing.png',
openName: '开', closeName: '关', hardware_type: 'WaterValve',longOpenName: '长开', timingName: '定时',iconNum:4,
openName: '开', closeName: '关', hardware_type: 'WaterValve', timingName: '定时',iconNum:4,
},
's12': {
id: 12, name: '互动设备管理', isOpen: true, isClose: true,
@ -774,4 +786,21 @@ function getShowArr(key){
}
}
}
.sm-fixed-space{
padding-bottom: 132upx;
@include isPd(132upx);
}
.sm-fixed{
position: fixed;
bottom: 0;
padding: 10upx 24upx;
width: 100%;
@include isPd(10upx);
.sf-btn{
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(32upx, 112upx, #fff, 500);
}
}
</style>

5
src/subpackage/order/pages/order_manage/order_manage.vue

@ -33,6 +33,11 @@ export default {
tabClick(ele){
let { brandInfo } = this;
if(!this.isFinish(ele.key))return util.showNone('待开放中!');
// 20240910 md0726
if([16].includes(ele.key)){
let _qryStr = `brand_id=${brandInfo?.brand?.id || ''}`;
return util.routeTo(`/subpackage/shower/pages/order/list?${_qryStr}`, 'nT');
}
// 20240813 tid1553
if([22, 23].includes(ele.key)){
let _qryStr = `order_type=${ele.orderType}&brand_id=${brandInfo?.brand?.id || ''}`;

5
src/subpackage/order/pages/timekeeping/modules/order_list/deposit.vue

@ -22,7 +22,7 @@
</template>
<script>
import kvLine from "../../../../components/kv_line.vue";
import kvLine from "@/components/kv_line.vue";
import listItemTemp from "./item_temp.vue";
import { routeTo } from '@/utils/util';
export default {
@ -49,8 +49,9 @@ export default {
.lc-info{
padding-top: 12upx;
.li-icon{
display: block;
margin-left: auto;
flex-shrink: 0;
margin-right: 0;
width: 32upx;
height: 32upx;
}

5
src/subpackage/order/pages/timekeeping/modules/order_list/person.vue

@ -31,7 +31,7 @@
</template>
<script>
import kvLine from "../../../../components/kv_line.vue";
import kvLine from "@/components/kv_line.vue";
import listItemTemp from "./item_temp.vue";
import { routeTo } from '@/utils/util';
export default {
@ -62,8 +62,9 @@ export default {
.lc-info{
padding-top: 12upx;
.li-icon{
display: block;
margin-left: auto;
flex-shrink: 0;
margin-right: 0;
width: 32upx;
height: 32upx;
}

5
src/subpackage/order/pages/timekeeping/modules/order_list/site.vue

@ -34,7 +34,7 @@
</template>
<script>
import kvLine from "../../../../components/kv_line.vue";
import kvLine from "@/components/kv_line.vue";
import listItemTemp from "./item_temp.vue";
import { routeTo } from '@/utils/util';
export default {
@ -69,8 +69,9 @@ export default {
.lc-info{
padding-top: 12upx;
.li-icon{
display: block;
margin-left: auto;
flex-shrink: 0;
margin-right: 0;
width: 32upx;
height: 32upx;
}

16
src/subpackage/order/pages/timekeeping/order_detail/deposit.vue

@ -7,7 +7,7 @@
<kv-line label="订单编号:">
<template v-slot:default>{{ orderInfo.order_no || '-' }}</template>
<template v-slot:right>
<text class="dod-status">{{ orderInfo.status_text || '-' }}</text>
<view class="dod-status">{{ orderInfo.status_text || '-' }}</view>
</template>
</kv-line>
<kv-line label="创建时间:">{{ orderInfo.created_at || '-' }}</kv-line>
@ -38,7 +38,7 @@
<kv-line label="押金订单号:">
<template v-slot:default>{{ link_order.order_no || '-' }}</template>
<template v-slot:right>
<text class="od-check" @click="toTimingOrder">查看计时订单</text>
<view class="od-check" @click="toTimingOrder">查看计时订单</view>
</template>
</kv-line>
<kv-line label="押金金额:">{{ orderInfo.amount || '-' }}</kv-line>
@ -84,8 +84,8 @@
<script>
import headerTemp from "../modules/order_detail/header_temp.vue";
import infoTemp from "../modules/order_detail/info_temp.vue";
import kvLine from "../../../components/kv_line.vue";
import kvsLine from "../../../components/kvs_line.vue";
import kvLine from "@/components/kv_line.vue";
import kvsLine from "@/components/kvs_line.vue";
import bottomFixed from "../modules/order_detail/bottom_fixed.vue";
import refundButton from "@/components/order_refund/permission_btn.vue";
@ -179,8 +179,8 @@ export default {
.deposit-order-detail{
padding: 24upx;
.dod-status{
margin-left: auto;
flex-shrink: 0;
margin-left: 10upx;
text-align: right;
@include flcw(28upx, 40upx, #9C9C9F);
}
@ -203,8 +203,8 @@ export default {
}
}
.od-check{
margin-left: auto;
flex-shrink: 0;
margin-left: 10upx;
text-align: right;
@include flcw(24upx, 40upx, $mColor);
}
}

12
src/subpackage/order/pages/timekeeping/order_detail/person.vue

@ -7,10 +7,10 @@
<kv-line label="订单编号:">
<template v-slot:default>{{ orderInfo.order_no || '-' }}</template>
<template v-slot:right>
<text
<view
class="pod-status"
:class="{ active: [ '计费中', '待支付' ].includes(orderInfo.status_text) }"
>{{ orderInfo.status_text || '-' }}</text>
>{{ orderInfo.status_text || '-' }}</view>
</template>
</kv-line>
<kv-line label="创建时间:">{{ orderInfo.created_at || '-' }}</kv-line>
@ -98,8 +98,8 @@
<script>
import headerTemp from "../modules/order_detail/header_temp.vue";
import infoTemp from "../modules/order_detail/info_temp.vue";
import kvLine from "../../../components/kv_line.vue";
import kvsLine from "../../../components/kvs_line.vue";
import kvLine from "@/components/kv_line.vue";
import kvsLine from "@/components/kvs_line.vue";
import endBillingModal from "../modules/order_detail/end_billing_modal.vue";
import bottomFixed from "../modules/order_detail/bottom_fixed.vue";
@ -232,8 +232,8 @@ export default {
.person-order-detail{
padding: 24upx;
.pod-status{
margin-left: auto;
flex-shrink: 0;
margin-left: 10upx;
text-align: right;
@include flcw(28upx, 40upx, #9C9C9F);
&.active{
color: $mColor;

12
src/subpackage/order/pages/timekeeping/order_detail/site.vue

@ -7,10 +7,10 @@
<kv-line label="订单编号:">
<template v-slot:default>{{ orderInfo.order_no || '-' }}</template>
<template v-slot:right>
<text
<view
class="od-status"
:class="[ '待使用', '使用中', '待支付', '计费中' ].includes(orderInfo.status_text) ? 'active' : ''"
>{{ orderInfo.status_text || '-' }}</text>
>{{ orderInfo.status_text || '-' }}</view>
</template>
</kv-line>
<kv-line label="创建时间:">{{ orderInfo.created_at || '-' }}</kv-line>
@ -172,8 +172,8 @@
*/
import headerTemp from "../modules/order_detail/header_temp.vue";
import infoTemp from "../modules/order_detail/info_temp.vue";
import kvLine from "../../../components/kv_line.vue";
import kvsLine from "../../../components/kvs_line.vue";
import kvLine from "@/components/kv_line.vue";
import kvsLine from "@/components/kvs_line.vue";
import endBillingModal from "../modules/order_detail/end_billing_modal.vue";
import bottomFixed from "../modules/order_detail/bottom_fixed.vue";
import refundButton from "@/components/order_refund/permission_btn.vue";
@ -335,8 +335,8 @@ export default {
.site-order-detail{
padding: 24upx;
.od-status{
margin-left: auto;
flex-shrink: 0;
margin-left: 10upx;
text-align: right;
@include flcw(28upx, 40upx, #9C9C9F);
&.active{
color: $mColor;

8
src/subpackage/order/pages/ym_card/detail/detail.vue

@ -55,6 +55,7 @@
<!-- 退款列表 -->
<view v-for="(e, i) in refundList" :key="i">
<view class="ym-space" v-if="i!=0"></view>
<order-refund-info
:refund_price="e.amount || 0"
:refund_no="e.refund_no || '-'"
@ -66,7 +67,7 @@
</view>
<!-- 这里没有退款金额直接用可退款金额 - 0 也是可以判断按钮显示 -->
<order-refund-fixed
:pay_amount="_extension.refundable_amount || 0"
:refund_amount="0"
@ -139,7 +140,7 @@ export default {
order_no: orderInfo?.order_no ?? '',
mobile: orderInfo?.mobile ?? '',
refundable_amount: +_extension?.refundable_amount || 0,
refundable_integral: 0,
refundable_integral: +_extension?.refundable_integral || 0,
refund_times: refundList?.length || 0,
confirm: e => {
this.userMonthlyCardRefundFixed({
@ -289,6 +290,9 @@ export default {
.ym-card-info{
@include isPd;
}
.ym-space{
padding-top: 24upx;
}
.yci-box{
padding: 30upx;
margin-bottom: 24upx;

6
src/subpackage/party/pages/login/regist.vue

@ -19,6 +19,7 @@
<view class="rb-upload-box b-flex-y b-flex-s">
<view class="rb-upload-title b-flex-x">营业执照<text>*</text></view>
<view class="rb-upload-tip">请上传最新的营业执照图片确保营业执照四个边角齐全文字清晰可见请勿加盖公章</view>
<view class="rb-upload-list b-flex-x">
<view class="rb-upload-item b-flex-y b-flex-c" @click="clickUploadLicenseImg">
<image class="rb-upload-bg" src="../../static/regist/upload_bg.png"
@ -858,7 +859,10 @@
color: $b-color-red;
}
}
.rb-upload-tip{
padding: 24upx 0;
@include flcw(28upx, 40upx, #9C9C9F);
}
.rb-upload-list {
width: 100%;

103
src/subpackage/shower/components/number_edit.vue

@ -0,0 +1,103 @@
<template>
<view class="number-edit">
<view class="rpu-number">
<view class="rn-name"><text class="red">*</text>{{ label || '' }}</view>
<view class="rn-operate">
<view class="rn-btn" @click="number--"></view>
<input type="number" class="rn-num" v-model="number">
<view class="rn-btn" @click="number++"></view>
</view>
<view class="ne-unit">{{ unit || '' }}</view>
</view>
</view>
</template>
<script>
export default {
props: {
label: {
type: String,
default: ''
},
unit: {
type: String,
default: '分钟'
},
value: {
default: 0
},
},
computed: {
number: {
get(){
return this.$props.value;
},
set(val){
this.$emit('input', val);
}
}
},
data(){
return {
}
}
}
</script>
<style lang="scss">
.number-edit{
.rpu-number{
@include ctf(space-between);
.rn-name{
@include flcw(28upx, 40upx, #333333);
.red{
color: #EA5061;
}
}
.rn-operate{
margin-left: auto;
margin-right: 0;
padding: 2upx;
border-radius: 10upx;
background: #E8ECEF;
@include ctf;
.rn-btn{
position: relative;
width: 56upx;
height: 54upx;
&::before, &::after{
content: '';
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: block;
width: 30upx;
height: 4upx;
background: #9A9A9D;
border-radius: 2upx;
}
}
.rn-num{
margin: 0 2upx;
text-align: center;
height: 54upx;
width: 104upx;
background: #fff;
@include flcw(28upx, 40upx, #333);
&+.rn-btn{
&::after{
transform: translate(-50%, -50%) rotate(90deg);
}
}
}
}
.ne-unit{
min-width: 60upx;
margin-left: 28upx;
@include flcw(28upx, 40upx, #333333);
}
}
}
</style>

276
src/subpackage/shower/components/popup/deduction.vue

@ -0,0 +1,276 @@
<template>
<view class="deduction-popup">
<popup-template
title="扣减时长"
:show="isShow"
@click:close="hide"
>
<view class="rp-container">
<kv-line label="水阀卡卡号:">{{ initData.water_card_no || '-' }}</kv-line>
<kv-line label="手机号码:">{{ initData.mobile || '-' }}</kv-line>
<kv-line label="可用时长:"><text class="rc-orange">{{ initData.valid_duration_text || '-' }}</text></kv-line>
<view class="rc-space"></view>
<number-edit label="扣减时长" v-model="editData.deductDuration"></number-edit>
<view class="rc-time-select">
<view class="rts-name">开始日期</view>
<picker mode="date" :value="editData.sDate" @change="sDateChange">
<input disabled type="text" v-model="editData.sDate" class="rts-ipt" placeholder="开始日期">
</picker>
</view>
<view class="rc-time-select">
<view class="rts-name">开始时间</view>
<picker mode="multiSelector" :range="timePickerList" :value="editData.sTime" @change="sTimeChange">
<input disabled type="text" v-model="editData.sTime" class="rts-ipt" placeholder="开始时间">
</picker>
</view>
<view class="rc-time-select">
<view class="rts-name">结束日期</view>
<picker mode="date" :value="editData.eDate" @change="eDateChange">
<input disabled type="text" v-model="editData.eDate" class="rts-ipt" placeholder="结束日期">
</picker>
</view>
<view class="rc-time-select">
<view class="rts-name">结束时间</view>
<picker mode="multiSelector" :range="timePickerList" :value="editData.eTime" @change="eTimeChange">
<input disabled type="text" v-model="editData.eTime" class="rts-ipt" placeholder="结束时间">
</picker>
</view>
<textarea class="rc-remark" placeholder="备注(选填)" v-model="editData.remark"></textarea>
<view class="rc-btn" @click="confirmBtn">确认</view>
</view>
</popup-template>
</view>
</template>
<script>
import popup_template from "./template.vue";
import kv_line from "@/components/kv_line.vue";
import number_edit from "../number_edit.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { formatDate, formatNumber, formatTime, showNone, debounce, showLoad, hideLoad, showModal } from "@/utils/util";
export default {
components: {
'popup-template': popup_template,
'kv-line': kv_line,
'number-edit': number_edit
},
watch: {
'editData.deductDuration': {
handler(nVal, oVal){
this.initDeductTime();
},
deep: true
}
},
data(){
return {
isShow: false,
timePickerList: this.getTimePickerList(),
initData: {
/**
* @param {String} water_card_no
* @param {String} mobile
* @param {String} valid_duration_text
* @param {Number} valid_duration
* @param {Function} success
* */
},
editData: {
deductDuration: 10,
sDate: '',
eDate: '',
sTime: '',
eTime: '',
remark: ''
}
}
},
methods: {
confirmBtn: debounce(function(){
let { deductDuration, sDate, sTime, eDate, eTime, remark } = this.editData;
let { initData } = this;
let _queryObj = {
duration: deductDuration,
start_time: `${sDate} ${sTime}`,
end_time: `${eDate} ${eTime}`,
remark,
water_card_no: initData?.water_card_no || '',
brand_id: initData?.brand_id || '',
stadium_id: initData?.stadium_id || '',
resource_type: 2 // 02
}
// console.log('', _queryObj);
// initData?.success?.();
this.deductionReq(_queryObj);
this.hide();
}, 300, true),
deductionReq(data){
showLoad();
return server.post({
url: SHOWER_API.CardFeeDeduction,
data,
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showModal({
title: '提示',
content: _data.message || '操作成功!'
})
this.initData?.success?.('SUCCESS');
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('deduction deductionReq err --->', err);
})
},
sDateChange(e){
if(!this.isCorrectTimeScope({ sDate: e.detail.value }))return;
this.editData.sDate = e.detail.value;
this.getDeductDurationForTimeChange();
},
sTimeChange(e){
let { timePickerList, editData } = this;
let _timeStr = this.getPickerTimeStr(e.detail.value, timePickerList);
if(!this.isCorrectTimeScope({ sTime: _timeStr }))return;
this.editData.sTime = _timeStr;
this.getDeductDurationForTimeChange();
},
eDateChange(e){
if(!this.isCorrectTimeScope({ eDate: e.detail.value }))return;
this.editData.eDate = e.detail.value;
this.getDeductDurationForTimeChange();
},
eTimeChange(e){
let { timePickerList } = this;
let _timeStr = this.getPickerTimeStr(e.detail.value, timePickerList);
if(!this.isCorrectTimeScope({ eTime: _timeStr }))return;
this.editData.eTime = _timeStr;
this.getDeductDurationForTimeChange();
},
getDeductDurationForTimeChange(){
let { sDate, sTime, eDate, eTime } = this.editData;
let _startStr = `${sDate} ${sTime}`;
let _endStr = `${eDate} ${eTime}`;
let _startStamp = new Date(this.getCalDate(_startStr)).getTime();
let _endStamp = new Date(this.getCalDate(_endStr)).getTime();
this.editData.deductDuration = (_endStamp - _startStamp) / 1000 / 60;
},
isCorrectTimeScope({
sDate = '', sTime = '', eDate = '', eTime = ''
}){
let _sDate = sDate || this.editData.sDate;
let _sTime = sTime || this.editData.sTime;
let _eDate = eDate || this.editData.eDate;
let _eTime = eTime || this.editData.eTime;
let _startStr = `${_sDate} ${_sTime}`;
let _endStr = `${_eDate} ${_eTime}`;
let _startStamp = new Date(this.getCalDate(_startStr)).getTime();
let _endStamp = new Date(this.getCalDate(_endStr)).getTime();
if(_startStamp >= _endStamp){
showNone('开始时间不能大于结束时间!');
return false;
}else{
return true;
}
},
getPickerTimeStr(value = [], pickList = []){
let [ _hourList, _minuteList, _secondList ] = pickList;
let [ _hour, _minute, _second ] = value;
return `${_hourList[_hour]}:${_minuteList[_minute]}:${_secondList[_second]}`;
},
getCalDate(dateStr){
return dateStr.replace(/\-/g, '/') || '';
},
initDeductTime(){
let { deductDuration, sDate, sTime } = this.editData;
let _startStr = `${this.getCalDate(sDate)} ${sTime}`;
let _curDate = _startStr === ' ' ? new Date() : new Date(_startStr);
let _curTimeStamp = _curDate.getTime();
let _deductLaterTimeStamp = _curTimeStamp + deductDuration * 60 * 1000;
let _startArr = formatTime(new Date(_curTimeStamp)).split(' ');
let _endStr = formatTime(new Date(_deductLaterTimeStamp)).split(' ');
this.editData.sDate = _startArr[0];
this.editData.sTime = _startArr[1];
this.editData.eDate = _endStr[0];
this.editData.eTime = _endStr[1];
},
getTimePickerList(){
let _hourList = new Array(24).fill(0).map((e, i) => formatNumber(i));
let _minuteList = new Array(60).fill(0).map((e, i) => formatNumber(i));
let _secondList = new Array(60).fill(0).map((e, i) => formatNumber(i));
return [_hourList, _minuteList, _secondList];
},
init(data){
this.initData = data || {};
this.show();
this.initDeductTime();
},
show(){
this.isShow = true;
},
hide(){
this.isShow = false;
}
}
}
</script>
<style lang="scss">
.rp-container{
padding: 0 60upx;
.rc-space{
padding-top: 40upx;
}
.rc-orange{
color: #FF873D;
}
.rc-time-select{
margin-top: 40upx;
@include ctf(space-between);
.rts-name{
flex-shrink: 0;
@include flcw(28upx, 40upx, #1A1A1A);
}
.rts-ipt{
display: block;
box-sizing: border-box;
padding: 0upx 30upx;
width: 358upx;
height: 56upx;
border-radius: 10upx;
background: #F2F2F7;
@include flcw(24upx, 34upx, #333333);
}
}
.rc-remark{
width: auto;
margin-top: 40upx;
padding: 20upx 24upx;
height: 144upx;
background: #F2F2F7;
@include flcw(24upx, 34upx, #333333);
}
.rc-btn{
margin: 64upx auto 0upx;
width: 240upx;
height: 88upx;
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(28upx, 88upx, #fff);
}
}
</style>

146
src/subpackage/shower/components/popup/device_deduction.vue

@ -0,0 +1,146 @@
<template>
<view class="recharge-popup">
<popup-template
title="结束计时"
:show="isShow"
@click:close="hide"
>
<view class="rp-container">
<kv-line label="时长合计:">{{ initData.settle_duration || '-' }}</kv-line>
<view class="rc-space"></view>
<number-edit label="扣减时长" unit="分钟" v-model="editData.minute"></number-edit>
<view class="rc-btn" @click="confirm">确认</view>
</view>
</popup-template>
</view>
</template>
<script>
import popup_template from "./template.vue";
import kv_line from "@/components/kv_line.vue";
import number_edit from "../number_edit.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { formatDate, formatNumber, formatTime, showNone, debounce, showLoad, hideLoad, showModal } from "@/utils/util";
export default {
components: {
'popup-template': popup_template,
'kv-line': kv_line,
'number-edit': number_edit
},
data(){
return {
isShow: false,
initData: {},
editData: {
minute: 10
}
}
},
methods: {
confirm: debounce(function(){
let { initData, editData } = this;
// initData?.success?.({
// deductionMinute: +editData?.minute
// });
let _query = {
duration: +editData?.minute,
id: initData?.id,
stadium_id: initData?.stadium_id,
brand_id: initData?.brand_id,
}
this.deductionReq(_query);
this.hide();
}, 300, true),
init(data){
this.initData = data;
this.editData.minute = data?.settle_duration || 10;
this.show();
},
show(){
this.isShow = true;
},
hide(){
this.isShow = false;
},
deductionReq(data){
showLoad();
return server.post({
url: SHOWER_API.endOfUseWaterWave,
data,
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showModal({
title: '提示',
content: _data.message || '操作成功!'
})
this.initData?.success?.('SUCCESS');
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('deduction deductionReq err --->', err);
})
},
}
}
</script>
<style lang="scss">
.rp-container{
padding: 0 60upx;
.rc-space{
padding-top: 40upx;
}
.rc-orange{
color: #FF873D;
}
.rc-time-select{
margin-top: 40upx;
@include ctf(space-between);
.rts-name{
flex-shrink: 0;
@include flcw(28upx, 40upx, #1A1A1A);
}
.rts-ipt{
display: block;
box-sizing: border-box;
padding: 10upx 30upx;
width: 358upx;
height: 56upx;
border-radius: 10upx;
background: #F2F2F7;
@include flcw(24upx, 34upx, #333333);
}
}
.rc-remark{
width: auto;
margin-top: 40upx;
padding: 20upx 24upx;
height: 144upx;
background: #F2F2F7;
@include flcw(24upx, 34upx, #333333);
}
.rc-btn{
margin: 64upx auto 0upx;
width: 240upx;
height: 88upx;
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(28upx, 88upx, #fff);
}
}
</style>

111
src/subpackage/shower/components/popup/recharge.vue

@ -0,0 +1,111 @@
<template>
<view class="recharge-popup">
<popup-template
title="充值时长"
:show="isShow"
@click:close="hide"
>
<view class="rp-container">
<kv-line label="水阀卡卡号:">{{ initData.water_card_no || '-' }}</kv-line>
<kv-line label="手机号码:">{{ initData.mobile || '-' }}</kv-line>
<kv-line label="可用时长:"><text class="rc-orange">{{ initData.valid_duration_text || '-' }}</text></kv-line>
<view class="rc-space"></view>
<number-edit label="充值时长" v-model="editData.duration"></number-edit>
<view class="rc-space"></view>
<number-edit label="支付金额" unit="元" v-model="editData.amount"></number-edit>
<view class="rc-btn" @click="confirm">确认</view>
</view>
</popup-template>
</view>
</template>
<script>
import popup_template from "./template.vue";
import kv_line from "@/components/kv_line.vue";
import number_edit from "../number_edit.vue";
import { debounce } from "@/utils/util";
export default {
components: {
'popup-template': popup_template,
'kv-line': kv_line,
'number-edit': number_edit
},
data(){
return {
isShow: false,
initData: {},
editData: {
duration: 10,
amount: 1
}
}
},
methods: {
confirm: debounce(function(){
let { initData, editData } = this;
initData?.success?.({
pay_amount: +editData?.amount,
duration: +editData?.duration
});
this.hide();
}, 300, true),
init(data){
this.initData = data;
this.show();
},
show(){
this.isShow = true;
},
hide(){
this.isShow = false;
}
}
}
</script>
<style lang="scss">
.rp-container{
padding: 0 60upx;
.rc-space{
padding-top: 40upx;
}
.rc-orange{
color: #FF873D;
}
.rc-time-select{
margin-top: 40upx;
@include ctf(space-between);
.rts-name{
flex-shrink: 0;
@include flcw(28upx, 40upx, #1A1A1A);
}
.rts-ipt{
display: block;
box-sizing: border-box;
padding: 10upx 30upx;
width: 358upx;
height: 56upx;
border-radius: 10upx;
background: #F2F2F7;
@include flcw(24upx, 34upx, #333333);
}
}
.rc-remark{
width: auto;
margin-top: 40upx;
padding: 20upx 24upx;
height: 144upx;
background: #F2F2F7;
@include flcw(24upx, 34upx, #333333);
}
.rc-btn{
margin: 64upx auto 0upx;
width: 240upx;
height: 88upx;
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(28upx, 88upx, #fff);
}
}
</style>

72
src/subpackage/shower/components/popup/template.vue

@ -0,0 +1,72 @@
<template>
<view class="popup-template" v-show="show">
<view class="pt-mask">
<view class="pm-container">
<image
class="pc-close"
mode="aspectFit"
src="/subpackage/shower/static/images/close.png"
@click="$emit('click:close')"
></image>
<view class="pc-title">{{ title }}</view>
<view class="pc-content">
<slot></slot>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
},
show: {
type: Boolean,
default: false
}
},
}
</script>
<style lang="scss">
.pt-mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
.pm-container{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding-top: 44upx;
padding-bottom: 48upx;
width: 640upx;
background: #fff;
border-radius: 10upx;
.pc-close{
position: absolute;
top: 24upx;
right: 24upx;
width: 34upx;
height: 34upx;
}
.pc-title{
padding: 0 30upx;
text-align: center;
@include flcw(32upx, 44upx, #333, 500);
@include tHide;
}
.pc-content{
padding-top: 24upx;
}
}
}
</style>

100
src/subpackage/shower/components/time_range.vue

@ -0,0 +1,100 @@
<template>
<view class="time-range">
<view class="tr-name">日期</view>
<view class="tr-right">
<picker mode="date" @change="startDateChange">
<view class="tr-frame">
<input type="text" disabled class="tf-ipt" :value="sDate" placeholder="请选择时间">
<image class="tf-icon" mode="aspectFit" src="/subpackage/shower/static/images/calendar.png"></image>
</view>
</picker>
<view class="tr-txt"></view>
<picker mode="date" @change="endDateChange">
<view class="tr-frame">
<input type="text" disabled class="tf-ipt" :value="eDate" placeholder="请选择时间">
<image class="tf-icon" mode="aspectFit" src="/subpackage/shower/static/images/calendar.png"></image>
</view>
</picker>
</view>
</view>
</template>
<script>
import { showNone } from "@/utils/util";
export default {
data(){
return {
sDate: '',
eDate: ''
}
},
methods: {
init(data){
this.sDate = data?.start_time || '';
this.eDate = data?.end_time || '';
},
endDateChange(e){
let { sDate, getDateStrTimeStamp } = this;
let _val = e.detail.value || '';
if(getDateStrTimeStamp(_val) < getDateStrTimeStamp(sDate)){
return showNone('结束时间不能小于开始时间');
}
this.eDate = e.detail.value;
this.$emit('change:date', { start_time: this.sDate, end_time: this.eDate });
},
startDateChange(e){
let { eDate, getDateStrTimeStamp } = this;
let _val = e.detail.value || '';
if(getDateStrTimeStamp(_val) > getDateStrTimeStamp(eDate)){
return showNone('开始时间不能大于结束时间');
}
this.sDate = e.detail.value;
this.$emit('change:date', { start_time: this.sDate, end_time: this.eDate });
},
getDateStrTimeStamp(dateStr){
let _str = dateStr.replace(/-/g, '/');
return new Date(`${_str} 00:00:00`).getTime();
}
}
}
</script>
<style lang="scss">
.time-range{
padding: 20upx 24upx;
background: #fff;
@include ctf(space-between);
.tr-name{
flex-shrink: 0;
@include flcw(32upx, 44upx, #1A1A1A, 500);
}
.tr-right{
@include ctf(flex-end);
.tr-frame{
flex-shrink: 0;
padding: 0 18upx;
width: 230upx;
height: 62upx;
border: 2upx solid #979797;
border-radius: 4upx;
@include ctf(space-between);
.tf-ipt{
flex-grow: 1;
@include flcw(24upx, 34upx, #1A1A1A, 500);
}
.tf-icon{
margin-left: 10upx;
flex-shrink: 0;
display: block;
width: 32upx;
height: 32upx;
}
}
.tr-txt{
margin: 0 28upx;
flex-shrink: 0;
@include flcw(32upx, 44upx, #1A1A1A, 500);
}
}
}
</style>

17
src/subpackage/shower/js/api.js

@ -0,0 +1,17 @@
import { ORIGIN } from '../../../js/api';
export const SHOWER_API = {
CardList:`${ORIGIN}/admin/stadiumWaterCard/list`, // 后台-水卡-列表
CardDetails:`${ORIGIN}/admin/stadiumWaterCard/details`, // 后台-水卡-详情
CardUseRecord:`${ORIGIN}/admin/stadiumWaterCardUseRecord/listOfCardByAssistant`, // 后台-水卡使用记录-(某张水卡的)列表
CardRecharge:`${ORIGIN}/admin/stadiumWaterCard/recharge`, // 后台-水卡-充值
CardFeeDeduction:`${ORIGIN}/admin/stadiumWaterCard/feeDeduction`, // 后台-水卡-扣费
waterOrderList:`${ORIGIN}/admin/stadiumWaterCardUseRecord/list`, // 后台-水阀订单-列表
waterOrderDetails:`${ORIGIN}/admin/stadiumWaterCardUseRecord/details`, // 后台-水阀订单-详情
endOfUseWaterWave:`${ORIGIN}/admin/stadiumWaterCardUseRecord/endOfUseWaterWave`, // 后台-水卡使用记录-详情
cardOrderList:`${ORIGIN}/admin/stadiumWaterCardOrder/list`, // 后台-水阀订单-列表
cardOrderDetails:`${ORIGIN}/admin/stadiumWaterCardOrder/details`, // 后台-水阀订单-详情
orderRefund:`${ORIGIN}/admin/stadium/order/refund`, // A订单管理-场馆预订订单-订单退款
}
export default SHOWER_API;

10
src/subpackage/shower/js/server.js

@ -0,0 +1,10 @@
import { Server } from '../../../js/server';
class DeviceServer extends Server {
constructor(props){
super(props)
}
}
export default new DeviceServer();

274
src/subpackage/shower/pages/card/detail.vue

@ -0,0 +1,274 @@
<template>
<view class="card-detail">
<view class="cd-box cb-card">
<view class="cc-stadium">
<view class="cs-txt">{{ water_card.stadium_name || '' }}</view>
</view>
<view class="cc-lines">
<kv-line label-style="color: #1A1A1A;" label="水阀卡卡号:">{{ water_card.water_card_no || '' }}</kv-line>
<kv-line label-style="color: #1A1A1A;" label="手机号码:">{{ water_card.mobile || '' }}</kv-line>
<kv-line label-style="color: #1A1A1A;" label="微信昵称:">{{ water_card.nick_name || '' }}</kv-line>
<kv-line label-style="color: #1A1A1A;font-weitht: 500;" label="可用时长:"><text class="cl-orange">{{ water_card.valid_duration_text || '' }}</text></kv-line>
</view>
<view class="cc-btns">
<view class="cb-item" @click="toRecords">消费记录</view>
<view class="cb-item red" @click="deductionBtn">扣费</view>
<view class="cb-item orange" @click="rechargeBtn">充值</view>
</view>
</view>
<view class="cd-box cd-record">
<view class="cr-tit">购卡记录</view>
<view class="cr-time">购卡 {{ water_card_order.pay_time || '-' }}</view>
<view class="cr-lines">
<kv-line label="名称:">{{ water_card.water_card_name || '-' }}</kv-line>
<kv-line label="时长:">{{ water_card_order.duration_text || '-' }}</kv-line>
<kv-line label="售价:">{{ water_card_order.amount || '0' }}</kv-line>
<kv-line label="支付金额:">{{ water_card_order.pay_amount || '0' }}</kv-line>
<kv-line label="途径:">{{ water_card.resource_text || '-' }}</kv-line>
</view>
</view>
<recharge-popup ref="rechangePopup"></recharge-popup>
<deduction-popup ref="deductionPopup"></deduction-popup>
</view>
</template>
<script>
import kv_line from "@/components/kv_line.vue";
import rechargePopup from "../../components/popup/recharge.vue";
import deductionPopup from "../../components/popup/deduction.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { routeTo, showLoad, hideLoad, showNone, formatDate, jsonStr, showModal } from "@/utils/util";
export default {
components: {
'kv-line': kv_line,
'recharge-popup': rechargePopup,
'deduction-popup': deductionPopup
},
computed: {
water_card(){
let { cardInfo } = this;
return cardInfo?.water_card || {};
},
water_card_order(){
let { cardInfo } = this;
return cardInfo?.water_card_order || {};
}
},
data(){
return {
cardInfo: {}
}
},
onLoad(options){
let { id, brand_id } = options;
this.getCardInfo({ id, brand_id });
},
methods: {
rechargeBtn(){
let { water_card } = this;
this.$refs.rechangePopup.init({
water_card_no: water_card?.water_card_no || '',
mobile: water_card?.mobile || '',
valid_duration_text: water_card?.valid_duration_text || '',
success: rData => {
let _query = {
type: 5,
brand_id: water_card?.brand_id || '',
amount: rData?.pay_amount || 0,
stadiumInfo: {
id: water_card?.stadium_id || '',
name: water_card?.stadium_name || '',
logo: water_card?.stadium_logo || '',
},
};
uni.navigateTo({
url: `/subpackage/common/pages/pay_type_select?query=${jsonStr(_query)}`,
events: { payConfirm: pData => this.rechargePayConfirm({ rData, pData }) },
})
}
});
},
async rechargePayConfirm({ rData, pData }){
let { water_card } = this;
let _reqData = {
brand_id: water_card?.brand_id || '',
stadium_id: water_card?.stadium_id || '',
water_card_no: water_card?.water_card_no || '',
duration: rData?.duration || 0,
mobile: water_card?.mobile || '',
pay_type: this.getTextForType(pData?.payTypeName || ''),
pay_amount: pData?.amount || 0,
//
card_no: pData?.selectedCardNo || '',
order_no: pData?.selectedCardNo || ''
}
let _rechargeRes = await this.rechargePayReq(_reqData);
if(_rechargeRes === 'SUCCESS'){
setTimeout(_=>{
this.getCardInfo({ id: water_card?.id, brand_id: water_card?.brand_id });
}, 1200)
}
},
rechargePayReq(data){
showLoad();
return server.post({
url: SHOWER_API.CardRecharge,
data,
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showModal({
title: '提示',
content: _data.message || '操作成功!'
})
return "SUCCESS";
// this.initData?.success?.('SUCCESS');
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('deduction deductionReq err --->', err);
})
},
// // 0: 1: 2: 3: 4: 5 6 7
getTextForType(payTxt){
switch(payTxt){
case '微信支付': return 0;
case '支付宝支付': return 1;
case '储值卡支付': return 2;
case '商家主动': return 3;
case '押金抵扣': return 4;
case '任务奖品': return 5;
case '抖音团购兑换': return 6;
case '付款码支付': return 7;
default: return 8;
}
},
deductionBtn(){
let { water_card } = this;
this.$refs.deductionPopup.init({
water_card_no: water_card?.water_card_no || '',
mobile: water_card?.mobile || '',
valid_duration_text: water_card?.valid_duration_text || '',
valid_duration: water_card?.valid_duration || '',
//
brand_id: water_card?.brand_id || '',
stadium_id: water_card?.stadium_id || '',
success: () => {
this.getCardInfo({ id: water_card?.id, brand_id: water_card?.brand_id });
}
});
},
toRecords(){
let { water_card } = this;
let _qryStr = `brand_id=${water_card?.brand_id || ''}&water_card_no=${water_card?.water_card_no || ''}`;
routeTo(`/subpackage/shower/pages/card/use_record?${_qryStr}`, 'nT');
},
getCardInfo({
id = '', brand_id = ''
}){
showLoad();
return server.get({
url: SHOWER_API.CardDetails,
data: { brand_id, id },
failMsg: '获取用水卡信息失败'
})
.then(res => {
hideLoad();
this.cardInfo = res || {};
})
.catch(err => {
hideLoad();
console.warn('card detail getCardInfo err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.card-detail{
padding: 24upx;
@include isPd(24upx);
.cd-box{
position: relative;
padding: 30upx;
background: #fff;
border-radius: 10upx;
&+.cd-box{
margin-top: 24upx;
}
}
.cb-card{
.cc-stadium{
.cs-txt{
@include flcw(28upx, 40upx, #333333, 500);
@include tHide;
}
&::after{
content: '';
display: block;
margin-top: 26upx;
height: 1px;
background: #D8D8D8;
}
}
.cc-lines{
padding-top: 20upx;
.cl-orange{
color: #FF873D;
font-weight: 500;
}
}
.cc-btns{
margin-top: 30upx;
@include ctf;
.cb-item{
width: 176upx;
text-align: center;
border: 2upx solid #9A9A9D;
border-radius: 10upx;
@include flcw(32upx, 76upx, #9a9a9d, 500);
&.red{
color: #EA5061;
border-color: #EA5061;
}
&.orange{
color: #FF873D;
border-color: #FF873D;
}
&+.cb-item{
margin-left: 58upx;
}
}
}
}
.cd-record{
.cr-tit{
@include flcw(28upx, 40upx, #9C9C9F);
}
.cr-time{
margin-top: 20upx;
@include flcw(28upx, 52upx, #1A1A1A, 500);
}
}
}
</style>

192
src/subpackage/shower/pages/card/manage.vue

@ -0,0 +1,192 @@
<template>
<view class="card-manage">
<order-filter
ref="orderFilter"
@change:stadium="condition.stadium_id = $event.id"
@click:time="showPeriodModal"
@click:filter="showFilterModal"
:start-time="condition.start_time"
:end-time="condition.end_time"
></order-filter>
<view class="cm-list">
<view class="cl-item" v-for="(e, i) in cardOrderList" :key="i" @click="toDetail(e)">
<view class="ci-bar">
<view class="cb-name">{{ e.stadium_name || '-' }}</view>
<view class="cb-status">{{ e.status_text || '' }}</view>
</view>
<view class="ci-lines">
<kv-line label="卡名称:">{{ e.water_card_no || '-' }}</kv-line>
<kv-line label="水阀卡号:">{{ e.water_card_name || '-' }}</kv-line>
<kv-line label="手机号码:">{{ e.mobile || '-' }}</kv-line>
<kv-line label="微信昵称:">{{ e.nick_name || '-' }}</kv-line>
<kv-line label="可用时长:"><text class="cl-txt">{{ e.valid_duration_text || '-' }}</text></kv-line>
</view>
</view>
</view>
<!-- 时间段选择 -->
<period-modal ref="periodModal"></period-modal>
<!-- status窗口 -->
<filter-modal ref="filterModal"></filter-modal>
</view>
</template>
<script>
import orderFilter from "@/components/filter/header.vue";
import periodModal from "@/components/filter/period_modal.vue";
import filterModal from "@/components/filter/filter_modal.vue";
import kv_line from "@/components/kv_line.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { routeTo, showLoad, hideLoad, showNone, formatDate } from "@/utils/util";
export default {
components: {
'order-filter': orderFilter,
'period-modal': periodModal,
'filter-modal': filterModal,
'kv-line': kv_line
},
watch: {
condition: {
handler(nVal, oVal){
this.cardOrderList = [];
this.page = 1;
let { brand_id } = this;
this.getCardList({
brand_id: brand_id || '',
...nVal,
})
},
deep: true
}
},
data(){
return {
brand_id: '',
condition: {
stadium_id: '', // id
start_time: '',
end_time: '',
status: '', // 0: 1:
},
cardOrderList: [],
page: 1
}
},
onReachBottom(){
let { brand_id, condition, page } = this;
this.getCardList({ brand_id, page: ++page, ...condition });
},
onLoad(options){
let _bid = options?.brand_id || '';
this.brand_id = _bid;
this.$refs.orderFilter.initStadiumSelect({ brand_id: _bid });
// this.getCardList({ brand_id: _bid });
this.initDate(); // watch
},
methods: {
toDetail(e){
routeTo(`/subpackage/shower/pages/card/detail?id=${e?.id || ''}&brand_id=${e?.brand_id || ''}`, 'nT')
},
//
initDate(){
let _Date = new Date();
let _timeStamp = _Date.getTime();
let _start = _timeStamp - 90 * 24 * 60 * 60 * 1000;
this.condition.start_time = formatDate({ date: _start });
this.condition.end_time = formatDate({ date: _timeStamp });
},
showPeriodModal(){
let { start_time, end_time } = this.condition;
this.$refs.periodModal.init({
start: start_time,
end: end_time,
success: res=>{
this.condition.start_time = res.start;
this.condition.end_time = res.end;
}
});
},
showFilterModal(){
this.$refs.filterModal.init({
title: '订单状态',
list: [
{ label: '全部', value: '' },
{ label: '使用中', value: '1' },
{ label: '待激活', value: '0' },
],
curValue: this.condition.status,
success: res=>{
this.condition.status = res.value;
}
});
},
/**
* @param {String} number 卡号或者手机号
* */
getCardList({
brand_id = '', stadium_id = '',
start_time = '', end_time = '', number = '', status = '',
page = 1, page_size = 10
}){
showLoad();
return server.get({
url: SHOWER_API.CardList,
data: { brand_id, stadium_id, start_time, end_time, number, status, page, page_size },
failMsg: '获取用水卡列表失败'
})
.then(res => {
hideLoad();
let _ls = res?.list || [];
if(page === 1)return this.cardOrderList = _ls;
if(!_ls.length)return showNone('没有更多!');
this.page = page;
return this.cardOrderList = [...this.cardOrderList, ..._ls];
})
.catch(err => {
hideLoad();
console.warn('manage getCardList err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.cm-list{
padding: 24upx;
@include isPd(24upx);
.cl-item{
padding: 0upx 20upx 30upx;
border-radius: 10upx;
background: #fff;
.ci-bar{
padding: 30upx 0upx;
border-bottom: 1upx solid #D8D8D8;
@include ctf(space-between);
.cb-name{
@include flcw(28upx, 40upx, #1A1A1A, 500);
@include tHide;
}
.cb-status{
flex-shrink: 0;
max-width: 30%;
@include tHide;
@include flcw(28upx, 40upx, $mColor);
}
}
.ci-lines{
padding-top: 20upx;
.cl-txt{
color: #FF873D;
}
}
&+.cl-item{
margin-top: 24upx;
}
}
}
</style>

172
src/subpackage/shower/pages/card/use_record.vue

@ -0,0 +1,172 @@
<template>
<view class="use-record">
<time-range ref="timeRange" @change:date="condition = $event"></time-range>
<view class="ur-list">
<view class="ul-item" v-for="(e, i) in recordList" :key="i">
<view class="ui-tit-bar">
<view class="utb-tit" v-if="e.type === 0">消费时长</view>
<view class="utb-tit" v-if="e.type === 1">充值时长</view>
<view class="utb-right">
<view class="ur-time" v-if="e.status === 0">正在计时</view>
<block v-if="(e.type === 0&&e.status === 1) || e.type === 1">
<view class="ur-time" v-if="e.type === 0">-{{ e.deduction_duration || '-' }}</view>
<view class="ur-time orange" v-if="e.type === 1">+{{ e.recharge_duration || '-' }}分钟</view>
<view class="ur-surplus">剩余{{ e.total_duration_text || '-' }}</view>
</block>
</view>
</view>
<view class="ui-lines">
<kv-line label="门店名称:">{{ e.stadium_name || '-' }}</kv-line>
<kv-line label="开始时间:">{{ e.start_time || e.created_at || '-' }}</kv-line>
<block v-if="e.type === 0">
<kv-line label="设备名称:">{{ e.hardware_name || '-' }}</kv-line>
<kv-line label="结束时间:">{{ e.end_time || '-' }}</kv-line>
<kv-line label="时长合计:">{{ e.settle_duration || '-' }}</kv-line>
</block>
</view>
</view>
</view>
</view>
</template>
<script>
import time_range from "../../components/time_range.vue";
import kv_line from "@/components/kv_line.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { routeTo, showLoad, hideLoad, showNone, formatDate } from "@/utils/util";
export default {
components: {
'time-range': time_range,
'kv-line': kv_line
},
watch: {
condition: {
handler(val){
let { start_time, end_time } = val;
let { optionsQuery } = this;
this.recordList = [];
this.page = 1;
this.getCardUseRecord({
brand_id: optionsQuery?.brand_id || '',
water_card_no: optionsQuery?.water_card_no || '',
start_time, end_time
})
},
deep: true
}
},
data(){
return {
condition: {
start_time: '',
end_time: ''
},
optionsQuery: {},
recordList: [],
page: 1
}
},
onReachBottom(){
let { optionsQuery, page, condition } = this;
this.getCardUseRecord({
brand_id: optionsQuery?.brand_id || '',
water_card_no: optionsQuery?.water_card_no || '',
page: ++page,
...condition
});
},
onLoad(options){
this.optionsQuery = options;
this.initDate();
// this.getCardUseRecord({ brand_id, water_card_no });
},
methods: {
initDate(){
let _Date = new Date();
let _timeStamp = _Date.getTime();
let _start = _timeStamp - 90 * 24 * 60 * 60 * 1000;
this.condition.start_time = formatDate({ date: _start });
this.condition.end_time = formatDate({ date: _timeStamp });
this.$refs.timeRange.init(this.condition);
return this.condition
},
getCardUseRecord({
brand_id = '', water_card_no = '', page = 1, page_size = 10,
start_time, end_time
}){
showLoad();
return server.get({
url: SHOWER_API.CardUseRecord,
data: {
brand_id, water_card_no, page, page_size,
start_time: `${start_time} 00:00:00`,
end_time: `${end_time} 23:59:59`
},
failMsg: '获取用水卡使用记录失败'
})
.then(res => {
hideLoad();
let _ls = res?.list || [];
if(page === 1)return this.recordList = _ls;
if(!_ls.length)return showNone('没有更多!');
this.page = page;
return this.recordList = [...this.recordList, ..._ls];
})
.catch(err => {
hideLoad();
console.warn('card use_record getCardUseRecord err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.use-record{
.ur-list{
padding: 24upx;
@include isPd(24upx);
.ul-item{
padding: 0 20upx;
background: #fff;
border-radius: 10upx;
&+.ul-item{
margin-top: 24upx;
}
}
.ui-tit-bar{
padding: 16upx 0upx;
@include ctf(space-between);
.utb-tit{
flex-shrink: 0;
@include flcw(28upx, 40upx, #1A1A1A, 500);
}
.utb-right{
.ur-time{
text-align: right;
@include flcw(28upx, 40upx, $mColor);
@include tHide;
&.orange{
color: #FF873D;
}
}
.ur-surplus{
text-align: right;
@include tHide;
@include flcw(20upx, 28upx, #9C9C9F);
}
}
}
.ui-lines{
padding-top: 10upx;
padding-bottom: 30upx;
border-top: 1px solid #D8D8D8;
}
}
}
</style>

203
src/subpackage/shower/pages/device/use_detail.vue

@ -0,0 +1,203 @@
<template>
<view class="use-detail">
<view class="ud-box">
<view class="ub-stadium">
<image class="us-logo" mode="aspectFit" :src="orderInfo.logo"></image>
<view class="us-name">{{ orderInfo.stadium_name || '-' }}</view>
</view>
<view class="ub-lines">
<kv-line label="创建时间:">
<template slot="default">{{ orderInfo.created_at || '-' }}</template>
<template slot="right">
<view class="ul-status">{{ statusTxt || '' }}</view>
</template>
</kv-line>
<kv-line label="手机号码:">{{ orderInfo.mobile || '-' }}</kv-line>
<kv-line label="用户昵称:">{{ orderInfo.nick_name || '-' }}</kv-line>
<kv-line label="结束计时方式:">小程序(欧轩智能场馆)</kv-line>
</view>
</view>
<view class="ud-box">
<kv-line label="计时信息"></kv-line>
<view class="ub-lines">
<kv-line label="设备名称:">{{ orderInfo.hardware_name || '-' }}</kv-line>
<kv-line label="开始时间:">{{ orderInfo.start_time || '-' }}</kv-line>
<kv-line label="结束时间:" v-if="orderInfo.status === 1">{{ orderInfo.end_time || '-' }}</kv-line>
<kv-line label="时长合计:">{{ orderInfo.settle_duration || '-' }}</kv-line>
<kv-line label="免费时长:" v-if="orderInfo.status === 1">{{ orderInfo.free_duration || '-' }}</kv-line>
</view>
<view class="ui-deduction" v-if="orderInfo.status === 1">
<text class="ud-txt">扣减时长</text>{{ orderInfo.deduction_duration || '-' }}
</view>
<view class="ui-btn" v-if="orderInfo.status === 0" @click="endTimekeeping">结束计时</view>
</view>
<!-- 结束计时弹窗 -->
<device-deduction ref="deviceDeduction"></device-deduction>
</view>
</template>
<script>
import kv_line from "@/components/kv_line.vue";
import deviceDeduction from "../../components/popup/device_deduction.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { routeTo, showLoad, hideLoad } from "@/utils/util";
export default {
components: {
'kv-line': kv_line,
'device-deduction': deviceDeduction
},
computed: {
statusTxt(){
let { orderInfo } = this;
switch(+orderInfo.status){
case 0:
return '正在计时';
case 1:
return '已完成';
default:
return '-';
}
},
/**
* SettleType = 0 //3
SettleType = 1 // 2
SettleType = 2 // 2
SettleType = 3 //
* */
settleTypeTxt(){
let { orderInfo } = this;
switch(+orderInfo.settle_type){
case 0:
return '小程序结束';
case 1:
return '后台结束';
case 2:
return '硬件结束';
case 3:
return '商家助手结束';
default:
return '-';
}
}
},
data(){
return {
orderInfo: {},
}
},
onLoad(options){
this.getUseDetail({
id: options?.id || '',
brand_id: options?.brand_id || ''
})
.then(res=>{
if(res?.status === 0&&options?.isRefund === '1')this.endTimekeeping();
})
},
methods: {
endTimekeeping(){
let { orderInfo } = this;
this.$refs.deviceDeduction.init({
settle_duration: orderInfo?.settle_duration || 0,
id: orderInfo?.id || '',
stadium_id: orderInfo?.stadium_id || '',
brand_id: orderInfo?.brand_id || '',
success: res=>{
this.getUseDetail({
id: orderInfo?.id || '',
brand_id: orderInfo?.brand_id || ''
});
}
});
},
getUseDetail({
id = '', brand_id = ''
}){
showLoad();
return server.get({
url: SHOWER_API.waterOrderDetails,
data: { brand_id, id },
failMsg: '获取信息失败'
})
.then(res => {
hideLoad();
return this.orderInfo = res || {};
})
.catch(err => {
hideLoad();
console.warn('use_detail getUseDetail err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.use-detail{
padding: 24upx;
@include isPd(24upx);
.ud-box{
padding: 30upx;
background: #fff;
border-radius: 10upx;
&+.ud-box{
margin-top: 24upx;
}
}
.ub-stadium{
@include ctf;
padding-bottom: 26upx;
border-bottom: 1px solid #D8D8D8;
.us-logo{
margin-right: 14upx;
flex-shrink: 0;
width: 40upx;
height: 40upx;
}
.us-name{
margin-left: 20upx;
font-size: 28upx;
color: #333333;
@include tHide;
@include flcw(28upx, 40upx, #333333, 500);
}
}
.ub-lines{
padding-top: 24upx;
.ul-status{
text-align: right;
@include flcw(28upx, 40upx, #9C9C9F);
}
}
.ui-deduction{
margin-top: 30upx;
padding-top: 24upx;
text-align: right;
border-top: 1upx solid #D8D8D8;
@include flcw(28upx, 40upx, $mColor);
@include tHide;
.ud-txt{
color: #9C9C9F;
}
}
.ui-btn{
margin: 10upx 0upx 0upx auto;
width: 192upx;
height: 80upx;
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(32upx, 80upx, #fff);
}
}
</style>

263
src/subpackage/shower/pages/device/use_record.vue

@ -0,0 +1,263 @@
<template>
<view class="use-record">
<order-filter
ref="orderFilter"
@change:stadium="condition.stadium_id = $event.id"
@click:time="showPeriodModal"
@click:filter="showFilterModal"
:start-time="condition.start_time"
:end-time="condition.end_time"
></order-filter>
<view class="ur-list">
<!-- status 0进行中1已完成 -->
<view class="ul-item" v-for="(e, i) in orderList" :key="i" @click="toDetail(e)">
<view class="ui-bar">
<view class="ub-name">{{ e.stadium_name || '-' }}</view>
<image class="ub-icon" mode="aspectFit" src="/subpackage/shower/static/images/arrow.png"></image>
</view>
<view class="ui-lines">
<kv-line label="设备名称:">{{ e.hardware_name || '-' }}</kv-line>
<kv-line label="时长合计:">
{{ e.settle_duration || '-' }}
<block v-if="e.free_duration">
(免费时长{{ e.free_duration || 0 }})
</block>
</kv-line>
<kv-line label="手机号码:">{{ e.mobile || '-' }}</kv-line>
<kv-line label="创建时间:">{{ e.created_at || '-' }}</kv-line>
</view>
<view class="ui-deduction" v-if="e.status === 1">
<text class="ud-txt">扣减时长</text>{{ e.deduction_duration || '0' }}
</view>
<view class="ui-btn" v-if="e.status === 0" @click.stop="endTimekeeping(e)">结束计时</view>
</view>
</view>
<!-- 时间段选择 -->
<period-modal ref="periodModal"></period-modal>
<!-- status窗口 -->
<filter-modal ref="filterModal"></filter-modal>
<!-- 结束计时弹窗 -->
<device-deduction ref="deviceDeduction"></device-deduction>
</view>
</template>
<script>
import orderFilter from "@/components/filter/header.vue";
import periodModal from "@/components/filter/period_modal.vue";
import filterModal from "@/components/filter/filter_modal.vue";
import kv_line from "@/components/kv_line.vue";
import deviceDeduction from "../../components/popup/device_deduction.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { routeTo, showLoad, hideLoad, showNone, formatDate } from "@/utils/util";
export default {
components: {
'order-filter': orderFilter,
'period-modal': periodModal,
'filter-modal': filterModal,
'kv-line': kv_line,
'device-deduction': deviceDeduction
},
watch: {
condition: {
handler(nVal, oVal){
this.orderList = [];
this.page = 1;
let { brand_id } = this;
this.getOrderList({
brand_id: brand_id || '',
...nVal,
})
},
deep: true
},
},
data(){
return {
condition: {
stadium_id: '',
start_time: '',
end_time: '',
status: '' // 01
},
orderList: [],
page: 1,
brand_id: ''
}
},
onReachBottom(){
let { condition, page, brand_id } = this;
this.getOrderList({
...condition,
page: ++page,
brand_id
})
},
async onLoad(options){
this.$refs.orderFilter.initStadiumSelect({
brand_id: options?.brand_id || '',
stadium_id: options?.stadium_id || ''
});
this.brand_id = options?.brand_id || '';
this.condition.stadium_id = options?.stadium_id || '';
// this.getOrderList({ brand_id: options?.brand_id });
this.initDate();
},
methods: {
endTimekeeping(e){
let _qryStr = `id=${e?.id || ''}&brand_id=${e?.brand_id || ''}&isRefund=1`;
routeTo(`/subpackage/shower/pages/device/use_detail?${_qryStr}`, 'nT');
return
this.$refs.deviceDeduction.init({
settle_duration: e?.settle_duration || 0,
id: e?.id || '',
stadium_id: e?.stadium_id || '',
brand_id: e?.brand_id || '',
success: res=>{
let { condition, brand_id } = this;
this.orderList = [];
this.page = 1;
this.getOrderList({
brand_id: brand_id || '',
...condition
})
}
});
},
toDetail(e){
let _qryStr = `id=${e?.id || ''}&brand_id=${e?.brand_id || ''}`;
routeTo(`/subpackage/shower/pages/device/use_detail?${_qryStr}`, 'nT');
},
//
initDate(){
let _Date = new Date();
let _timeStamp = _Date.getTime();
let _start = _timeStamp - 90 * 24 * 60 * 60 * 1000;
Object.assign(this.condition, {
start_time: formatDate({ date: _start }),
end_time: formatDate({ date: _timeStamp })
})
},
showPeriodModal(){
let { start_time, end_time } = this.condition;
this.$refs.periodModal.init({
start: start_time,
end: end_time,
success: res=>{
this.condition.start_time = res.start;
this.condition.end_time = res.end;
}
});
},
// 012使4退
// 5退6退78使
showFilterModal(){
this.$refs.filterModal.init({
title: '订单状态',
list: [
{ label: '全部', value: '' },
{ label: '进行中', value: '0' },
{ label: '已完成', value: '1' },
],
curValue: this.condition.status,
success: res=>{
this.condition.status = res.value;
}
});
},
/**
* @param { status } 状态0进行中1已完成
* */
getOrderList({
brand_id = '', stadium_id = '',
start_time = '', end_time = '', status = '',
page = 1, page_size = 10
}){
showLoad();
return server.get({
url: SHOWER_API.waterOrderList,
data: {
brand_id,
stadium_id,
start_time: `${start_time} 00:00:00`,
end_time: `${end_time} 23:59:59`,
status,
page,
page_size
},
failMsg: '获取水阀订单失败'
})
.then(res => {
hideLoad();
let _ls = res?.list || [];
if(page === 1)return this.orderList = _ls;
if(!_ls.length)return showNone('没有更多!');
this.page = page;
return this.orderList = [...this.orderList, ..._ls];
})
.catch(err => {
hideLoad();
console.warn('device user_record getOrderList err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.use-record{
.ur-list{
padding: 24upx;
@include isPd(24upx);
.ul-item{
padding: 0upx 20upx 30upx;
border-radius: 10upx;
background: #fff;
&+.ul-item{
margin-top: 24upx;
}
.ui-bar{
padding: 30upx 0upx;
border-bottom: 1upx solid #D8D8D8;
@include ctf(space-between);
.ub-name{
@include flcw(28upx, 40upx, #1A1A1A, 500);
@include tHide;
}
.ub-icon{
margin-left: 12upx;
flex-shrink: 0;
width: 28upx;
height: 28upx;
}
}
.ui-lines{
padding-top: 14upx;
}
.ui-deduction{
margin-top: 30upx;
padding-top: 24upx;
text-align: right;
border-top: 1upx solid #D8D8D8;
@include flcw(28upx, 40upx, #1A1A1A);
@include tHide;
.ud-txt{
color: #9C9C9F;
}
}
.ui-btn{
margin: 10upx 0upx 0upx auto;
width: 192upx;
height: 80upx;
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(32upx, 80upx, #fff);
}
}
}
}
</style>

289
src/subpackage/shower/pages/order/detail.vue

@ -0,0 +1,289 @@
<template>
<view class="order-detail">
<view class="od-header">
<view class="oh-stadium">
<image class="os-img" mode="aspectFit" :src="orderInfo.logo || ''"></image>
<view class="os-name">{{ orderInfo.stadium_name || '-' }}</view>
</view>
<view class="oh-bot">
<kv-line label="订单编号:">
<template slot="default">{{ orderInfo.order_no || '-' }}</template>
<template slot="right">
<view class="ob-status">{{ getPayStatusTxt(orderInfo.pay_status || -1) }}</view>
</template>
</kv-line>
<kv-line label="创建时间:">{{ orderInfo.created_at || '-' }}</kv-line>
<kv-line label="手机号码:">{{ orderInfo.mobile || '-' }}</kv-line>
<kv-line label="用户昵称:">{{ orderInfo.nick_name || '-' }}</kv-line>
<kv-line label="来源:">{{ orderInfo.source || '-' }}</kv-line>
</view>
</view>
<view class="od-goods">
<view class="og-tit">订单信息</view>
<view class="og-lines">
<kv-line label="名称:">{{ orderInfo.water_card_name || '-' }}</kv-line>
<kv-line label="水阀卡号:">{{ orderInfo.water_card_no || '-' }}</kv-line>
<kv-line label="时长:">{{ orderInfo.duration_text || '-' }}</kv-line>
<kv-line label="途径:">{{ orderInfo.desc || '-' }}</kv-line>
</view>
</view>
<view class="od-price-info">
<kvs-line label="金额小计">¥{{ orderInfo.amount || 0 }}</kvs-line>
<kvs-line label="积分抵扣">-¥{{ orderInfo.deduction_amount || 0 }}</kvs-line>
<kvs-line label="折扣金额">-¥{{ orderInfo.discount_amount || 0 }}</kvs-line>
<kvs-line label="优惠券优惠">-¥{{ orderInfo.coupons_amount || 0 }}</kvs-line>
<view class="opi-total">
合计支付<text class="ot-num">{{ orderInfo.pay_amount || 0 }}</text>
</view>
</view>
<view class="od-pay-info">
<view class="opi-tit">支付信息</view>
<view class="opi-lines">
<kv-line label="支付方式:">{{ _extension.pay_type_text || '-' }}</kv-line>
<kv-line label="支付时间:">{{ orderInfo.pay_time || '-' }}</kv-line>
<kv-line label="微信交易号:">{{ orderInfo.trade_no || '-' }}</kv-line>
</view>
</view>
<!-- 退款列表 -->
<block v-for="(e, i) in refundList" :key="i">
<view class="od-space"></view>
<order-refund-info
:refund_price="e.amount || 0"
:refund_no="e.refund_no || '-'"
:refund_time="e.refund_time || '-'"
:refund_reason="e.reason || '-'"
:nameKey="i + 1"
></order-refund-info>
</block>
<order-refund-fixed
:pay_amount="orderInfo.pay_amount || 0"
:refund_amount="orderInfo.refund_amount || 0"
:refund_times="(refundList&&refundList.length) || 0"
@click:button="refunndBtn"
></order-refund-fixed>
<order-refund-modal
ref="orderRefundModal"
></order-refund-modal>
</view>
</template>
<script>
import kv_line from "@/components/kv_line.vue";
import kvs_line from "@/components/kvs_line.vue";
import orderRefundFixed from '@/components/order_refund/fixed.vue'
import orderRefundModal from '@/components/order_refund/modal.vue';
import orderRefundInfo from '@/components/order_refund/info.vue';
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { routeTo, showLoad, hideLoad, showNone, formatDate } from "@/utils/util";
export default {
components: {
'kv-line': kv_line,
'kvs-line': kvs_line,
'order-refund-fixed': orderRefundFixed,
'order-refund-modal': orderRefundModal,
'order-refund-info': orderRefundInfo
},
computed: {
_extension(){
return this.orderInfo?.extension || {};
}
},
data(){
return {
orderInfo: {},
refundList: [],
}
},
onLoad(options){
this.getCardOrderDetails({
id: options?.id,
brand_id: options?.brand_id,
order_no: options?.order_no
})
.then(res=>{
if(res.order_no)this.getRefundInfo(res.order_no || '');
})
},
methods: {
refunndBtn(){
let { orderInfo, _extension, refundList } = this;
this.$refs.orderRefundModal.show({
stadium_name: orderInfo?.stadium_name ?? '',
order_no: orderInfo?.order_no ?? '',
mobile: orderInfo?.mobile ?? '',
refundable_amount: +_extension?.refundable_amount || 0,
refundable_integral: +_extension?.refundable_integral || 0,
refund_times: refundList?.length || 0,
confirm: e => {
console.log('confirm --->', e);
this.orderRefund({
order_no: orderInfo?.order_no || '',
amount: e.refund_amount || 0,
integral: e.refund_integral || 0,
brand_id: orderInfo?.brand_id || ''
})
}
});
},
orderRefund({ order_no = '', amount = 0, integral = 0, brand_id }){
showLoad();
server.post({
url: SHOWER_API.orderRefund,
data: { order_no, amount, integral, brand_id },
isDefaultGet: false
})
.then(res=>{
hideLoad();
if(res.data.code == 0){
showNone(res.data.message || '操作成功!');
}else{
showNone(res.data.message || '操作失败!');
}
})
.catch(hideLoad)
.finally(_=>
setTimeout(_=>{
let { orderInfo } = this;
this.getCardOrderDetails({
id: orderInfo?.id,
order_no: orderInfo?.order_no,
brand_id: orderInfo?.brand_id,
})
.then(res=>{
if(res.order_no)this.getRefundInfo(res.order_no || '');
})
}, 1200)
);
},
async getRefundInfo(order_no){
try{
let _refundRes = await this.$store.dispatch('getOrderRefundList', order_no);
let _ls = _refundRes?.data?.data?.list || [];
this.refundList = _ls;
}catch(err){
console.warn('shower order detail getRefundInfo err', err);
}
},
getPayStatusTxt(payStatus){
let _txt = '';
switch(payStatus){
case 0: _txt = '未支付'; break;
case 1: _txt = '已支付'; break;
case 2: _txt = '已使用'; break;
case 4: _txt = '已退款'; break;
case 5: _txt = '退款中'; break;
case 6: _txt = '拒绝退款'; break;
case 7: _txt = '关闭支付'; break;
case 8: _txt = '使用中'; break;
default: _txt = '-';
}
return _txt;
},
getCardOrderDetails({
id = '', brand_id = '', order_no = ''
}){
showLoad();
return server.get({
url: SHOWER_API.cardOrderDetails,
data: { brand_id, id, order_no },
failMsg: '获取信息失败'
})
.then(res => {
hideLoad();
return this.orderInfo = res || {};
})
.catch(err => {
hideLoad();
console.warn('shower order details err --->', err);
})
},
}
}
</script>
<style lang="scss">
.order-detail{
@include isPd(24upx);
.od-space{
padding-top: 24upx;
}
.od-header{
padding: 0 24upx;
background: #fff;
.oh-stadium{
padding: 24upx 0;
border-bottom: 1px solid #D8D8D8;
@include ctf;
.os-img{
flex-shrink: 0;
margin-right: 14upx;
width: 40upx;
height: 40upx;
}
.os-name{
@include flcw(28upx, 40upx, #1A1A1A, 500);
@include tHide;
}
}
.oh-bot{
padding-top: 24upx;
padding-bottom: 30upx;
.ob-status{
text-align: right;
@include flcw(28upx, 40upx, #9A9A9D);
}
}
}
.od-goods{
margin-top: 24upx;
padding: 20upx 24upx 0;
background: #fff;
.opi-tit{
@include flcw(32upx, 44upx, #1A1A1A);
}
.og-lines{
margin-top: 28upx;
padding-bottom: 30upx;
border-bottom: 1px solid #D8D8D8;
}
}
.od-price-info{
padding: 30upx;
background: #fff;
.opi-total{
text-align: right;
@include flcw(28upx, 50upx, #9A9A9D);
.ot-num{
color: #1A1A1A;
font-weight: 500;
}
}
}
.od-pay-info{
margin-top: 24upx;
padding: 30upx 24upx;
background: #fff;
.opi-tit{
@include flcw(32upx, 44upx, #1A1A1A);
}
.opi-lines{
margin-top: 16upx;
.ob-copy{
margin-left: 20upx;
@include flcw(28upx, 40upx, $mColor);
}
}
}
}
</style>

252
src/subpackage/shower/pages/order/list.vue

@ -0,0 +1,252 @@
<template>
<view class="order-list">
<order-filter
ref="orderFilter"
@change:stadium="condition.stadium_id = $event.id"
@click:time="showPeriodModal"
@click:filter="showFilterModal"
:start-time="condition.start_time"
:end-time="condition.end_time"
></order-filter>
<view class="ol-ls">
<view class="ol-item" v-for="(e, i) in cardOrderList" :key="i" @click="itemClick(e)">
<view class="oi-header">
<view class="oh-top">
<view class="ot-name">{{ e.stadium_name || '-' }}</view>
<view class="ot-status">{{ getPayStatusTxt(e.pay_status || -1) }}</view>
</view>
<!-- <view class="oh-device-name">
<image class="odn-img" mode="aspectFit" src="/subpackage/shower/static/images/locate.png"></image>
<view class="odn-txt">1号女沐浴间</view>
</view> -->
</view>
<view class="oi-lines">
<kv-line label="订单编号:">{{ e.order_no || '-' }}</kv-line>
<kv-line label="名称:">{{ e.water_card_name || '-' }}</kv-line>
<kv-line label="手机号码:">{{ e.mobile || '-' }}</kv-line>
<kv-line label="支付时间:">{{ e.pay_time || '-' }}</kv-line>
<kv-line label="途径:">{{ e.source || '-' }}</kv-line>
</view>
<view class="oi-total">
合计支付<text class="ot-price">¥{{ e.pay_amount || 0 }}</text>
</view>
</view>
</view>
<!-- 时间段选择 -->
<period-modal ref="periodModal"></period-modal>
<!-- status窗口 -->
<filter-modal ref="filterModal"></filter-modal>
</view>
</template>
<script>
import orderFilter from "@/components/filter/header.vue";
import periodModal from "@/components/filter/period_modal.vue";
import filterModal from "@/components/filter/filter_modal.vue";
import kv_line from "@/components/kv_line.vue";
import { SHOWER_API } from "../../js/api";
import server from "../../js/server";
import { routeTo, showLoad, hideLoad, showNone, formatDate } from "@/utils/util";
export default {
components: {
'order-filter': orderFilter,
'period-modal': periodModal,
'filter-modal': filterModal,
'kv-line': kv_line
},
watch: {
condition: {
handler(nVal, oVal){
console.log('nVal --->', nVal);
this.cardOrderList = [];
this.page = 1;
let { brand_id } = this;
this.getCardOrderList({
brand_id: brand_id || '',
...nVal,
})
},
deep: true
},
},
data(){
return {
condition: {
stadium_id: '',
start_time: '',
end_time: '',
pay_status: ''
},
brand_id: '',
cardOrderList: [],
page: 1
}
},
onReachBottom(){
let { brand_id, condition, page } = this;
this.getCardOrderList({ brand_id, page: ++page, ...condition });
},
onLoad(options){
let _bid = options?.brand_id || '';
this.brand_id = _bid;
this.$refs.orderFilter.initStadiumSelect({ brand_id: _bid });
this.initDate(); // watch
},
methods: {
getPayStatusTxt(payStatus){
let _txt = '';
switch(payStatus){
case 0: _txt = '未支付'; break;
case 1: _txt = '已支付'; break;
case 2: _txt = '已使用'; break;
case 4: _txt = '已退款'; break;
case 5: _txt = '退款中'; break;
case 6: _txt = '拒绝退款'; break;
case 7: _txt = '关闭支付'; break;
case 8: _txt = '使用中'; break;
default: _txt = '-';
}
return _txt;
},
itemClick(e){
let _qryStr = `id=${e.id || ''}&brand_id=${e.brand_id || ''}&order_no=${e.order_no || ''}`;
routeTo(`/subpackage/shower/pages/order/detail?${_qryStr}`, 'nT');
},
//
initDate(){
let _Date = new Date();
let _timeStamp = _Date.getTime();
let _start = _timeStamp - 90 * 24 * 60 * 60 * 1000;
this.condition.start_time = formatDate({ date: _start });
this.condition.end_time = formatDate({ date: _timeStamp });
},
//
showPeriodModal(){
let { start_time, end_time } = this.condition;
this.$refs.periodModal.init({
start: start_time,
end: end_time,
success: res=>{
this.condition.start_time = res.start;
this.condition.end_time = res.end;
}
});
},
//
showFilterModal(){
this.$refs.filterModal.init({
title: '订单状态',
list: [
{ label: '全部', value: '' },
{ label: '已支付', value: '1' },
{ label: '已使用', value: '2' },
{ label: '已退款', value: '4' },
],
curValue: this.condition.pay_status,
success: res=>{
this.condition.pay_status = res.value;
}
});
},
/**
* @param {String} number 卡号或者手机号
* @param {String} pay_status
* 0未支付1已支付2已使用4已退款5退款中6拒绝退款7关闭支付8使用中
*
* */
getCardOrderList({
brand_id = '', stadium_id = '',
start_time = '', end_time = '', number = '', pay_status = '',
page = 1, page_size = 10, order_no = ''
}){
showLoad();
return server.get({
url: SHOWER_API.cardOrderList,
data: {
brand_id, stadium_id, start_time, end_time,
number, pay_status, page, page_size,
order_no
},
failMsg: '获取用水卡列表失败'
})
.then(res => {
hideLoad();
let _ls = res?.list || [];
if(page === 1)return this.cardOrderList = _ls;
if(!_ls.length)return showNone('没有更多!');
this.page = page;
return this.cardOrderList = [...this.cardOrderList, ..._ls];
})
.catch(err => {
hideLoad();
console.warn('shower order list getCardOrderList err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.order-list{
}
.ol-ls{
padding: 24upx;
.ol-item{
padding: 30upx 20upx;
border-radius: 10upx;
background: #fff;
&+.ol-item{
margin-top: 24upx;
}
.oi-header{
padding-bottom: 20upx;
border-bottom: 1px solid #D8D8D8;
.oh-top{
@include ctf(space-between);
.ot-name{
@include flcw(28upx, 40upx, #1A1A1A, 500);
@include tHide;
}
.ot-status{
margin-left: 10upx;
flex-shrink: 0;
@include flcw(28upx, 40upx, #9A9A9D);
}
}
.oh-device-name{
margin-top: 10upx;
@include ctf;
.odn-img{
margin-right: 12upx;
flex-shrink: 0;
width: 28upx;
height: 28upx;
}
.odn-txt{
@include flcw(28upx, 40upx, #9A9A9D);
@include tHide;
}
}
}
.oi-lines{
padding-top: 20upx;
}
.oi-total{
text-align: right;
@include flcw(28upx, 40upx, #9A9A9D);
.ot-price{
font-weight: 500;
color: #333333;
}
}
}
}
</style>

BIN
src/subpackage/shower/static/images/arrow.png

After

Width: 28  |  Height: 28  |  Size: 258 B

BIN
src/subpackage/shower/static/images/calendar.png

After

Width: 32  |  Height: 32  |  Size: 323 B

BIN
src/subpackage/shower/static/images/close.png

After

Width: 34  |  Height: 34  |  Size: 256 B

BIN
src/subpackage/shower/static/images/locate.png

After

Width: 28  |  Height: 28  |  Size: 714 B

Loading…
Cancel
Save