Browse Source

add api logic & login

dev
刘嘉炜 2 weeks ago
parent
commit
4049261e8c
  1. 12
      src/api/server.js
  2. 19
      src/pages.json
  3. 16
      src/pages/index/index.vue
  4. BIN
      src/static/images/tab/index_a.png
  5. BIN
      src/static/images/tab/index_g.png
  6. BIN
      src/static/images/tab/mine_a.png
  7. BIN
      src/static/images/tab/mine_g.png
  8. BIN
      src/static/images/tab/order_a.png
  9. BIN
      src/static/images/tab/order_g.png
  10. 3
      src/subpackage/authorize/api/index.js
  11. 129
      src/subpackage/authorize/pages/index.vue
  12. 69
      src/subpackage/groupon/components/site/footer.vue
  13. 61
      src/subpackage/groupon/components/site/table.vue
  14. 252
      src/subpackage/groupon/js/site_select/autoSelect.js
  15. 0
      src/subpackage/groupon/js/site_select/handle.js
  16. 50
      src/subpackage/groupon/pages/site_select.vue
  17. 16
      src/utils/polish.js

12
src/api/server.js

@ -19,7 +19,7 @@ const server = ajax.create({
server.interceptors.request.use( server.interceptors.request.use(
config => { config => {
let _token = uni.getStorageSync('token'); let _token = uni.getStorageSync('token');
if(_token)config.data.ustoken = _token;
if(_token)config.data.token = _token;
loading.show(config.loading); loading.show(config.loading);
return config; return config;
}, },
@ -34,11 +34,12 @@ server.interceptors.response.use(
response => { response => {
loading.hide(response.config.loading); loading.hide(response.config.loading);
if(response?.data?.code === 0)return formatResponse(response); if(response?.data?.code === 0)return formatResponse(response);
console.error(`get ${response?.config?.url} err -> `, response)
// console.error(`get ${response?.config?.url} err -> `, response)
return errorHandle({ return errorHandle({
Catch: response?.config?.catch || false, Catch: response?.config?.catch || false,
content: response?.data?.message ?? '请求失败', content: response?.data?.message ?? '请求失败',
data: response, data: response,
errorModal: response?.config?.errorModal,
success: () => { success: () => {
// 登录超时 // 登录超时
// if(response?.data?.error === '201')loginHandle(); // if(response?.data?.error === '201')loginHandle();
@ -54,12 +55,13 @@ server.interceptors.response.use(
// 404 拦截 data 会存在错误信息 // 404 拦截 data 会存在错误信息
content: error?.data ?? error?.errMsg ?? '请求失败', content: error?.data ?? error?.errMsg ?? '请求失败',
data: error, data: error,
errorModal: error?.config?.errorModal,
}) })
} }
) )
function formatResponse(response){ function formatResponse(response){
let _data = response?.data?.data || null;
let _data = response?.data ?? null;
return _data; return _data;
} }
@ -69,9 +71,9 @@ function formatResponse(response){
* *
* 传值 config.catch 判断是否需要返回错误如果是 true 返回错误信息否则不返回 * 传值 config.catch 判断是否需要返回错误如果是 true 返回错误信息否则不返回
*/ */
function errorHandle({ Catch = true, content = '', data = {}, success }) {
function errorHandle({ Catch = true, content = '', data = {}, success, errorModal = true }) {
console.error(`get ${data?.config?.url} err -> `, data) console.error(`get ${data?.config?.url} err -> `, data)
showModal({
if(errorModal)showModal({
content: content || '请求失败', content: content || '请求失败',
success: success success: success
}) })

19
src/pages.json

@ -81,6 +81,25 @@
] ]
} }
], ],
"tabBar": {
"color": "#666666",
"selectedColor": "#009874",
"backgroundColor": "#FFFFFF",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/images/tab/index_g.png",
"selectedIconPath": "static/images/tab/index_a.png"
},
{
"pagePath": "pages/order/list",
"text": "订单",
"iconPath": "static/images/tab/order_g.png",
"selectedIconPath": "static/images/tab/order_a.png"
}
]
},
"globalStyle": { "globalStyle": {
"navigationBarTextStyle": "#FFFFFF", "navigationBarTextStyle": "#FFFFFF",
"navigationBarTitleText": "欧轩智能场馆", "navigationBarTitleText": "欧轩智能场馆",

16
src/pages/index/index.vue

@ -2,17 +2,23 @@
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { deepClone } from "@/utils"; import { deepClone } from "@/utils";
import { accAdd } from "@/utils/calculation"; import { accAdd } from "@/utils/calculation";
import { routeTo } from "@/utils/polish";
import { stadiumFind } from "@/api"; import { stadiumFind } from "@/api";
onLoad(() => { onLoad(() => {
console.log('deepClone', deepClone({ a: 1, b: { c: 2 } })); console.log('deepClone', deepClone({ a: 1, b: { c: 2 } }));
console.log('accAdd', accAdd(0.1, 0.2)); console.log('accAdd', accAdd(0.1, 0.2));
stadiumFind({ data: { brand_id: 63 }, loading: '加载...' })
.then(res=>{
console.log('stadiumFind', res);
})
// stadiumFind({ data: { brand_id: 63 }, loading: '...' })
// .then(res=>{
// console.log('stadiumFind', res);
// })
}); });
function verifyBtn(){
routeTo(`/subpackage/groupon/pages/list`);
}
</script> </script>
<template> <template>
@ -22,7 +28,7 @@ onLoad(() => {
<image class="is-img"></image> <image class="is-img"></image>
</swiper-item> </swiper-item>
</swiper> </swiper>
<view class="ii-line-btn">
<view class="ii-line-btn" @click="verifyBtn">
<image class="ilb-dy-icon" src="/static/images/douyin_logo.png"></image> <image class="ilb-dy-icon" src="/static/images/douyin_logo.png"></image>
<view class="ilb-txt">抖音快捷验券</view> <view class="ilb-txt">抖音快捷验券</view>
<text class="ilb-arrow">&#xe660;</text> <text class="ilb-arrow">&#xe660;</text>

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

After

Width: 104  |  Height: 104  |  Size: 1.6 KiB

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

After

Width: 104  |  Height: 104  |  Size: 1.3 KiB

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

After

Width: 104  |  Height: 104  |  Size: 1.6 KiB

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

After

Width: 104  |  Height: 104  |  Size: 1.4 KiB

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

After

Width: 104  |  Height: 104  |  Size: 831 B

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

After

Width: 104  |  Height: 104  |  Size: 688 B

3
src/subpackage/authorize/api/index.js

@ -1,7 +1,8 @@
import server from '@/api/server'; import server from '@/api/server';
// 公共 // 公共
export const douyinMiniAppBaseAuth = params => server.post({ url: '/user/douyinMiniAppBaseAuth', ...params }); // 抖音小程序code登录
export const douyinMiniAppBaseAuth = params => server.post({ url: '/user/douyinMiniAppBaseAuth', ...params }); // 用户获取信息
export const douyinMiniAppLoginAndSync = params => server.post({ url: '/user/douyinMiniAppLoginAndSync', ...params }); // 用户同步信息

129
src/subpackage/authorize/pages/index.vue

@ -1,36 +1,26 @@
<script setup> <script setup>
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { showLoad, hideLoad, showModal } from "@/utils/polish";
import { douyinMiniAppBaseAuth } from "../api";
import { showLoad, hideLoad, showModal, showNone, routeTo } from "@/utils/polish";
import { douyinMiniAppBaseAuth, douyinMiniAppLoginAndSync } from "../api";
let loginRes = ref(null); let loginRes = ref(null);
let dyPhoneAuthorize = ref(false); let dyPhoneAuthorize = ref(false);
const APPID = tt.getEnvInfoSync()?.microapp?.appId ?? ''; const APPID = tt.getEnvInfoSync()?.microapp?.appId ?? '';
const optionsQuery = ref(null)
onLoad(() => { onLoad(() => {
dysilentLogin(); dysilentLogin();
console.log(process.env.NODE_ENV)
}); });
// code code // code code
let dyGetLoginCode = async function(){ let dyGetLoginCode = async function(){
try{ try{
showLoad();
let _loginRes = await uni.login(); let _loginRes = await uni.login();
hideLoad();
if(!_loginRes.code){
return showModal({
content: '获取登陆凭证失败!请稍后重试',
// success: _=> this.routeOperate(),
})
}
if(!_loginRes.code)return showModal({ content: '获取登陆凭证失败!请稍后重试' });
return loginRes.value = _loginRes; return loginRes.value = _loginRes;
}catch(err){ }catch(err){
hideLoad();
return showModal({
content: '获取登陆凭证失败!请稍后重试',
// success: _=> this.routeOperate(),
})
return showModal({ content: '获取登陆凭证失败!请稍后重试' })
} }
} }
@ -39,10 +29,17 @@ let dyGetLoginCode = async function(){
const dysilentLogin = async function(){ const dysilentLogin = async function(){
try{ try{
let _loginRes = await dyGetLoginCode(); let _loginRes = await dyGetLoginCode();
let _bData = await douyinMiniAppBaseAuth({ data: { code: _loginRes.code, appid: APPID } });
console.log(_bData);
// await this.dyCheckPhoneStatus(_bData, true);
let _bData = await douyinMiniAppBaseAuth({
data: { code: _loginRes.code, appid: APPID },
catch: true,
errorModal: false
})
.catch(err=>{
// 11005
if(err?.data?.code === 11005)return err;
return Promise.reject(err);
})
dyCheckPhoneStatus(_bData, true);
}catch(err){ }catch(err){
hideLoad(); hideLoad();
console.warn('silentLogin err --->', err); console.warn('silentLogin err --->', err);
@ -63,7 +60,8 @@ let dyCheckPhoneStatus = async function(_bData, isSilent = false){
return; return;
} }
// //
if(_data?.token&&!isSilent)return this.successOperate(_bData);
console.log('成功后登陆操作')
// if(_data?.token&&!isSilent)return this.successOperate(_bData);
}else if(_bData.data.code == 11005){ // }else if(_bData.data.code == 11005){ //
let _codeInfo = await dyGetLoginCode(); let _codeInfo = await dyGetLoginCode();
if(_codeInfo&&_codeInfo.code){ if(_codeInfo&&_codeInfo.code){
@ -75,6 +73,93 @@ let dyCheckPhoneStatus = async function(_bData, isSilent = false){
} }
} }
//
async function douyinGetPhoneNumberHandler(e){
console.log('douyinGetPhoneNumberHandler', e);
let _detail = e.detail || {};
if(!_detail?.encryptedData || !_detail?.iv){
return showModal({
content: _detail?.errMsg ?? '授权手机失败,请重新操作',
showCancel: true,
success: mRes => {
if(mRes.confirm){
//
if(process.env.NODE_ENV === 'development'){
showModal({
content: '调试登录',
showCancel: true,
success(debugRes){
if(debugRes?.confirm)debugLogin();
}
})
}
}
if(mRes.cancel){ showNone('已取消登录!'); }
}
})
}
douyinLoginReq({ phoneDetail: _detail });
}
function debugLogin(){
douyinMiniAppLoginAndSync({ data: {
code: loginRes?.value?.code ?? '',
appid: APPID,
version: 'dev',
dev_data: { phoneNumber: 15521015328 }
} })
.then(res=>{
successOperate(res);
})
}
//
function douyinLoginReq({ phoneDetail = null, }){
let _query = {
appid: APPID ?? '',
code: loginRes?.value?.code ?? '',
};
if(phoneDetail){
_query = {
..._query,
encryptedData: phoneDetail?.encryptedData ?? '',
iv: phoneDetail?.iv ?? '',
}
}
douyinMiniAppLoginAndSync({ data: _query, catch: true })
.then(res=>{
successOperate(res);
})
.catch(err=>{
setTimeout(_=>dyGetLoginCode(), 1000);
})
}
// token &
function successOperate(res){
console.log(res);
showNone(res?.message || '登陆成功!');
let _data = res?.data ?? {};
uni.setStorageSync('token',_data?.token ?? '');
setTimeout(routeOperate, 1200);
}
//
function routeOperate(){
let _pageLs = getCurrentPages();
let _opts = optionsQuery?.value ?? {}
if(+_opts?.type === 2 &&_opts?.path){
let _path = decodeURIComponent(_opts?.path);
// getCurrentPagessecneencodeURIComponent
return routeTo(decodeURIComponent(_path), 'rT');
}
if(+_opts?.type === 1&&_pageLs.length > 1)return routeTo();
if(_pageLs.length <= 1)return routeTo('/pages/index/index', 'sT');
routeTo();
}
</script> </script>
<template> <template>
@ -83,7 +168,7 @@ let dyCheckPhoneStatus = async function(_bData, isSilent = false){
<view class="ac-tip">请先登录再进行操作</view> <view class="ac-tip">请先登录再进行操作</view>
<image class="ac-img" mode="aspectFit" src="@/subpackage/authorize/static/images/authorize.png"></image> <image class="ac-img" mode="aspectFit" src="@/subpackage/authorize/static/images/authorize.png"></image>
<view class="ac-btns"> <view class="ac-btns">
<button v-if="false" plain class="ab-btn" open-type="getPhoneNumber">抖音手机号授权快捷登录</button>
<button v-if="dyPhoneAuthorize" plain class="ab-btn" open-type="getPhoneNumber" @getphonenumber="douyinGetPhoneNumberHandler">抖音手机号授权快捷登录</button>
<button v-else plain class="ab-btn" >抖音用户信息授权登录</button> <button v-else plain class="ab-btn" >抖音用户信息授权登录</button>
<button plain class="ab-btn" hover-class="hover-active" @click="cancel">暂不登录</button> <button plain class="ab-btn" hover-class="hover-active" @click="cancel">暂不登录</button>
</view> </view>

69
src/subpackage/groupon/components/site/footer.vue

@ -1,7 +1,9 @@
<script setup> <script setup>
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
const props = defineProps({
selectedData: { default: [] },
})
onLoad(() => { onLoad(() => {
}); });
@ -10,7 +12,22 @@ onLoad(() => {
<template> <template>
<view class="site-footer"> <view class="site-footer">
<view class="vsm-explain">
<view class="sf-selected" v-if="selectedData&&selectedData.length">
<view class="ss-top-bar">
<view class="stb-select">已选择场次<text class="ss-num">{{ (selectedData&&selectedData.length) || 0 }}</text></view>
<view class="stb-clear" @click="clearSelected">清空</view>
</view>
<scroll-view class="vsm-selected" scroll-y>
<view class="vs-list">
<view class="vl-item" v-for="(e, i) in selectedData" :key="i">
<view class="vi-view">{{ e.time || '-' }}</view>
<view class="vi-view">2</view>
</view>
</view>
</scroll-view>
</view>
<view class="vsm-explain" v-else>
<view class="ve-item"> <view class="ve-item">
<view class="vi-view"></view> <view class="vi-view"></view>
<view class="vi-view">可预订</view> <view class="vi-view">可预订</view>
@ -28,6 +45,8 @@ onLoad(() => {
<view class="vi-view">已选择</view> <view class="vi-view">已选择</view>
</view> </view>
</view> </view>
<view class="sf-submit-bar"> <view class="sf-submit-bar">
<view class="ssb-price">¥0.00 </view> <view class="ssb-price">¥0.00 </view>
<view class="ssb-btn">请选择场地</view> <view class="ssb-btn">请选择场地</view>
@ -96,5 +115,51 @@ onLoad(() => {
} }
} }
} }
.sf-selected{
.ss-top-bar{
padding: 0 30upx;
height: 70upx;
@include ctf(space-between);
.stb-select, .stb-clear{
@include flcw(24upx, 1.2em, #9a9a9d);
.ss-num{
margin-left: 6upx;
color: #333333;
}
}
}
.vsm-selected{
padding: 0 20upx;
height: 150upx;
background-color: #f8f8f8;
.vs-list{
.vl-item{
position: relative;
display: inline-block;
margin: 0 10upx 20upx;
width: 120upx;
height: 76upx;
border: 2upx solid $mColor;
border-radius: 6upx;
overflow: hidden;
background-color: rgba(0, 152, 116, 0.1);
>.vi-view{
@include tHide;
&:first-child{
text-align: center;
background-color: $mColor;
@include flcw(20upx, 28upx, #fff);
}
&+.vi-view{
margin-top: 10upx;
text-align: center;
@include flcw(20upx, 1.2em, #333);
}
}
}
}
}
}
} }
</style> </style>

61
src/subpackage/groupon/components/site/table.vue

@ -1,26 +1,57 @@
<script setup> <script setup>
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { reactive, ref, computed } from 'vue';
import { reactive, ref, computed, nextTick } from 'vue';
import { deepClone } from "@/utils";
import { showNone } from "@/utils/polish";
import { AutoSelect } from "../../js/site_select/autoSelect";
const emits = defineEmits([ 'update:table' ]);
const props = defineProps({ const props = defineProps({
tableData: { default: [] }
tableData: { default: [] },
altitude: { default: 0 }
}) })
const timeList = computed(_=> { const timeList = computed(_=> {
if(!props?.tableData?.length)return [];
let _items = props?.tableData?.[0]?.items ?? [], _tempArr;
let _table = props?.tableData;
if(!_table?.length)return [];
let _items = _table?.[0]?.items ?? [], _tempArr;
_tempArr = _items.reduce((_arr, val)=>_arr.concat(val.time.split('-')), []); _tempArr = _items.reduce((_arr, val)=>_arr.concat(val.time.split('-')), []);
// //
// 00:00-00:30 && 23:00-00:00 // 00:00-00:30 && 23:00-00:00
return [...new Set(_tempArr.splice(0, _tempArr.length-2)), _tempArr[_tempArr.length-1]]; return [...new Set(_tempArr.splice(0, _tempArr.length-2)), _tempArr[_tempArr.length-1]];
}) })
const scrollViewStyle = computed(_=>`height: ${props?.altitude ?? 0}px;`);
onLoad(() => { onLoad(() => {
}); });
const itemClick = function(k, i, j){
if(k.is_valid === false)return showNone('该场次已出售');
let _tableCopy = deepClone(props?.tableData);
_tableCopy[i].items[j]._is_selected = !k?._is_selected;
//
//
const autoSelect = new AutoSelect({
items: _tableCopy[i].items ?? [],
index: j,
timeUnit: 2,
bookingHour: 3,
})
let [ _handleSelectItems, _noticeList ] = autoSelect.getInfo();
_tableCopy[i].items = _handleSelectItems;
//
nextTick(_=>autoSelect.showModalList(_noticeList || []));
emits('update:table', _tableCopy);
}
</script> </script>
<template> <template>
<scroll-view class="sm-table-data" scroll-x scroll-y>
<scroll-view class="sm-table-data" scroll-x scroll-y :style="scrollViewStyle">
<view class="std-box"> <view class="std-box">
<!-- 左侧栏 --> <!-- 左侧栏 -->
<view class="std-left-sticky"> <view class="std-left-sticky">
@ -38,16 +69,16 @@ onLoad(() => {
<view <view
class="sr-item" class="sr-item"
:class="{ :class="{
orange: i === 0&j%2,
gray: i === 1&j%3,
sold: i === 2&j%5,
green: k._is_selected,
sold: k.is_valid === false,
}" }"
@click="itemClick(k, i, j)"
> >
<view class="sr-sold" v-if="i === 2&j%5">已售</view>
<block v-else>
<block v-if="k.is_valid">
<view class="sr-price">¥60.00</view> <view class="sr-price">¥60.00</view>
<view class="sr-del" v-if="i === 0&j%2">¥60.00</view>
<view class="sr-del" v-if="false">¥60.00</view>
</block> </block>
<view class="sr-sold" v-else>已售</view>
</view> </view>
</view> </view>
</view> </view>
@ -59,7 +90,6 @@ onLoad(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.sm-table-data{ .sm-table-data{
position: relative; position: relative;
height: 600upx;
background-color: #F2F2F7; background-color: #F2F2F7;
.std-box{ .std-box{
width: fit-content; width: fit-content;
@ -164,7 +194,12 @@ onLoad(() => {
.sr-del{ .sr-del{
@include flcw(20upx, 28upx, #9A9A9D); @include flcw(20upx, 28upx, #9A9A9D);
} }
&.green{
background: $mColor;
.sr-price{
color: #fff;
}
}
&.orange{ &.orange{
border-color: #FF873D; border-color: #FF873D;
.sr-price{ .sr-price{

252
src/subpackage/groupon/js/site_select/autoSelect.js

@ -0,0 +1,252 @@
import { showModal } from '@/utils/polish';
import { deepClone } from '@/utils'
/**
* 获取自动勾选场地
* 起订和连场混处理
* 只会向下判断
*
*/
export class AutoSelect {
items = []; // 场地列表 -> /stadium/book/list (不要修改,只读)
index = -1; // 当前点击索引
timeUnit = 0; // 时间间隔单位 (1=>1/ 2=>0.5)
bookingHour = 0; // 起订小时
tagItem = {}; // 当前点击 tag
constructor({ items, index, timeUnit, bookingHour }){
this.items = deepClone(items);
this.index = index;
this.timeUnit = timeUnit === 1 ? 1 : timeUnit === 2 ? .5 : ''; // 时间间隔单位
this.bookingHour = bookingHour ?? 0; // 起订小时
this.tagItem = deepClone(items[index]); // 当前点击 tag
}
// 获取连场/起订场地信息
getInfo(){
let _bookNum = (this.bookingHour / (this.timeUnit ?? 0)) - 1; // 起订场地数量 (-1 剔除_curTag)
let _handleItems = deepClone(this.items);
let _curTag = deepClone(_handleItems[this.index]); // 当前命中 tag
let _startIdx = this.index;
let _endIdx = this.index;
let _noticeList = [];
// 命中连场
if(_curTag?.time_arrow_id){
while(
_handleItems?.[_startIdx-1]?.is_valid
&&!_handleItems?.[_startIdx-1]?.isDisable
&&_handleItems?.[_startIdx-1]?.time_arrow_id === _curTag.time_arrow_id
){
_startIdx--;
_bookNum--
}
while(
_handleItems?.[_endIdx+1]?.is_valid
&&!_handleItems?.[_endIdx+1]?.isDisable
&&_handleItems?.[_endIdx+1]?.time_arrow_id === _curTag.time_arrow_id
){
_endIdx++;
_bookNum--
}
// 连场提示新增
if(_curTag?._is_selected)_noticeList.push(this.getCtnOrderTip({
ctnStartIdx: _startIdx,
ctnEndIdx: _endIdx
}))
}
// 当前tag为已选中,只对命中连场处理,后续逻辑不处理
if(!_curTag?._is_selected)return [
this.getHandleItems({
startIdx: _startIdx,
endIdx: _endIdx,
referTargetBolean: _curTag?._is_selected ?? true
}),
[]
];
// 获取目标后剩余空场地数量
let _afterEmptyNum = this.getAfterEmptyNum({ index: _endIdx });
if(
_bookNum > 0 // 起订场地数量
&&!this.otherItemIsSelect({ // 当前场馆存在其它已选,不执行起订逻辑
oStartIdx: _startIdx,
oEndIdx: _endIdx
})
&&_afterEmptyNum >= _bookNum // 后续场地不满足起订,不执行
&&_handleItems?.[_endIdx+1]?.is_valid
&&!_handleItems?.[_endIdx+1]?.isDisable
){
// 起订提示新增
_noticeList.push(`此场地${ this.bookingHour }小时起订`)
// 获取后续场地所需自动勾选范围
let [ rangeEndIdx, noticeLs ] = this.getAutoSelectRange({
rangeIndex: _endIdx + 1,
nums: _bookNum
})
_endIdx = rangeEndIdx;
_noticeList.push(...noticeLs);
}
return [
this.getHandleItems({
handleItems: _handleItems,
startIdx: _startIdx,
endIdx: _endIdx,
referTargetBolean: _curTag?._is_selected ?? true
}),
_noticeList
];
}
/**
* 获取勾选范围
* @param {Number} rangeIndex 开始索引
* @param {Number} nums 订场数量
* @param {Array} ctnNoticeList 连场提示信息列表
* @return {Array} [ rangeEndIdx, ctnNoticeList, nums ]
* */
getAutoSelectRange({
rangeIndex, nums, ctnNoticeList = []
}){
let _rangeItems = this.items;
let _curRangeTag = _rangeItems?.[rangeIndex] || {};
let _rangeEndIdx = rangeIndex;
if (
_curRangeTag.is_valid
&&!_curRangeTag?.isDisable
&&nums>0
)nums--;
// 只要命中到第一个,就继续往后,不需要考虑nums
if(!!_curRangeTag?.time_arrow_id){
while(
_rangeItems?.[_rangeEndIdx+1]?.is_valid
&&!_rangeItems?.[_rangeEndIdx+1]?.isDisable
&&_rangeItems?.[_rangeEndIdx+1]?.time_arrow_id === _curRangeTag.time_arrow_id
){
_rangeEndIdx++;
nums--
}
// 连场提示新增
ctnNoticeList.push(this.getCtnOrderTip({
ctnStartIdx: rangeIndex,
ctnEndIdx: _rangeEndIdx
}))
}
// 起订模式用递归,起订时长可能存在多个连场
if(
nums>0
&&_rangeItems?.[_rangeEndIdx+1]?.is_valid
&&!_rangeItems?.[_rangeEndIdx+1]?.isDisable
){
let [ _endIdx, _num ] = this.getAutoSelectRange({
rangeIndex: _rangeEndIdx + 1,
nums,
ctnNoticeList
})
_rangeEndIdx = _endIdx;
nums = _num;
}
return [
_rangeEndIdx,
ctnNoticeList,
nums
]
}
/**
* 获取连场提示
* @param { Number } ctnStartIdx 连场开始索引
* @param { Number } ctnEndIdx 连场结束索引
* @return { String } 连场提示
* */
getCtnOrderTip({ ctnStartIdx, ctnEndIdx }){
let _endItem = this.items[ctnEndIdx];
let _endTime = _endItem.time.split('-')[1];
return `${this.items[ctnStartIdx].duration}-${_endTime}为连场(${(ctnEndIdx - ctnStartIdx) + 1}场) 预订已自动为您勾选`;
}
/**
* 获取处理后的场地列表
* @param { Number } startIdx 开始索引
* @param { Number } endIdx 结束索引
* @param { Boolean } referTargetBolean 参考目标
* @return { Array } [ this.items ]
* */
getHandleItems({
startIdx, endIdx, referTargetBolean
}){
return this.items.map((e, i)=>{
if(i>=startIdx&&i<=endIdx)e._is_selected = referTargetBolean;
return e;
})
}
/**
* 获取后续剩余空场地数量
* @param { Number } index 当前点击索引
* @return { Number } 后续剩余空场地数量
* */
getAfterEmptyNum({ index }){
let _afterEmptyNum = 0;
while(
this.items?.[index + _afterEmptyNum + 1]?.is_valid
&&!this.items?.[index + _afterEmptyNum + 1]?.isDisable
&&!this.items?.[index + _afterEmptyNum + 1]?._is_selected
){
_afterEmptyNum++;
}
return _afterEmptyNum;
}
/**
* 当前列表是否存在其他已选场地
* @param { Number } oStartIdx 排除开始索引
* @param { Number } oEndIdx 排除结束索引
* @return { Boolean } 是否存在其他已选场地
* */
otherItemIsSelect({
oStartIdx, oEndIdx
}){
for(let i=0; i<this.items.length; i++){
if(i>=oStartIdx&&i<=oEndIdx)continue;
if(this.items[i]._is_selected)return true;
}
return false;
}
/**
* 循环提示
* @param {Array} arr 提示列表
* */
async showModalList(arr = []){
if(!arr.length)return;
while(arr.length > 0){
await this.showModal({ title: '提示', content: arr.shift() });
}
}
/**
* 提示
* @param {String} title 标题
* @param {String} content 内容
* @returns {Promise} Promise
* */
showModal({ title, content }){
return new Promise((resolve, reject)=>{
showModal({
title: title,
content: content,
success: res=>{
resolve(res);
}
})
})
}
}

0
src/subpackage/groupon/js/site_select/handle.js

50
src/subpackage/groupon/pages/site_select.vue

@ -1,15 +1,28 @@
<script setup> <script setup>
import { onLoad } from '@dcloudio/uni-app';
import { reactive, ref, computed } from 'vue';
import { onLoad, onReady } from '@dcloudio/uni-app';
import { reactive, ref, computed, watch, nextTick } from 'vue';
import { stadiumBookList } from "@/api"; import { stadiumBookList } from "@/api";
import { getNodeMes } from "@/utils/polish"
import siteTable from "../components/site/table.vue"; import siteTable from "../components/site/table.vue";
import siteFooter from "../components/site/footer.vue"; import siteFooter from "../components/site/footer.vue";
const siteData = ref([]); const siteData = ref([]);
const scrollHeight = ref(0);
const selectedData = computed(_=>{
let _temArr = [];
siteData.value.forEach(e=>{
if(e?.items?.length)_temArr.push(...e?.items?.filter(ele=>ele?._is_selected))
});
return _temArr
})
watch(selectedData, val=>nextTick(setScrollViewSize));
onLoad(() => { onLoad(() => {
stadiumBookList({ data: { stadiumBookList({ data: {
stadium_id: 167, stadium_id: 167,
date: `2025-06-06`,
date: `2025-06-12`,
type_key: `631284d5-aed4-11ea-a89a-7085c2772a68`, type_key: `631284d5-aed4-11ea-a89a-7085c2772a68`,
rule_type: 0, rule_type: 0,
brand_id: 63, brand_id: 63,
@ -19,15 +32,30 @@ onLoad(() => {
from_platform: `weixin`, from_platform: `weixin`,
} }) } })
.then(res=>{ .then(res=>{
siteData.value = res;
siteData.value = res?.data;
}) })
}); });
// Page.onReady
onReady(_=>{
setScrollViewSize();
})
async function setScrollViewSize(){
const _sysInfo = uni.getSystemInfoSync(true);
let _headerInfo = await getNodeMes('#ssHeader');
let _dateBarInfo = await getNodeMes(`#smDates`);
let _footerInfo = await getNodeMes('#ssFooter');
let _otherHeight = _headerInfo?.height + _footerInfo?.height + _dateBarInfo?.height;
scrollHeight.value = _sysInfo?.windowHeight - (_otherHeight || 0);
console.log(scrollHeight.value);
}
</script> </script>
<template> <template>
<view class="site-select"> <view class="site-select">
<view class="ss-header">
<view class="ss-header" id="ssHeader">
<view class="sh-stadium"> <view class="sh-stadium">
<view class="ss-logo"></view> <view class="ss-logo"></view>
<view class="ss-name">MJ体育(天空篮球从云店)</view> <view class="ss-name">MJ体育(天空篮球从云店)</view>
@ -45,16 +73,20 @@ onLoad(() => {
</view> </view>
</view> </view>
<view class="ss-main"> <view class="ss-main">
<scroll-view class="sm-dates" scroll-x>
<scroll-view class="sm-dates" id="smDates" scroll-x>
<view class="sd-item" :class="{ active: i === 1 }" v-for="i in 20" :key="i"> <view class="sd-item" :class="{ active: i === 1 }" v-for="i in 20" :key="i">
<view class="si-week">今天</view> <view class="si-week">今天</view>
<view class="si-day">6月10日</view> <view class="si-day">6月10日</view>
</view> </view>
</scroll-view> </scroll-view>
<site-table :table-data="siteData"></site-table>
<site-table
:table-data="siteData"
:altitude="scrollHeight"
v-model:table="siteData"
></site-table>
</view> </view>
<view class="ss-footer">
<site-footer></site-footer>
<view class="ss-footer" id="ssFooter">
<site-footer :selected-data="selectedData"></site-footer>
</view> </view>
</view> </view>
</template> </template>

16
src/utils/polish.js

@ -41,7 +41,7 @@ export const showModal = ({
}) })
} }
export function showNone(txt, duration=1500){
export function showNone(txt = '', duration=1500){
uni.showToast({ uni.showToast({
mask: true, mask: true,
title: txt, title: txt,
@ -99,6 +99,17 @@ export function hideLoad(){
uni.hideLoading() uni.hideLoading()
} }
// 获取节点信息
export function getNodeMes(selector, _this=null){
return new Promise(rs=>{
let query = _this ? uni.createSelectorQuery().in(_this) : uni.createSelectorQuery();
query.select(selector).boundingClientRect(res=>{
rs(res)
// this.height = uni.getSystemInfoSync().windowHeight - res.height;
}).exec()
})
}
export default { export default {
routeTo, routeTo,
@ -106,5 +117,6 @@ export default {
showNone, showNone,
debounce, debounce,
jsonStr, jsonStr,
jsonPar
jsonPar,
getNodeMes
} }
Loading…
Cancel
Save