Browse Source

fixed merge dev conflict

tid1509
刘嘉炜 4 months ago
parent
commit
c2c555b9be
  1. 21914
      package-lock.json
  2. 3
      package.json
  3. 12121
      pnpm-lock.yaml
  4. 3
      src/App.vue
  5. 193
      src/components/end_billing_modal/end_billing_modal.vue
  6. 38
      src/components/fixed_bar/fixed_bar.vue
  7. 22
      src/components/order_refund/button.vue
  8. 57
      src/components/order_refund/fixed.vue
  9. 56
      src/components/order_refund/info.vue
  10. 234
      src/components/order_refund/modal.vue
  11. 49
      src/components/order_refund/permission_btn.vue
  12. 138
      src/components/period_select.vue
  13. 185
      src/components/timing_order/timing_order.vue
  14. 8
      src/js/api.js
  15. 12
      src/js/server.js
  16. 3
      src/main.js
  17. 4
      src/manifest.json
  18. 260
      src/pages.json
  19. 377
      src/pages/index/index.vue
  20. 6
      src/pages/login/login.vue
  21. 243
      src/pages/menu/forth.vue
  22. 32
      src/pages/menu/second.vue
  23. 117
      src/pages/menu/third.vue
  24. 4
      src/pages/merchant_login/merchant_login.vue
  25. 12
      src/pages/order_list/order_list.vue
  26. 6
      src/pages/order_search/order_search.vue
  27. 2
      src/pages/site/confirm/confirm.vue
  28. 3
      src/pages/web_view/web_view.vue
  29. 175
      src/pages/write_off/douyin/poi_list.vue
  30. 237
      src/pages/write_off/mall/list/list.vue
  31. 122
      src/pages/write_off/menu/menu.vue
  32. 70
      src/pages/write_off/null/null.vue
  33. 496
      src/pages/write_off/number_of_people/number_of_people.vue
  34. 364
      src/pages/write_off/operate/operate.vue
  35. 339
      src/pages/write_off/search_result/search_result.vue
  36. 8
      src/pages/write_off/ym_confirm/ym_confirm.vue
  37. BIN
      src/static/images/code_null.png
  38. BIN
      src/static/images/icon/arrow_b2.png
  39. BIN
      src/static/images/icon/index/green_bg_circle.png
  40. BIN
      src/static/images/icon/index/tab_12.png
  41. BIN
      src/static/images/icon/index/tab_13.png
  42. BIN
      src/static/images/icon/index/tab_8.png
  43. BIN
      src/static/images/icon/scan_code_btn.png
  44. BIN
      src/static/images/icon/selected_ring.png
  45. BIN
      src/static/images/icon/write_off_fail.png
  46. BIN
      src/static/images/order_null.png
  47. BIN
      src/static/images/scan_null.png
  48. BIN
      src/static/images/tab/ta_1.png
  49. BIN
      src/static/images/tab/ta_2.png
  50. BIN
      src/static/images/tab/ta_3.png
  51. BIN
      src/static/images/tab/ta_4.png
  52. BIN
      src/static/images/tab/tab_1.png
  53. BIN
      src/static/images/tab/tab_2.png
  54. BIN
      src/static/images/tab/tab_3.png
  55. BIN
      src/static/images/tab/tab_4.png
  56. BIN
      src/static/images/third_pages/banner.png
  57. BIN
      src/static/images/third_pages/bg.png
  58. BIN
      src/static/images/third_pages/tab_a.png
  59. BIN
      src/static/images/third_pages/tab_b.png
  60. BIN
      src/static/images/write_off/mall.png
  61. BIN
      src/static/images/write_off/order.png
  62. BIN
      src/static/images/write_off/people.png
  63. BIN
      src/static/images/write_off/site.png
  64. 85
      src/store/actions.js
  65. 23
      src/store/index.js
  66. 13
      src/store/mutations.js
  67. 188
      src/subpackage/authorization/components/login.vue
  68. 25
      src/subpackage/authorization/components/user_info/iconfont.css
  69. 133
      src/subpackage/authorization/components/user_info/impower.vue
  70. 357
      src/subpackage/authorization/components/user_info/tuniaoui-wx-user-info.vue
  71. 9
      src/subpackage/authorization/js/api.js
  72. 10
      src/subpackage/authorization/js/server.js
  73. 16
      src/subpackage/authorization/pages/index.vue
  74. 0
      src/subpackage/authorization/static/images/author_modal.png
  75. 4
      src/subpackage/blacklist/pages/abnormal_list/abnormal_list.vue
  76. 173
      src/subpackage/device/components/brand_change_modal.vue
  77. 30
      src/subpackage/device/components/order/reservation_people_detail/reservation_people_detail.vue
  78. 42
      src/subpackage/device/components/order/reservation_site_detail/reservation_site_detail.vue
  79. 265
      src/subpackage/device/components/order/timing_detail/timing_detail.vue
  80. 6
      src/subpackage/device/js/device_api.js
  81. 90
      src/subpackage/device/pages/index/index.vue
  82. 180
      src/subpackage/device/pages/lighting_time/list.vue
  83. 271
      src/subpackage/device/pages/lighting_time/setting.vue
  84. 212
      src/subpackage/device/pages/order_details/order_details.vue
  85. 8
      src/subpackage/device/pages/switch_manage/switch_manage.vue
  86. BIN
      src/subpackage/device/static/images/delete.png
  87. 41
      src/subpackage/menu/components/bottom_logo.vue
  88. 87
      src/subpackage/menu/components/mine/header.vue
  89. 63
      src/subpackage/menu/components/mine/line_tab.vue
  90. 16
      src/subpackage/menu/pages/index.vue
  91. BIN
      src/subpackage/menu/static/images/arrow_b2.png
  92. BIN
      src/subpackage/menu/static/images/bot_logo.png
  93. BIN
      src/subpackage/menu/static/images/mine_tab/0.png
  94. BIN
      src/subpackage/menu/static/images/mine_tab/1.png
  95. BIN
      src/subpackage/menu/static/images/mine_tab/2.png
  96. BIN
      src/subpackage/menu/static/images/mine_tab/3.png
  97. BIN
      src/subpackage/menu/static/images/mine_tab/4.png
  98. BIN
      src/subpackage/menu/static/images/mine_tab/5.png
  99. BIN
      src/subpackage/menu/static/images/mine_tab/6.png
  100. 59
      src/subpackage/message/components/detail/answer_item.vue

21914
package-lock.json
File diff suppressed because it is too large
View File

3
package.json

@ -64,8 +64,9 @@
"@vue/shared": "^3.0.0",
"core-js": "^3.6.5",
"flyio": "^0.6.2",
"pinyin-engine": "^1.2.2",
"regenerator-runtime": "^0.12.1",
"sass": "^1.63.3",
"sass": "1.63.3",
"vue": "^2.6.11"
},
"devDependencies": {

12121
pnpm-lock.yaml
File diff suppressed because it is too large
View File

3
src/App.vue

@ -3,9 +3,8 @@
import util from './utils/util';
export default {
onLaunch: function() {
// #ifndef H5
// this.$store.commit('setLoginState', { loginState: false });
this.updateManager();
// #endif
},
methods: {
isLogin(){

193
src/components/end_billing_modal/end_billing_modal.vue

@ -1,193 +0,0 @@
<template>
<!-- 弹框 结束计费 -->
<view class="ox-dark-mask" @touchmove.stop.prevent="moveHandle" @click.stop="">
<view class="odm-end-modal" >
<view class="oem-close">
<image src="/static/images/icon/x_close.png" @click="closeChange"></image>
</view>
<view class="oem-tit">结束计费</view>
<view class="oem-line">操作人{{orderInfo.end_bill_operator_name || '-'}}</view>
<view class="oem-line">时长合计<text class="ol-txt1">{{orderInfo.extension.duration || '-'}}</text></view>
<view class="oem-line">金额合计<text class="ol-txt2">¥{{orderInfo.amount || 0}}</text></view>
<view class="oem-box">
<view @click="selectBtn(1)">
<image :class="[selectType==1?'ob-img':'']" :src="selectType==1?'/static/images/icon/selected_ring.png':''"></image>
<view :class="[selectType==1?'ov-active':'']">停止计时</view>
</view>
<view @click="selectBtn(2)">
<image :class="[selectType==2?'ob-img':'']" :src="selectType==2?'/static/images/icon/selected_ring.png':''"></image>
<view :class="[selectType==2?'ov-active':'']">完结订单</view>
</view>
</view>
<view class="oem-tips">*<text>请输入金额</text></view>
<view class="oem-ipt"><input type="digit" v-model="input_amount"/></view>
<view class="oem-btn" hover-class="hover-active" @click="confirmChange" >确认</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import deviceApi from '../../subpackage/device/js/device_api.js';
import deviceServer from '../../subpackage/device/js/device_server.js';
import { mapState } from 'vuex';
export default {
props: {
orderInfo: {
type: Object,
default: ()=>({})
}
},
computed: {
...mapState(['brandInfo']),
},
data() {
return {
input_amount: "",
selectType: 1, //1() 2()
}
},
methods: {
moveHandle(){},
closeChange(){
this.$emit('close');
},
confirmChange(){
let { brandInfo, orderInfo, input_amount, selectType } = this
if(input_amount == '')return util.showNone('请输入金额!');
util.showLoad();
deviceServer.get({
url: deviceApi.timeOrderEnd,
data: {
brand_id: brandInfo.brand.id,
order_no: orderInfo.order_no,
pay_amount: parseFloat(input_amount),
end_type: selectType==1?'end_timing':selectType==2?'end_order':'',
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad()
util.showNone('操作成功!');
this.$emit('close');
// setTimeout(()=>uni.navigateBack(),1200);
this.$emit('timeEndBtn');
})
},
selectBtn(type){
this.selectType = type
},
},
}
</script>
<style lang="scss" >
@import '~style/public.scss';
.odm-end-modal{
position: absolute;
left: 65rpx;
top: 12%;
background-color: #fff;
width: 620rpx;
padding: 30rpx 0rpx 60rpx;
border-radius: 10rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.oem-close{
width: 100%;
@include centerFlex(flex-end);
>image{
width: 32rpx;
height: 32rpx;
margin-right: 30rpx;
}
}
.oem-tit {
color: #1A1A1A;
font-size: 32rpx;
font-weight: 700;
margin: 16rpx 0rpx 34rpx;
}
.oem-line{
width: 456rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 48rpx;
.ol-txt1{
color: #009874;
}
.ol-txt2{
color: #FF873D;
}
}
.oem-box{
margin: 58rpx 0 48rpx;
width: 456rpx;
@include centerFlex(space-between);
>view{
@include centerFlex(flex-start);
>image{
flex-shrink: 0;
width: 28rpx;
height: 28rpx;
border-radius: 50%;
border: 2rpx solid #D8D8D8;
&.ob-img{
width: 32rpx;
height: 32rpx;
border: 0rpx solid #D8D8D8;
}
}
>view{
margin-left: 12rpx;
font-size: 28rpx;
line-height: 48rpx;
color: #9C9C9F;
&.ov-active{
color: #1A1A1A;
}
}
}
}
.oem-tips{
width: 456rpx;
margin: 0rpx 0rpx 8rpx;
color: #EA5061;
font-size: 28rpx;
>text{
color: #333333;
}
}
.oem-ipt{
width: 456rpx;
border: 2rpx solid #D8D8D8;
border-radius: 10rpx;
& input {
flex-grow: 1;
height: 88rpx;
color: #1A1A1A;
font-size: 28rpx;
padding: 0rpx 20rpx;
}
}
.oem-btn{
width: 240rpx;
height: 88rpx;
margin-top: 60rpx;
border-radius: 10rpx;
background-color: #009874;
color: #FFF;
font-size: 32rpx;
line-height: 88rpx;
text-align: center;
}
}
</style>

38
src/components/fixed_bar/fixed_bar.vue

@ -0,0 +1,38 @@
<template>
<view class="fixed-bar" :style="'padding-top:'+ fbHeight +'px;'">
<view class="fb-fixed" id="fb">
<slot></slot>
</view>
</view>
</template>
<script>
import { getNodeMes } from '@/utils/util'
export default {
data(){
return {
fbHeight: 0,
}
},
mounted() {
getNodeMes('#fb', this).then(res => {
this.fbHeight = res?.height || 0
console.log(res)
})
},
}
</script>
<style lang="scss">
.fixed-bar{
}
.fb-fixed{
position: fixed;
padding-top: 20upx;
bottom: 0;
left: 0;
width: 100%;
z-index: 10;
}
</style>

22
src/components/order_refund/button.vue

@ -0,0 +1,22 @@
<template>
<view class="order-refund-button" @click="$emit('click')">
<slot>退款</slot>
</view>
</template>
<script>
export default {
}
</script>
<style lang="scss">
.order-refund-button{
width: 192upx;
text-align: center;
border-radius: 10upx;
background-color: #fff;
border: 2upx solid $mColor;
@include flcw(32upx, 84upx, $mColor, 500);
}
</style>

57
src/components/order_refund/fixed.vue

@ -0,0 +1,57 @@
<template>
<fixed-bar v-if="isShow">
<view class="order-refund-fixed">
<or-button @click="$emit('click:button')"></or-button>
</view>
</fixed-bar>
</template>
<script>
import fixedBar from "../fixed_bar/fixed_bar.vue";
import orButton from "./button.vue";
import { mapState } from 'vuex';
/**
* 全部订单都能退两次, 所有订单最多只能退2次款
* 只要订单金额是未全退的都能退
* 0元订单不能退
*
*/
export default {
components: {
fixedBar,
orButton
},
props: [ 'pay_amount', 'refund_amount', 'refund_times' ],
computed: {
...mapState([ 'brandInfo' ]),
payAmount(){
let { pay_amount } = this;
return +pay_amount || 0
},
refundAmount(){
let { refund_amount } = this;
return +refund_amount || 0
},
isShow(){
console.log('brandInfo', this.brandInfo)
let { payAmount, refundAmount, refund_times, brandInfo } = this;
if(
payAmount - refundAmount > 0
&&[0, 1].includes(refund_times)
&&brandInfo?.permission?.['1018'] // 退
){
return true
}
return false
}
},
}
</script>
<style lang="scss">
.order-refund-fixed{
padding: 10upx 24upx;
background: #F2F2F7;
@include ctf(flex-end);
}
</style>

56
src/components/order_refund/info.vue

@ -0,0 +1,56 @@
<template>
<view class="refund-info">
<view class="ri-name">退款信息{{ nameKey || '' }}</view>
<view class="ri-line">
<view class="ri-name">退款金额</view>
<view class="ri-value">{{ refund_price }}</view>
</view>
<view class="ri-line">
<view class="ri-name">退款单号</view>
<view class="ri-value">{{ refund_no }}</view>
</view>
<view class="ri-line">
<view class="ri-name">退款时间</view>
<view class="ri-value">{{ refund_time }}</view>
</view>
<view class="ri-line">
<view class="ri-name">退款原因</view>
<view class="ri-value">{{ refund_reason }}</view>
</view>
</view>
</template>
<script>
export default {
props: {
nameKey: { default: '' },
refund_price: { default: '-' },
refund_no: { default: '-' },
refund_time: { default: '-' },
refund_reason: { default: '-' },
}
}
</script>
<style lang="scss">
.refund-info{
padding: 30upx;
border-radius: 10upx;
background: #fff;
.ri-name{
flex-shrink: 0;
@include flcw(28upx, 40upx, #9C9C9F);
&+.ri-line{
margin-top: 16upx;
}
}
.ri-line{
@include ctf(space-between);
.ri-value{
flex-grow: 1;
@include flcw(28upx, 60upx, #1A1A1A);
@include tHide;
}
}
}
</style>

234
src/components/order_refund/modal.vue

@ -0,0 +1,234 @@
<template>
<view class="order-refund-modal">
<view class="orm-mask" v-show="isShow">
<view class="om-content">
<image class="oc-close" mode="aspectFit" src="../../static/images/icon/x_close.png" @click="hide"></image>
<view class="oc-title">退款</view>
<view class="oc-section">
<view class="os-info">
<view class="oi-name">{{ refundInfo.stadium_name || '' }}</view>
<view class="oi-line">订单编号{{ refundInfo.order_no || '' }}</view>
<view class="oi-line">手机号码{{ refundInfo.mobile || '' }}</view>
<view class="os-ipts">
<view class="oi-item">
<view class="oi-name">
<text class="on-txt">*</text>
退款金额
</view>
<view class="oi-right">
<input
class="or-ipt"
type="digit"
v-model="iptInfo.refund_amount"
:disabled="refundInfo.refund_times > 0"
:class="{ 'or-disabled': refundInfo.refund_times > 0 }"
/>
<view class="or-tip">可退{{ refundInfo.refundable_amount || 0 }}</view>
</view>
</view>
<view class="oi-item">
<view class="oi-name">
<text class="on-txt">*</text>
退款积分
</view>
<view class="oi-right">
<input
class="or-ipt" type="digit"
v-model="iptInfo.refund_integral"
:disabled="refundInfo.refund_times > 0"
:class="{ 'or-disabled': refundInfo.refund_times > 0 }"
/>
<view class="or-tip">可退{{ refundInfo.refundable_integral || 0 }}积分</view>
</view>
</view>
</view>
</view>
</view>
<view class="oc-btns">
<view class="ob-item" @click="cancelBtn">取消</view>
<view class="ob-item" @click="confirmBtn">确认</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* PM:
* 什么状态下都能退
* 全部订单都能退两次, 所有订单最多只能退2次款
* 只要订单金额是未全退的都能退
* 0元订单不能退
*
*/
import { mapState } from 'vuex';
import { showModal } from '@/utils/util';
export default {
computed: {
...mapState([ 'brandInfo' ])
},
data() {
return {
isShow: false,
refundInfo: {
/**
* @param {String} stadium_name 店铺名称
* @param {String} order_no 订单编号
* @param {String} mobile 手机号码
* @param {String} refundable_amount 退款金额
* @param {String} refundable_integral 退款积分
* @param {Number} refund_times 退款次数
* @param {Function} cancel 取消回调
* @param {Function} confirm 确认回调
*/
},
iptInfo: {
refund_amount: '',
refund_integral: ''
}
}
},
methods: {
show(initData){
let { brandInfo } = this;
if(brandInfo?.permission?.['1018']){
this.isShow = true;
this.init(initData);
}else{
showModal({
content: '您没有退款权限',
})
}
},
init(data){
this.iptInfo.refund_amount = data?.refundable_amount || '0';
this.iptInfo.refund_integral = data?.refundable_integral || '0';
this.refundInfo = data;
},
hide(){
this.isShow = false;
},
cancelBtn() {
this.hide();
this.$emit('click:cancel');
this.refundInfo?.cancel?.(0);
},
confirmBtn() {
let { iptInfo, refundInfo } = this;
this.$emit('click:confirm');
this.refundInfo?.confirm?.({
refund_amount: +iptInfo?.refund_amount || 0,
refund_integral: +iptInfo?.refund_integral || 0
});
this.hide();
}
}
}
</script>
<style lang="scss">
.order-refund-modal{}
.orm-mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,.5);
z-index: 10;
}
.om-content{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding-bottom: 80upx;
width: 620upx;
border-radius: 10px;
background: #fff;
.oc-close{
position: absolute;
top: 30upx;
right: 30upx;
width: 34upx;
height: 34upx;
}
.oc-title{
padding-top: 78upx;
text-align: center;
@include flcw(32upx, 44upx, #333, 500);
}
.oc-section{
padding: 30upx 30upx 0;
@include ctf(center);
.os-info{
.oi-name{
@include flcw(28upx, 48upx, #333);
}
.oi-line{
@include flcw(28upx, 48upx, #9C9C9F);
}
.os-ipts{
padding-top: 34upx;
.oi-item{
display: flex;
align-items: flex-start;
.oi-name{
@include flcw(28upx, 56upx, #333333);
.on-txt{
color: #EA5061;
}
}
.oi-right{
margin-left: 16upx;
flex-shrink: 0;
width: 312upx;
.or-ipt{
display: block;
box-sizing: border-box;
padding: 0 20upx;
width: 100%;
height: 56upx;
border-radius: 10upx;
border: 2upx solid #D8D8D8;
@include flcw(28upx, 48upx, #9C9C9F);
&.or-disabled{
background: #cecece;
color: #9C9C9F;
}
}
.or-tip{
padding-left: 8upx;
@include flcw(24upx, 48upx, #EA5061);
@include tHide;
}
}
}
}
}
}
.oc-btns{
margin-top: 32upx;
padding: 0 54upx;
@include ctf(space-between);
.ob-item{
width: 240upx;
height: 88upx;
text-align: center;
border: 2upx solid $mColor;
border-radius: 10upx;
@include flcw(32upx, 84upx, $mColor, 500);
&+.ob-item{
background: $mColor;
color: #fff;
}
}
}
}
</style>

49
src/components/order_refund/permission_btn.vue

@ -0,0 +1,49 @@
<template>
<view class="permissinon-btn">
<or-button v-if="isShow" @click="$emit('click:button')"><slot>退款</slot></or-button>
</view>
</template>
<script>
import orButton from "./button.vue";
import { mapState } from 'vuex';
/**
* 全部订单都能退两次, 所有订单最多只能退2次款
* 只要订单金额是未全退的都能退
* 0元订单不能退
*
*/
export default {
components: {
orButton
},
props: [ 'pay_amount', 'refund_amount', 'refund_times' ],
computed: {
...mapState([ 'brandInfo' ]),
payAmount(){
let { pay_amount } = this;
return +pay_amount || 0
},
refundAmount(){
let { refund_amount } = this;
return +refund_amount || 0
},
isShow(){
let { payAmount, refundAmount, refund_times, brandInfo } = this;
console.log('brandInfo', payAmount, refundAmount, refund_times, brandInfo)
if(
payAmount - refundAmount > 0
&&[0, 1].includes(refund_times)
&&brandInfo?.permission?.['1018'] // 退
){
return true
}
return false
}
},
}
</script>
<style>
</style>

138
src/components/period_select.vue

@ -0,0 +1,138 @@
<template>
<!-- 时间段选择 -->
<view class="period-select-mask" v-if="isShow" @click="isShow = false">
<view class="ol-period" @click.stop="()=>false">
<view class="op-tit">日期范围</view>
<picker mode="date" class="op-picker" @change="startTimeChange" :value="startTime">
<view class="op-time">
<text class="ot-tit">开始时间</text>
<view class="ot-right">
<input class="or-ipt" placeholder="选择时间" disabled :value="startTime"/>
<image class="or-icon" mode="aspectFit" src="/static/images/icon/arrow_b2.png"></image>
</view>
</view>
</picker>
<picker mode="date" class="op-picker" @change="endTimeChange" :value="endTime">
<view class="op-time">
<text class="ot-tit">截止时间</text>
<view class="ot-right">
<input class="or-ipt" placeholder="选择时间" disabled :value="endTime"/>
<image class="or-icon" mode="aspectFit" src="/static/images/icon/arrow_b2.png"></image>
</view>
</view>
</picker>
<view class="pl-btn active" @click="confirmBtn">确定</view>
</view>
</view>
</template>
<script>
import { showNone, showModal } from '@/utils/util';
export default {
props: {
value: {
type: String,
default: ''
}
},
watch: {
value: {
handler(val) {
let [start, end] = val.split('_');
this.startTime = start;
this.endTime = end;
},
immediate: true
}
},
data(){
return {
isShow: false,
startTime: '',
endTime: '',
}
},
methods: {
show(){
this.isShow = true
},
hide(){
this.isShow = false
},
startTimeChange(e){
this.startTime = e.detail.value;
},
endTimeChange(e){
this.endTime = e.detail.value;
},
confirmBtn(){
let { startTime, endTime } = this;
let _sTimestemp = new Date(startTime.replace(/\-/g,'/')).getTime();
let _eTimestemp = new Date(endTime.replace(/\-/g,'/')).getTime();
if(_sTimestemp > _eTimestemp)return showModal({ content: '开始时间不能大于结束时间' });
this.$emit('input', `${startTime}_${endTime}`);
this.hide();
}
}
}
</script>
<style lang="scss">
.period-select-mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.4);
z-index: 9999;
}
.ol-period{
position: absolute;
left: 0;
top: 0;
padding-left: 38upx;
padding-top: 66upx;
height: 472upx;
width: 100%;
background-color: #fff;
.op-tit{
@include flcw(24upx, 34upx, #9c9c9f);
}
.op-picker{
width: 100%;
}
.op-time{
padding-right: 40upx;
height: 124upx;
border-bottom: 2upx solid #D8D8D8;
@include ctf(space-between);
.ot-tit{
flex-shrink: 0;
@include flcw(32upx, 44upx, #1a1a1a);
}
.ot-right{
flex-grow: 1;
@include ctf(flex-end);
.or-ipt{
flex-grow: 1;
height: 100%;
text-align: right;
@include flcw(32upx, 44upx, #1a1a1a);
}
.or-icon{
flex-shrink: 0;
margin-left: 36upx;
width: 28upx;
height: 28upx;
}
}
}
.pl-btn{
min-height: 124upx;
text-align: center;
@include flcw(32upx, 124upx, $mColor, 500);
}
}
</style>

185
src/components/timing_order/timing_order.vue

@ -1,185 +0,0 @@
<template>
<view class="timing-order" @click="toOrderDetails">
<view class="ro-header">
<view class="rh-view">{{orderInfo.stadium_name || '-'}}</view>
<text :class="[ 'rh-text', orderInfo.pay_status == 0?'rh-active':'' ]">{{zh_order_status(orderInfo.pay_status)}}</text>
</view>
<view class="ro-section">
<view class="rs-line">
<view class="rl-view">订单编号</view>
<view class="rl-view">
<view class="rv-view">{{orderInfo.order_no || '-'}}</view>
</view>
<image class="rl-image" src="/static/images/icon/arrow_b2.png"></image>
</view>
<!-- 场时有 人时没有 order_type: 1场时 2人时 -->
<view class="rs-line" v-if="orderInfo.order_type == 1">
<view class="rl-view">项目</view>
<view class="rl-view">
<view class="rv-view">{{orderInfo.project_name || '-'}}</view>
</view>
</view>
<view class="rs-line">
<view class="rl-view">时长</view>
<view class="rl-view">
<view :class="[ 'rv-view', orderInfo.pay_status == 0?'rv-active':'' ]">{{ orderInfo.extension.duration || '-'}}</view>
</view>
</view>
<view class="rs-btn" v-if="orderInfo.pay_status == 0&&orderInfo.early_end_timing==false"><view @click.stop="isEndBill=true">结束计费</view></view>
</view>
<view class="ro-bot" v-if="orderInfo.pay_status != 0">
<view class="rb-total">实付款 <text class="rt-txt"> ¥{{orderInfo.pay_amount || 0}}</text></view>
</view>
<!-- 结束计费弹框 -->
<end-billing-modal
v-if="isEndBill==true"
@close="isEndBill=false"
@timeEndBtn="timeEndBtn"
:orderInfo="orderInfo"
></end-billing-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import end_billing_modal from '../end_billing_modal/end_billing_modal.vue';
export default {
components: {
'end-billing-modal': end_billing_modal,
},
props: {
orderInfo: {
type: Object,
default: ()=>({})
}
},
computed: {
zh_order_status(){
// 01
let { orderInfo } = this
return status =>{
const _obj = {
'0': orderInfo.early_end_timing==true?'待支付':'计费中',
'1': '已完成',
'4': '已退款',
}
return _obj[`${status}`] || '-'
}
},
},
data() {
return {
isEndBill: false,
}
},
methods:{
toOrderDetails(){
let { orderInfo } = this
// let _query = {}
// _query["stadium_id"] = orderInfo.stadium_id
// _query["order_no"] = orderInfo.order_no
// _query["order_type"] = 1
let _str = `stadium_id=${orderInfo.stadium_id || ''}&order_no=${orderInfo.order_no || ''}&order_type=1`
util.routeTo(`/subpackage/device/pages/order_details/order_details?${_str}`,'nT');
// this.$emit('orderDetailChange');
},
timeEndBtn(){
this.$emit('refreshList');
},
}
}
</script>
<style lang="scss" >
@import '~style/public.scss';
.timing-order{
padding: 0 24upx;
border-radius: 10upx;
background-color: #fff;
.ro-header{
margin-bottom: 18upx;
height: 98upx;
line-height: 96upx;
border-bottom: 2upx solid #D8D8D8;
@include centerFlex(space-between);
.rh-view{
flex-grow: 1;
font-size: 28upx;
color: #1a1a1a;
@include textHide(1);
}
.rh-text{
margin-left: 20upx;
flex-shrink: 0;
font-size: 28upx;
color: #9C9C9F;
}
.rh-active{
color: $themeColor;
}
}
.ro-section{
padding-bottom: 30upx;
.rs-line{
display: flex;
.rl-view,.rv-view{
line-height: 40upx;
font-size: 24upx;
color: #9c9c9f;
}
.rl-view{
&:first-child{
flex-shrink: 0;
}
&+.rl-view{
flex-grow: 1;
.rv-view{
@include textHide(1);
}
}
}
.rv-active{
color: $themeColor;
}
>.rl-image{
width: 32rpx;
height: 32rpx;
}
}
.rs-btn{
margin-top: 8rpx;
@include centerFlex(flex-end);
>view{
width: 192rpx;
height: 80rpx;
border: 2rpx solid #009874;
border-radius: 10rpx;
color: #009874;
font-size: 32rpx;
font-weight: 500;
text-align: center;
line-height: 80rpx;
}
}
}
.ro-bot{
padding-top: 20upx;
padding-bottom: 30upx;
border-top: 2upx solid #D8D8D8;
.rb-total{
line-height: 40upx;
text-align: right;
font-size: 24upx;
color: #9c9c9f;
.rt-txt{
color: #1A1A1A;
margin-left: 10rpx;
}
}
}
}
</style>

8
src/js/api.js

@ -30,7 +30,6 @@ export const API = {
// reservationOrder: `${ORIGIN}/admin/stadium/order/list`, // 预约订单
reservationOrder:`${ORIGIN}/admin/assistant/stadiumOrder/list`, //k-订单管理-预约订单列表 PM:2021/4/19
timeOrder:`${ORIGIN}/admin/assistant/timeOrder/list`, //k-订单管理-计时订单列表 PM:2021/4/19
membershipOrder: `${ORIGIN}/admin/member_card/order/list`, // 会员卡订单
@ -152,6 +151,7 @@ API['writeOff'] = {
API['order'] = {
reservationDetail:`${ORIGIN}/admin/assistant/stadiumOrder/detail`, //k-订单管理-场馆预约订单-预约场馆订单详情/ 次卡订单详情
sessionVerify:`${ORIGIN}/admin/person/number/rule/orderinfo/sessionVerify`, // 次票核销,输入二维码数量核销
orderRefundList:`${ORIGIN}/admin/stadium/order/refundList`, // 硬件管理(所有的订单)-退款信息列表(包括预订,计时)
}
// 20240325 后台功能迁移到小程序端
@ -167,6 +167,10 @@ API['party'] = {
bindingOpenId: `${ORIGIN}/assistant/bindingOpenId`, //改为小程序端绑定openid
}
export default { ORIGIN, API };
API['mine'] = {
userCurrent:`${ORIGIN}/user/current`, //获取账户信息, (不要传品牌id)
unbindAssistant:`${ORIGIN}/admin/user/unbindAssistant`, // 解绑退出
}
export default { ORIGIN, API };

12
src/js/server.js

@ -1,5 +1,6 @@
import util from '../utils/util';
// import { app as vm } from '../main';
import { app as vm } from '../main';
const islog = true;
@ -27,6 +28,9 @@ export class Server {
method: method,
data: res,
})
handleUserExceptions({ res });
if(isDefaultGet){
if(failMsg == '')throw Error('默认回调,失败提示不能为空 key -> failMsg');
defaultGet({
@ -70,6 +74,14 @@ export class Server {
reject({url,res,data});
}
}
// 用户信息异常
function handleUserExceptions({ res }){
if(res?.data?.code === 401&&res?.data?.message?.indexOf('用户') !== -1){
console.log('用户信息异常,请重新登录');
const _store = vm.$store;
_store.commit('setLoginState', { loginState: false });
}
}
}
get({url,data={},header={},isDefaultGet=true,failMsg=''}){
return this.request(url,data,'GET',header,isDefaultGet,failMsg);

3
src/main.js

@ -6,8 +6,9 @@ Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
export const app = new Vue({
...App,
store
})
app.$mount()

4
src/manifest.json

@ -76,7 +76,9 @@
"appid" : "wx7106e84614cf0060",
"setting" : {
"urlCheck" : false,
"minified" : true
"minified" : true,
"ignoreDevUnusedFiles": false,
"ignoreUploadUnusedFiles": false
},
"usingComponents" : true,
"optimization": {

260
src/pages.json

@ -1,12 +1,47 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"componentPlaceholder": {
"bottom-logo": "view",
"authorization-login": "view"
}
}
},
{
"path": "pages/menu/second",
"style": {
"navigationBarTitleText": "推广营销",
"componentPlaceholder": {
"bottom-logo": "view"
}
}
},
{
"path": "pages/menu/third",
"style": {
"navigationBarTitleText": "商家服务",
"componentPlaceholder": {
"bottom-logo": "view"
}
}
},
{
"path": "pages/menu/forth",
"style": {
"navigationBarTitleText": "个人中心",
"componentPlaceholder": {
"mine-header": "view",
"bottom-logo": "view",
"line-tab": "view",
"authorization-login": "view",
"authorization-user": "view"
}
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
@ -171,24 +206,6 @@
}
},
{
"path": "pages/write_off/mall/list/list",
"style": {
"navigationBarTitleText": "商城订单核销"
}
},
{
"path": "pages/write_off/number_of_people/number_of_people",
"style": {
"navigationBarTitleText": "查询"
}
},
{
"path": "pages/write_off/menu/menu",
"style": {
"navigationBarTitleText": "核销订单"
}
},
{
"path": "pages/write_off/ym_card_gated/ym_card_gated",
"style": {
"navigationBarTitleText": "核销订单"
@ -201,36 +218,12 @@
}
},
{
"path": "pages/write_off/search_result/search_result",
"style": {
"navigationBarTitleText": "核销查询"
}
},
{
"path": "pages/write_off/operate/operate",
"style": {
"navigationBarTitleText": "核销订单"
}
},
{
"path": "pages/write_off/confirm_order/confirm_order",
"style": {
"navigationBarTitleText": "核销订单"
}
},
{
"path": "pages/write_off/null/null",
"style": {
"navigationBarTitleText": "确认订单信息"
}
},
{
"path": "pages/write_off/douyin/poi_list",
"style": {
"navigationBarTitleText": "核销订单"
}
},
{
"path": "pages/employee/review_list/review_list",
"style": {
"navigationBarTitleText": "员工管理"
@ -444,6 +437,18 @@
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/lighting_time/list",
"style": {
"navigationBarTitleText": "照明管理"
}
},
{
"path": "pages/lighting_time/setting",
"style": {
"navigationBarTitleText": "照明管理"
}
}
]
},
@ -486,6 +491,36 @@
"root": "subpackage/order",
"pages": [
{
"path": "pages/timekeeping/order_search",
"style" : {
"navigationBarTitleText": "订单搜索"
}
},
{
"path": "pages/timekeeping/order_list",
"style" : {
"navigationBarTitleText": "计时订单"
}
},
{
"path": "pages/timekeeping/order_detail/person",
"style" : {
"navigationBarTitleText": "散客计时订单详情"
}
},
{
"path": "pages/timekeeping/order_detail/site",
"style" : {
"navigationBarTitleText": "场地计时订单详情"
}
},
{
"path": "pages/timekeeping/order_detail/deposit",
"style" : {
"navigationBarTitleText": "场地计时订单详情"
}
},
{
"path": "pages/stored_value_card/detail/detail",
"style" : {
"navigationBarTitleText": "储值卡订单详情"
@ -723,9 +758,15 @@
}
},
{
"path": "pages/index/recharge_record",
"style" : {
"navigationBarTitleText": "充值记录"
}
},
{
"path": "pages/douyin_withdraw/index",
"style" : {
"navigationBarTitleText": "抖音提现"
"navigationBarTitleText": "平台提现"
}
},
{
@ -913,6 +954,106 @@
}
}
]
},
{
"root": "subpackage/verification",
"pages": [
{
"path": "pages/null",
"style" : {
"navigationBarTitleText": "核销查询"
}
},
{
"path": "pages/record_search",
"style" : {
"navigationBarTitleText": "核销查询"
}
},
{
"path": "pages/index",
"style" : {
"navigationBarTitleText": "核销查询"
}
},
{
"path": "pages/site_people/index",
"style" : {
"navigationBarTitleText": "现场人数"
}
},
{
"path": "pages/record",
"style" : {
"navigationBarTitleText": "核销记录"
}
}
]
},
{
"root": "subpackage/message",
"pages": [
{
"path": "pages/complaint/edit",
"style" : {
"navigationBarTitleText": "投诉建议"
}
},
{
"path": "pages/complaint/list",
"style" : {
"enablePullDownRefresh" : true,
"navigationBarTitleText": "投诉建议"
}
},
{
"path": "pages/complaint/detail",
"style" : {
"navigationBarTitleText": "投诉详情"
}
},
{
"path": "pages/work_order/list",
"style" : {
"enablePullDownRefresh" : true,
"navigationBarTitleText": "工单列表"
}
},
{
"path": "pages/work_order/detail",
"style" : {
"navigationBarTitleText": "工单详情"
}
},
{
"path": "pages/work_order/edit",
"style" : {
"navigationBarTitleText": "提交工单"
}
}
]
},
{
"root": "subpackage/menu",
"pages": [
{
"path": "pages/index",
"style" : {
"navigationBarTitleText": ""
}
}
]
},
{
"root": "subpackage/authorization",
"pages": [
{
"path": "pages/index",
"style" : {
"navigationBarTitleText": ""
}
}
]
}
],
"globalStyle": {
@ -920,5 +1061,36 @@
"navigationBarTitleText": "欧轩智能商家助手",
"navigationBarBackgroundColor": "#009874",
"backgroundColor": "#f2f2f7"
},
"tabBar": {
"color": "#B2B2B2",
"selectedColor": "#009874",
"backgroundColor": "#FFFFFF",
"list": [
{
"pagePath": "pages/index/index",
"text": "工作台",
"iconPath": "static/images/tab/tab_1.png",
"selectedIconPath": "static/images/tab/ta_1.png"
},
{
"pagePath": "pages/menu/second",
"text": "推广营销",
"iconPath": "static/images/tab/tab_2.png",
"selectedIconPath": "static/images/tab/ta_2.png"
},
{
"pagePath": "pages/menu/third",
"text": "商家服务",
"iconPath": "static/images/tab/tab_3.png",
"selectedIconPath": "static/images/tab/ta_3.png"
},
{
"pagePath": "pages/menu/forth",
"text": "我的",
"iconPath": "static/images/tab/tab_4.png",
"selectedIconPath": "static/images/tab/ta_4.png"
}
]
}
}

377
src/pages/index/index.vue

@ -2,22 +2,22 @@
<view class="index-container">
<view class="ic-content">
<view class="ic-header">
<view class="ih-address" v-if="loginStatus" @click="toStoreList">
<view class="ih-address" v-if="loginState" @click="toStoreList">
<view>{{indexData.brand_name || '-'}}({{indexData.stadium_num || '0'}})</view>
<image src="/static/images/icon/arrow_ff.png" mode="aspectFit"></image>
</view>
<view v-else class="ih-btn" hover-class="hover-active" @click="showAuthor">点击登陆</view>
<block v-if="isPermissionShowTab({ serverKey: 1001 })">
<view class="ih-tip">今日总收入</view>
<view class="ih-price"><text>{{loginStatus?'¥':''}}</text>{{loginStatus?(indexData.amount || '0'):'***'}}</view>
<view class="ih-price"><text>{{loginState?'¥':''}}</text>{{loginState?(indexData.amount || '0'):'***'}}</view>
<view class="ih-amount">
<view>
<view>收款笔数</view>
<view>{{loginStatus?(indexData.in_count || '0'):'**'}}</view>
<view>{{loginState?(indexData.in_count || '0'):'**'}}</view>
</view>
<view>
<view>退款笔数</view>
<view>{{loginStatus?(indexData.out_count || '0'):'**'}}</view>
<view>{{loginState?(indexData.out_count || '0'):'**'}}</view>
</view>
</view>
</block>
@ -37,7 +37,8 @@
</view>
<view class="ic-tabs" v-if="indexData&&indexData.permission">
<block v-for="(e, i) in tabList" :key="i">
<view class="it-item" @click="toPageInfo(e)" v-if="isPermissionShowTab(e)">
<!-- tid1807 去掉"核销查询菜单(已有悬浮按钮) -->
<view class="it-item" @click="toPageInfo(e)" v-if="isPermissionShowTab(e)&&e.id!=4">
<view>
<image mode="aspectFit" :src="'/static/images/icon/index/tab_'+ e.id + '.png'"></image>
<view>{{e.name}}</view>
@ -49,33 +50,12 @@
<navigator url="/subpackage/party/pages/login/login">party/pages/login/login</navigator>
</view> -->
</view>
<view class="ox-dark-mask" v-if="isShowAuthorModal">
<view class="ic-author-modal">
<view class="iam-title">微信授权</view>
<view class="iam-tip">您的信息和数据将受到保护</view>
<image class="iam-pic" mode="aspectFit" src="/static/images/icon/author_modal.png"></image>
<view class="iam-btns">
<button plain hover-class="hover-active" @click="cancelAuthor">取消</button>
<bottom-logo></bottom-logo>
<button
v-if="isProfile"
plain
hover-class="hover-active"
@click="profileConfirm"
>授权并登录</button>
<button
v-else
plain
hover-class="hover-active"
open-type="getUserInfo"
lang="zh_CN"
@getuserinfo="confirmAuthor"
>授权并登录</button>
</view>
</view>
</view>
<authorization-login ref="authorizationLogin"></authorization-login>
<view class="fly_btn" @click="toPageInfo(tabList[4])" v-if="loginStatus">
<view class="fly_btn" @click="toPageInfo(getTabForID(4))" v-if="loginState&&isPermissionShowTab(getTabForID(4))">
<image class="f_bg" src="/static/images/icon/index/green_bg_circle.png" mode="scaleToFill"/>
<image class="f_icon" src="/static/images/icon/index/scan_icon_white.png" mode="scaleToFill"/>
<text>核销</text>
@ -88,6 +68,19 @@
import util from '../../utils/util';
import { servers } from '../../js/server';
import { API } from '../../js/api';
import { mapGetters } from 'vuex';
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
import authorizationLogin from "@/subpackage/authorization/components/login.vue";
/**
* tid1807
* 1去掉"核销查询菜单(已有悬浮按钮) *
* 2去掉收款记录(在收入统计页面已有入口进入收款记录) *
* 3去掉进场人数异常(该入口计划放入新改版的核销查询页面) *
* 4系统工具文案改为小程序管理;钱包提现文案改为钱包&提现: 设备管理文案改为智能设备; *
* 5增加底部 (工作台推广营销商家服务我的) *
* 6增加工单功能
*
* */
const tabList = [
{
id: 0,
@ -96,16 +89,10 @@
serverKey: 1001 //
},
{
id: 1,
name: '收款记录',
path: '/subpackage/income/pages/details_record/details_record',
serverKey: 1002 //
},
{
id: 2,
name: '订单管理',
path: '/subpackage/order/pages/order_manage/order_manage',
serverKey: 1012 //
id: 11,
name: '钱包&提现',
path: '/subpackage/wallet/pages/index/index',
serverKey: 1017 // 1017
},
{
id: 3,
@ -114,10 +101,10 @@
serverKey: 1007 //
},
{
id: 4,
name: '核销查询',
path: '/pages/write_off/menu/menu',
serverKey: 1008 //
id: 2,
name: '订单管理',
path: '/subpackage/order/pages/order_manage/order_manage',
serverKey: 1012 //
},
{
id: 5,
@ -127,23 +114,11 @@
serverKey: 1009 //
},
{
id: 6,
name: '设备管理',
path: '/subpackage/device/pages/index/index',
serverKey: 1010 //
},
{
id: 7,
name: '商品零售',
path: '/subpackage/retail/pages/index/index',
serverKey: 1011 //
},
// {
// id: 8,
// name: '',
// path: '/subpackage/course/pages/index/index',
// serverKey: 1013 // 1013
// },
{
id: 9,
name: '储值卡管理',
@ -151,79 +126,85 @@
serverKey: 1014 // 1014
},
{
id: 10,
name: '进场人数异常',
path: '/subpackage/blacklist/pages/abnormal_list/abnormal_list',
serverKey: 1015 // 1015
},
{
id: 11,
name: '钱包提现',
path: '/subpackage/wallet/pages/index/index',
serverKey: 1017 // 1017
id: 6,
name: '智能设备',
path: '/subpackage/device/pages/index/index',
serverKey: 1010 //
},
{//ICON
id: 12,
name: '系统工具',
name: '小程序管理',
path: '/subpackage/party/pages/index/index',
serverKey: 1016 // 1016
},
{
id: 13,
name: '工单',
path: '/subpackage/message/pages/work_order/list',
serverKey: 1022 // 1022
},
// {
// id: 1,
// name: '',
// path: '/subpackage/income/pages/details_record/details_record',
// serverKey: 1002 //
// },
{
id: 4,
name: '核销查询',
path: '/subpackage/verification/pages/index',
serverKey: 1008 //
},
// {
// id: 8,
// name: '',
// path: '/subpackage/course/pages/index/index',
// serverKey: 1013 // 1013
// },
// {
// id: 10,
// name: '',
// path: '/subpackage/blacklist/pages/abnormal_list/abnormal_list',
// serverKey: 1015 // 1015
// },
];
const app = getApp();
// #ifndef H5
const uniGetSetting = util.promisify(uni.getSetting);
const uniLogin = util.promisify(uni.login);
const uniGetUserInfo= util.promisify(uni.getUserInfo);
const APPID = uni.getAccountInfoSync().miniProgram.appId;
// #endif
// #ifdef H5
const APPID = "wx7106e84614cf0060" //TODO
// #endif
export default {
components: { bottomLogo, authorizationLogin },
computed: {
isProfile: _=>util.isProfile(),
// loginStatus(){
// return app.isLogin();
// }
...mapGetters([ 'loginState' ]),
},
data() {
return {
tabList,
isShowAuthorModal: false,
indexData: {},
loginStatus: false,
}
},
async onLoad(options) {
try{
util.showLoad();
let _loginRes = {};
// 20230829
if(options.role !== 'ADMIN-PLATFORM')await this.checkUserAuthor();
util.hideLoad();
this.loginStatus = app.isLogin();
if(!!app.isLogin())this.getIndexInfo();
if(options.role !== 'ADMIN-PLATFORM')_loginRes = await this.$store.dispatch('checkUserAuthor');
// 20240325 openid
if(_loginRes?.data === '')this.webBrandUserCheck();
if(_loginRes?.data)this.getIndexInfo();
}catch(err){
util.hideLoad();
uni.removeStorageSync('token');
// util.showNone(err.message || '');
console.log('onLoad err',err);
this.loginStatus = app.isLogin();
console.warn('pages index onLoad err--->', err);
}
},
onShow(){
let { indexData } = this;
if(JSON.stringify(indexData)!='{}'&&!!app.isLogin())this.getIndexInfo();
let { indexData, loginState, getIndexInfo } = this;
if(JSON.stringify(indexData)!='{}'&&loginState)getIndexInfo();
},
methods: {
getTabForID(id){
return tabList.find(e=>e.id === id) || {}
},
// ID1000840
isPermissionShowTab(e){
let { indexData } = this;
@ -233,13 +214,13 @@
return false;
},
toNoticeList(){
if(!app.isLogin())return this.showAuthor();
if(!this.loginState)return this.showAuthor();
util.routeTo(`/pages/message/list/list`,'nT');
},
toPageInfo(tabInfo){
let { indexData } = this;
let { indexData, loginState } = this;
let _permission = indexData.permission || {};
if(!app.isLogin())return this.showAuthor();
if(!loginState)return this.showAuthor();
if(!tabInfo.path)return util.showNone('暂未开放!');
//if(tabInfo.id == 2)return util.routeTo(tabInfo.path,'nT'); //
if(!_permission[tabInfo.serverKey])return util.showNone('暂无权限,请联系管理员开启!')
@ -251,10 +232,9 @@
});
}
if(tabInfo.id === 10 || tabInfo.id === 11)return util.routeTo(tabInfo.path + `?brand_id=${indexData.brand.id}`,'nT');
if([ 10, 11, 4, 13 ].includes(tabInfo.id))return util.routeTo(tabInfo.path + `?brand_id=${indexData.brand.id}`,'nT');
if(tabInfo.id === 12){
// if(!indexData.brand.mini_wechat_appid)return util.showNone('appid')
let url = tabInfo.path + `?appid=${indexData.brand.mini_wechat_appid}`
console.log(222,url)
return util.routeTo(url,'nT');
@ -275,137 +255,15 @@
this.$store.commit('setBrandInfo',res);
})
},
// token
async checkUserAuthor(){
try{
let loginRes =""
// #ifndef H5
loginRes = await uniLogin();
// #endif
// #ifdef H5
loginRes = {errMsg: "login:ok", code: "0c3xEi0w3kr1t23zcU3w3ZQR3w3xEi0h"} //TODO H5
// #endif
console.log(loginRes)
return servers.get({
url: API.wechatMiniAppLoginAndSync,
data: {
code: loginRes.code,
appid: APPID,
// #ifdef H5
token:"3d2b0092-e761-11ee-8a66-5254005df464", //TODO
// #endif
},
isDefaultGet: false,
})
.then(res=>{
util.hideLoad();
let _data = res.data;
if(_data.code == 0){
// let _data = res.data.data;
if(_data.data == ''){
uni.removeStorageSync('token');
this.loginStatus = app.isLogin();
// 20240325 openid
this.webBrandUserCheck();
// return util.routeTo(`/pages/login/login`,'rL');
}
uni.setStorageSync('token',_data.data);
return _data;
}else{
util.showNone(_data.message || '校验身份失败!');
throw res.data || {};
}
})
.catch(err=>{
throw err;
})
}catch(err){
throw err;
}
},
showAuthor(){
this.isShowAuthorModal = true
},
closeAuthor(){
this.isShowAuthorModal = false
},
cancelAuthor(){
this.closeAuthor();
},
getLoginQuery({
userInfo,
loginRes
}){
return {
appid: APPID,
code: loginRes.code,
encryptedData: userInfo.encryptedData,
// is_details: 1,
//
user_info: userInfo.userInfo,
user_raw_data: userInfo.rawData,
...userInfo.userInfo,
}
},
//
profileConfirm(){
uni.getUserProfile({
lang: 'zh_CN', desc: '授权登陆',
success: res => {
this.confirmAuthor({detail: {...res}});
this.$refs?.authorizationLogin?.alert?.({
success: ()=>{
setTimeout(this.getIndexInfo, 1000);
},
fail: function(err) {
util.showNone('获取用户信息失败!请重试');
console.warn('getUserProfile Err', err)
fail: err =>{
// console.warn('pages index showAuthor authorizationLogin Err ->', err)
}
})
},
async confirmAuthor(userRes){
if(!userRes.detail.userInfo){
this.closeAuthor();
return util.showNone('获取用户信息失败!请稍后重试');
}
let loginRes = await uniLogin();
if(!loginRes.code){
this.closeAuthor();
return util.showNone('获取登陆凭证失败!稍后重试');
}
servers.post({
url: API.wechatMiniAppLoginAndSync,
data: this.getLoginQuery({
userInfo: userRes.detail,
loginRes
}),
isDefaultGet: false,
})
.then(res=>{
util.hideLoad();
let _data = res.data || {};
if(_data.code == 0){
if(_data.data == '')return util.routeTo(`/pages/login/login`,'rL');
util.showNone(_data.message || '登陆成功!');
// let _data = res.data.data;
// if(_data.user.role == '')return util.routeTo(`/pages/merchant_login/merchant_login`,'rL');
uni.setStorageSync('token',_data.data);
setTimeout(_=>{
this.getIndexInfo();
this.closeAuthor();
this.loginStatus = app.isLogin();
}, 1200);
}else{
util.showNone(_data.message || '后台登陆失败!');
setTimeout(_=>this.closeAuthor(), 1200);
}
}).catch(util.hideLoad)
},
webBrandUserCheck(){
@ -451,8 +309,8 @@
})
},
toStoreList(){
if(!app.isLogin())return this.showAuthor();
let { indexData } = this;
let { indexData, loginState } = this;
if(!loginState)return this.showAuthor();
util.routeTo(`/pages/store_list/store_list?brand_id=${indexData.brand.id}`,'nT');
},
bindUserOpenid(){
@ -682,57 +540,6 @@
}
}
.ic-author-modal{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding-top: 60upx;
width: 662upx;
height: 884upx;
border-radius: 10upx;
background-color: #fff;
.iam-title{
margin-bottom: 22upx;
text-align: center;
line-height: 60upx;
font-size: 44upx;
font-weight: 500;
color: #1a1a1a;
}
.iam-tip{
margin-bottom: 52upx;
line-height: 40upx;
text-align: center;
font-size: 28upx;
color: #9c9c9f;
}
.iam-pic{
margin: 0 auto 62upx;
display: block;
width: 488upx;
height: 416upx;
}
.iam-btns{
@include centerFlex(center);
>button{
margin: 0 20upx;
width: 240upx;
height: 92upx;
line-height: 88upx;
text-align: center;
border-radius: 46upx;
border: 2upx solid $themeColor;
font-size: 32upx;
color: $themeColor;
&+button{
background-color: $themeColor;
color: #fff;
}
}
}
}
// fly_btn
.fly_btn{

6
src/pages/login/login.vue

@ -43,7 +43,7 @@
<script>
import { API } from '../../js/api';
import { servers } from '../../js/server';
import util from '../../utils/util';
import util, { jsonStr } from '../../utils/util';
// import wxplugin from '../../utils/wx_plugin';
const uniGetSetting = util.promisify(uni.getSetting);
const uniLogin = util.promisify(uni.login);
@ -67,7 +67,7 @@ export default {
//
MiniprogramThirdpartyPlugin.init(wx)
// MiniprogramThirdpartyPlugin.init(wx)
},
@ -189,7 +189,7 @@ export default {
}
},
toWebView(){
util.routeTo(`/pages/web_view/web_view?src=${encodeURIComponent("https://www.ouxuanzhineng.cn")}`,'rL');
util.routeTo(`/pages/web_view/web_view?src=${jsonStr("https://www.ouxuanzhineng.cn")}`,'rL');
}
},
}

243
src/pages/menu/forth.vue

@ -0,0 +1,243 @@
<template>
<view class="forth-menu">
<!-- 后端 account-> 如果有手机号就显示手机号 没有的话显示username, 都没有的话就空好了 -->
<mine-header
:is-login="loginState"
:nickname="userInfo.nickname"
:name="userInfo.actual_name"
:account="userInfo.mobile || userInfo.username || ''"
:photo="userInfo.avatar_url"
@on:munted="menuPackageLoaded = true"
@click:login="loginBtn"
@click:update="updateUser"
></mine-header>
<block v-if="menuPackageLoaded">
<line-tab :icon-num='0'>
<template slot="default">账号管理</template>
<template slot="right">
<view class="ft-account">
<view class="fc-name" v-if="loginState">{{ accountName || '-' }}</view>
<image class="fc-icon" mode="aspectFit" src="/static/images/icon/arrow_b2.png"></image>
</view>
</template>
</line-tab>
<line-tab :icon-num='1' @click="toWebView(deadData.helpCenterLink)">帮助中心</line-tab>
<block v-if="loginState">
<line-tab :icon-num='2' @click="toMiniProgram(deadData.assistantCoachAppid)">教练助手</line-tab>
<line-tab :icon-num='3'>
<template slot="default">收银系统</template>
<template slot="right">
<view class="fm-copy" @click="copyLink(deadData.cashierSystemLink)">(复制网址)</view>
</template>
</line-tab>
<line-tab :icon-num='4' @click="toWebView(deadData.backstageLink)">
<view class="fm-admin">
<view class="fa-txt">总后台<text class="ft-txt">({{ deadData.backstageLink }})</text></view><view class="fa-copy" @click.stop="copyLink(deadData.backstageLink)">复制</view>
</view>
</line-tab>
</block>
<line-tab :icon-num='5' @click="toComplaint">投诉建议</line-tab>
<line-tab :icon-num='6' v-if="loginState" @click="unBindBtn">解绑退出</line-tab>
</block>
<bottom-logo></bottom-logo>
<authorization-login ref="authorizationLogin"></authorization-login>
<authorization-user ref="authorizationUser"></authorization-user>
</view>
</template>
<script>
import mineHeader from "@/subpackage/menu/components/mine/header.vue";
import lineTab from "@/subpackage/menu/components/mine/line_tab.vue";
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
import authorizationLogin from "@/subpackage/authorization/components/login.vue";
import authorizationUser from "@/subpackage/authorization/components/user_info/impower.vue";
import { routeTo, debounce, showLoad, hideLoad, showModal, showNone, jsonStr } from "@/utils/util.js";
import { mapGetters, mapState } from 'vuex';
import { servers } from '../../js/server';
import { API } from '../../js/api';
export default {
components:{
mineHeader,
lineTab,
bottomLogo,
authorizationLogin,
authorizationUser,
},
data(){
return {
//
menuPackageLoaded: false,
userInfo: {},
deadData: {
helpCenterLink: 'https://help.ouxuanzhineng.cn/', //
cashierSystemLink: 'https://kb.ouxuanzhineng.cn/', //
backstageLink: 'https://admin.ouxuanzhineng.cn/', //
assistantCoachAppid: 'wxd71043ec955dfecf', // AppID
},
}
},
computed: {
...mapGetters([ 'loginState' ]),
...mapState([ 'brandInfo' ]),
extension(){
return this.userInfo?.extension || {}
},
/**
* @param {string} permission_type 1:ADMIN-STADIUM 2:ADMIN-BRAND
* */
permissionType(){
return this.userInfo?.extension?.permission_type || '';
},
accountName(){
let { permissionType, extension } = this;
if(permissionType === 'ADMIN-STADIUM')return extension?.stadium_name ?? '';
if(permissionType === 'ADMIN-BRAND')return extension?.brand_name ?? '';
return '';
}
},
onLoad(options){
if(this.loginState)this.getUserInfo();
},
methods: {
//
unBindBtn: debounce(function(){
let { userInfo } = this;
if(!userInfo?.id)return showModal({ content: '用户信息有误' })
showModal({
content: '您确定要解绑退出吗?',
showCancel: true,
success: mRes=>{
if(mRes.confirm)this.unBindReq(userInfo.id);
}
})
}, 300, true),
//
unBindReq(user_id){
showLoad();
return servers.post({
url: API.mine.unbindAssistant,
data: { user_id },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showNone('操作成功!');
setTimeout(() => {
this.$store.commit('setLoginState', { loginState: false });
routeTo(`/pages/login/login`,'rL');
}, 1000);
console.log('pages menu unBindReq res --->', _data);
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({ content: err?.message || '操作失败!' });
console.warn('pages menu unBindReq err --->', err);
// return Promise.reject(err);
})
},
//
updateUser(){
this.$refs?.authorizationUser?.show?.({ success: this.getUserInfo });
},
//
toMiniProgram(appid){
uni.navigateToMiniProgram({ appId: appid });
},
//
toWebView(url){
routeTo(`/pages/web_view/web_view?src=${jsonStr(url)}`, 'nT');
},
//
copyLink(url){
uni.setClipboardData({ data: url });
},
//
toComplaint(){
let { loginState, showAuthor, brandInfo } = this;
if(!loginState)return showAuthor();
routeTo(`/subpackage/message/pages/complaint/list?brand_id=${brandInfo?.brand?.id}`, 'nT');
},
//
loginBtn(){
this.showAuthor();
},
showAuthor(){
this.$refs?.authorizationLogin?.alert?.({ success: this.getUserInfo });
},
getUserInfo(){
showLoad();
return servers.post({
url: API.mine.userCurrent,
data: {},
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
this.userInfo = _data?.data || {};
console.log('pages menu getUserInfo res --->', _data);
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({ content: err?.message || '加载用户失败!' });
console.warn('pages menu getUserInfo err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.forth-menu{
}
.fm-txt{
font-size: 28upx;
.ft-copy{
color: $mColor;
}
}
.ft-account{
@include ctf(flex-end);
.fc-name{
@include flcw(32upx, 48upx, #9A9A9D);
@include tHide;
}
.fc-icon{
flex-shrink: 0;
margin-left: 22upx;
width: 30upx;
height: 30upx;
}
}
.fm-copy{
@include flcw(32upx, 44upx, $mColor);
}
.fm-admin{
@include ctf;
.fa-txt{
@include flcw(32upx, 44upx, #9A9A9D);
@include tHide;
.ft-txt{
font-size: 28upx;
}
}
.fa-copy{
padding: 0upx 10upx;
flex-shrink: 0;
@include flcw(28upx, 44upx, $mColor);
}
}
</style>

32
src/pages/menu/second.vue

@ -0,0 +1,32 @@
<template>
<view class="second-pages">
<view class="sp-post">
<view class="sp-box">
<view class="tl-tit">板块更新中</view>
</view>
</view>
<bottom-logo is-fixed></bottom-logo>
</view>
</template>
<script>
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
export default {
components:{ bottomLogo },
}
</script>
<style lang="scss">
.sp-post{
padding: 24upx;
.sp-box{
padding: 22upx 28upx;
height: 400upx;
background-image: linear-gradient(270deg, #d7d7da 0%, $mColor 100%);
.tl-tit{
@include flcw(43upx, 60upx, #fff, 500);
@include tHide;
}
}
}
</style>

117
src/pages/menu/third.vue

@ -0,0 +1,117 @@
<template>
<view class="third-page">
<view class="tp-container">
<image class="tc-post" mode="aspectFit" src="/static/images/third_pages/banner.png"></image>
<view class="tc-bot">
<view class="tb-left" @click="clickTap">
<image class="tl-bg" mode="aspectFill" src="/static/images/third_pages/bg.png"></image>
<view class="tl-tit">采购商城</view>
<view class="tl-txt">采购专业装备</view>
</view>
<view class="tb-right">
<view class="tr-item active" @click="clickTap">
<view class="ti-title">客服外包</view>
<view class="ti-text">客服外包</view>
<image class="tr-icon" src="/static/images/third_pages/tab_a.png"></image>
</view>
<view class="tr-item" @click="clickTap">
<view class="ti-title">敬请期待</view>
<view class="ti-text">敬请期待</view>
<image class="tr-icon" src="/static/images/third_pages/tab_b.png"></image>
</view>
<view class="tr-item" @click="clickTap">
<view class="ti-title">敬请期待</view>
<view class="ti-text">敬请期待</view>
<image class="tr-icon" src="/static/images/third_pages/tab_b.png"></image>
</view>
</view>
</view>
</view>
<bottom-logo is-fixed></bottom-logo>
</view>
</template>
<script>
import bottomLogo from "@/subpackage/menu/components/bottom_logo.vue";
import { showNone } from "@/utils/util";
export default {
components:{ bottomLogo },
methods: {
clickTap(){
showNone('板块更新中');
}
}
}
</script>
<style lang="scss">
.third-page{
.tp-container{
padding: 28upx 24upx 0upx;
.tc-post{
display: block;
height: 400upx;
width: 100%;
}
.tc-bot{
margin-top: 24upx;
display: flex;
align-items: stretch;
.tb-left{
position: relative;
flex-shrink: 0;
margin-right: 22upx;
padding: 22upx 28upx;
width: 340upx;
height: 420upx;
.tl-bg{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.tl-tit{
@include flcw(43upx, 60upx, #fff, 500);
@include tHide;
}
.tl-txt{
@include flcw(30upx, 42upx, #fff, 500);
@include tHide;
}
}
.tb-right{
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.tr-item{
position: relative;
height: 124upx;
background-image: linear-gradient(270deg, #FFFFFF 0%, #C8C8CA 100%);
padding: 16upx;
&.active{
background-image: linear-gradient(270deg, #F2D286 0%, #E0A46F 100%);
}
.ti-title{
@include flcw(32upx, 44upx, #fff, 500);
@include tHide;
}
.ti-text{
@include flcw(22upx, 32upx, #fff, 500);
@include tHide;
}
.tr-icon{
position: absolute;
right: 6upx;
bottom: 0upx;
width: 136upx;
height: 104upx;
}
}
}
}
}
}
</style>

4
src/pages/merchant_login/merchant_login.vue

@ -8,11 +8,11 @@
</view>
</template>
<script>
import util from '../../utils/util'
import util, { jsonStr } from '../../utils/util'
export default {
methods: {
toWebView(){
util.routeTo(`/pages/web_view/web_view?src=${encodeURIComponent("https://www.ouxuanzhineng.cn")}`,'rL');
util.routeTo(`/pages/web_view/web_view?src=${jsonStr("https://www.ouxuanzhineng.cn/")}`,'rL');
}
}
}

12
src/pages/order_list/order_list.vue

@ -50,7 +50,6 @@
<reservation-order :order-info="e" v-if="orderType == 3" @verify="reservationVerify(i)"></reservation-order>
<membership-order :order-info="e" v-if="orderType == 4"></membership-order>
<integral-order :order-info="e" v-if="orderType == 5"></integral-order>
<timing-order :order-info="e" @refreshList="refreshList" v-if="orderType == 1"></timing-order>
<organize-order :order-info="e" v-if="orderType == 10" @refreshList="refreshList"></organize-order>
<curriculum :order-info="e" v-if="orderType == 12"></curriculum>
@ -127,7 +126,6 @@
import reservation_order from '../../components/reservation_order/reservation_order';
import integral_order from '../../components/integral_order/integral_order';
import membership_order from '../../components/membership_order/membership_order';
import timing_order from '../../components/timing_order/timing_order.vue';
import organize_order from '../../components/organize_order/organize_order.vue';
// 20210910
import value_card from '../../components/order_list/value_card/value_card.vue'; // 20220725
@ -157,7 +155,6 @@
'reservation-order': reservation_order,
'membership-order': membership_order,
'integral-order': integral_order,
'timing-order': timing_order,
'organize-order': organize_order,
'spectacular-monent': spectacular_monent,
'value-card': value_card,
@ -180,7 +177,7 @@
let { orderType } = this;
return (
orderType == 3 || orderType == 4 ||
orderType == 1 || orderType == 10 ||
orderType == 10 ||
orderType == 18 || orderType == 12 ||
orderType == 18 || orderType == 12 ||
orderType == 15 || orderType == 16 ||
@ -444,7 +441,6 @@
if(type == 3)return API.reservationOrder;
if(type == 4)return API.membershipOrder;
if(type == 5)return API.integralOrder;
if(type == 1)return API.timeOrder;
if(type == 10)return API.organizeOrderList; //
if(type == 12)return API.venueCourseOrderList; //
if(type == 18)return API.userValueCardList; //
@ -488,7 +484,7 @@
delete _obj['status'];
_obj['order_status_in'] = +curTabID === -1 ? '0,1,2,3' : curTabID + ''
}
// :0,1; :/[1/2]
// :0,1;
if(orderType == 3)_obj['order_type'] = appointOrderType;
// , 0430.0507()
@ -549,8 +545,6 @@
return '会员卡订单';
case 5:
return '积分商城订单';
case 1:
return '计时订单';
case 10:
return '约玩订单';
case 12:
@ -596,8 +590,6 @@
return [ {id: '', name: '全部'}, {id: 1, name: '使用中'}, {id: 0, name: '已失效'},];
case 5: // tab
return [ {id: '', name: '全部'}, {id: 1, name: '待发货'}, {id: 2, name: '已发货'}, {id: 3, name: '已完成'}, ];
case 1: // tab ///退 [/0/1/4]
return [ {id: '', name: '全部'}, {id: 0, name: '计费中'}, {id: 1, name: '已完成'}, {id: 4, name: '已退款'}, ];
case 10: // tab ///退 [/0/1/4]
return [ {id: '', name: '全部'}, {id: 1, name: '已付款'}, {id: 2, name: '已完成'}, {id: 4, name: '已退款'}, ];
case 12: // tab ///退 [/0/1/4]

6
src/pages/order_search/order_search.vue

@ -3,7 +3,7 @@
<view class="os-bar">
<view>
<image mode="aspectFit" src="/static/images/icon/search.png"></image>
<input placeholder="输入订单号" v-model="searchTxt" confirm-type="search" @confirm="iptConfirm" />
<input placeholder="输入订单号" v-model="searchTxt" confirm-type="search" @confirm="iptConfirm" />
<image v-if="searchTxt!=''" mode="aspectFit" src="/static/images/icon/round_close.png" @click="clearSearch"></image>
</view>
</view>
@ -17,7 +17,6 @@
<reservation-order :order-info="e" v-if="orderType == 3"></reservation-order>
<membership-order :order-info="e" v-if="orderType == 4"></membership-order>
<integral-order :order-info="e" v-if="orderType == 5"></integral-order>
<timing-order :order-info="e" v-if="orderType == 1"></timing-order>
<organize-order :order-info="e" v-if="orderType == 10"></organize-order>
<!-- 20210910 -->
@ -44,7 +43,6 @@
import reservation_order from '../../components/reservation_order/reservation_order';
import integral_order from '../../components/integral_order/integral_order';
import membership_order from '../../components/membership_order/membership_order';
import timing_order from '../../components/timing_order/timing_order.vue'
import organize_order from '../../components/organize_order/organize_order.vue'
// 20210910
import value_card from '../../components/order_list/value_card/value_card.vue';
@ -71,7 +69,6 @@ export default {
'reservation-order': reservation_order,
'membership-order': membership_order,
'integral-order': integral_order,
'timing-order': timing_order,
'organize-order': organize_order,
// <!-- 20210910 -->
'spectacular-monent': spectacular_monent,
@ -137,7 +134,6 @@ export default {
if(type == 3)return API.reservationOrder;
if(type == 4)return API.membershipOrder;
if(type == 5)return API.integralOrder;
if(type == 1)return API.timeOrder;
if(type == 10)return API.organizeOrderList;
// <!-- 20210910 -->
if(type == 12)return API.venueCourseOrderList; //

2
src/pages/site/confirm/confirm.vue

@ -94,7 +94,7 @@ export default {
ocPrice: '',
ocReaon: '',
ocUsage: 1, // 1 -> 2-> 3-> , 4 ->
light_up: false,
light_up: true,
usageLs: [
{ name: '客户订场', type: 1 }, { name: '散客', type: 2 },
{ name: '锁场', type: 3 }, { name: '挂账', type: 4 },

3
src/pages/web_view/web_view.vue

@ -2,6 +2,7 @@
<web-view :src="address"></web-view>
</template>
<script>
import { jsonPar } from "@/utils/util.js";
export default {
data(){
return {
@ -10,7 +11,7 @@ export default {
},
onLoad(options){
console.log('web_view options', options);
this.address = decodeURIComponent(options?.src || 'https://www.ouxuanzhineng.cn');
this.address = jsonPar(decodeURIComponent(options?.src ?? ''));
}
}
</script>

175
src/pages/write_off/douyin/poi_list.vue

@ -1,175 +0,0 @@
<template>
<view class="dy-poi-ls">
<view class="dpl-header">
<stadium-picker
:stadium-list="stadiumList"
@change:stadium="stadiumChange"
></stadium-picker>
<!-- <period-picker></period-picker> -->
<view class="dh-number">核销数量{{ totalNum || 0 }}</view>
</view>
<view class="dpl-list">
<view class="dl-item" v-for="(e, i) in writeOffList" :key="i">
<list-item
:order-no="e.order_no"
:user-phone="e.user_phone"
:verify-code="e.verify_code"
:verify-method="e.verify_method"
:verify-time="e.verify_time"
></list-item>
</view>
</view>
<view class="r-bottom-btn"><view @click="toOperate">核销团购券</view></view>
</view>
</template>
<script>
import periodPicker from "../all_components/period_picker.vue";
import stadiumPicker from "../all_components/stadium_picker.vue";
import listItem from "../all_components/list_item.vue";
import { API } from '../../../js/api';
import { servers } from '../../../js/server';
import util from '../../../utils/util';
import { mapState } from 'vuex';
import { WRITE_OFF_STORE_NAME } from '../../../js/once_name';
export default {
computed: {
...mapState([ 'brandInfo' ]),
},
components: {
periodPicker,
stadiumPicker,
listItem
},
data() {
return {
totalNum: 0,
writeOffList: [],
stadiumList: [],
curStadium: {},
page: 1,
}
},
async onLoad() {
let _brand_id = this.brandInfo?.brand?.id || 63;
let _list = await this.getStoreList({ brand_id: _brand_id });
this.stadiumList = _list || [];
this.getList({ brand_id: _brand_id });
},
onReachBottom(){
let { page, curStadium } = this;
this.getList({
brand_id: curStadium?.brand_id,
stadium_id: curStadium?.id,
page: page + 1,
})
},
methods: {
toOperate(){
let { stadiumList, curStadium } = this;
util.$_emit(WRITE_OFF_STORE_NAME, {
stadiumList,
curStadium,
})
util.routeTo(`/pages/write_off/operate/operate?type=dypoi`, 'rT');
},
stadiumChange(stadium){
this.curStadium = stadium;
this.page = 1;
this.totalNum = 0;
this.writeOffList = [];
this.getList({
brand_id: stadium?.brand_id,
stadium_id: stadium?.id,
})
},
getList({ brand_id, stadium_id = '', date = '', page = 1, page_size = 20, order_status = 'used' }){
util.showLoad();
servers.get({
url: API.writeOff.dyPoiOrderList,
data: { brand_id, stadium_id, date, page, page_size, order_status },
failMsg: '加载失败!',
})
.then(res=>{
util.hideLoad();
this.totalNum = res.total || 0;
let _list = (res.list || []).map(e=>this.formating(e));
if(page == 1)return this.writeOffList = _list;
if(!_list.length)return util.showNone('没有更多!');
this.page = page;
this.writeOffList = [...this.writeOffList, ..._list];
})
},
formating(data){
let _code = data?.order_codes?.find(e=>e?.code_status == 'used');
return {
order_no: data?.order_no || '',
user_phone: data?.mobile || '',
verify_code: _code?.code || '',
verify_method: _code?.verification_method || '',
verify_time: _code?.verification_time || '',
}
},
getStoreList({
page=1,
page_size=9999,
brand_id='',
}){
return servers.get({
url: API.stadiumList,
data: {
page,
page_size,
brand_id,
},
failMsg: '获取列表失败!'
})
.then(res=>res.list || [])
},
}
}
</script>
<style lang="scss">
.dy-poi-ls{
@include isPd(150upx);
}
.dpl-header{
position: sticky;
padding-bottom: 24upx;
background: #fff;
.dh-number{
padding: 0 24upx;
text-align: right;
@include flcw(32upx, 44upx);
@include tHide;
}
}
.dpl-list{
padding: 24upx;
.dl-item+ .dl-item{
margin-top: 24upx;
}
}
.r-bottom-btn{
position: fixed;
left: 0;
bottom: 0;
width: 100%;
padding: 10upx 24upx;
padding-bottom: calc( 10upx + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
padding-bottom: calc( 10upx + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
background-color: #f2f2f7;
>view{
height: 112upx;
line-height: 112upx;
text-align: center;
font-size: 32upx;
border-radius: 10upx;
color: #fff;
background-color: $mColor;
}
}
</style>

237
src/pages/write_off/mall/list/list.vue

@ -1,237 +0,0 @@
<template>
<view class="mall-order-ls">
<view class="mol-date">
<view class="md-txt">核销日期</view>
<view class="md-picker">
<picker mode="date" @change="stChange">
<view>
<input :value="startTime" disabled placeholder="请选择时间" />
<image mode="aspectFit" src="/static/images/icon/arrow_c33.png"></image>
</view>
</picker>
</view>
<view class="md-txt"></view>
<view class="md-picker">
<picker mode="date" @change="edChange">
<view>
<input :value="endTime" disabled placeholder="请选择时间" />
<image mode="aspectFit" src="/static/images/icon/arrow_c33.png"></image>
</view>
</picker>
</view>
</view>
<view class="mol-list">
<view class="ml-item" v-for="(e, i) in writeOffList" :key="i">
<view class="mi-order-no">
<view class="mon-num">订单编号{{ e.product_order_no || '-' }}</view>
<view class="mon-btn" @click="copyBtn(e.product_order_no || '-')">复制</view>
</view>
<view class="mi-info">
<view class="mi-line">取货码{{ e.product_order_no || '-' }}</view>
<view class="mi-line">取货人{{ e.product_order_self_pickup_info.name || '-' }} {{ e.product_order_self_pickup_info.phone || '-' }}</view>
<view class="mi-line">商品{{ e.product_order_goods.join(';') }}</view>
<view class="mi-line">核验人{{ e.optuname }}</view>
<view class="mi-line">取货时间{{ e.created_at || '-' }}</view>
</view>
</view>
</view>
<view class="mol-fixed">
<view @click="toOperate">核销订单</view>
</view>
</view>
</template>
<script>
import { API } from '../../../../js/api';
import { servers } from '../../../../js/server';
import util from '../../../../utils/util';
import { mapState } from 'vuex';
import { WRITE_OFF_STORE_NAME } from '../../../../js/once_name';
export default {
computed: {
...mapState([ 'brandInfo' ]),
},
data(){
return {
writeOffList: [],
page: 1,
startTime: '',
endTime: '',
}
},
onReachBottom(){
let { brandInfo, startTime, endTime, page } = this;
this.getList({
brand_id: brandInfo.brand.id,
stime: startTime || '',
etime: endTime || '',
page: ++page
})
},
onLoad(){
// 1
let _startDate = util.formatDate({
date: new Date().getTime() - 30*24*60*60*1000
}).substr(0, 10);
let _endDate = util.formatDate({ }).substr(0, 10);
this.startTime = _startDate;
this.endTime = _endDate;
// 1
this.$nextTick(_=> this.refreshPage());
},
methods: {
refreshPage(){
let { brandInfo, startTime, endTime } = this;
this.page = 1;
this.writeOffList = [];
this.$nextTick(_=>{
this.getList({
brand_id: brandInfo.brand.id,
stime: startTime || '',
etime: endTime || '',
})
})
},
copyBtn(data){
uni.setClipboardData({ data })
},
stChange(e){
console.warn(e)
this.startTime = e.detail.value;
this.$nextTick(_=>this.refreshPage());
},
edChange(e){
this.endTime = e.detail.value;
this.$nextTick(_=>this.refreshPage());
},
toOperate(){
util.$_emit(WRITE_OFF_STORE_NAME, null);
util.routeTo(`/pages/write_off/operate/operate?type=mall`, 'nT');
},
getList({ brand_id, page = 1, page_size = 15, stime = '', etime = '' }){
util.showLoad();
servers.get({
url: API.writeOff.shop2WriteoffList,
data: { brand_id, page, page_size, stime, etime },
failMsg: '加载失败!',
})
.then(res=>{
util.hideLoad();
this.totalNum = res.total || 0;
let _list = res.list || [];
if(page == 1)return this.writeOffList = _list;
if(!_list.length)return util.showNone('没有更多!');
this.page = page;
this.writeOffList = [...this.writeOffList, ..._list];
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.mall-order-ls{
padding-bottom: 122upx;
padding-bottom: calc( 122upx + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
padding-bottom: calc( 122upx + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
}
.mol-date{
padding: 0 28upx;
height: 148upx;
background-color: #fff;
@include centerFlex(flex-start);
.md-txt{
font-size: 32upx;
line-height: 44upx;
color: #1a1a1a;
}
.md-picker{
margin: 0 20upx;
width: 226upx;
border: 2upx solid #D8D8D8;
border-radius: 10upx;
overflow: hidden;
view{
padding: 0 10upx;
height: 88upx;
background-color: #f2f2f7;
@include centerFlex(space-between);
>input{
flex-grow: 1;
height: 100%;
font-size: 28upx;
color: #1a1a1a;
}
>image{
margin-left: 10upx;
flex-shrink: 0;
width: 22upx;
height: 22upx;
}
}
}
}
.mol-list{
padding: 24upx;
.ml-item{
margin-bottom: 24upx;
padding: 0 24upx;
background-color: #fff;
.mi-order-no{
padding: 30upx 4upx;
border-bottom: 2upx solid #f2f2f7;
@include centerFlex(space-between);
.mon-num{
font-size: 28upx;
font-weight: 500;
line-height: 40upx;
color: #1a1a1a;
}
.mon-btn{
flex-shrink: 0;
margin-left: 20upx;
font-size: 28upx;
line-height: 40upx;
color: $themeColor;
}
}
.mi-info{
padding: 30upx 4upx;
.mi-line{
font-size: 28upx;
line-height: 52upx;
color: #9C9C9F;
@include textHide(1);
}
}
}
}
.mol-fixed{
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: #fff;
padding: 10upx 40upx;
padding-bottom: calc( 10upx + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
padding-bottom: calc( 10upx + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
>view{
height: 112upx;
line-height: 112upx;
text-align: center;
font-size: 32upx;
color: #fff;
background-color: $themeColor;
border-radius: 10upx;
}
}
</style>

122
src/pages/write_off/menu/menu.vue

@ -1,122 +0,0 @@
<template>
<view class="write-off-menu">
<view class="wom-section">
<view class="ws-tit">场地订单核销</view>
<view class="ws-ls">
<view class="wl-item" @click="toOperate('site')">
<image mode="aspectFit" src="/static/images/write_off/site.png"></image>
<view>场地订单核销</view>
</view>
<view class="wl-item" @click="toSiteList">
<image mode="aspectFit" src="/static/images/write_off/order.png"></image>
<view>场地订单核销记录</view>
</view>
</view>
</view>
<view class="wom-section">
<view class="ws-tit">商城订单核销</view>
<view class="ws-ls">
<view class="wl-item" @click="toOperate('mall')">
<image mode="aspectFit" src="/static/images/write_off/mall.png"></image>
<view>商城订单核销</view>
</view>
<view class="wl-item" @click="toMallLs">
<image mode="aspectFit" src="/static/images/write_off/order.png"></image>
<view>商城订单核销记录</view>
</view>
</view>
</view>
<view class="wom-section">
<view class="ws-tit">抖音团购核销</view>
<view class="ws-ls">
<view class="wl-item" @click="toOperate('dypoi')">
<image mode="aspectFit" src="/static/images/write_off/site.png"></image>
<view>抖音团购核销</view>
</view>
<view class="wl-item" @click="toDypoiOrderLs">
<image mode="aspectFit" src="/static/images/write_off/order.png"></image>
<view>抖音团购核销记录</view>
</view>
</view>
</view>
<view class="wom-section">
<view class="ws-tit">现场散客人数</view>
<view class="ws-ls">
<view class="wl-item" @click="toPeopleNum">
<image mode="aspectFit" src="/static/images/write_off/people.png"></image>
<view>现场散客人数查询</view>
</view>
</view>
</view>
</view>
</template>
<script>
import util from '../../../utils/util';
import { WRITE_OFF_STORE_NAME } from '../../../js/once_name';
export default {
methods: {
toPeopleNum(){
util.routeTo(`/pages/write_off/number_of_people/number_of_people`, 'nT');
},
toOperate(type){
util.$_emit(WRITE_OFF_STORE_NAME, null);
util.routeTo(`/pages/write_off/operate/operate?type=${type}`, 'nT');
},
toSiteList(){
util.routeTo(`/pages/write_off/search_result/search_result`, 'nT');
},
toMallLs(){
util.routeTo(`/pages/write_off/mall/list/list`, 'nT');
},
toDypoiOrderLs(){
util.routeTo(`/pages/write_off/douyin/poi_list`, 'nT');
}
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.write-off-menu{
padding: 60upx 68upx 0upx;
.wom-section{
margin-bottom: 30upx;
.ws-tit{
margin-bottom: 20upx;
font-size: 32upx;
font-weight: 500;
line-height: 44upx;
color: #1a1a1a;
}
.ws-ls{
@include centerFlex(space-between);
flex-wrap: wrap;
.wl-item{
padding-top: 58upx;
margin-bottom: 32upx;
width: 288upx;
height: 220upx;
border-radius: 10upx;
background-color: #fff;
>image{
margin: 0 auto 22upx;
display: block;
width: 60upx;
height: 60upx;
}
>view{
text-align: center;
line-height: 40upx;
font-size: 28upx;
color: #9c9c9f;
@include textHide(1);
}
}
}
}
}
</style>

70
src/pages/write_off/null/null.vue

@ -1,70 +0,0 @@
<template>
<view class="null-container">
<image mode="aspectFit" :src="imgPath"></image>
<view class="c-tip" v-if="operateType == 'decrypt_text'">很抱歉获取不到二维码订单信息</view>
<view class="c-tip" v-if="operateType == 'verify_code'">很抱歉获取不到验证码订单信息</view>
<view class="c-btn" @click="toBlack">返回</view>
</view>
</template>
<script>
import util from '../../../utils/util';
export default {
computed: {
imgPath(){
let { operateType } = this;
if(operateType == 'verify_code')return '/static/images/code_null.png';
if(operateType == 'decrypt_text')return '/static/images/scan_null.png';
}
},
onLoad(options){
let { type } = options;
this.operateType = options.type
},
data(){
return {
isScan: false,
operateType: '', // verify_code()/decrypt_text()
}
},
methods: {
toBlack(){
util.routeTo();
}
}
}
</script>
<style lang="scss">
@import "~style/public.scss";
page{
background-color: #fff;
}
.null-container{
padding-top: 90upx;
>image{
display: block;
margin: 0 auto 86upx;
width: 420upx;
height: 420upx;
}
.c-tip{
margin-bottom: 260upx;
line-height: 40upx;
text-align: center;
font-size: 28upx;
color: #9c9c9f;
}
.c-btn{
margin: 0 auto;
width: 280upx;
text-align: center;
height: 92upx;
line-height: 88upx;
font-size: 32upx;
border: 2upx solid $themeColor;
color: $themeColor;
border-radius: 46upx;
}
}
</style>

496
src/pages/write_off/number_of_people/number_of_people.vue

@ -1,496 +0,0 @@
<template>
<view class="number-of-people">
<view class="nop-store-name">
<picker :range="stadiumList" range-key="name" @change="stadiumChange">
<view class="nsn-frame">
<input placeholder="请选择店铺" :value="curStadium.name" disabled />
<image mode="aspectFit" src="/static/images/icon/arrow_c33.png"></image>
</view>
</picker>
</view>
<view class="nop-main">
<view class="nm-date">日期{{ dateStr || '-' }}</view>
<view class="nm-tit">现场散客人数</view>
<view class="nm-num">
<image mode="aspectFit" src="/static/images/countdown_bg.png"></image>
<view class="nn-txt-num">{{peopleInfo.present_person_number || 0}}</view>
</view>
<view class="nm-txt" @click="checkNotLeave">查看未离场订单</view>
<view class="nm-btn" @click="isChangeNum = true">修改人数</view>
<view class="nm-line">
<view class="nl-txt">凌晨自动清零</view>
<view class="nl-switch" @click="switchChange">
<switch color="#009777" disabled style="transform:scale(0.8)" :checked="peopleInfo. present_person_number_clear"></switch>
</view>
</view>
<view class="nm-tip">
<text>* 不开启凌晨自动清零则现场灯光按现场散客人数去判断是否开启或关闭修改人数会直接影响现场灯光开关\n\r* 开启凌晨自动清零则现场灯光按散客订单未离场数量去判断是否开启或关闭修改人数不会影响现场灯光开关但如有散客订单一直未扫码离场可能会无法关灯需要将未离场的散客订单设置为已离场后才可关闭灯光</text>
</view>
</view>
<view class="ox-dark-mask" v-if="isChangeNum">
<view class="nop-modifies-modal">
<image class="nmm-close" @click="isChangeNum = false" src="/static/images/icon/x_close.png"></image>
<view class="nmm-tit">修改现场散客人数</view>
<view class="nmm-info">
<view class="ni-num">当前现场散客人数为{{peopleInfo.present_person_number || 0}}</view>
<view class="ni-ipt">
<input placeholder="请输入散客人数" v-model="changeNum" type="number" />
</view>
<view class="ni-tip">修改现场人数可能会影响现场灯光开关请谨慎操作</view>
</view>
<view class="nmm-btns">
<view @click="isChangeNum = false">取消</view>
<view @click="confirmChange">确认</view>
</view>
</view>
</view>
<view class="ox-dark-mask" v-if="isNotLeave">
<view class="nop-modifies-modal">
<image class="nmm-close" @click="isNotLeave = false" src="/static/images/icon/x_close.png"></image>
<view class="nmm-tit nmm-btm">未离场订单</view>
<view class="nmm-line" v-if="orderNum.person_number>0">
<view>次卡未离场{{orderNum.person_number || 0}}</view>
<view @click="checkBtn(0)">查看</view>
</view>
<view class="nmm-line" v-if="orderNum.person_timing>0">
<view>计时未离场{{orderNum.person_timing || 0}}</view>
<view @click="checkBtn(1)">查看</view>
</view>
<view class="nmm-line">
<view>年月卡未离场{{orderNum.monthly_card||0}}</view>
<view @click="checkBtn(0)">查看</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { API } from '../../../js/api'
import { servers } from '../../../js/server'
import { mapState } from 'vuex';
import util from '../../../utils/util';
let timer = null;
export default {
computed: {
...mapState([ 'brandInfo' ]),
},
data(){
return {
isChangeNum: false,
stadiumList: [],
curStadium: {
name: ''
},
peopleInfo: {},
dateStr: '-',
changeNum: '',
isNotLeave: false,
orderNum: {}, //
}
},
onLoad(options){
this.initStore();
this.dateStr = util.formatDate({ partition: 'zh' }) || '-';
},
onUnload(){
this.clearTime();
},
methods: {
confirmChange: util.debounce(function(){
let { changeNum } = this;
if(isNaN(changeNum))return util.showNone('请输入正确人数!');
this.isChangeNum = false;
this.setStadiumPresentNumber(changeNum);
}, 200, true),
setStadiumPresentNumber(num){
let { curStadium } = this;
util.showLoad();
servers.get({
url: API.writeOff.setStadiumPresentNumber,
data: {
brand_id: curStadium.brand_id,
stadium_id: curStadium.id,
number: +num,
},
isDefaultGet: false,
})
.then(res=>{
util.hideLoad();
if(res.data.code == 0){
util.showNone(res.data.message || '操作成功!');
setTimeout(_=>{
this.getUserNum({
stadium_id: curStadium.id,
brand_id: curStadium.brand_id
})
this.initInertval();
}, 1200)
}else{
util.showNone(res.data.message || '操作失败!');
}
})
.catch(util.hideLoad)
},
initInertval(){
this.clearTime();
let { curStadium } = this;
if(!curStadium.id || !curStadium.brand_id)return;
timer = setInterval(_=>{
if(!curStadium.id || !curStadium.brand_id)return this.clearTime();
this.getUserNum({
stadium_id: curStadium.id,
brand_id: curStadium.brand_id
})
}, 3000);
},
clearTime(){
clearInterval(timer);
timer = null;
},
stadiumChange(e){
let { stadiumList } = this;
let _curStadium = stadiumList[e.detail.value] || {};
if(!_curStadium.id || !_curStadium.brand_id)return;
this.curStadium = _curStadium;
this.getUserNum({
stadium_id: _curStadium.id,
brand_id: _curStadium.brand_id
})
this.initInertval();
},
async initStore(){
let { brandInfo } = this;
try{
util.showLoad();
let _storeList = await this.getStoreList({ brand_id: brandInfo.brand.id || '' });
if(!_storeList || !_storeList.length)return util.showNone('没有店铺信息');
this.stadiumList = _storeList || [];
let _curStadium = _storeList[0] || {};
this.curStadium = _curStadium;
this.initInertval();
this.getUserNum({
stadium_id: _curStadium.id,
brand_id: _curStadium.brand_id
})
util.hideLoad();
}catch(err){
util.hideLoad();
util.showNone('初始化店铺数据失败!');
console.warn('加载数据失败!', err);
}
},
//
getStoreList({
page=1,
page_size=9999,
brand_id='',
}){
return servers.get({
url: API.stadiumList,
data: {
page,
page_size,
brand_id,
},
failMsg: '获取列表失败!'
})
.then(res=>{
let _list = res.list || [];
return _list
})
},
getUserNum({
stadium_id,
brand_id,
}){
servers.get({
url: API.writeOff.timingNumber,
data: { stadium_id, brand_id },
failMsg: '加载现场人数失败!'
})
.then(res=>{
this.peopleInfo = res
})
},
// -
checkNotLeave(){
let { curStadium } = this;
util.showLoad();
servers.get({
url: API.writeOff.notLeavingNums,
data: {
brand_id: curStadium.brand_id,
stadium_id: curStadium.id,
},
// isDefaultGet: false,
failMsg: '请求失败!'
})
.then(res=>{
util.hideLoad();
this.orderNum = res
this.$nextTick(_=>{
this.isNotLeave = true
})
})
},
checkBtn(type){
if(type == 0)return util.routeTo(`/pages/write_off/search_result/search_result`, 'nT');
if(type == 1)return util.routeTo(`/pages/order_list/order_list?order_type=1`, 'nT');
},
switchChange(){
let { peopleInfo, curStadium } = this
this.$nextTick(_=>{
util.showModal({
title: '提示',
content: peopleInfo.present_person_number_clear==false?'是否确认开启凌晨自动清零?':peopleInfo.present_person_number_clear==true?'是否确认关闭凌晨自动清零?':'',
showCancel: true,
success: modalRes=>{
if(modalRes.confirm){
util.showLoad();
servers.get({
url: API.writeOff.timingOpen, ///
data: {
brand_id: curStadium.brand_id,
stadium_id: curStadium.id,
status: peopleInfo.present_person_number_clear==false?1:peopleInfo.present_person_number_clear==true?0:'',
},
failMsg: '请求失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
})
}
}
})
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background-color: #fff;
}
.number-of-people{
.nop-store-name{
height: 144upx;
@include centerFlex(center);
.nsn-frame{
padding: 0 20upx;
width: 702upx;
height: 92upx;
border-radius: 10upx;
background-color: #F2F2F7;
@include centerFlex(space-between);
>input{
flex-grow: 1;
line-height: 40upx;
font-size: 28upx;
color: #1A1A1A;
}
>image{
flex-shrink: 0;
margin-left: 20upx;
width: 28upx;
height: 28upx;
}
}
}
.nop-main{
padding: 8upx 0upx 0;
.nm-date{
margin: 0 24rpx 86rpx;
line-height: 44upx;
font-size: 32upx;
color: #1a1a1a;
}
.nm-tit{
margin: 0 30rpx 16rpx;
text-align: center;
font-size: 32upx;
font-weight: 500;
}
.nm-num{
position: relative;
margin: 0 auto;
display: block;
width: 400upx;
height: 400upx;
.nn-txt-num{
line-height: 400upx;
text-align: center;
font-size: 96upx;
font-weight: 500;
color: $themeColor;
}
>image {
position: absolute;
left: 50%;
top: 50%;
z-index: -1;
margin-left: -250upx;
margin-top: -250upx;
display: block;
width: 500upx;
height: 500upx;
animation: Rotate 6s linear infinite
}
@keyframes Rotate{
0% {transform: rotate(360deg);}
50% {transform: rotate(180deg);}
100% {transform: rotate(0deg);}
}
}
.nm-txt{
margin: 10rpx auto 42rpx;
color: #9C9C9F;
font-size: 28rpx;
text-align: center;
line-height: 40rpx;
text-decoration: underline;
}
.nm-btn{
margin: 0 auto;
width: 618upx;
line-height: 112upx;
height: 112upx;
text-align: center;
border-radius: 10upx;
font-size: 38upx;
color: #fff;
background-color: $themeColor;
}
.nm-line{
margin: 46rpx 0 0;
padding: 34rpx 28rpx 20rpx 34rpx;
border-top: 2rpx solid #F2F2F7;
@include centerFlex(space-between);
.nl-txt{
color: #1A1A1A;
font-size: 32rpx;
line-height: 44rpx;
}
.nl-switch{
flex-shrink: 0;
flex-grow: 0;
}
}
.nm-tip{
margin: 0 32rpx 30rpx 34rpx;
font-size: 28upx;
line-height: 52upx;
color: #9C9C9F;
}
}
.nop-modifies-modal{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding-top: 78upx;
width: 620upx;
height: 706upx;
background-color: #fff;
border-radius: 10upx;
.nmm-close{
position: absolute;
right: 30upx;
top: 30upx;
width: 34upx;
height: 34upx;
}
.nmm-tit{
line-height: 44upx;
text-align: center;
font-weight: 500;
font-size: 32upx;
color: #1A1A1A;
}
.nmm-info{
padding: 54upx 80upx 80upx;
.ni-num{
margin-bottom: 30upx;
font-size: 28upx;
line-height: 48upx;
color: #1A1A1A;
@include textHide(1);
}
.ni-ipt{
margin-bottom: 26upx;
padding: 0 20upx;
height: 88upx;
border-radius: 10upx;
border: 2upx solid #D8D8D8;
>input{
flex-grow: 1;
height: 100%;
font-size: 28upx;
color: #1A1A1A;
}
}
.ni-tip{
font-size: 24upx;
line-height: 34upx;
color: #EA5061;
}
}
.nmm-btns{
@include centerFlex(center);
>view{
margin: 0 10upx;
width: 240upx;
height: 88upx;
line-height: 84upx;
text-align: center;
font-size: 32upx;
border-radius: 10upx;
border: 2upx solid $themeColor;
color: $themeColor;
&+view{
color: #fff;
background-color: $themeColor;
}
}
}
.nmm-btm{
margin-bottom: 8rpx;
}
.nmm-line{
margin: 78rpx 70rpx 0 74rpx;
@include centerFlex(space-between);
>view{
&:first-child{
color: #1A1A1A;
font-size: 28rpx;
line-height: 48rpx;
}
&:nth-child(2){
width: 156rpx;
height: 68rpx;
border: 2rpx solid #009874;
border-radius: 10rpx;
color: #009874;
font-size: 32rpx;
line-height: 64rpx;
text-align: center;
}
}
}
}
}
</style>

364
src/pages/write_off/operate/operate.vue

@ -1,364 +0,0 @@
<template>
<view class="operate-container">
<view class="store-bar">
<text>当前门店</text>
<picker mode="selector" :range="stadiumList" range-key="name" @change="stadiumChange">
<view>
<input disabled v-model="curStadium.name" />
<image mode="aspectFit" src="/static/images/icon/arrow_c33.png"></image>
</view>
</picker>
</view>
<view class="c-scan-btn" @click="scanCodeBtn">
<image mode="aspectFit" src="/static/images/icon/scan_code_btn.png"></image>
</view>
<view class="c-verification-code">
<view><input placeholder="输入订单验证码" v-model="iptCode" /></view>
<view @click="confirmBtn">确认核销</view>
</view>
</view>
</template>
<script>
import util from '../../../utils/util';
import { API } from '../../../js/api';
import { servers } from '../../../js/server';
import { WRITE_OFF_STORE_NAME, WRITE_OFF_ORDER_INFO, WRITE_OFF_MALL_ORDER_INFO } from '../../../js/once_name';
import { mapState } from 'vuex';
export default {
data(){
return {
iptCode: '',
stadiumList: [],
curStadium: {},
writeOffType: '', // site()/ mall()/ dypoi()
}
},
computed: {
...mapState([ 'brandInfo' ]),
},
onLoad(options){
this.writeOffType = options.type || '';
util.$_once(WRITE_OFF_STORE_NAME, data => { //
if(!data)return this.initStore();
this.curStadium = data.curStadium;
this.stadiumList = data.stadiumList;
})
},
methods: {
async initStore(){
let { brandInfo } = this;
try{
util.showLoad();
let _storeList = await this.getStoreList({ brand_id: brandInfo.brand.id || '' });
if(!_storeList || !_storeList.length)return util.showNone('没有店铺信息');
this.stadiumList = _storeList || [];
if(_storeList.length) this.curStadium = _storeList[0];
util.hideLoad();
}catch(err){
util.hideLoad();
util.showNone('初始化店铺数据失败!');
}
},
//
getStoreList({
page=1,
page_size=99999,
brand_id='',
}){
return servers.get({
url: API.stadiumList,
data: {
page,
page_size,
brand_id,
},
failMsg: '获取列表失败!'
})
.then(res=>{
let _list = res.list || [];
return _list
})
},
scanCodeBtn: util.debounce(function(){
uni.scanCode({
onlyFromCamera: true,
scanType: 'qrCode',
success: res=> {
if(util.changeLowerCase(res.scanType) !== 'qr_code')return util.showNone('不支持此类型!');
this.analysisOrder({ decrypt_text: res.result });
},
fail: function(err) {
util.showNone('扫码失败!');
console.warn('扫码失败--->', err);
}
})
}, 300, true),
stadiumChange(e){
let { stadiumList } = this;
this.curStadium = stadiumList[e.detail.value];
},
confirmBtn: util.debounce(function(){
let { iptCode } = this;
if(!iptCode)return util.showNone('请输入核销码!');
this.analysisOrder({ verify_code: this.iptCode });
}, 300, true),
//
analysisOrder({ verify_code = '', decrypt_text = '' }){
let { curStadium, writeOffType } = this;
if(!verify_code&&!decrypt_text)return;
let _query = {
brand_id: curStadium.brand_id,
stadium_id: curStadium.id,
}
let _vType = '';
if(!!verify_code){
_vType = 'verify_code';
writeOffType == 'site'&&(_query['verify_code'] = verify_code);
writeOffType == 'mall'&&(_query['vcode'] = verify_code);
}
if(!!decrypt_text){
_vType = 'decrypt_text';
writeOffType == 'site'&&(_query['decrypt_text'] = decrypt_text);
writeOffType == 'mall'&&(_query['vcode'] = decrypt_text);
}
if(writeOffType == 'site')return this.siteGet({ query: _query, vType: _vType, });
if(writeOffType == 'mall')return this.mallGet({ query: _query, vType: _vType, });
if(writeOffType == 'dypoi'){
let _code = verify_code;
if(decrypt_text){
try{
_code = JSON.parse(decrypt_text)?.code;
}catch(err){console.warn(err)}
}
return this.dypoiGet({
..._query,
code: _code,
});
}
},
//
dypoiGet(query){
util.showLoad();
servers.get({
url: API.writeOff.dyPoiEnterVerifyOrder,
data: query,
isDefaultGet: false
})
.then(res=>{
util.hideLoad();
let { code, data, message } = res?.data || {};
if(code!==0)return util.showModal({
title: '提示',
content: message || '查询失败!',
confirmText: '好的'
})
if(data?.order?.order_no)return util.showModal({
title: data?.order?.product_cache?.name || '提示',
content: '核销数量:1',
showCancel: true,
confirmText: '确认核销',
cancelText: '取消',
success: res=>{
if(res.confirm){
this.dypoiConfirm({ ...query });
}
}
})
console.log('订单查询---->', res);
})
.catch(util.hideLoad)
},
dypoiConfirm({
stadium_id = '',
brand_id = '',
code = '',
}){
util.showLoad();
servers.post({
url: API.writeOff.dyPoiAssistantVerify,
data: {
stadium_id,
brand_id,
code,
},
isDefaultGet: false
})
.then(res=>{
util.hideLoad();
let { code, data, message } = res?.data || {};
if(code!==0)return util.showModal({
title: '提示',
content: message || '核销失败!',
confirmText: '好的'
})
util.showModal({
title: '提示',
content: message || '核销成功!',
confirmText: '关闭'
})
})
.catch(util.hideLoad)
},
//
mallGet({ query = {}, vType = '', }){
util.showLoad();
servers.get({
url: API.writeOff.shop2WriteoffGet,
data: query,
isDefaultGet: false
})
.then(res=>{
util.hideLoad();
if(res.data.code == 0){
let _data = res.data.data || {};
if(_data.has_order){
util.$_emit(WRITE_OFF_MALL_ORDER_INFO, {..._data.order || {}});
util.routeTo(`/pages/write_off/mall/confirm/confirm?type=${vType}`, 'nT');
}else{
util.routeTo(`/pages/write_off/null/null?type=${vType}`, 'nT');
}
}else{
util.showNone(res.data.message || '操作失败!')
// util.routeTo(`/pages/write_off/null/null?type=${vType}`, 'nT');
}
})
.catch(util.hideLoad)
},
// / /
// @vType verify_code()/ decrypt_text()
siteGet({ query = {}, vType = '', }){
util.showLoad();
servers.get({
url: API.writeOff.enterVerifyOrder,
data: query,
isDefaultGet: false
})
.then(res=>{
util.hideLoad();
if(res.data.code == 0){
let _data = res.data.data || {}
//
if(_data.extension&&_data.extension.verify_order_type === 'monthly_card'){
util.$_emit(WRITE_OFF_ORDER_INFO, {..._data});
util.routeTo(`/pages/write_off/ym_confirm/ym_confirm?type=${vType}`, 'nT');
return
}
//
if(_data.extension&&_data.extension.verify_order_type === 'match_order'){
util.$_emit(WRITE_OFF_ORDER_INFO, {..._data});
util.routeTo(`/pages/write_off/events_order/events_order?type=${vType}`, 'nT');
return
}
util.$_emit(WRITE_OFF_ORDER_INFO, {..._data});
util.routeTo(`/pages/write_off/confirm_order/confirm_order?type=${vType}`, 'nT');
}else{
util.routeTo(`/pages/write_off/null/null?type=${vType}`, 'nT');
}
console.log('订单查询---->', res);
})
.catch(util.hideLoad)
}
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background-color: #f2f2f7;
}
.operate-container{
.store-bar{
margin-bottom: 24upx;
padding: 0 24upx;
height: 144upx;
background-color: #fff;
@include centerFlex(space-between);
>text{
margin-right: 20upx;
flex-shrink: 0;
font-size: 28upx;
color: #9C9C9F;
}
>picker{
flex-grow: 1;
}
view{
padding: 0 20upx;
height: 92upx;
border-radius: 10upx;
background: #f2f2f2;
@include centerFlex(space-between);
>input{
flex-grow: 1;
height: 100%;
font-size: 28upx;
color: #1A1A1A;
}
>image{
flex-shrink: 0;
flex-grow: 0;
width: 28upx;
height: 28upx;
}
}
}
.c-scan-btn{
margin: 0 auto 24upx;
width: 702upx;
height: 360upx;
border-radius: 10upx;
background-color: #fff;
@include centerFlex(center);
>image{
width: 172upx;
height: 172upx;
}
}
.c-verification-code{
padding: 40upx;
border-radius: 10upx;
background-color: #fff;
>view{
&:first-child{
margin-bottom: 30upx;
padding: 0 20upx;
height: 112upx;
border-radius: 10upx;
background-color: #f2f2f7;
>input{
height: 100%;
width: 100%;
font-size: 32upx;
color: #1a1a1a;
}
}
&+view{
height: 112upx;
text-align: center;
line-height: 112upx;
border-radius: 10upx;
font-size: 32upx;
color: #fff;
background-color: $themeColor;
}
}
}
}
</style>

339
src/pages/write_off/search_result/search_result.vue

@ -1,339 +0,0 @@
<template>
<view class="search-result">
<view class="store-bar">
<text>当前门店</text>
<picker mode="selector" :range="stadiumList" range-key="name" @change="stadiumChange">
<view>
<input disabled v-model="curStadium.name" />
<image mode="aspectFit" src="/static/images/icon/arrow_c33.png"></image>
</view>
</picker>
</view>
<view class="r-timer-select">
<picker mode="date" @change="dateChange">
<view>
<text>核销日期{{curDate || '-'}}</text>
<image mode="aspectFit" src="/static/images/icon/arrow_c33.png"></image>
</view>
</picker>
<view>核销数量{{writeOffList.length || 0}}</view>
</view>
<view class="r-order-list">
<view class="l-item" v-for="(e, i) in writeOffList" :key="i">
<view class="i-name">{{ e.extension.stadium_name || '-' }}</view>
<view class="i-lines">
<view>
<view>订单编号{{ e.order_no || '-' }}</view>
<view>{{ e.type || '-' }}</view>
</view>
<view>
<view>用户信息{{ e.extension.user_phone || '-' }}{{ e.extension.nickname || '-' }}</view>
</view>
<view>
<view>核销码&#12288;{{e.verify_code || '-' }}</view>
</view>
<view>
<view>验证方式{{ e.desc || '-' }}</view>
</view>
<view>
<view>核销时间{{ e.verify_time || '-'}}</view>
</view>
<view>
<view>离场时间{{ e.verify_leave_time || ''}}</view>
</view>
</view>
<view class="i-btn" v-if="e.verify_leave_time==''" @click="leaveBtn(e)">手动离场</view>
</view>
</view>
<view class="r-bottom-btn"><view @click="toOperate">核销订单</view></view>
</view>
</template>
<script>
import util from '../../../utils/util';
import { API } from '../../../js/api';
import { servers } from '../../../js/server';
import { mapState } from 'vuex';
import { WRITE_OFF_STORE_NAME } from '../../../js/once_name';
export default {
computed: {
...mapState([ 'brandInfo' ]),
},
data(){
return {
stadiumList: [], //
curStadium: {}, //
writeOffList: [], // ,
curDate: util.formatDate({}),
page: 1,
totalNum: 0,
}
},
// 20210716
// onReachBottom(){
// let { page, curDate, curStadium } = this;
// this.getList({
// brand_id: curStadium.brand_id || '',
// stadium_id: curStadium.id || '',
// date: curDate || '',
// page: ++page,
// });
// },
onLoad(){
this.initPage();
},
onShow(){
let { curStadium } = this;
if(curStadium&&curStadium.id)this.refreshList();
},
methods: {
refreshList(){
let { curDate, curStadium } = this;
this.page = 1;
this.writeOffList = [];
this.getList({
brand_id: curStadium.brand_id || '',
stadium_id: curStadium.id || '',
date: curDate || '',
});
},
dateChange(e){
this.curDate = e.detail.value;
this.$nextTick(this.refreshList);
},
stadiumChange(e){
let { stadiumList } = this;
this.curStadium = stadiumList[e.detail.value];
this.$nextTick(this.refreshList);
},
async initPage(){
let { brandInfo } = this;
try{
let _storeList = await this.getStoreList({ brand_id: brandInfo.brand.id || '' });
this.stadiumList = _storeList || [];
if(_storeList.length) this.curStadium = _storeList[0];
this.$nextTick(this.refreshList);
}catch(err){
console.warn('加载数据失败!', err);
}
},
toOperate(){
let { stadiumList, curStadium } = this;
util.$_emit(WRITE_OFF_STORE_NAME, {
stadiumList,
curStadium,
})
util.routeTo(`/pages/write_off/operate/operate?type=site`, 'nT');
},
getList({ brand_id, stadium_id = '', date = '', page = 1, page_size = '' }){
util.showLoad();
servers.get({
url: API.writeOff.listVerifyRecord,
data: { brand_id, stadium_id, date, page, page_size },
failMsg: '加载失败!',
})
.then(res=>{
util.hideLoad();
this.totalNum = res.total || 0;
let _list = res.list || [];
if(page == 1)return this.writeOffList = _list;
if(!_list.length)return util.showNone('没有更多!');
this.page = page;
this.writeOffList = [...this.writeOffList, ..._list];
})
},
getStoreList({
page=1,
page_size=9999,
brand_id='',
}){
return servers.get({
url: API.stadiumList,
data: {
page,
page_size,
brand_id,
},
failMsg: '获取列表失败!'
})
.then(res=>{
let _list = res.list || [];
return _list
})
},
leaveBtn(item){
let { curStadium } = this;
util.showModal({
title: '提示',
content: '是否确认手动离场?',
showCancel: true,
success: modalRes=>{
if(modalRes.confirm){
util.showLoad();
servers.get({
url: API.writeOff.leaveVerifyOrder,
data: {
brand_id: curStadium.brand_id,
id: item.id,
},
failMsg: '请求失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
this.refreshList()
})
}
}
})
},
}
}
</script>
<style lang="scss">
@import "~style/public.scss";
page{
background-color: #f2f2f7;
}
.search-result{
padding-bottom: 132upx;
padding-bottom: calc( 132upx + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
padding-bottom: calc( 132upx + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
.store-bar{
padding: 0 24upx;
height: 144upx;
background-color: #fff;
@include centerFlex(space-between);
>text{
margin-right: 20upx;
flex-shrink: 0;
font-size: 28upx;
color: #9C9C9F;
}
>picker{
flex-grow: 1;
}
view{
padding: 0 20upx;
height: 92upx;
border-radius: 10upx;
background: #f2f2f2;
@include centerFlex(space-between);
>input{
flex-grow: 1;
height: 100%;
font-size: 28upx;
color: #1A1A1A;
}
>image{
flex-shrink: 0;
flex-grow: 0;
width: 28upx;
height: 28upx;
}
}
}
.r-timer-select{
padding: 0 24upx;
margin-bottom: 10upx;
@include centerFlex(space-between);
picker{
flex-shrink: 0;
view{
padding: 24upx 0;
>text{
font-weight: 500;
font-size: 32upx;
}
>image{
margin-left: 16upx;
vertical-align: middle;
width: 22upx;
height: 22upx;
}
}
}
>view{
max-width: 40%;
text-align: right;
font-weight: 500;
font-size: 32upx;
@include textHide(1);
}
}
.r-order-list{
padding: 0 24upx;
.l-item{
position: relative;
margin-bottom: 24upx;
padding: 0 20upx;
border-radius: 10upx;
background-color: #fff;
.i-name{
height: 100upx;
line-height: 98upx;
border-bottom: 2upx solid #D8D8D8;
font-weight: 500;
font-size: 28upx;
color: #1a1a1a;
}
.i-lines{
padding-top: 8upx;
padding-bottom: 32upx;
>view{
display: flex;
>view{
min-width: 0;
line-height: 52upx;
font-size: 28upx;
color: #9c9c9f;
@include textHide(1);
&+view{
flex-shrink: 0;
color: $themeColor;
}
}
}
}
.i-btn{
position: absolute;
right: 20rpx;
bottom: 32rpx;
background-color: #009874;
border-radius: 10rpx;
width: 156rpx;
height: 68rpx;
color: #FFFFFF;
font-size: 28rpx;
text-align: center;
line-height: 68rpx;
}
}
}
.r-bottom-btn{
position: fixed;
left: 0;
bottom: 0;
width: 100%;
padding: 10upx 24upx;
padding-bottom: calc( 10upx + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
padding-bottom: calc( 10upx + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
background-color: #f2f2f7;
>view{
height: 112upx;
line-height: 112upx;
text-align: center;
font-size: 32upx;
border-radius: 10upx;
color: #fff;
background-color: $themeColor;
}
}
}
</style>

8
src/pages/write_off/ym_confirm/ym_confirm.vue

@ -45,14 +45,16 @@ export default {
orderInfo: {
extension: {},
card_info: {}
}
},
operate_stadium_id: '', // id, id
}
},
onLoad(options){
if(options.type)this.type = options.type || '';
util.$_once(WRITE_OFF_ORDER_INFO, data => {
console.warn(data)
this.orderInfo = data;
this.orderInfo = data?.data || {};
this.operate_stadium_id = data?.query?.stadium_id ?? data?.data?.stadium_id ?? '';
})
},
methods: {
@ -67,7 +69,7 @@ export default {
data: {
brand_id: orderInfo.brand_id,
card_no: orderInfo.card_no,
stadium_id: orderInfo.stadium_id,
stadium_id: this.operate_stadium_id ?? '',
type: type == 'verify_code'? 5 : 6,
},
isDefaultGet: false,

BIN
src/static/images/code_null.png

Before

Width: 380  |  Height: 380  |  Size: 7.4 KiB

BIN
src/static/images/icon/arrow_b2.png

Before

Width: 40  |  Height: 40  |  Size: 251 B

After

Width: 15  |  Height: 15  |  Size: 226 B

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

Before

Width: 296  |  Height: 296  |  Size: 51 KiB

After

Width: 296  |  Height: 296  |  Size: 7.7 KiB

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

Before

Width: 52  |  Height: 52  |  Size: 2.4 KiB

After

Width: 52  |  Height: 52  |  Size: 901 B

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

After

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

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

Before

Width: 52  |  Height: 52  |  Size: 2.4 KiB

After

Width: 52  |  Height: 52  |  Size: 775 B

BIN
src/static/images/icon/scan_code_btn.png

Before

Width: 172  |  Height: 172  |  Size: 932 B

BIN
src/static/images/icon/selected_ring.png

Before

Width: 32  |  Height: 32  |  Size: 419 B

BIN
src/static/images/icon/write_off_fail.png

Before

Width: 420  |  Height: 420  |  Size: 9.6 KiB

BIN
src/static/images/order_null.png

Before

Width: 380  |  Height: 380  |  Size: 8.5 KiB

BIN
src/static/images/scan_null.png

Before

Width: 420  |  Height: 420  |  Size: 11 KiB

BIN
src/static/images/tab/ta_1.png

After

Width: 56  |  Height: 56  |  Size: 558 B

BIN
src/static/images/tab/ta_2.png

After

Width: 56  |  Height: 56  |  Size: 795 B

BIN
src/static/images/tab/ta_3.png

After

Width: 56  |  Height: 56  |  Size: 726 B

BIN
src/static/images/tab/ta_4.png

After

Width: 56  |  Height: 56  |  Size: 553 B

BIN
src/static/images/tab/tab_1.png

After

Width: 56  |  Height: 56  |  Size: 653 B

BIN
src/static/images/tab/tab_2.png

After

Width: 56  |  Height: 56  |  Size: 986 B

BIN
src/static/images/tab/tab_3.png

After

Width: 56  |  Height: 56  |  Size: 939 B

BIN
src/static/images/tab/tab_4.png

After

Width: 56  |  Height: 56  |  Size: 668 B

BIN
src/static/images/third_pages/banner.png

After

Width: 351  |  Height: 200  |  Size: 23 KiB

BIN
src/static/images/third_pages/bg.png

After

Width: 170  |  Height: 210  |  Size: 2.5 KiB

BIN
src/static/images/third_pages/tab_a.png

After

Width: 68  |  Height: 57  |  Size: 2.3 KiB

BIN
src/static/images/third_pages/tab_b.png

After

Width: 68  |  Height: 57  |  Size: 1.9 KiB

BIN
src/static/images/write_off/mall.png

Before

Width: 128  |  Height: 128  |  Size: 1.4 KiB

BIN
src/static/images/write_off/order.png

Before

Width: 128  |  Height: 128  |  Size: 1.0 KiB

BIN
src/static/images/write_off/people.png

Before

Width: 128  |  Height: 128  |  Size: 1.8 KiB

BIN
src/static/images/write_off/site.png

Before

Width: 120  |  Height: 120  |  Size: 1.4 KiB

85
src/store/actions.js

@ -1,6 +1,8 @@
// 异步方法
import { servers } from '../js/server';
import { API } from '../js/api';
import { showLoad, hideLoad, showModal, promisify } from '../utils/util';
export default {
getBrandInfo({commit, state}){
if(state?.brandInfo?.brand?.id)return Promise.resolve(state.brandInfo);
@ -13,5 +15,86 @@ export default {
commit('setBrandInfo',res);
return res;
})
}
},
getOrderRefundList({commit, state}, order_no){
return servers.get({
url: API.order.orderRefundList,
data: { order_no },
isDefaultGet: false
})
.catch(err=>{
console.warn('actions getOrderRefundList err -->', err);
})
},
// 获取门店列表
getStadiumLs({ commit, state }, brand_id){
showLoad();
return servers.post({
url: API.stadiumList,
data: { page_size: 9999, page: 1, brand_id },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
let _ls = _data?.data?.list || [];
return _ls;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '加载店铺失败!'
})
console.warn('store actions getStadiumLs err --->', err);
// return Promise.reject(err);
})
},
// 看是否授权,清除token 也可以理解自动登陆
async checkUserAuthor({ commit, state }){
let loginRes = {};
try{
const uniLogin = promisify(uni.login);
loginRes = await uniLogin();
}catch(err){
console.warn('store actions checkUserAuthor login err', err);
showModal({ content: '获取登陆凭证失败!请稍后重新登录!' });
return Promise.reject(err);
}
showLoad();
return servers.post({
url: API.wechatMiniAppLoginAndSync,
data: {
code: loginRes.code,
appid: state.APPID,
},
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
if(_data.data === ''){
commit('setLoginState', { loginState: false, token: '' });
return _data;
}
commit('setLoginState', { loginState: true, token: _data.data });
return _data;
}else{
return Promise.reject(_data);
}
})
.catch(err=>{
console.warn('store actions checkUserAuthor wechatMiniAppLoginAndSync err --->', err);
hideLoad();
showModal({ content: err?.message || '静默登录失败!请稍后重试!' });
return Promise.reject(err);
})
},
}

23
src/store/index.js

@ -33,18 +33,20 @@ export default new Vuex.Store({
// },
permissionObj: { // 权限代号对应
'1001': '营业额',
'1002': '收款记录',
// '1002': '收款记录',
'1012': '订单管理',
'1007': '员工管理',
'1008': '核销查询',
'1009': '场地管理',
'1010': '设备管理',
'1009': '场地看板',
'1010': '智能设备',
'1011': '商品零售',
// '1013': '课程管理',
'1014': '储值卡管理',
'1015': '进场人数异常',
'1016': '系统工具',
'1017': '钱包提现'
// '1015': '进场人数异常',
'1016': '小程序管理',
'1017': '钱包&提现',
'1018': '退款权限',
'1022': '工单'
},
// 场地占用提交页面信息
occupyInfo: {
@ -53,7 +55,8 @@ export default new Vuex.Store({
typeInfo: {}, // 球场类型
venueList: [], // 选择场地列表
orderInfo: {}, // 订单信息 // 20230620 新增挂账需求,用于回显确认订单
}
},
isLogin: false, // 登陆状态
},
mutations,
actions,
@ -68,6 +71,12 @@ export default new Vuex.Store({
}
console.warn(_arr.length)
return _arr;
},
loginState: state =>{
let _isLogin = state.isLogin;
let _token = uni.getStorageSync('token');
// 只是想获得一个响应的token状态
if(_isLogin || !_isLogin)return !!_token;
}
}
});

13
src/store/mutations.js

@ -10,5 +10,18 @@ export default {
// 场地占用信息
setOccupyInfo(state, _occupyInfo){
state.occupyInfo = _occupyInfo;
},
//
setLoginState(state, { loginState = false, token = '' }){
console.log(loginState, token);
if(loginState&&token){
uni.setStorageSync('token', token);
state.isLogin = loginState;
}else{
uni.removeStorageSync('token', token);
state.isLogin = false;
}
}
}

188
src/subpackage/authorization/components/login.vue

@ -0,0 +1,188 @@
<template>
<view class="authorization-login" v-show="isShow">
<view class="ic-author-modal">
<view class="iam-title">微信授权</view>
<view class="iam-tip">您的信息和数据将受到保护</view>
<image class="iam-pic" mode="aspectFit" src="../static/images/author_modal.png"></image>
<view class="iam-btns">
<button plain hover-class="hover-active" @click="hide">取消</button>
<button
v-if="isProfile"
plain
hover-class="hover-active"
@click="profileConfirm"
>授权并登录</button>
<button
v-else
plain
hover-class="hover-active"
open-type="getUserInfo"
lang="zh_CN"
@getuserinfo="confirmAuthor"
>授权并登录</button>
</view>
</view>
</view>
</template>
<script>
import { promisify, showModal, showNone, showLoad, hideLoad, routeTo } from "@/utils/util";
import { AUTHOR_API } from "../js/api";
import server from "../js/server";
export default {
data(){
return {
isShow: false,
initData: {
/**
* @param {Object} data
* @param {Function} data.success
* @param {Function} data.fail
* */
},
}
},
methods: {
alert(data){
this.isShow = true;
this.initData = data ?? {};
},
hide(){
this.isShow = false;
},
isProfile(){
return !!uni.getUserProfile
},
//
profileConfirm(){
uni.getUserProfile({
lang: 'zh_CN', desc: '授权登陆',
success: res => { this.confirmAuthor({detail: {...res}}) },
fail: err => {
showModal({ content: '获取用户信息失败!请重试' });
console.warn('subpackage authorization components login getUserProfile Err ->', err)
}
})
},
async confirmAuthor(userRes){
if(!userRes.detail.userInfo){
this.hide();
return showModal({ content: '获取用户信息失败!请稍后重试' });
}
let loginRes = await promisify(uni.login)();
if(!loginRes.code){
this.hide();
return showModal({ content: '获取登陆凭证失败!请稍后重试' });
}
this.serverLogin({ userRes, loginRes })
},
getLoginQuery({
userInfo,
loginRes
}){
return {
appid: uni.getAccountInfoSync?.()?.miniProgram?.appId ?? '',
code: loginRes?.code ?? '',
encryptedData: userInfo?.encryptedData ?? '',
// is_details: 1,
//
user_info: userInfo?.userInfo ?? {},
user_raw_data: userInfo?.rawData ?? {},
...(userInfo?.userInfo ?? {}),
}
},
serverLogin({ userRes, loginRes }){
showLoad();
return server.post({
url: AUTHOR_API.wechatMiniAppLoginAndSync,
data: this.getLoginQuery({
userInfo: userRes.detail,
loginRes
}),
isDefaultGet: false,
})
.then(res=>{
hideLoad();
let _data = res.data || {};
if(_data.code == 0){
if(_data.data == '')return routeTo(`/pages/login/login`,'rL');
showNone(_data?.message || '登陆成功!');
this.$store.commit('setLoginState', { loginState: true, token: _data.data });
this.initData?.success?.(_data);
this.hide();
return _data;
}else{
return Promise.reject(_data);
}
})
.catch(err=>{
hideLoad();
showModal({ content: err?.message || '后台登陆失败!' });
console.warn('subpackage authorization components login serverLogin Err ->', err);
if(this.initData?.fail)return this.initData?.fail?.(err);
return Promise.reject(err);
})
},
cancelAuthor(){
}
}
}
</script>
<style lang="scss">
.authorization-login{
position: fixed;
left: 0;
top: var(--window-top);
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .5);
}
.ic-author-modal{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding-top: 60upx;
width: 662upx;
height: 884upx;
border-radius: 10upx;
background-color: #fff;
.iam-title{
margin-bottom: 22upx;
text-align: center;
@include flcw(44upx, 60upx, #1a1a1a, 500);
}
.iam-tip{
margin-bottom: 52upx;
text-align: center;
@include flcw(28upx, 40upx, #9c9c9f);
}
.iam-pic{
margin: 0 auto 62upx;
display: block;
width: 488upx;
height: 416upx;
}
.iam-btns{
@include ctf(center);
>button{
margin: 0 20upx;
width: 240upx;
height: 92upx;
text-align: center;
border-radius: 46upx;
border: 2upx solid $mColor;
@include flcw(32upx, 88upx, $mColor);
&+button{
background-color: $mColor;
color: #fff;
}
}
}
}
</style>

25
src/subpackage/authorization/components/user_info/iconfont.css

@ -0,0 +1,25 @@
@charset "UTF-8";
@font-face {
font-family: "tuniaoFont"; /* Project id 3784643 */
src:
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAN8AAsAAAAAB4gAAAMvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDBgqCQIIdATYCJAMMCwgABCAFhH8HPRuYBiMRpmvPIfurAzsYPj7DCVbrRAmElfXCs4V6xacO8aHO69VXu54tTy8h0hcJ7ufgieY+3+xscrn9B8wKQQFBywplLZCw9SwcOyZ5pKsoAQAbcICJTr7AjPPqr06NRkeIAemFpXv8cje1+/LkuLvD789WJZUANjOe9F76qptDOSMUui/Ua0WQKpZGXaqf5hHZdAD6x8tRJtRKtQd7j5UKkk6lWywYkGADCZBnGLugJd6CAI4GOjH956Ep4k7imdNkYTGFAkm2nJyPrBNwA7YhWwgQ/K4bLXITD8wl8xxuBN+XwyYJwOAp5jFjOwNbPNwd3l3WiyFAv3sCBLjcAB6gQM3GjLLSbJpDo1ANiqsiFiX7WAQzqTcMA06j/pb/eAbEx8yMAaAChg3sDjs6COwub8IAoAD4PEZkA7iAZClPKdicmJLc2OiZcQC3cV613sc3Ab9W7YItQtio9YgdKGjgr+ANlbg5gPvXFacv8IyvKTH4pcjV7VIcfnuIAUd3VCbxcCIKj++rvr/hpx/iwxd4/5mxDXeILbBp2+ByMIlGMuQEU3gjiVaMcXk8Dn2gWo5wuRwuIldLRqRwWogvmCRXE9/kSvrVQP4iFDVWRSbRakMk/AtcBYVpO6rpah1V9J/m/Wt+q8WfsOV+S5KFTGD5dwW9KFstZUs5smuYK3V495zwUG+r/Izf0PWtbWj/HUQsAL9u+cGK8NCiAgRnaRJ49fqljrxWc4PaKs4pQwBlMLUeAzgcEGLakvg5nCZ3zgTBkZzAEEUuFEeJyMrq4BGhGT6OPjiqGd4eIZkLKGKjgTKuAAgpPIAhjmdQUngnsrJdeKTxBz4pkgjHjJQeFqFMXv8PQqg0QP+F2cbAdoHqFmaM52jYlcbNoeTq4CtPkjzHAE1Zl0QvFEjGmp9fY6vKwBI9kOvIuQhJ4kJWy0k1dVXFVXcpbfTmIIRKA/RfmG0M7G1T3cKM8VwMXGlEnR1KY0uU+CTJnM1ODk1ZdxJdSOjYdGOp+TW2k6szsNg8qIvIuQipetFCVstpQCB1lcXHXUl5ZOpfdBvgMI9QMaJi8TZdzLS36K8/ueDZOWMAAA==') format('woff2'),
url('//at.alicdn.com/t/c/font_3784643_5jru9pe5fad.woff?t=1669045092678') format('woff'),
url('//at.alicdn.com/t/c/font_3784643_5jru9pe5fad.ttf?t=1669045092678') format('truetype');
}
[class*='tn-icon-'] {
font-family: 'tuniaoFont' !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
text-align: center;
text-decoration: none;
}
.tn-icon-close:before {
content: "\e74d";
}
.tn-icon-camera-fill:before {
content: "\e75d";
}

133
src/subpackage/authorization/components/user_info/impower.vue

@ -0,0 +1,133 @@
<template>
<wx-user-info-modal
v-model="showAuthorizationModal"
@updated="updatedUserInfoEvent"
:isModal="isModal"
:alway="alway"
></wx-user-info-modal>
</template>
<script>
import WxUserInfoModal from './tuniaoui-wx-user-info.vue';
import { AUTHOR_API } from '../../js/api';
import servers from '../../js/server';
import { showModal, jsonPar, showLoad, hideLoad } from '@/utils/util';
export default {
components: { WxUserInfoModal },
props: {
isModal: {
type: Boolean,
default: true
},
alway: {
type: Boolean,
default: false
}
},
watch: {
showAuthorizationModal(nVal, oVal){
let { initData } = this;
if(!nVal&&oVal)initData?.close?.();
}
},
data() {
return {
showAuthorizationModal: false,
initData: {},
}
},
methods: {
show(data){
this.showAuthorizationModal = true;
this.initData = data;
},
hide(){
this.showAuthorizationModal = false;
},
//
async updatedUserInfoEvent(info) {
let { initData, userEdit } = this;
try{
let _imgUrl = await this.uploadImg(info.avatar);
let _editObj = { nickname: info.nickname, avatar_url: _imgUrl };
await userEdit(_editObj);
this.hide();
initData?.success?.(_editObj);
}catch(err){
console.warn('authorize components user info impower updatedUserInfoEvent err --->', err)
initData?.fail?.(err);
}
},
//
async uploadImg(url){
try{
showLoad();
let _imgInfo = await servers.uploadFile({
url: AUTHOR_API.zs_user_avatar,
filePath: url,
});
if(_imgInfo.statusCode != 200)throw(_imgInfo);
let _imgRes = jsonPar(_imgInfo.data);
if(_imgRes.code != 0)throw(_imgRes);
hideLoad();
return _imgRes.data.url || '';
}catch(err){
hideLoad();
showModal({ content: err?.errMsg ?? err?.message ?? '上传图片失败!' });
console.warn('authorize components user info impower uploadImg err --->', err)
return Promise.reject(err);
}
},
//
userEdit({ nickname, avatar_url }){
showLoad();
return servers.post({
url: AUTHOR_API.changeAvatar,
data: { nickname, avatar_url },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
console.log('userEdit res--->', _data);
return _data?.data;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({ content: err.message || '操作失败!' });
console.warn('authorize components user info impower userEdit err --->', err);
return Promise.reject(err);
})
},
//
refreshUserInfo(){
return servers.post({
url: ATR_API.userCurrent,
data: {},
isDefaultGet: false,
})
.then(res => {
let _data = res?.data || {};
if(_data.code === 0){
uni.setStorageSync('userInfo', _data?.data);
this.$store.commit('refreshUserInfo', _data?.data);
return _data?.data;
}else{
return Promise.reject(_data);
}
})
.catch(err => {
console.warn('authorize components user info impower refreshUserInfo err --->', err);
// return Promise.reject(err);
})
}
}
}
</script>
<style lang="scss">
</style>

357
src/subpackage/authorization/components/user_info/tuniaoui-wx-user-info.vue

@ -0,0 +1,357 @@
<template>
<view
v-if="openModal || alway"
class="wx-authorization-modal"
:class="{ 'modal-class': isModal }"
>
<view
class="wam__mask"
@touchmove.prevent=""
@tap.stop="closeModal"
></view>
<!-- 内容区域 -->
<view class="wam__wrapper">
<!-- 关闭按钮 -->
<view class="wam__close-btn" @tap.stop="closeModal">
<text class="tn-icon-close"></text>
</view>
<!-- 标题 -->
<view class="wam__title">获取您的昵称头像</view>
<!-- tips -->
<view class="wam__sub-title">
获取用户头像昵称主要用于向用户提供具有辨识度的用户中心界面
</view>
<!-- 头像选择 -->
<view class="wam__avatar">
<view class="button-shadow">
<button
class="button"
open-type="chooseAvatar"
@chooseavatar="chooseAvatarEvent"
>
<view v-if="userInfo.avatar" class="avatar__image">
<image class="image" :src="userInfo.avatar" mode="aspectFill"></image>
</view>
<view v-else class="avatar__empty">
<!-- <image class="image" src="https://cdn.nlark.com/yuque/0/2022/jpeg/280373/1668928062708-assets/web-upload/764843cf-055a-4cb6-b5d3-dca528b33fd4.jpeg" mode="aspectFill"></image> -->
</view>
<view class="avatar--icon">
<view class="tn-icon-camera-fill"></view>
</view>
</button>
</view>
</view>
<!-- 昵称输入 -->
<view class="wam__nickname">
<view class="nickname__data">
<input class="input" type="nickname" v-model="userInfo.nickname" placeholder="请输入昵称" placeholder-style="color: #AAAAAA;">
</view>
</view>
<!-- 保存按钮 -->
<view
class="wam__submit-btn"
:class="[{
'disabled': !userInfo.avatar || !userInfo.nickname
}]"
hover-class="tn-btn-hover-class"
:hover-stay-time="150"
@tap.stop="submitUserInfo"
>
</view>
</view>
</view>
</template>
<script>
export default {
options: {
// Vue(shadow)
virtualHost: true
},
props: {
value: {
type: Boolean,
default: false
},
isModal: {
type: Boolean,
default: true
},
alway: {
type: Boolean,
default: false
}
},
data() {
return {
openModal: false,
userInfo: {
avatar: '',
nickname: ''
}
}
},
watch: {
value: {
handler(val) {
this.openModal = val
},
immediate: true
}
},
methods: {
//
chooseAvatarEvent(e) {
this.userInfo.avatar = e.detail.avatarUrl
},
//
submitUserInfo() {
//
if (!this.userInfo.avatar || !this.userInfo.nickname) {
return uni.showToast({
icon: 'none',
title: '请选择头像和输入用户信息'
})
}
//
this.$emit('updated', this.userInfo)
},
//
closeModal() {
this.$emit('input', false)
},
}
}
</script>
<style lang="scss" scoped>
@import './iconfont.css';
.modal-class{
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
z-index: 99998;
.wam {
&__mask {
display: block !important;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
animation: showMask 0.25s ease 0.1s forwards;
}
&__close-btn {
display: block !important;
display: none;
position: absolute;
top: 30rpx;
right: 30rpx;
z-index: 99999;
}
&__wrapper{
position: absolute;
left: 0;
bottom: 0;
width: 100%;
transform-origin: center bottom;
transform: scaleY(0);
animation: showWrapper 0.25s ease 0.1s forwards;
z-index: 99999;
}
}
}
.wx-authorization-modal {
// view {
// box-sizing: border-box;
// }
.image {
width: 100%;
height: 100%;
border-radius: inherit;
}
.wam {
/* mask */
&__mask {
display: none;
}
/* close-btn */
&__close-btn {
display: none;
}
/* wrapper */
&__wrapper {
background-color: #FFFFFF;
border-radius: 20rpx 20rpx 0rpx 0rpx;
padding: 40rpx;
padding-top: 60rpx;
padding-bottom: 40rpx;
padding-bottom: calc(constant(safe-area-inset-bottom) + 40rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 40rpx);
}
/* title */
&__title {
font-size: 34rpx;
}
/* sub-title */
&__sub-title {
font-size: 26rpx;
color: #AAAAAA;
margin-top: 16rpx;
padding-bottom: 30rpx;
}
/* 头像选择 */
&__avatar {
width: 100%;
margin-top: 30rpx;
display: flex;
align-items: center;
justify-content: center;
.button-shadow {
border: 8rpx solid rgba(255,255,255,0.05);
box-shadow: 0rpx 0rpx 80rpx 0rpx rgba(0, 0, 0, 0.15);
border-radius: 50%;
}
.button {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 50%;
overflow: visible;
background-image: repeating-linear-gradient(45deg, #E4E9EC, #F8F7F8);
color: #FFFFFF;
background-color: transparent;
padding: 0;
margin: 0;
font-size: inherit;
line-height: inherit;
border: none;
&::after {
border: none;
}
}
.avatar {
&__empty, &__image {
width: 100%;
height: 100%;
border-radius: inherit;
}
&--icon {
position: absolute;
right: -10rpx;
bottom: -6rpx;
width: 60rpx;
height: 60rpx;
// transform: translate(50%, 50%);
background-color: #1D2541;
color: #FFFFFF;
border-radius: 50%;
border: 6rpx solid #FFFFFF;
line-height: 1;
font-size: 36rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
}
/* 昵称 */
&__nickname {
margin-top: 40rpx;
.nickname {
&__data {
margin-top: 16rpx;
width: 100%;
padding: 26rpx 20rpx;
border-radius: 10rpx;
background-color: #F8F7F8;
.input {
color: #080808;
}
}
}
}
/* 保存按钮 */
&__submit-btn {
width: 100%;
background-color: $mColor;
color: #FFFFFF;
margin-top: 60rpx;
border-radius: 10rpx;
padding: 25rpx;
font-size: 32rpx;
display: flex;
align-items: center;
justify-content: center;
&.disabled {
background-color: #E6E6E6;
}
}
}
}
.tn-btn-hover-class {
box-shadow: inset 10rpx 2rpx 40rpx 0rpx rgba(0, 0, 0, 0.05);
}
@keyframes showMask {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes showWrapper {
0% {
transform: scaleY(0);
}
100% {
transform: scaleY(1);
}
}
</style>

9
src/subpackage/authorization/js/api.js

@ -0,0 +1,9 @@
import { ORIGIN } from '@/js/api';
export const AUTHOR_API = {
wechatMiniAppLoginAndSync: `${ORIGIN}/assistant/WechatMiniAppGetToken`, // 小程序授权获取token,为空就登录
zs_user_avatar:`${ORIGIN}/upload/file/zs_user_avatar`, // 头像图片上传
changeAvatar: `${ORIGIN}/admin/assistant/changeAvatar`, // 修改用户头像、昵称
}
export default AUTHOR_API;

10
src/subpackage/authorization/js/server.js

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

16
src/subpackage/authorization/pages/index.vue

@ -0,0 +1,16 @@
<template>
<!-- 需要定义一个页面以及在pagesjson里声明才能生成static文件夹 -->
<view>&nbsp;</view>
</template>
<script>
export default {
}
</script>
<style lang="scss">
page{
background: #fff;
}
</style>

0
src/static/images/icon/author_modal.png → src/subpackage/authorization/static/images/author_modal.png

Before

Width: 488  |  Height: 416  |  Size: 14 KiB

After

Width: 488  |  Height: 416  |  Size: 14 KiB

4
src/subpackage/blacklist/pages/abnormal_list/abnormal_list.vue

@ -102,10 +102,10 @@ export default {
async onLoad(options){
try{
let _date = this.curDate = util.formatDate({});
let _stadiumLs = await this.getStoreList({});
let _stadiumLs = await this.getStoreList({ brand_id: options?.brand_id ?? '' });
let { curTimeTab } = this;
this.getRecords({
brand_id: options.brand_id,
brand_id: options?.brand_id ?? '',
[ curTimeTab?.label || '-' ]: _date
});
}catch(err){

173
src/subpackage/device/components/brand_change_modal.vue

@ -0,0 +1,173 @@
<template>
<view class="brand-change-modal">
<view class="bcm-mask" v-show="isShow">
<view class="bm-content">
<view class="bc-title">品牌切换</view>
<input type="text" class="bc-ipt" v-model="searchTxt" placeholder="模糊搜索..." />
<view class="bc-tip">当前选中{{ curSelected.name || '-' }}</view>
<view class="bc-tip">Tip控件有bug当前选中与下方不匹配时滑动纠正</view>
<picker-view class="bc-picker-view" immediate-change @change="pickerChange">
<picker-view-column>
<view class="bpv-item" v-for="(e, i) in showBrandList" :key="i">{{ e.name || '-' }}</view>
</picker-view-column>
</picker-view>
<view class="bc-btns">
<view class="bb-item" @click="hide">取消</view>
<view class="bb-item" @click="confirm">确认</view>
</view>
</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import deviceServer from '../js/device_server';
import deviceApi from '../js/device_api';
import PinyinEngine from "pinyin-engine";
export default {
data(){
return {
isShow: false,
searchTxt: '',
brandList: [],
initOptions: {},
pickerIdx: 0,
pyEngine: null,
}
},
computed: {
showBrandList(){
let { brandList, searchTxt, pyEngine } = this;
if(searchTxt&&pyEngine?.query){
let _ls = pyEngine.query(searchTxt);
return _ls || [];
}
return brandList.filter(item=>{
return item.name.includes(searchTxt);
});
},
curSelected(){
let { pickerIdx, showBrandList } = this;
return showBrandList?.[pickerIdx] || {};
}
},
mounted(){
// this.getBrandList();
},
methods: {
confirm(){
let { pickerIdx, showBrandList, initOptions } = this;
console.log('pickerIdx', pickerIdx);
console.log('showBrandList', showBrandList);
this.hide();
if(showBrandList?.length <= 0) return;
let _brand = showBrandList[pickerIdx];
initOptions?.success?.(_brand);
this.$emit('click:confirm', _brand);
},
pickerChange(e){
this.pickerIdx = e?.detail?.value?.[0];
},
async init(options){
if(this.brandList?.length <= 0) await this.getBrandList();
this.initOptions = options;
this.show();
},
show(){
this.isShow = true;
},
hide(){
this.isShow = false;
},
//
getBrandList(){
util.showLoad();
return deviceServer.get({
url: deviceApi.brandList,
data: {},
failMsg: '加载品牌列表失败!'
})
.then(res=>{
util.hideLoad();
let _ls = res || [];
this.brandList = _ls;
if(_ls?.length > 0&&PinyinEngine){
this.pyEngine = new PinyinEngine(_ls, ['name']);
}
})
.catch(util.hideLoad)
},
}
}
</script>
<style lang="scss">
.bcm-mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 10;
.bm-content{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 40upx;
width: 620upx;
border-radius: 10px;
background-color: #fff;
}
.bc-title{
text-align: center;
@include flcw(32upx, 44upx, #1a1a1a, 500);
}
.bc-tip{
margin-top: 10upx;
@include flcw(20upx, 30upx, #9A9A9D);
}
.bc-ipt{
box-sizing: border-box;
margin-top: 38upx;
padding: 0 20upx;
height: 88upx;
border-radius: 10upx;
border: 2upx solid #d8d8d8;
@include flcw(28upx, 40upx, #1a1a1a, 500);
}
.bc-picker-view{
margin-top: 20upx;
width: 100%;
height: 300upx;
.bpv-item{
font-size: 28upx;
color: #1a1a1a;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.bc-btns{
margin-top: 30upx;
@include ctf(space-between);
.bb-item{
width: 240upx;
height: 88upx;
border-radius: 10upx;
border: 2upx solid $mColor;
text-align: center;
@include flcw(32upx, 84upx, $mColor, 500);
&+.bb-item{
background: $mColor;
color: #fff;
}
}
}
}
</style>

30
src/subpackage/device/components/order/reservation_people_detail/reservation_people_detail.vue

@ -161,6 +161,26 @@
<view>微信交易号<text>{{orderInfo.trade_no || ''}}</text></view>
</view>
</view>
<!-- 退款列表 -->
<view v-for="(e, i) in orderInfo.refunds" :key="i" style="padding: 24rpx 24rpx 0;">
<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>
</view>
<!-- 退款按钮 -->
<order-refund-fixed
:pay_amount="orderInfo.pay_amount || 0"
:refund_amount="orderInfo.refund_amount || 0"
:refund_times="(orderInfo.refunds&&orderInfo.refunds.length) || 0"
@click:button="refunndBtn"
></order-refund-fixed>
<!-- 弹框 绑定ic卡 -->
<view class="ox-dark-mask" v-if="isShowBindIC" @touchmove.stop.prevent="moveHandle">
<view class="odm-ic-modal">
@ -237,7 +257,13 @@ import util from '@/utils/util';
import deviceServer from '../../../js/device_server';
import deviceApi from '../../../js/device_api';
import { mapState } from 'vuex';
import orderRefundFixed from '@/components/order_refund/fixed.vue'
import orderRefundInfo from '@/components/order_refund/info.vue';
export default {
components: {
'order-refund-fixed': orderRefundFixed,
'order-refund-info': orderRefundInfo,
},
props: {
orderInfo: {
type: Object,
@ -592,6 +618,10 @@ export default {
this.isShowSuccessIC = !this.isShowSuccessIC
this.$emit('okChange');
},
refunndBtn(){
// this.$emit('refundbtn');
this.$emit('click:refund');
}
}
}

42
src/subpackage/device/components/order/reservation_site_detail/reservation_site_detail.vue

@ -98,6 +98,12 @@
<view>优惠券优惠</view>
<view>-¥{{orderInfo.coupons_amount || 0}}</view>
</view>
<view class="rs-line"
v-if="orderInfo.extension&&orderInfo.extension.meituan_args&&orderInfo.extension.meituan_args.meituan_coupon_deduction_amount"
>
<view>团购券抵扣</view>
<view>-¥{{ orderInfo.extension.meituan_args.meituan_coupon_deduction_amount || 0}}</view>
</view>
</view>
<view class="rb-total">
<view>合计支付<text></text><text>{{orderInfo.pay_amount || 0}}</text></view>
@ -135,14 +141,35 @@
</view>
</view>
<!-- 2022/01/04 PM: 禁用退款功能 -->
<!-- <view class="rs-btn" @click="refunndBtn" v-if="orderInfo.pay_status == 1 || orderInfo.pay_status == 2">退款</view> -->
<!-- 退款列表 -->
<view v-for="(e, i) in orderInfo.refunds" :key="i" style="padding: 24rpx 24rpx 0;">
<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>
</view>
<order-refund-fixed
:pay_amount="orderInfo.pay_amount || 0"
:refund_amount="orderInfo.refund_amount || 0"
:refund_times="(orderInfo.refunds&&orderInfo.refunds.length) || 0"
@click:button="refunndBtn"
></order-refund-fixed>
</view>
</template>
<script>
import orderRefundFixed from '@/components/order_refund/fixed.vue';
import orderRefundInfo from '@/components/order_refund/info.vue';
export default {
components: {
'order-refund-fixed': orderRefundFixed,
'order-refund-info': orderRefundInfo
},
props: {
orderInfo: {
type: Object,
@ -191,7 +218,7 @@ export default {
return _obj[`${_status}`] || '-'
},
refunndBtn(){
this.$emit('refundbtn');
this.$emit('click:refund');
}
}
@ -344,15 +371,6 @@ export default {
}
}
.rs-btn{
margin: 24rpx 24rpx 0 auto;
width: 192upx;
line-height: 88upx;
border-radius: 10upx;
background-color: #fff;
text-align: center;
color: $themeColor;
}
}

265
src/subpackage/device/components/order/timing_detail/timing_detail.vue

@ -1,265 +0,0 @@
<template>
<view class="timing-details">
<view class="td-head">
<view class="th-store">
<image :src="orderInfo.stadium_logo || ''"></image>
<view>{{orderInfo.stadium_name || '-'}}</view>
</view>
<view class="th-line">
<view>订单编号<text>{{orderInfo.order_no || '-'}}</text></view>
<view><text :class="[orderInfo.pay_status == 0?'tl-active':'']">{{zh_order_status(orderInfo.pay_status)}}</text></view>
</view>
<view class="th-line">
<view>创建时间<text>{{orderInfo.model.created_at || '-'}}</text></view>
</view>
<view class="th-line">
<view>用户昵称<text>{{orderInfo.nickname || '-'}}</text></view>
</view>
</view>
<view class="td-box">
<view class="tb-title">计时信息</view>
<view class="tb-line">
<!-- 项目 场时有 人时没有 order_type: 1场时 2人时 -->
<view v-if="orderInfo.order_type==1">项目<text>{{orderInfo.project_name || '-'}}({{orderInfo.venue_name || '-'}})</text></view>
<view>进场时间<text>{{orderInfo.start_time || '-'}}</text></view>
<!-- 离场时间 已完成显示 -->
<view v-if="orderInfo.pay_status != 0">离场时间<text>{{orderInfo.end_time || '-'}}</text></view>
<view>时长合计<text>{{orderInfo.extension.duration || '-'}}</text></view>
</view>
<view class="tb-section" v-if="!(orderInfo.pay_status== 0&&orderInfo.early_end_timing==false)">
<view class="ts-line">
<view>金额小计</view>
<view>¥{{orderInfo.amount || 0}}</view>
</view>
<view class="ts-line">
<view>积分抵扣</view>
<view>-¥{{orderInfo.deduction_amount ||0}}</view>
</view>
<view class="ts-line">
<view>折扣金额</view>
<view>-¥{{orderInfo.discount_amount||0}}</view>
</view>
<view class="ts-line">
<view>优惠券优惠</view>
<view>-¥{{orderInfo.coupons_amount || 0}}</view>
</view>
<view class="ts-total">
<view>合计支付<text></text><text>{{orderInfo.pay_amount ||0}}</text></view>
</view>
</view>
</view>
<view class="td-box" v-if="orderInfo.pay_status != 0">
<view class="tb-title">支付信息</view>
<view class="tb-line">
<view>支付方式<text>{{zh_pay_type(orderInfo.pay_type)}}</text></view>
<view>支付时间<text>{{orderInfo.pay_time || '-'}}</text></view>
<view v-if="orderInfo.pay_type==0">微信交易号<text>{{orderInfo.trade_no || ''}}</text></view>
<view v-if="orderInfo.pay_type==3">操作人<text>{{orderInfo.end_bill_operator_name || ''}}</text></view>
</view>
</view>
<view class="td-box" v-if="orderInfo.pay_status == 4">
<view class="tb-title">退款信息</view>
<view class="tb-line">
<view>退款原因<text>{{orderInfo.refund_reason || '-'}}</text></view>
<view>退款时间<text>{{orderInfo.refund_time || '-'}}</text></view>
<view>退款单号<text>{{orderInfo.refund_no || '-'}}</text></view>
</view>
</view>
<view class="td-btn" @click="isEndBill = true" v-if="orderInfo.pay_status == 0&&orderInfo.early_end_timing==false">结束计费</view>
<view class="td-btn" @click="completeBtn" v-if="orderInfo.pay_status == 0&&orderInfo.early_end_timing==true">完结订单</view>
<!-- 结束计费弹框 -->
<end-billing-modal
v-if="isEndBill==true"
@close="isEndBill=false"
@timeEndBtn="timeEndBtn"
:orderInfo="orderInfo"
></end-billing-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import deviceServer from '../../../js/device_server';
import deviceApi from '../../../js/device_api';
import { mapState } from 'vuex';
import end_billing_modal from '../../../../../components/end_billing_modal/end_billing_modal.vue';
export default {
components: {
'end-billing-modal': end_billing_modal,
},
props: {
orderInfo: {
type: Object,
default: ()=>({})
}
},
computed: {
...mapState(['brandInfo']),
zh_order_status(){
// 01
let { orderInfo } = this
return status =>{
const _obj = {
'0': orderInfo.early_end_timing==true?'待支付':'计费中',
'1': '已完成',
'4': '已退款',
}
return _obj[`${status}`] || '-'
}
},
zh_pay_type(){
// 012
return status =>{
const _obj = {
'0': '微信支付',
'1': '支付宝支付',
'2': '储值卡支付',
'3': '商家主动结束计费',
}
return _obj[`${status}`] || '-'
}
},
},
data() {
return {
isEndBill: false,
}
},
methods: {
completeBtn(){
this.$emit('completeBtn');
},
timeEndBtn(){
this.$emit('refreshPage');
},
}
}
</script>
<style lang="scss" >
@import '~style/public.scss';
.timing-details{
margin-bottom: 24rpx;
.td-head{
margin: 24rpx 24rpx 0rpx;
padding: 30rpx;
background-color: #FFF;
border-radius: 10rpx 10rpx 0px 0px;
.th-store{
padding-bottom: 26rpx;
margin-bottom: 18rpx;
border-bottom: 2rpx solid #D8D8D8;
display: flex;
justify-content: flex-start;
>image{
flex-shrink: 0;
width: 40rpx;
height: 40rpx;
background-color: skyblue;
}
>view{
flex-grow: 1;
margin-left: 14rpx;
color: #333333;
font-size: 28rpx;
}
}
.th-line{
margin-top: 8rpx;
@include centerFlex(space-between);
>view{
line-height: 40rpx;
font-size: 28rpx;
&:first-child{
color: #9A9A9D;
>text{
color: #333333;
}
}
&:nth-child(2){
>text{
color: #9A9A9D;
}
.tl-active{
color: #009874;
}
}
}
}
}
.td-box{
margin: 24rpx 24rpx 0rpx;
padding: 30rpx;
background-color: #FFF;
border-radius: 10rpx;
.tb-title{
color: #9A9A9D;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.tb-line{
>view{
line-height: 52rpx;
font-size: 28rpx;
color: #9A9A9D;
>text{
color: #333333;
}
}
}
.tb-section{
margin-top: 30rpx;
border-top: 2rpx solid #D8D8D8;
.ts-line{
margin-top: 26rpx;
@include centerFlex(space-between);
>view{
color: #333333;
&:first-child{
font-size: 28rpx;
}
&:nth-child(2){
font-size: 24rpx;
margin-right: 8rpx;
}
}
}
.ts-total{
margin-top: 22rpx;
@include centerFlex(flex-end);
>view{
color: #333333;
font-size: 28rpx;
>text{
color: #FF873D;
&:first-child{
font-size: 28rpx;
}
&:nth-child(2){
font-size: 36rpx;
margin-right: 8rpx;
}
}
}
}
}
}
.td-btn{
height: 112rpx;
margin: 80rpx 24rpx;
border-radius: 10rpx;
background-color: #009874;
color: #FFF;
font-size: 32rpx;
line-height: 112rpx;
text-align: center;
}
}
</style>

6
src/subpackage/device/js/device_api.js

@ -35,7 +35,6 @@ export const DEVICE_API = {
checkIccard:`${ORIGIN}/admin/assistant/checkIccard`, //k-订单管理-校验ic卡
storeHouseList:`${ORIGIN}/admin/assistant/storeHouse/list`, //k-仓库列表
iccardList:`${ORIGIN}/admin/assistant/iccard/list`, //k-ic卡列表
timeOrderDetail:`${ORIGIN}/admin/assistant/timeOrder/detail`, //k-订单管理-计时订单详情
timeOrderEnd:`${ORIGIN}/admin/assistant/timeOrder/end`, //k-订单管理-订单计时结束
timeOrderComplete:`${ORIGIN}/admin/assistant/timeOrder/complete`, //【20220208】k-订单管理-计时订单完结
@ -70,6 +69,11 @@ export const DEVICE_API = {
brandList:`${ORIGIN}/stadium/brand/all`, // 获取品牌列表
brandInfo:`${ORIGIN}/stadium/brand/get`, // 获取品牌信息
// 20240830 tid1641 小程序-商家助手小程序照明定时任务优化
cacScheduledTaskGet:`${ORIGIN}/admin/cacScheduledTask/data`, // 获取YZK照明定时列表接口
cacScheduledTaskSave:`${ORIGIN}/admin/cacScheduledTask/save`, // 设置YZK照明定时
cacScheduledTaskDelete:`${ORIGIN}/admin/cacScheduledTask/delete`, // 删除YZK照明定时
}
export default DEVICE_API;

90
src/subpackage/device/pages/index/index.vue

@ -10,7 +10,8 @@
<store-name :theme="'light'"></store-name>
<view class="ih-container">
<view class="ic-title">欧轩智能中控</view>
<view class="ic-info">
<view class="ic-info" v-if="curStoreInfo.device_name">
<view class="ii-veiw">
<view class="iv-view">
<veiw class="iv-txt">设备id{{ curStoreInfo.device_name || "-" }}</veiw>
@ -32,18 +33,17 @@
</view>
<view class="ic-btn-box">
<view
v-if="oxAcInfo.extension&&oxAcInfo.extension.switch_device_name"
v-if="oxAcInfo.extension&&oxAcInfo.extension.switch_device_name&&curStoreInfo.device_name"
class="ic-btn"
hover-class="hover-active"
@click="restartBtn"
>重启</view>
<picker mode="selector" :range="brandList" range-key="name" @change="brandChange">
<view
v-if="isBrandSwitch"
class="ic-btn select-brand"
hover-class="hover-active"
>切换品牌</view>
</picker>
<view
v-if="isBrandSwitch"
class="ic-btn select-brand"
hover-class="hover-active"
@click="interiorBrandSwitch"
>切换品牌</view>
</view>
</view>
@ -60,6 +60,8 @@
</view>
</view>
<brand-change-modal ref="brandChangeModal" ></brand-change-modal>
</view>
</template>
@ -68,7 +70,7 @@ import util from '../../../../utils/util';
import store_name from '../../components/store_name/store_name';
import deviceServer from '../../js/device_server';
import deviceApi from '../../js/device_api';
import brandChangeModal from "../../components/brand_change_modal.vue";
const rootPage = '/subpackage/device'
const tabArr = [
{id: 1, name: '照明', path: `/pages/switch_manage/switch_manage`},
@ -94,7 +96,8 @@ const tabArr = [
import { mapState } from 'vuex'
export default {
components: {
'store-name': store_name
'store-name': store_name,
'brand-change-modal': brandChangeModal,
},
computed: {
...mapState({
@ -116,20 +119,19 @@ export default {
line-height:${titleBarHeight}px;
`
return _styleStr
},
}
},
watch: {
async curStoreInfo(nw, od){
this.getOuxuanacInfo(nw.device_name);
util.hideLoad();
this.getHardwareTypeList(nw.id);
if(!nw.device_name)return; // tid1641 idid线
this.getOuxuanacInfo(nw.device_name);
this.updateAC(nw.device_name);
console.log('curStoreInfo', nw, od);
},
isBrandSwitch(nw, od){
if(nw)this.getBrandList();
},
switchBrandInfo(nw, od){
if(nw?.id === od?.id)return;
util.showLoad();
let { brandInfo } = this;
this.$store.commit('setBrandInfo', { ...brandInfo, brand: nw });
this.$store.dispatch('getStoreList', { storeChange: true });
@ -146,7 +148,6 @@ export default {
},
oxAcInfo: {},
isBrandSwitch: false,
brandList: [],
switchBrandInfo: {},
}
},
@ -157,7 +158,7 @@ export default {
await this.$store.dispatch('getStoreList', { stadium_id: +options.stadium_id || '', storeChange: true });
let { curStoreInfo } = this;
this.getHardwareTypeList(curStoreInfo.id);
this.getOuxuanacInfo(curStoreInfo.device_name);
if(curStoreInfo?.device_name)this.getOuxuanacInfo(curStoreInfo.device_name);
this.getBrandSwitch();
util.hideLoad();
}catch(err){
@ -175,25 +176,18 @@ export default {
this.initSysBarInfo();
},
methods: {
//
brandChange(e){
let { brandList } = this;
let { value } = e.detail;
this.switchBrandInfo = brandList?.[value] || {};
this.getBrandInfo(brandList?.[value]?.id);
},
//
getBrandList(){
return deviceServer.get({
url: deviceApi.brandList,
data: {},
failMsg: '加载品牌列表失败!'
})
.then(res=>{
console.log('getBrandList --->', res);
this.brandList = res || [];
})
interiorBrandSwitch(){
let { isBrandSwitch } = this;
if(!isBrandSwitch)return;
this.$refs.brandChangeModal.init({
success: e=>{
if(!e?.id)return;
this.switchBrandInfo = { ...e };
this.getBrandInfo(e.id);
}
});
},
getBrandInfo(bid){
return deviceServer.get({
url: deviceApi.brandInfo + `/${bid}`,
@ -254,14 +248,26 @@ export default {
deviceServer.get({
url: deviceApi.acUpdate,
data: { id: device_name },
failMsg: '加载失败!'
isDefaultGet: false,
// failMsg: ''
})
.then(res=>{
util.hideLoad();
this.deviceInfo = res || {};
uni.setStorageSync("deviceInfo",res)
let _data = res?.data || {}
util.hideLoad();
if(_data.code === 0){
this.deviceInfo = _data?.data || {};
uni.setStorageSync("deviceInfo",res)
}else{
return Promise.reject(_data);
}
})
.catch(err=>{
util.hideLoad();
util.showModal({
title: '提示',
content: err?.message || '中控在线状态查询失败!'
})
})
.catch(util.hideLoad)
},
goBack(){
uni.navigateBack({ delta:1 });

180
src/subpackage/device/pages/lighting_time/list.vue

@ -0,0 +1,180 @@
<template>
<view class="lt-list">
<view class="ll-title">{{ optionsQuery.hardware_name || '-' }}</view>
<view class="ll-list">
<view class="ll-item" v-for="(e, i) in timingLs" :key="i">
<view class="li-line">
<view class="ll-txt">指令{{ getInstructTxt(e.operate) }}</view>
<image class="ll-icon" @click="deleteItem(e)" mode="aspectFit" src="/subpackage/device/static/images/delete.png"></image>
</view>
<view class="li-line">
<view class="ll-txt">时间{{ e.h_m_s || '-'}}</view>
</view>
<view class="li-line">
<view class="ll-txt">日期{{ e.day_of_week || '-' }}</view>
</view>
</view>
</view>
<view class="ll-fixed-space"></view>
<view class="ll-fixed">
<view class="lf-btn" @click="addTask">添加定时任务</view>
</view>
</view>
</template>
<script>
import deviceServer from '../../js/device_server';
import deviceApi from '../../js/device_api';
import { showModal, showNone, showLoad, hideLoad, routeTo, jsonPar, jsonStr } from "@/utils/util";
export default {
data(){
return {
timingLs: [],
optionsQuery: {},
}
},
onLoad(options){
options.hardware_name = jsonPar(options?.hardware_name);
this.optionsQuery = options;
},
onShow(){
let { optionsQuery } = this;
this.getTimingList({
hardware_id: optionsQuery?.hardware_id || ''
})
},
methods: {
addTask(){
let { hardware_id, hardware_name } = this.optionsQuery;
let _qryStr = `hardware_id=${hardware_id || ''}&hardware_name=${jsonStr(hardware_name || '')}`
routeTo(`/subpackage/device/pages/lighting_time/setting?${_qryStr}`, 'nT')
},
deleteItem(e){
showModal({
title: '提示',
content: '是否删除该项?',
showCancel: true,
confirmText: '是',
cancelText: '否',
success: res =>{
if(res?.confirm){
this.cacScheduledTaskDelete(e?.id)
.then(res=>{
if(res === 'SUCCESS')this.getTimingList({ hardware_id: e?.hardware_id })
})
}
}
})
},
cacScheduledTaskDelete(id){
showLoad();
return deviceServer.post({
url: deviceApi.cacScheduledTaskDelete,
data: { id },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showModal({
title: '提示',
content: _data.message || '操作成功!'
})
return "SUCCESS"
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('setting cacScheduledTaskSave err', err);
})
},
getInstructTxt(operate){
let _obj = {
'open': '开启',
'close': '关闭'
}
return _obj?.[operate] || operate || '未知'
},
getTimingList({
hardware_id = '',
}){
showLoad();
return deviceServer.get({
url: deviceApi.cacScheduledTaskGet,
data: {
hardware_id,
},
failMsg: '获取定时列表失败'
})
.then(res => {
hideLoad();
let _ls = res || [];
this.timingLs = _ls;
})
.catch(err => {
hideLoad();
console.warn('lighting_time list getTimingList err --->', err);
// return Promise.reject(err);
})
},
}
}
</script>
<style lang="scss">
.ll-title{
padding: 46upx 40upx 60upx;
@include flcw(44upx, 60upx, #1a1a1a, 500);
@include tHide;
}
.ll-list{
.ll-item{
padding: 24upx 30upx;
background: #fff;
&+.ll-item{
margin-top: 24upx;
}
.li-line{
@include ctf(space-between);
align-items: baseline;
&+.li-line{
margin-top: 16upx;
}
.ll-txt{
@include flcw(28upx, 40upx, #1a1a1a);
}
.ll-icon {
flex-shrink: 0;
margin-left: 12upx;
width: 44upx;
height: 44upx;
}
}
}
}
.ll-fixed-space{
padding-bottom: 132upx;
@include isPd(132upx);
}
.ll-fixed{
position: fixed;
bottom: 0;
padding: 10upx 24upx;
width: 100%;
@include isPd(10upx);
.lf-btn{
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(32upx, 112upx, #fff, 500);
}
}
</style>

271
src/subpackage/device/pages/lighting_time/setting.vue

@ -0,0 +1,271 @@
<template>
<view class="lt-setting">
<view class="ll-title">{{ optionsQuery.hardware_name || '-' }}</view>
<view class="ls-container">
<view class="lc-select">
<view class="lc-name">指令</view>
<view class="ls-options">
<view class="lp-item" @click="condition.instruct = 'open'">
<view class="li-txt"></view>
<image
class="li-icon"
mode="aspectFit"
:class="{active: condition.instruct === 'open'}"
:src="condition.instruct === 'open' ? '/subpackage/device/static/images/selected_987.png' : ''"
></image>
</view>
<view class="lp-item" @click="condition.instruct = 'close'">
<view class="li-txt"></view>
<image
class="li-icon"
mode="aspectFit"
:class="{active: condition.instruct === 'close'}"
:src="condition.instruct === 'close' ? '/subpackage/device/static/images/selected_987.png' : ''"
></image>
</view>
</view>
</view>
<view class="lc-picker">
<view class="lc-name">时间点</view>
<picker mode="time" @change="condition.time = $event.detail.value">
<view class="lp-box">
<input disabled type="text" class="lb-ipt" v-model="condition.time">
<image class="lb-icon" mode="aspectFit" src="/subpackage/device/static/images/arrow_b2.png"></image>
</view>
</picker>
</view>
<view class="lc-switch">
<view class="lc-name">日期</view>
<view class="ls-right">
<switch class="lr-switch" color="#009874" @change="switchChange"></switch>
<view class="lr-txt">全选</view>
</view>
</view>
<view class="lc-week-ls">
<view
class="lwl-item"
v-for="(e, i) in weeks"
:key="i"
:class="{active: condition.weekLs.includes(e)}"
@click="weekItemClick(e)"
>{{ e }}</view>
</view>
</view>
<view class="ll-fixed-space"></view>
<view class="ll-fixed">
<view class="lf-btn" @click="confirmBtn">添加定时任务</view>
</view>
</view>
</template>
<script>
import deviceServer from '../../js/device_server';
import deviceApi from '../../js/device_api';
import { showModal, showNone, showLoad, hideLoad, routeTo, jsonPar } from "@/utils/util";
export default {
data(){
return {
weeks: [ '日', '一', '二', '三', '四', '五', '六' ].map(e=>`星期${e}`),
condition: {
instruct: 'open', // open: , close:
time: '00:00', //
weekLs: [], //
},
optionsQuery: {}
}
},
onLoad(options){
options.hardware_name = jsonPar(options?.hardware_name);
this.optionsQuery = options || {}
},
methods: {
switchChange(e){
if(e.detail.value){
this.condition.weekLs = [ ...this.weeks];
}else{
this.condition.weekLs = [];
}
},
weekItemClick(e){
let { weekLs } = this.condition;
if(weekLs.includes(e)){
weekLs.splice(weekLs.indexOf(e), 1);
}else{
weekLs.push(e);
}
},
confirmBtn(){
let { optionsQuery, condition } = this;
if(!condition?.weekLs?.length)return showNone('请选择日期!');
if(!condition?.time)return showNone('请选时间点!');
this.cacScheduledTaskSave({
hardware_id: optionsQuery?.hardware_id || '',
day_of_week: condition?.weekLs?.join(','),
h_m_s: `${condition?.time}:00`,
operate: condition?.instruct,
})
},
cacScheduledTaskSave({
hardware_id = '',
day_of_week = '',
h_m_s = '',
operate = ''
}){
showLoad();
return deviceServer.post({
url: deviceApi.cacScheduledTaskSave,
data: { hardware_id, day_of_week, h_m_s, operate },
isDefaultGet: false,
})
.then(res => {
hideLoad();
let _data = res?.data || {};
if(_data.code === 0){
showModal({
title: '提示',
content: _data.message || '操作成功!',
})
}else{
return Promise.reject(_data);
}
})
.catch(err => {
hideLoad();
showModal({
title: '提示',
content: err.message || '操作失败!'
})
console.warn('setting cacScheduledTaskSave err', err);
})
},
}
}
</script>
<style lang="scss">
.ll-title{
padding: 46upx 40upx 60upx;
@include flcw(44upx, 60upx, #1a1a1a, 500);
@include tHide;
}
.ls-container{
margin: 0 auto;
padding: 50upx 30upx 100upx;
width: 678upx;
border-radius: 10upx;
background: #fff;
.lc-name{
flex-shrink: 0;
@include flcw(32upx, 44upx, #1a1a1a);
}
.lc-select{
@include ctf(space-between);
.ls-options{
width: 480upx;
@include ctf;
.lp-item{
width: 50%;
@include ctf;
.li-txt{
@include flcw(32upx, 44upx, #1a1a1a);
}
.li-icon{
margin-left: 22upx;
flex-shrink: 0;
width: 40upx;
height: 40upx;
border: 2upx solid #D8D8D8;
border-radius: 50%;
&.active{
border-width: 0upx;
border-color: transparent;
}
}
}
}
}
.lc-picker{
margin-top: 50upx;
@include ctf(space-between);
.lp-box{
@include ctf(space-between);
width: 480upx;
height: 96upx;
border: 2upx solid #D8D8D8;
border-radius: 10upx;
padding: 0 20upx;
.lb-ipt{
flex-grow: 1;
@include flcw(32upx, 44upx, #1a1a1a);
}
.lb-icon{
flex-shrink: 0;
width: 28upx;
height: 28upx;
transform: rotateZ(90deg);
}
}
}
.lc-switch{
margin-top: 50upx;
@include ctf(space-between);
.ls-right{
width: 480upx;
@include ctf;
.lr-switch{
transform: scale(.8);
}
.lr-txt{
margin-left: 20upx;
@include flcw(32upx, 44upx, #1a1a1a);
}
}
}
.lc-week-ls{
margin-top: 60upx;
display: flex;
flex-wrap: wrap;
.lwl-item{
margin: 0 20upx;
width: 160upx;
height: 88upx;
border-radius: 6upx;
border: 2upx solid #979797;
text-align: center;
@include flcw(32upx, 84upx, #979797);
&:nth-child(n + 4){
margin-top: 40upx;
}
&.active{
color: $mColor;
border-color: $mColor;
background: rgba($color: $mColor, $alpha: .12);
}
}
}
}
.ll-fixed-space{
padding-bottom: 132upx;
@include isPd(132upx);
}
.ll-fixed{
position: fixed;
bottom: 0;
padding: 10upx 24upx;
width: 100%;
@include isPd(10upx);
.lf-btn{
text-align: center;
border-radius: 10upx;
background: $mColor;
@include flcw(32upx, 112upx, #fff, 500);
}
}
</style>

212
src/subpackage/device/pages/order_details/order_details.vue

@ -2,67 +2,56 @@
<view class="order-details">
<block v-if="optionsQuery.order_type==3">
<!-- pageInfo.order_type 场次(0)/人次(1) -->
<reservation-site-detail :orderInfo="pageInfo" v-if="pageInfo.order_type == 0" @refundbtn='refundBtn' ></reservation-site-detail>
<reservation-site-detail
v-if="pageInfo.order_type == 0"
:orderInfo="pageInfo"
@click:refund="siteAndPeopleDetailRefund"
></reservation-site-detail>
<reservation-people-detail
v-if="pageInfo.order_type == 1"
:orderInfo="pageInfo"
@recoverBtn="peopleRecoverBtn"
@okChange="okChange"
@verify="verifyBtn"
@click:refund="siteAndPeopleDetailRefund"
></reservation-people-detail>
</block>
<timing-detail :orderInfo="pageInfo" @completeBtn="completeBtn" @refreshPage="refreshPage" v-if="optionsQuery.order_type==1"></timing-detail>
<organize-detail :orderInfo="pageInfo" v-if="optionsQuery.order_type==10"></organize-detail>
<!-- 次卡订场退款弹窗 -->
<view class="refund-mask" v-if="isOrderRefund" @click="isOrderRefund = false">
<view class="rm-content" @click.stop="_=>false">
<view class="rc-tit">退款</view>
<view class="rc-info">
<view class="ri-view">{{ pageInfo.stadium_name || '-' }}</view>
<view class="ri-view">订单编号{{ pageInfo.order_no || '-' }}</view>
<view class="ri-view">手机号码{{ pageInfo.mobile || '-' }}</view>
</view>
<view class="rc-price">
<view class="rp-name"><text class="rn-txt">*</text>退款金额</view>
<view class="rp-frame">
<input class="rf-ipt" v-model="refundPrice" type="digit" placeholder="请输入金额" />
</view>
<view class="rp-tip">最多可退{{ (pageInfo.extension&&pageInfo.extension.refundable_amount) || 0 }}</view>
</view>
<view class="ri-btn" @click="confirmRefund">确认退款</view>
</view>
</view>
<!-- 次票核销弹窗 -->
<ticket-check
ref="ticketCheckModal"
@confirm="verifyConfirm"
></ticket-check>
<order-refund-modal
ref="orderRefundModal"
></order-refund-modal>
</view>
</template>
<script>
import timingDetail from '../../components/order/timing_detail/timing_detail.vue'
import reservationSiteDetail from '../../components/order/reservation_site_detail/reservation_site_detail.vue';
import reservationPeopleDetail from '../../components/order/reservation_people_detail/reservation_people_detail.vue';
import organizeDetail from '../../components/order/organize_detail/organize_detail.vue';
import ticket_check from '../../../../components/order_list/modal/ticket_check.vue';
import orderRefundModal from '../../../../components/order_refund/modal.vue';
import util from '../../../../utils/util';
import deviceServer from '../../js/device_server';
import deviceApi from '../../js/device_api';
import { mapState } from 'vuex';
export default {
components: {
'timing-detail': timingDetail,
'reservation-site-detail': reservationSiteDetail,
'reservation-people-detail': reservationPeopleDetail,
'organize-detail': organizeDetail,
'ticket-check': ticket_check,
'order-refund-modal': orderRefundModal,
},
computed:{
titleName(){
let { optionsQuery } = this;
if(optionsQuery.order_type == 1)return '计时订单详情'; //PM: 2021/4/19
if(optionsQuery.order_type == 3)return '预约订单详情'; //PM: 2021/4/19
if(optionsQuery.order_type == 4)return '储值卡订单详情';
if(optionsQuery.order_type == 5)return '积分商城订单详情';
@ -78,11 +67,6 @@
order_type: -1,
},
pageInfo: {},
// /退 ---
isOrderRefund: false,
refundPrice: '',
// /退 ---
}
},
onLoad(options) {
@ -99,6 +83,44 @@
})
},
methods: {
// /-退
siteAndPeopleDetailRefund(){
let { pageInfo } = this;
this.$refs.orderRefundModal.show({
stadium_name: pageInfo?.stadium_name ?? '',
order_no: pageInfo?.order_no ?? '',
mobile: pageInfo?.mobile ?? '',
refundable_amount: pageInfo?.extension?.refundable_amount ?? 0,
refundable_integral: pageInfo?.extension?.refundable_integral ?? 0,
refund_times: pageInfo?.refunds?.length ?? 0,
confirm: e => {
this.siteAndPeopleRefund({
order_no: pageInfo?.order_no ?? '',
amount: e?.refund_amount || 0,
integral: e?.refund_integral || 0,
})
}
});
},
// -退(/)
siteAndPeopleRefund({ order_no = '', amount = 0, integral = 0}){
util.showLoad();
deviceServer.get({
url: deviceApi.orderRefund,
data: { order_no, amount, integral },
isDefaultGet: false
})
.then(res=>{
util.hideLoad();
if(res.data.code == 0){
util.showNone(res.data.message || '操作成功!');
setTimeout(this.refreshPage, 1200);
}else{
util.showNone(res.data.message || '操作失败!');
}
})
.catch(util.hideLoad)
},
verifyConfirm(){
let { optionsQuery } = this;
this.getPageInfo({
@ -116,40 +138,17 @@
},
refreshPage(){
let { optionsQuery } = this;
this.isOrderRefund = false;
this.refundPrice = '';
this.getPageInfo({
orderType: optionsQuery.order_type,
stadium_id: optionsQuery.stadium_id,
order_no: optionsQuery.order_no,
})
},
refundBtn(){
this.isOrderRefund = true;
},
confirmRefund: util.debounce(function(){
let { pageInfo, refundPrice } = this;
deviceServer.get({
url: deviceApi.orderRefund,
data: { order_no: pageInfo.order_no || '', amount: refundPrice || 0, integral: 0 }, // integral 0
isDefaultGet: false
})
.then(res=>{
if(res.data.code == 0){
util.showNone(res.data.message || '操作成功!');
setTimeout(this.refreshPage, 1200);
}else{
util.showNone(res.data.message || '操作失败!');
}
})
.catch(util.hideLoad)
}, 300, true),
//
peopleRecoverBtn(){
this.okChange(); //
},
getApiUrl(type){
if(type == 1)return deviceApi.timeOrderDetail;
if(type == 3)return deviceApi.reservationDetail;
if(type == 10)return deviceApi.organizeOrderDetail;
@ -183,38 +182,6 @@
order_no: optionsQuery.order_no,
})
},
// -
completeBtn(){
let { pageInfo } = this
util.showModal({
title: '提示',
content: '是否确认完结订单?',
showCancel: true,
success: modalRes=>{
if(modalRes.confirm){
util.showLoad();
deviceServer.get({
url: deviceApi.timeOrderComplete, //
data: {
brand_id: pageInfo.brand_id,
order_no: pageInfo.order_no,
},
failMsg: '请求失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
this.refreshPage()
util.previousPageFunction({
fnName: 'refreshList',
query: { isLoad: false},
});
})
}
}
})
},
}
}
</script>
@ -225,82 +192,5 @@
}
.refund-mask{
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
background-color: rgba($color: #000, $alpha: .5);
.rm-content{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 80upx;
background-color: #fff;
width: 620upx;
border-radius: 10upx;
.rc-tit{
margin-bottom: 30upx;
text-align: center;
font-size: 32upx;
font-weight: 500;
color: #1A1A1A;
}
.rc-info{
margin-bottom: 34upx;
.ri-view{
line-height: 40upx;
font-size: 28upx;
color: #9C9C9F;
@include textHide(1);
&:first-child{
font-weight: 500;
color: #1A1A1A;
}
}
}
.rc-price{
margin-bottom: 32upx;
.rp-name{
margin-bottom: 8upx;
font-size: 28upx;
line-height: 48upx;
color: #1A1A1A;
.rn-txt{
color: #EA5061;
}
}
.rp-frame{
padding: 0 20upx;
height: 88upx;
border-radius: 10upx;
border: 2upx solid #D8D8D8;
.rf-ipt{
width: 100%;
height: 100%;
font-size: 28upx;
color: #1A1A1A;
}
}
.rp-tip{
font-size: 24upx;
color: #EA5061;
}
}
.ri-btn{
margin: 0 auto;
width: 240upx;
line-height: 88upx;
text-align: center;
border-radius: 10upx;
font-size: 32upx;
font-weight: 500;
color: #fff;
background-color: $themeColor;
}
}
}
</style>

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

@ -210,7 +210,13 @@ export default {
if(this.pageInfo.hardware_type=='IotSim')return util.routeTo(`/subpackage/device/pages/index/lot_manage?mac=${switchInfo.hardware_standard}`, 'nT');
}
if(status == 3) return (()=>{
//
if(status === 3 && switchInfo?.hardware_type === "Light" && switchInfo?.hardware_connect_method === "YZK" ){
let { hardware_name, id } = switchInfo;
let _qryStr = `hardware_id=${id || ''}&hardware_name=${util.jsonStr(hardware_name || '')}`
return util.routeTo(`/subpackage/device/pages/lighting_time/list?${_qryStr}`, 'nT');
} else if (status == 3) return (()=>{
switchInfo.switchData = _data;//
let timing_setting = `/subpackage/device/pages/timing/timing_setting?switchInfo=${encodeURIComponent(JSON.stringify(switchInfo))}`
util.routeTo(`${timing_setting}`, 'nT');

BIN
src/subpackage/device/static/images/delete.png

After

Width: 36  |  Height: 36  |  Size: 365 B

41
src/subpackage/menu/components/bottom_logo.vue

@ -0,0 +1,41 @@
<template>
<view class="bottom-logo" :class="{ 'fixed-active': isFixed }" @click="toWebView">
<image @click="toWebView" class="bl-img" mode="aspectFit" src="/subpackage/menu/static/images/bot_logo.png"></image>
</view>
</template>
<script>
import { routeTo, jsonStr } from '@/utils/util.js'
export default {
props: {
isFixed: {
type: Boolean,
default: false
}
},
methods: {
toWebView(){
routeTo(`/pages/web_view/web_view?src=${jsonStr('https://www.ouxuanzhineng.cn/')}`, 'nT');
}
}
}
</script>
<style lang="scss">
.bottom-logo{
height: 200upx;
width: 100%;
@include ctf(center);
&.fixed-active{
position: fixed;
left: 50%;
bottom: 0;
transform: translateX(-50%);
z-index: -1;
}
}
.bl-img{
width: 312upx;
height: 100upx;
}
</style>

87
src/subpackage/menu/components/mine/header.vue

@ -0,0 +1,87 @@
<template>
<view class="mine-header">
<view class="mh-user">
<image class="mu-photo" mode="aspectFill" :src="photo"></image>
<view class="mu-info" v-if="isLogin">
<view class="mi-line">昵称{{ nickname || '-' }}</view>
<view class="mi-line">姓名{{ name || '-' }}</view>
<view class="mi-line">账号{{ account || '-' }}</view>
</view>
<button v-else class="mu-login" @click="$emit('click:login')">点击登录</button>
</view>
<view class="mh-update" v-if="isLogin" @click="$emit('click:update')">更新头像昵称</view>
</view>
</template>
<script>
export default {
props: {
isLogin: {
type: Boolean,
default: false
},
nickname: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
account: {
type: String,
default: ''
},
photo: {
type: String,
default: ''
}
},
mounted(){
console.log('mounted');
this.$emit('on:munted');
},
}
</script>
<style lang="scss">
.mine-header{
padding-top: 70upx;
height: 270upx;
background: $mColor;
.mh-user{
padding-left: 68upx;
padding-right: 30upx;
@include ctf;
.mu-photo{
flex-shrink: 0;
margin-right: 28upx;
width: 120upx;
height: 120upx;
border-radius: 50%;
border: 4upx solid #FFFFFF;
}
.mu-info{
.mi-line{
@include flcw(32upx, 44upx, #FFFFFF);
@include tHide;
}
}
.mu-login{
@include clearBtn;
width: 180upx;
height: 72upx;
text-align: center;
border-radius: 6upx;
background: #fff;
@include flcw(32upx, 72upx, $mColor, 500);
}
}
.mh-update{
padding-left: 68upx;
margin-top: 8upx;
text-decoration: underline;
@include flcw(20upx, 28upx, #FFFFFF);
}
}
</style>

63
src/subpackage/menu/components/mine/line_tab.vue

@ -0,0 +1,63 @@
<template>
<view class="line-tab" @click="$emit('click')">
<image class="lt-icon" mode="aspectFit" :src="'/subpackage/menu/static/images/mine_tab/' + iconNum + '.png'"></image>
<view class="lt-right">
<view class="lr-left">
<view class="lr-name">
<slot name="default">账号管理</slot>
</view>
</view>
<view class="lr-right">
<slot name="right">
<image class="lr-arrow" mode="aspectFit" src="/subpackage/menu/static/images/arrow_b2.png"></image>
</slot>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
iconNum: {
type: Number,
default: 0
}
}
}
</script>
<style lang="scss">
.line-tab{
padding-left: 22upx;
padding-right: 44upx;
height: 108upx;
background: #fff;
@include ctf;
.lt-icon{
flex-shrink: 0;
margin-right: 22upx;
width: 48upx;
height: 48upx;
}
.lt-right{
flex-grow: 1;
@include ctf(space-between);
.lr-left{
.lr-name{
@include flcw(32upx, 48upx, #9A9A9D);
@include tHide;
}
}
.lr-right{
flex-shrink: 0;
max-width: 60%;
font-size: 0;
.lr-arrow{
width: 30upx;
height: 30upx;
}
}
}
}
</style>

16
src/subpackage/menu/pages/index.vue

@ -0,0 +1,16 @@
<template>
<!-- 需要定义一个页面以及在pagesjson里声明才能生成static文件夹 -->
<view class="menu-index">&nbsp;</view>
</template>
<script>
export default {
}
</script>
<style lang="scss">
page{
background: #fff;
}
</style>

BIN
src/subpackage/menu/static/images/arrow_b2.png

After

Width: 15  |  Height: 15  |  Size: 226 B

BIN
src/subpackage/menu/static/images/bot_logo.png

After

Width: 312  |  Height: 100  |  Size: 3.4 KiB

BIN
src/subpackage/menu/static/images/mine_tab/0.png

After

Width: 96  |  Height: 96  |  Size: 1.3 KiB

BIN
src/subpackage/menu/static/images/mine_tab/1.png

After

Width: 96  |  Height: 96  |  Size: 1.5 KiB

BIN
src/subpackage/menu/static/images/mine_tab/2.png

After

Width: 96  |  Height: 96  |  Size: 1.0 KiB

BIN
src/subpackage/menu/static/images/mine_tab/3.png

After

Width: 96  |  Height: 96  |  Size: 1.4 KiB

BIN
src/subpackage/menu/static/images/mine_tab/4.png

After

Width: 96  |  Height: 96  |  Size: 1.0 KiB

BIN
src/subpackage/menu/static/images/mine_tab/5.png

After

Width: 96  |  Height: 96  |  Size: 942 B

BIN
src/subpackage/menu/static/images/mine_tab/6.png

After

Width: 96  |  Height: 96  |  Size: 640 B

59
src/subpackage/message/components/detail/answer_item.vue

@ -0,0 +1,59 @@
<template>
<view class="answer-item" :class="{ 'active-bg': activeBg }">
<view class="ai-user">
<view class="au-name">{{ name || '-' }}</view>
<view class="au-time">[回复] {{ time || '-' }}</view>
</view>
<view class="ai-content">{{ content || '-' }}</view>
</view>
</template>
<script>
export default {
props: {
name: {
type: String,
default: ''
},
time: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
activeBg: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss">
.answer-item{
padding: 30upx 22upx;
background: #fff;
border-radius: 10upx;
&.active-bg{
background: #faeada;
}
}
.ai-user{
@include ctf;
@include flcw(32upx, 44upx, #1A1A1A);
.au-name{
@include tHide;
}
.au-time{
flex-shrink: 0;
color: #9C9C9F;
}
}
.ai-content{
padding-left: 26upx;
margin-top: 24upx;
@include flcw(28upx, 40upx, #9C9C9F);
}
</style>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save