111
This commit is contained in:
commit
7d450caf77
146 files changed
+39809
No files matched your search
@@ -0,0 +1,49 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {
|
||||
foreignList: (params: any) =>
|
||||
request({
|
||||
url: "pay/foreign-ticket/get-all",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
foreignItems: (params: any) =>
|
||||
request({
|
||||
url: "pay/foreign-ticket/get-items",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
foreignApply: (data: any) =>
|
||||
request({
|
||||
url: "pay/foreign-ticket/apply",
|
||||
method: "POST",
|
||||
data,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
strideList: (params: any) =>
|
||||
request({
|
||||
url: "pay/stride-dept/get-all",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
strideApply: (data: any) =>
|
||||
request({
|
||||
url: "pay/stride-dept/apply",
|
||||
method: "POST",
|
||||
data,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,3 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,17 @@
|
||||
export { default as applyApi } from "./apply";
|
||||
|
||||
export { default as commonApi } from "./common";
|
||||
|
||||
export { default as foodApi } from "./food";
|
||||
|
||||
export { default as merchantApi } from "./merchant";
|
||||
|
||||
export { default as orderApi } from "./order";
|
||||
|
||||
export { default as payApi } from "./pay";
|
||||
|
||||
export { default as productApi } from "./product";
|
||||
|
||||
export { default as userApi } from "./user";
|
||||
|
||||
export { default as walletApi } from "./wallet";
|
||||
@@ -0,0 +1,12 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {
|
||||
merchantList: () =>
|
||||
request({
|
||||
url: "/merchant/manage/page-join",
|
||||
method: "GET",
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,20 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {
|
||||
// 获得支付订单
|
||||
getOrder: (id, sync) => {
|
||||
return request({
|
||||
url: "/pay/order/get",
|
||||
method: "GET",
|
||||
params: { id, sync },
|
||||
});
|
||||
},
|
||||
// 提交支付订单
|
||||
submitOrder: (data) => {
|
||||
return request({
|
||||
url: "/pay/order/submit",
|
||||
method: "POST",
|
||||
data,
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {};
|
||||
@@ -0,0 +1,33 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {
|
||||
userInfo: () =>
|
||||
request({
|
||||
url: "/sys/user/get",
|
||||
method: "GET",
|
||||
custom: {
|
||||
showLoading: false,
|
||||
loadingMsg: "登录中",
|
||||
},
|
||||
}),
|
||||
|
||||
auth: (str: any) =>
|
||||
request({
|
||||
url: `/sys/auth/zzdsso/${str}`,
|
||||
method: "GET",
|
||||
custom: {
|
||||
showLoading: false,
|
||||
loadingMsg: "授权中",
|
||||
},
|
||||
}),
|
||||
|
||||
refreshToken: (params: any) =>
|
||||
request({
|
||||
url: `/sys/auth/refresh-token`,
|
||||
method: "POST",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
import request from "@/sheep/utils/request";
|
||||
|
||||
export default {
|
||||
all: () =>
|
||||
request({
|
||||
url: "pay/wallet/get-all",
|
||||
method: "GET",
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
detail: (params: any) =>
|
||||
request({
|
||||
url: "pay/wallet/detail",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
bind: (data: any) =>
|
||||
request({
|
||||
url: "pay/wallet/bind-card",
|
||||
method: "POST",
|
||||
data,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
freeze: (data: any) =>
|
||||
request({
|
||||
url: "pay/wallet/apply-freeze",
|
||||
method: "POST",
|
||||
data,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
balanceDetail: (params: any) =>
|
||||
request({
|
||||
url: "pay/wallet/get-id",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
transaction: (params: any) =>
|
||||
request({
|
||||
url: "pay/wallet-transaction/page-by-id",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
summary: (params: any) =>
|
||||
request({
|
||||
url: "pay/wallet-transaction/get-summary-by-id",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
rechargeList: (params: any) =>
|
||||
request({
|
||||
url: "pay/wallet-recharge-package/list",
|
||||
method: "GET",
|
||||
params,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
recharge: (data: any) =>
|
||||
request({
|
||||
url: "pay/wallet-recharge/create",
|
||||
method: "POST",
|
||||
data,
|
||||
custom: {
|
||||
showLoading: false,
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue'
|
||||
import { router } from '@@/index'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题'
|
||||
},
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'white'
|
||||
},
|
||||
showLeft: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const isHistiry = computed(() => {
|
||||
return router.hasHistory()
|
||||
})
|
||||
|
||||
const isTabbar = computed(() => {
|
||||
return router.hasTabbar()
|
||||
})
|
||||
|
||||
function doBackClick() {
|
||||
router.back()
|
||||
}
|
||||
|
||||
function getNavTop() {
|
||||
const { statusBarHeight } = uni.getSystemInfoSync();
|
||||
return `${statusBarHeight}px`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="navbar" :style="{ top: getNavTop() }" v-if="showLeft && !isTabbar" @tap="doBackClick">
|
||||
<text class="sicon-back icon" v-if="isHistiry" />
|
||||
<text class="sicon-home icon" v-else />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
background: #fff;
|
||||
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
|
||||
border-radius: 28rpx;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue'
|
||||
import { router } from '@@/index'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题'
|
||||
},
|
||||
titleColor: {
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'white'
|
||||
},
|
||||
showLeft: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const isHistiry = computed(() => {
|
||||
return router.hasHistory()
|
||||
})
|
||||
|
||||
const isTabbar = computed(() => {
|
||||
return router.hasTabbar()
|
||||
})
|
||||
|
||||
function doBackClick() {
|
||||
router.back()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="navbar" :style="{ backgroundColor: bgColor, height: '44px' }">
|
||||
<view class="navbar_left" v-if="showLeft && !isTabbar" @tap="doBackClick">
|
||||
<text class="sicon-back icon" v-if="isHistiry" />
|
||||
<text class="sicon-home icon" v-else />
|
||||
</view>
|
||||
<view class="navbar_title" :style="{ color: titleColor }">{{ props.title }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
position: relative;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&_left {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
background: #fff;
|
||||
box-shadow: 0px 0px 4rpx rgba(51, 51, 51, 0.08), 0px 4rpx 6rpx 2rpx rgba(102, 102, 102, 0.12);
|
||||
border-radius: 28rpx;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&_title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps, withDefaults } from "vue";
|
||||
|
||||
export interface Props {
|
||||
type: "top" | "bottom";
|
||||
bgColor: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: "top",
|
||||
bgColor: "transparent",
|
||||
});
|
||||
|
||||
function getHeight() {
|
||||
const { top, bottom } = uni.getSystemInfoSync().safeAreaInsets!;
|
||||
if (props.type && props.type === "bottom") {
|
||||
return bottom + "px";
|
||||
}
|
||||
return top + "px";
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="safe" :style="{ backgroundColor: props.bgColor, height: getHeight() }"></view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.safe-top {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,163 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps, withDefaults, watch, ref, reactive, onMounted } from "vue";
|
||||
import { router } from "@@/index"
|
||||
|
||||
|
||||
|
||||
export type Props = {
|
||||
tabBarType: "default" | "just-flot-back"
|
||||
title: string
|
||||
scrollHideTabbar: boolean
|
||||
navBgColor: string
|
||||
navBgTitleColor: string
|
||||
mainIsScroll: boolean
|
||||
bg: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
tabBarType: "default",
|
||||
title: "",
|
||||
scrollHideTabbar: true,
|
||||
navBgColor: "transparent",
|
||||
navBgTitleColor: "#333",
|
||||
mainIsScroll: false,
|
||||
bg: "",
|
||||
});
|
||||
|
||||
|
||||
const state = reactive({
|
||||
navBgTitleColor_: props.navBgTitleColor,
|
||||
visble: {
|
||||
topNav: true,
|
||||
topSafe: true
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.navBgTitleColor,
|
||||
(newVal) => {
|
||||
if (!newVal) return
|
||||
state.navBgTitleColor_ = newVal
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
initBackStyle()
|
||||
})
|
||||
|
||||
|
||||
function scroll(e) {
|
||||
if (!router.hasTabbar()) return
|
||||
if (!props.scrollHideTabbar) return
|
||||
const offset = 50
|
||||
const top = e.target.scrollTop;
|
||||
}
|
||||
|
||||
function defaultPage() {
|
||||
const { route } = router.getCurrentPage() as any
|
||||
const page = __ROUTES__.find(it => it.path == `/${route}`)
|
||||
if (page && page.name) {
|
||||
return page.name;
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function initBackStyle() {
|
||||
switch (props.tabBarType) {
|
||||
case "default":
|
||||
break;
|
||||
case "just-flot-back":
|
||||
state.visble.topSafe = false
|
||||
state.visble.topNav = false
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="layout">
|
||||
<image class="layout_bg" v-if="props.bg" :src="props.bg" mode="aspectFill" />
|
||||
<view class="layout_header">
|
||||
<base-safe-height :bgColor="props.navBgColor" v-if="state.visble.topSafe" type="top" />
|
||||
<base-nav v-if="state.visble.topNav" :titleColor="props.navBgTitleColor" :bgColor="props.navBgColor"
|
||||
:title="props.title || defaultPage()" />
|
||||
<base-just-flot-nav :bgColor="props.navBgColor" :titleColor="props.navBgTitleColor"
|
||||
v-if="!state.visble.topNav"></base-just-flot-nav>
|
||||
</view>
|
||||
<view class="layout_main">
|
||||
<view class="layout_main_top">
|
||||
<slot name="main-top"></slot>
|
||||
</view>
|
||||
<scroll-view :scroll-y="mainIsScroll" @scroll="scroll">
|
||||
<slot></slot>
|
||||
</scroll-view>
|
||||
<view class="layout_main_bottom">
|
||||
<slot name="main-bottom"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// #ifdef H5
|
||||
page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
.layout {
|
||||
width: 100%;
|
||||
// #ifndef H5
|
||||
height: 100vh;
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
height: 100%;
|
||||
// #endif
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&_bg {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&_header {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&_main {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 99;
|
||||
|
||||
&_top {}
|
||||
|
||||
scroll-view {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 0 30rpx;
|
||||
// background: #ecf4f9;
|
||||
}
|
||||
|
||||
&_bottom {}
|
||||
}
|
||||
|
||||
&_foot {}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,6 @@
|
||||
// z-tabs全局配置文件,注意避免更新时此文件被覆盖,若被覆盖,可在此文件中右键->点击本地历史记录,找回覆盖前的配置
|
||||
export default {
|
||||
'bg-color': 'transparent',
|
||||
'active-color': '#1777FF',
|
||||
'inactive-color': '#999999'
|
||||
}
|
||||
@@ -0,0 +1,736 @@
|
||||
<!-- z-tabs v0.2.5 by-ZXLee -->
|
||||
<!-- github地址:https://github.com/SmileZXLee/uni-z-tabs -->
|
||||
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?name=z-tabs -->
|
||||
<!-- 反馈QQ群:790460711 -->
|
||||
|
||||
<template name="z-tabs">
|
||||
<view class="z-tabs-conatiner" :style="[{background:bgColor}, tabsStyle]">
|
||||
<view class="z-tabs-left">
|
||||
<slot name="left" />
|
||||
</view>
|
||||
<view ref="z-tabs-scroll-view-conatiner" class="z-tabs-scroll-view-conatiner">
|
||||
<scroll-view ref="z-tabs-scroll-view" class="z-tabs-scroll-view" :scroll-x="true" :scroll-left="scrollLeft" :show-scrollbar="false" :scroll-with-animation="isFirstLoaded" @scroll="scroll">
|
||||
<view class="z-tabs-list-container" :style="[tabsListStyle]">
|
||||
<view class="z-tabs-list" :style="[tabsListStyle, {marginTop: -finalBottomSpace+'px'}]">
|
||||
<view :ref="`z-tabs-item-${index}`" :id="`z-tabs-item-${index}`" class="z-tabs-item" :style="[tabStyle]" v-for="(item,index) in list" :key="index" @click="tabsClick(index,item)">
|
||||
<view class="z-tabs-item-title-container">
|
||||
<text :class="{'z-tabs-item-title':true,'z-tabs-item-title-disabled':item.disabled}"
|
||||
:style="[{color:item.disabled?disabledColor:(currentIndex===index?activeColor:inactiveColor)},item.disabled?disabledStyle:(currentIndex===index?activeStyle:inactiveStyle)]">
|
||||
{{item[nameKey]||item}}
|
||||
</text>
|
||||
<text v-if="item.badge&&_formatCount(item.badge.count).length" class="z-tabs-item-badge" :style="[badgeStyle]">{{_formatCount(item.badge.count)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="z-tabs-bottom" :style="[{width: tabsContainerWidth+'px', bottom: finalBottomSpace+'px'}]">
|
||||
<view ref="z-tabs-bottom-dot" class="z-tabs-bottom-dot"
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
:style="[{transform:`translateX(${bottomDotX}px)`,transition:dotTransition,background:activeColor},finalDotStyle]"
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
:style="[{background:activeColor},finalDotStyle]"
|
||||
<!-- #endif -->
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="z-tabs-right">
|
||||
<slot name="right" />
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifdef APP-NVUE
|
||||
const weexDom = weex.requireModule('dom');
|
||||
const weexAnimation = weex.requireModule('animation');
|
||||
// #endif
|
||||
import zTabsConfig from './config/index'
|
||||
|
||||
//获取默认配置信息
|
||||
function _gc(key, defaultValue) {
|
||||
let config = null;
|
||||
if (zTabsConfig && Object.keys(zTabsConfig).length) {
|
||||
config = zTabsConfig;
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
const value = config[_toKebab(key)];
|
||||
return value === undefined ? defaultValue : value;
|
||||
}
|
||||
//驼峰转短横线
|
||||
function _toKebab(value) {
|
||||
return value.replace(/([A-Z])/g, "-$1").toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* z-tabs 标签
|
||||
* @description 一个简单轻量的tabs标签,全平台兼容,支持nvue、vue3
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?name=z-tabs
|
||||
* @property {Array} list 数据源数组,支持形如['tab1','tab2']的格式或[{name:'tab1',value:1}]的格式
|
||||
* @property {Number|String} current 当前选中的index,默认为0
|
||||
* @property {Number|String} scroll-count list数组长度超过scrollCount时滚动显示(不自动铺满全屏),默认为5
|
||||
* @property {Number|String} tab-width 自定义每个tab的宽度,默认为0,即代表根据内容自动撑开,单位rpx,支持传100、"100px"或"100rpx"
|
||||
* @property {Number|String} bar-width 滑块宽度,单位rpx,支持传100、"100px"或"100rpx"
|
||||
* @property {Number|String} bar-height 滑块高度,单位rpx,支持传100、"100px"或"100rpx"
|
||||
* @property {Object} bar-style 滑块样式,其中的width和height将被bar-width和bar-height覆盖
|
||||
* @property {Number|String} bottom-space tabs与底部的间距,单位rpx,支持传100、"100px"或"100rpx"
|
||||
* @property {String} bar-animate-mode 切换tab时滑块动画模式,与swiper联动时有效,点击切换tab时无效,必须调用setDx。默认为line,即切换tab时滑块宽度保持不变,线性运动。可选值为worm,即为类似毛毛虫蠕动效果
|
||||
* @property {String} name-key list中item的name(标题)的key,默认为name
|
||||
* @property {String} value-key list中item的value的key,默认为value
|
||||
* @property {String} active-color 激活状态tab的颜色
|
||||
* @property {String} inactive-color 未激活状态tab的颜色
|
||||
* @property {String} disabled-color 禁用状态tab的颜色
|
||||
* @property {Object} active-style 激活状态tab的样式
|
||||
* @property {Object} inactive-style 未激活状态tab的样式
|
||||
* @property {Object} disabled-style 禁用状态tab的样式
|
||||
* @property {Number|String} badge-max-count 徽标数最大数字限制,超过这个数字将变成badge-max-count+,默认为99
|
||||
* @property {Object} badge-style 徽标样式,例如可自定义背景色,字体等等
|
||||
* @property {String} bg-color z-tabs背景色
|
||||
* @property {Object} tabs-style z-tabs样式
|
||||
* @property {Boolean} init-trigger-change 初始化时是否自动触发change事件
|
||||
* @event {Function(index,value)} change tabs改变时触发,index:当前切换到的index;value:当前切换到的value
|
||||
* @example <z-tabs :list="list"></z-tabs>
|
||||
*/
|
||||
export default {
|
||||
name: 'z-tabs',
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
currentSwiperIndex: 0,
|
||||
bottomDotX: -1,
|
||||
bottomDotXForIndex: 0,
|
||||
showBottomDot: false,
|
||||
shouldSetDx: true,
|
||||
|
||||
barCalcedWidth: 0,
|
||||
pxBarWidth: 0,
|
||||
scrollLeft: 0,
|
||||
tabsSuperWidth: uni.upx2px(750),
|
||||
tabsWidth: uni.upx2px(750),
|
||||
tabsHeight: uni.upx2px(80),
|
||||
tabsLeft: 0,
|
||||
tabsContainerWidth: 0,
|
||||
itemNodeInfos: [],
|
||||
isFirstLoaded: false,
|
||||
currentScrollLeft: 0,
|
||||
changeTriggerFailed: false,
|
||||
currentChanged: false
|
||||
};
|
||||
},
|
||||
props: {
|
||||
//数据源数组,支持形如['tab1','tab2']的格式或[{name:'tab1',value:1}]的格式
|
||||
list: {
|
||||
type: Array,
|
||||
default: function() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
//当前选中的index
|
||||
current: {
|
||||
type: [Number, String],
|
||||
default: _gc('current',0)
|
||||
},
|
||||
//list数组长度超过scrollCount时滚动显示(不自动铺满全屏)
|
||||
scrollCount: {
|
||||
type: [Number, String],
|
||||
default: _gc('scrollCount',5)
|
||||
},
|
||||
//z-tabs样式
|
||||
tabsStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return _gc('tabsStyle',{})
|
||||
}
|
||||
},
|
||||
//自定义每个tab的宽度,默认为0,即代表根据内容自动撑开,单位rpx,支持传100、"100px"或"100rpx"
|
||||
tabWidth: {
|
||||
type: [Number, String],
|
||||
default: _gc('tabWidth',0)
|
||||
},
|
||||
//滑块宽度,单位rpx,支持传100、"100px"或"100rpx"
|
||||
barWidth: {
|
||||
type: [Number, String],
|
||||
default: _gc('barWidth',45)
|
||||
},
|
||||
//滑块高度,单位rpx,支持传100、"100px"或"100rpx"
|
||||
barHeight: {
|
||||
type: [Number, String],
|
||||
default: _gc('barHeight',8)
|
||||
},
|
||||
//滑块样式,其中的width和height将被barWidth和barHeight覆盖
|
||||
barStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return _gc('barStyle',{});
|
||||
}
|
||||
},
|
||||
//tabs与底部的间距,单位rpx,支持传100、"100px"或"100rpx"
|
||||
bottomSpace: {
|
||||
type: [Number, String],
|
||||
default: _gc('bottomSpace',8)
|
||||
},
|
||||
//切换tab时滑块动画模式,与swiper联动时有效,点击切换tab时无效,必须调用setDx。默认为line,即切换tab时滑块宽度保持不变,线性运动。可选值为worm,即为类似毛毛虫蠕动效果
|
||||
barAnimateMode: {
|
||||
type: String,
|
||||
default: _gc('barAnimateMode','line')
|
||||
},
|
||||
//list中item的name(标题)的key
|
||||
nameKey: {
|
||||
type: String,
|
||||
default: _gc('nameKey','name')
|
||||
},
|
||||
//list中item的value的key
|
||||
valueKey: {
|
||||
type: String,
|
||||
default: _gc('valueKey','value')
|
||||
},
|
||||
//激活状态tab的颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: _gc('activeColor','#007AFF')
|
||||
},
|
||||
//未激活状态tab的颜色
|
||||
inactiveColor: {
|
||||
type: String,
|
||||
default: _gc('inactiveColor','#666666')
|
||||
},
|
||||
//禁用状态tab的颜色
|
||||
disabledColor: {
|
||||
type: String,
|
||||
default: _gc('disabledColor','#bbbbbb')
|
||||
},
|
||||
//激活状态tab的样式
|
||||
activeStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return _gc('activeStyle',{});
|
||||
}
|
||||
},
|
||||
//未激活状态tab的样式
|
||||
inactiveStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return _gc('inactiveStyle',{});
|
||||
}
|
||||
},
|
||||
//禁用状态tab的样式
|
||||
disabledStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return _gc('disabledStyle',{});
|
||||
}
|
||||
},
|
||||
//z-tabs背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: _gc('bgColor','white')
|
||||
},
|
||||
//徽标数最大数字限制,超过这个数字将变成badgeMaxCount+
|
||||
badgeMaxCount: {
|
||||
type: [Number, String],
|
||||
default: _gc('badgeMaxCount',99)
|
||||
},
|
||||
//徽标样式,例如可自定义背景色,字体等等
|
||||
badgeStyle: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return _gc('badgeStyle',{})
|
||||
}
|
||||
},
|
||||
//初始化时是否自动触发change事件
|
||||
initTriggerChange: {
|
||||
type: Boolean,
|
||||
default: _gc('initTriggerChange',false)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.updateSubviewLayout();
|
||||
},
|
||||
watch: {
|
||||
current: {
|
||||
handler(newVal) {
|
||||
this.currentChanged && this._lockDx();
|
||||
this.currentIndex = newVal;
|
||||
this._preUpdateDotPosition(this.currentIndex);
|
||||
if (this.initTriggerChange) {
|
||||
if (newVal < this.list.length) {
|
||||
this.$emit('change', newVal, this.list[newVal][this.valueKey]);
|
||||
}else {
|
||||
this.changeTriggerFailed = true;
|
||||
}
|
||||
}
|
||||
this.currentChanged = true;
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
list: {
|
||||
handler(newVal) {
|
||||
this._handleListChange(newVal);
|
||||
},
|
||||
immediate: false
|
||||
},
|
||||
bottomDotX(newVal) {
|
||||
if(newVal >= 0){
|
||||
// #ifndef APP-NVUE
|
||||
this.showBottomDot = true;
|
||||
// #endif
|
||||
this.$nextTick(() => {
|
||||
// #ifdef APP-NVUE
|
||||
weexAnimation.transition(this.$refs['z-tabs-bottom-dot'], {
|
||||
styles: {
|
||||
transform: `translateX(${newVal}px)`
|
||||
},
|
||||
duration: this.showAnimate ? 200 : 0,
|
||||
delay: 0
|
||||
})
|
||||
setTimeout(() => {
|
||||
this.showBottomDot = true;
|
||||
},10)
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
},
|
||||
finalBarWidth: {
|
||||
handler(newVal) {
|
||||
this.barCalcedWidth = newVal;
|
||||
this.pxBarWidth = this.barCalcedWidth;
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
currentIndex: {
|
||||
handler(newVal) {
|
||||
this.currentSwiperIndex = newVal;
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
shouldScroll(){
|
||||
return this.list.length > this.scrollCount;
|
||||
},
|
||||
finalTabsHeight(){
|
||||
return this.tabsHeight;
|
||||
},
|
||||
tabStyle(){
|
||||
const stl = this.shouldScroll ? {'flex-shrink': 0} : {'flex': 1};
|
||||
if(this.finalTabWidth > 0){
|
||||
stl['width'] = this.finalTabWidth + 'px';
|
||||
}else{
|
||||
delete stl.width;
|
||||
}
|
||||
return stl;
|
||||
},
|
||||
tabsListStyle(){
|
||||
return this.shouldScroll ? {} : {'flex':1};
|
||||
},
|
||||
showAnimate(){
|
||||
return this.isFirstLoaded && !this.shouldSetDx;
|
||||
},
|
||||
dotTransition(){
|
||||
return this.showAnimate ? 'transform .2s linear':'none';
|
||||
},
|
||||
finalDotStyle(){
|
||||
return {...this.barStyle, width: this.barCalcedWidth + 'px', height: this.finalBarHeight + 'px', opacity: this.showBottomDot ? 1 : 0};
|
||||
},
|
||||
finalTabWidth(){
|
||||
return this._convertTextToPx(this.tabWidth);
|
||||
},
|
||||
finalBarWidth(){
|
||||
return this._convertTextToPx(this.barWidth);
|
||||
},
|
||||
finalBarHeight(){
|
||||
return this._convertTextToPx(this.barHeight);
|
||||
},
|
||||
finalBottomSpace(){
|
||||
return this._convertTextToPx(this.bottomSpace);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//根据swiper的@transition实时更新底部dot位置
|
||||
setDx(dx) {
|
||||
if (!this.shouldSetDx) return;
|
||||
const isLineMode = this.barAnimateMode === 'line';
|
||||
const isWormMode = this.barAnimateMode === 'worm';
|
||||
let dxRate = dx / this.tabsSuperWidth;
|
||||
this.currentSwiperIndex = this.currentIndex + parseInt(dxRate);
|
||||
const isRight = dxRate > 0;
|
||||
const barWidth = this.pxBarWidth;
|
||||
if(this.currentSwiperIndex !== this.currentIndex){
|
||||
dxRate = dxRate - (this.currentSwiperIndex - this.currentIndex);
|
||||
const currentNode = this.itemNodeInfos[this.currentSwiperIndex];
|
||||
if (!!currentNode){
|
||||
this.bottomDotXForIndex = this._getBottomDotX(currentNode, barWidth);
|
||||
}
|
||||
}
|
||||
const currentIndex = this.currentSwiperIndex;
|
||||
let nextIndex = currentIndex + (isRight ? 1 : -1);
|
||||
nextIndex = Math.max(0, nextIndex);
|
||||
nextIndex = Math.min(nextIndex, this.itemNodeInfos.length - 1);
|
||||
const currentNodeInfo = this.itemNodeInfos[currentIndex];
|
||||
const nextNodeInfo = this.itemNodeInfos[nextIndex];
|
||||
const nextBottomX = this._getBottomDotX(nextNodeInfo, barWidth);
|
||||
if (isLineMode){
|
||||
this.bottomDotX = this.bottomDotXForIndex + (nextBottomX - this.bottomDotXForIndex) * Math.abs(dxRate);
|
||||
} else if (isWormMode) {
|
||||
if ((isRight && currentIndex >= this.itemNodeInfos.length - 1) || (!isRight && currentIndex <= 0)) return;
|
||||
const spaceOffset = isRight ? nextNodeInfo.right - currentNodeInfo.left : currentNodeInfo.right - nextNodeInfo.left;
|
||||
let barCalcedWidth = barWidth + spaceOffset * Math.abs(dxRate);
|
||||
if (isRight) {
|
||||
if (barCalcedWidth > nextBottomX - this.bottomDotX + barWidth) {
|
||||
const barMinusWidth = barWidth + spaceOffset * (1 - dxRate);
|
||||
this.bottomDotX = this.bottomDotXForIndex + (barCalcedWidth - barMinusWidth) / 2;
|
||||
barCalcedWidth = barMinusWidth;
|
||||
}
|
||||
}else if (!isRight) {
|
||||
if (barCalcedWidth > this.bottomDotXForIndex + barWidth - nextBottomX){
|
||||
const barMinusWidth = barWidth + spaceOffset * (1 + dxRate);
|
||||
barCalcedWidth = barMinusWidth;
|
||||
this.bottomDotX = nextBottomX;
|
||||
} else{
|
||||
this.bottomDotX = this.bottomDotXForIndex - (barCalcedWidth - barWidth);
|
||||
}
|
||||
}
|
||||
barCalcedWidth = Math.max(barCalcedWidth, barWidth);
|
||||
this.barCalcedWidth = barCalcedWidth;
|
||||
}
|
||||
},
|
||||
//在swiper的@animationfinish中通知z-tabs结束多setDx的锁定,若在父组件中调用了setDx,则必须调用unlockDx
|
||||
unlockDx() {
|
||||
this.$nextTick(() => {
|
||||
this.shouldSetDx = true;
|
||||
})
|
||||
},
|
||||
//更新z-tabs内部布局
|
||||
updateSubviewLayout(tryCount = 0) {
|
||||
this.$nextTick(() => {
|
||||
let delayTime = 10;
|
||||
// #ifdef APP-NVUE || MP-BAIDU
|
||||
delayTime = 50;
|
||||
// #endif
|
||||
setTimeout(() => {
|
||||
this._getNodeClientRect('.z-tabs-scroll-view-conatiner').then(res=>{
|
||||
if (res){
|
||||
if (!res[0].width && tryCount < 10) {
|
||||
setTimeout(() => {
|
||||
tryCount ++;
|
||||
this.updateSubviewLayout(tryCount);
|
||||
}, 50);
|
||||
return;
|
||||
}
|
||||
this.tabsWidth = res[0].width;
|
||||
this.tabsHeight = res[0].height;
|
||||
this.tabsLeft = res[0].left;
|
||||
this._handleListChange(this.list);
|
||||
}
|
||||
})
|
||||
this._getNodeClientRect('.z-tabs-conatiner').then(res=>{
|
||||
if(res && res[0].width){
|
||||
this.tabsSuperWidth = res[0].width;
|
||||
}
|
||||
})
|
||||
},delayTime)
|
||||
})
|
||||
},
|
||||
//点击了tabs
|
||||
tabsClick(index,item) {
|
||||
if (item.disabled) return;
|
||||
if (this.currentIndex != index) {
|
||||
this.shouldSetDx = false;
|
||||
this.$emit('change', index, item[this.valueKey]);
|
||||
this.currentIndex = index;
|
||||
this._preUpdateDotPosition(index);
|
||||
} else {
|
||||
this.$emit('secondClick',index, item[this.valueKey]);
|
||||
}
|
||||
},
|
||||
//scroll-view滚动
|
||||
scroll(e){
|
||||
this.currentScrollLeft = e.detail.scrollLeft;
|
||||
},
|
||||
//锁定dx,用于避免在swiper被动触发滚动时候执行setDx中的代码
|
||||
_lockDx() {
|
||||
this.shouldSetDx = false;
|
||||
},
|
||||
//更新底部dot位置之前的预处理
|
||||
_preUpdateDotPosition(index) {
|
||||
// #ifndef APP-NVUE
|
||||
this.$nextTick(() => {
|
||||
uni.createSelectorQuery().in(this).select(".z-tabs-scroll-view").fields({
|
||||
scrollOffset: true
|
||||
}, data => {
|
||||
if (data) {
|
||||
this.currentScrollLeft = data.scrollLeft;
|
||||
this._updateDotPosition(index);
|
||||
} else {
|
||||
this._updateDotPosition(index);
|
||||
}
|
||||
}).exec();
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
this._updateDotPosition(index);
|
||||
// #endif
|
||||
},
|
||||
//更新底部dot位置
|
||||
_updateDotPosition(index){
|
||||
if(index >= this.itemNodeInfos.length) return;
|
||||
this.$nextTick(async ()=>{
|
||||
let node = this.itemNodeInfos[index];
|
||||
let offset = 0;
|
||||
let tabsContainerWidth = this.tabsContainerWidth;
|
||||
if (JSON.stringify(this.activeStyle) !== '{}') {
|
||||
const nodeRes = await this._getNodeClientRect(`#z-tabs-item-${index}`,true);
|
||||
if (nodeRes) {
|
||||
node = nodeRes[0];
|
||||
offset = this.currentScrollLeft;
|
||||
this.tabsHeight = Math.max(node.height + uni.upx2px(28), this.tabsHeight);
|
||||
tabsContainerWidth = 0;
|
||||
for(let i = 0;i < this.itemNodeInfos.length;i++){
|
||||
let oldNode = this.itemNodeInfos[i];
|
||||
tabsContainerWidth += i === index ? node.width : oldNode.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.bottomDotX = this._getBottomDotX(node, this.finalBarWidth, offset);
|
||||
this.bottomDotXForIndex = this.bottomDotX;
|
||||
if (this.tabsWidth) {
|
||||
setTimeout(()=>{
|
||||
let scrollLeft = this.bottomDotX - this.tabsWidth / 2 + this.finalBarWidth / 2;
|
||||
scrollLeft = Math.max(0,scrollLeft);
|
||||
if (tabsContainerWidth) {
|
||||
scrollLeft = Math.min(scrollLeft,tabsContainerWidth - this.tabsWidth + 10);
|
||||
}
|
||||
if (this.shouldScroll && tabsContainerWidth > this.tabsWidth) {
|
||||
this.scrollLeft = scrollLeft;
|
||||
}
|
||||
this.$nextTick(()=>{
|
||||
this.isFirstLoaded = true;
|
||||
})
|
||||
},200)
|
||||
}
|
||||
})
|
||||
},
|
||||
// 处理list改变
|
||||
_handleListChange(newVal) {
|
||||
this.$nextTick(async ()=>{
|
||||
if(newVal.length){
|
||||
let itemNodeInfos = [];
|
||||
let tabsContainerWidth = 0;
|
||||
let delayTime = 0;
|
||||
// #ifdef MP-BAIDU
|
||||
delayTime = 100;
|
||||
// #endif
|
||||
setTimeout(async()=>{
|
||||
for(let i = 0;i < newVal.length;i++){
|
||||
const nodeRes = await this._getNodeClientRect(`#z-tabs-item-${i}`,true);
|
||||
if(nodeRes){
|
||||
const node = nodeRes[0];
|
||||
node.left += this.currentScrollLeft;
|
||||
itemNodeInfos.push(node);
|
||||
tabsContainerWidth += node.width;
|
||||
}
|
||||
if (i === this.currentIndex){
|
||||
this.itemNodeInfos = itemNodeInfos;
|
||||
this.tabsContainerWidth = tabsContainerWidth;
|
||||
this._updateDotPosition(this.currentIndex);
|
||||
}
|
||||
}
|
||||
this.itemNodeInfos = itemNodeInfos;
|
||||
this.tabsContainerWidth = tabsContainerWidth;
|
||||
this._updateDotPosition(this.currentIndex);
|
||||
},delayTime)
|
||||
}
|
||||
})
|
||||
|
||||
if (this.initTriggerChange && this.changeTriggerFailed && newVal.length) {
|
||||
if (this.current < newVal.length) {
|
||||
this.$emit('change', this.current, newVal[this.current][this.valueKey]);
|
||||
}
|
||||
}
|
||||
},
|
||||
//根据node获取bottomX
|
||||
_getBottomDotX(node, barWidth = this.finalBarWidth, offset = 0){
|
||||
return node.left + node.width / 2 - barWidth / 2 + offset - this.tabsLeft;
|
||||
},
|
||||
//获取节点信息
|
||||
_getNodeClientRect(select, withRefArr = false) {
|
||||
// #ifdef APP-NVUE
|
||||
select = select.replace('.', '').replace('#', '');
|
||||
const ref = withRefArr ? this.$refs[select][0] : this.$refs[select];
|
||||
return new Promise((resolve, reject) => {
|
||||
if (ref) {
|
||||
weexDom.getComponentRect(ref, option => {
|
||||
if (option && option.result) {
|
||||
resolve([option.size]);
|
||||
} else resolve(false);
|
||||
})
|
||||
} else resolve(false);
|
||||
});
|
||||
return;
|
||||
// #endif
|
||||
const res = uni.createSelectorQuery().in(this);
|
||||
res.select(select).boundingClientRect();
|
||||
return new Promise((resolve, reject) => {
|
||||
res.exec(data => {
|
||||
resolve((data && data != '' && data != undefined && data.length) ? data : false);
|
||||
});
|
||||
});
|
||||
},
|
||||
//格式化badge中的count
|
||||
_formatCount(count) {
|
||||
if (!count) return '';
|
||||
if (count > this.badgeMaxCount) {
|
||||
return this.badgeMaxCount + '+';
|
||||
}
|
||||
return count.toString();
|
||||
},
|
||||
//将文本的px或者rpx转为px的值
|
||||
_convertTextToPx(text) {
|
||||
const dataType = Object.prototype.toString.call(text);
|
||||
if (dataType === '[object Number]') {
|
||||
return uni.upx2px(text);
|
||||
}
|
||||
let isRpx = false;
|
||||
if (text.indexOf('rpx') !== -1 || text.indexOf('upx') !== -1) {
|
||||
text = text.replace('rpx', '').replace('upx', '');
|
||||
isRpx = true;
|
||||
} else if (text.indexOf('px') !== -1) {
|
||||
text = text.replace('px', '');
|
||||
} else {
|
||||
text = uni.upx2px(text);
|
||||
}
|
||||
if (!isNaN(text)) {
|
||||
if (isRpx) return Number(uni.upx2px(text));
|
||||
return Number(text);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.z-tabs-conatiner{
|
||||
/* #ifndef APP-NVUE */
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
width: 750rpx;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.z-tabs-scroll-view-conatiner{
|
||||
flex: 1;
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
.z-tabs-scroll-view ::-webkit-scrollbar {
|
||||
display: none;
|
||||
-webkit-appearance: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
background: transparent;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.z-tabs-scroll-view{
|
||||
flex-direction: row;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.z-tabs-list-container{
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.z-tabs-list,.z-tabs-list-container{
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.z-tabs-item{
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0px 20rpx;
|
||||
}
|
||||
|
||||
.z-tabs-item-title-container{
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.z-tabs-item-title{
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.z-tabs-item-title-disabled{
|
||||
/* #ifndef APP-NVUE */
|
||||
cursor: not-allowed;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.z-tabs-item-badge{
|
||||
margin-left: 8rpx;
|
||||
background-color: #ec5b56;
|
||||
color: white;
|
||||
font-size: 22rpx;
|
||||
border-radius: 100px;
|
||||
padding: 0rpx 10rpx;
|
||||
}
|
||||
|
||||
.z-tabs-bottom{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.z-tabs-bottom-dot{
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
.z-tabs-left,.z-tabs-right{
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export default {
|
||||
logo: "/static/constant/logo.png"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as imgConstant } from "./img-constant";
|
||||
|
||||
export { default as storageConstant } from "./storage-constant";
|
||||
@@ -0,0 +1,4 @@
|
||||
export default {
|
||||
TOKEN: "token",
|
||||
USERINFO: "user_info",
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./use-page";
|
||||
|
||||
export * from "./use-unit";
|
||||
@@ -0,0 +1,78 @@
|
||||
import { reactive, onMounted } from "vue";
|
||||
|
||||
export const usePage = (
|
||||
ref: any = null,
|
||||
fn: Function | null = null,
|
||||
sticky = null,
|
||||
struct = "data"
|
||||
) => {
|
||||
const state = reactive<any>({
|
||||
loading: true,
|
||||
list: [],
|
||||
headerHeight: 0,
|
||||
scrollable: true,
|
||||
});
|
||||
const scroll = (e: any) => {
|
||||
const scrollTop = e.detail.scrollTop;
|
||||
if (scrollTop < state.headerHeight) {
|
||||
state.scrollable = true;
|
||||
} else {
|
||||
state.scrollable = false;
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
if (sticky != null) {
|
||||
const query = uni.createSelectorQuery();
|
||||
query
|
||||
.select(sticky)
|
||||
.boundingClientRect((data) => {
|
||||
state.headerHeight = (data as any).height;
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
});
|
||||
const setPage = async (list: Array<any>) => {
|
||||
if (ref == null) return;
|
||||
state.list.totalList = await ref.value.complete(list);
|
||||
console.log(state.list, "list");
|
||||
};
|
||||
const startPage = async (
|
||||
page: number,
|
||||
pagesize: number,
|
||||
isPage = false,
|
||||
query = {}
|
||||
) => {
|
||||
if (!fn || typeof fn !== "function") {
|
||||
console.warn("startPage 方法请传入 pageFunc 参数 模拟5条假数据");
|
||||
state.list = Array.from(new Array(5));
|
||||
setPage(state.list);
|
||||
state.startPage = startPage;
|
||||
state.scroll = scroll;
|
||||
return state;
|
||||
}
|
||||
const _data = {
|
||||
pageNo: page,
|
||||
pagesize: pagesize,
|
||||
...query,
|
||||
};
|
||||
try {
|
||||
const res = await fn(_data);
|
||||
let _arr = res;
|
||||
for (let key of struct.split(".")) {
|
||||
_arr = _arr[key];
|
||||
}
|
||||
setPage(_arr);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
setTimeout((_) => {
|
||||
if (state.loading == true) {
|
||||
state.loading = false;
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
|
||||
state.startPage = startPage;
|
||||
state.scroll = scroll;
|
||||
return state;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
export const useMoney = (num) => {
|
||||
if (typeof num === "undefined") return "0.00";
|
||||
const parsedNumber = typeof num === "string" ? parseFloat(num) : num;
|
||||
return (parsedNumber / 100.0).toFixed(1);
|
||||
};
|
||||
|
||||
export function useTime(time, pattern) {
|
||||
if (arguments.length === 0 || !time) {
|
||||
return null;
|
||||
}
|
||||
const format = pattern || "{y}-{m}-{d} {h}:{i}:{s}";
|
||||
let date;
|
||||
if (typeof time === "object") {
|
||||
date = time;
|
||||
} else {
|
||||
if (typeof time === "string" && /^[0-9]+$/.test(time)) {
|
||||
time = parseInt(time);
|
||||
} else if (typeof time === "string") {
|
||||
time = time
|
||||
.replace(new RegExp(/-/gm), "/")
|
||||
.replace("T", " ")
|
||||
.replace(new RegExp(/\.[\d]{3}/gm), "");
|
||||
}
|
||||
if (typeof time === "number" && time.toString().length === 10) {
|
||||
time = time * 1000;
|
||||
}
|
||||
date = new Date(time);
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay(),
|
||||
};
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
|
||||
let value = formatObj[key];
|
||||
if (key === "a") {
|
||||
return ["日", "一", "二", "三", "四", "五", "六"][value];
|
||||
}
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = "0" + value;
|
||||
}
|
||||
return value || 0;
|
||||
});
|
||||
return time_str;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from "./plugs/index";
|
||||
|
||||
export * as router from "./router";
|
||||
|
||||
export * as constant from "./constant";
|
||||
|
||||
export * as hooks from "./hooks";
|
||||
@@ -0,0 +1 @@
|
||||
export {useRouter} from "./use-router/vite-router-plug"
|
||||
@@ -0,0 +1,78 @@
|
||||
const singleComment = Symbol('singleComment');
|
||||
const multiComment = Symbol('multiComment');
|
||||
|
||||
const stripWithoutWhitespace = () => '';
|
||||
const stripWithWhitespace = (string: any, start: any = "", end: any) => string.slice(start, end).replace(/\S/g, ' ');
|
||||
|
||||
const isEscaped = (jsonString: any, quotePosition: any) => {
|
||||
let index = quotePosition - 1;
|
||||
let backslashCount = 0;
|
||||
|
||||
while (jsonString[index] === '\\') {
|
||||
index -= 1;
|
||||
backslashCount += 1;
|
||||
}
|
||||
|
||||
return Boolean(backslashCount % 2);
|
||||
};
|
||||
|
||||
export default function stripJsonComments(jsonString: any, { whitespace = true } = {}) {
|
||||
if (typeof jsonString !== 'string') {
|
||||
throw new TypeError(
|
||||
`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``,
|
||||
);
|
||||
}
|
||||
|
||||
const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;
|
||||
|
||||
let isInsideString = false;
|
||||
let isInsideComment: any = false;
|
||||
let offset = 0;
|
||||
let result = '';
|
||||
|
||||
for (let index = 0; index < jsonString.length; index++) {
|
||||
const currentCharacter = jsonString[index];
|
||||
const nextCharacter = jsonString[index + 1];
|
||||
|
||||
if (!isInsideComment && currentCharacter === '"') {
|
||||
const escaped = isEscaped(jsonString, index);
|
||||
if (!escaped) {
|
||||
isInsideString = !isInsideString;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInsideString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isInsideComment && currentCharacter + nextCharacter === '//') {
|
||||
result += jsonString.slice(offset, index);
|
||||
offset = index;
|
||||
isInsideComment = singleComment;
|
||||
index++;
|
||||
} else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\r\n') {
|
||||
index++;
|
||||
isInsideComment = false;
|
||||
result += strip(jsonString, offset, index);
|
||||
offset = index;
|
||||
continue;
|
||||
} else if (isInsideComment === singleComment && currentCharacter === '\n') {
|
||||
isInsideComment = false;
|
||||
result += strip(jsonString, offset, index);
|
||||
offset = index;
|
||||
} else if (!isInsideComment && currentCharacter + nextCharacter === '/*') {
|
||||
result += jsonString.slice(offset, index);
|
||||
offset = index;
|
||||
isInsideComment = multiComment;
|
||||
index++;
|
||||
continue;
|
||||
} else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') {
|
||||
index++;
|
||||
isInsideComment = false;
|
||||
result += strip(jsonString, offset, index + 1);
|
||||
offset = index + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return result + (isInsideComment ? (strip as any)(jsonString.slice(offset)) : jsonString.slice(offset));
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import fs from 'fs';
|
||||
import stripJsonComments from './strip-json-comments';
|
||||
|
||||
class TransformPages {
|
||||
routes: any = []
|
||||
tabbar: any = []
|
||||
includes = []
|
||||
uniPagesJSON: any = ""
|
||||
|
||||
constructor({
|
||||
includes,
|
||||
pagesJsonDir
|
||||
}: any) {
|
||||
this.includes = includes;
|
||||
this.uniPagesJSON = JSON.parse(stripJsonComments(fs.readFileSync(pagesJsonDir, 'utf-8')))
|
||||
this.routes = this.getPagesRoutes().concat(this.getSubPackagesRoutes())
|
||||
this.tabbar = this.getTabbarRoutes()
|
||||
}
|
||||
|
||||
getPagesRoutes(pages = this.uniPagesJSON.pages, rootPath = null) {
|
||||
let routes = [];
|
||||
for (let i = 0; i < pages.length; i++) {
|
||||
const item = pages[i];
|
||||
let route: any = {};
|
||||
for (let j = 0; j < this.includes.length; j++) {
|
||||
const key = this.includes[j];
|
||||
let value = item[key];
|
||||
if (key === 'path') {
|
||||
value = rootPath ? `/${rootPath}/${value}` : `/${value}`;
|
||||
}
|
||||
if (key === 'aliasPath' && i == 0 && rootPath == null) {
|
||||
route[key] = route[key] || '/';
|
||||
} else if (value !== undefined) {
|
||||
route[key] = value;
|
||||
}
|
||||
}
|
||||
routes.push(route);
|
||||
}
|
||||
return routes;
|
||||
}
|
||||
|
||||
getSubPackagesRoutes() {
|
||||
if (!(this.uniPagesJSON && this.uniPagesJSON.subPackages)) {
|
||||
return [];
|
||||
}
|
||||
const subPackages = this.uniPagesJSON.subPackages;
|
||||
let routes: any = [];
|
||||
for (let i = 0; i < subPackages.length; i++) {
|
||||
const subPages = subPackages[i].pages;
|
||||
const root = subPackages[i].root;
|
||||
const subRoutes = this.getPagesRoutes(subPages, root);
|
||||
routes = routes.concat(subRoutes);
|
||||
}
|
||||
return routes;
|
||||
}
|
||||
|
||||
getTabbarRoutes() {
|
||||
if (!(this.uniPagesJSON && this.uniPagesJSON.tabBar && this.uniPagesJSON.tabBar.list)) {
|
||||
return [];
|
||||
}
|
||||
const tabbar = this.uniPagesJSON.tabBar.list;
|
||||
let tabbarMap: any = [];
|
||||
tabbar.forEach((bar: { pagePath: string; }) => {
|
||||
tabbarMap.push('/' + bar.pagePath);
|
||||
});
|
||||
return tabbarMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const useRouter = ({
|
||||
pagesJsonDir,
|
||||
includes = []
|
||||
}: any) => {
|
||||
let defaultIncludes = ['path', 'aliasPath', 'name'];
|
||||
includes = [...defaultIncludes, ...includes];
|
||||
let pages = new TransformPages({
|
||||
pagesJsonDir,
|
||||
includes,
|
||||
});
|
||||
if (pages.tabbar && pages.tabbar.length) {
|
||||
console.table(pages.tabbar);
|
||||
}
|
||||
if (pages.routes && pages.routes.length) {
|
||||
console.table(pages.routes);
|
||||
}
|
||||
return {
|
||||
name: 'use-router',
|
||||
config() {
|
||||
return {
|
||||
define: {
|
||||
__ROUTES__: JSON.stringify(pages.routes),
|
||||
__TABBAR__: `(${JSON.stringify(pages.tabbar)})`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { throttle } from "radash";
|
||||
|
||||
function paramsToQuery(params: { [key: string]: string }) {
|
||||
let query = [];
|
||||
for (let key in params) {
|
||||
query.push(key + "=" + params[key]);
|
||||
}
|
||||
return query.join("&");
|
||||
}
|
||||
|
||||
// 路由跳转
|
||||
function _jump(path = "", params = {}, type = 1) {
|
||||
if (!path) {
|
||||
console.error("router::jump>>path 不能为空");
|
||||
}
|
||||
if (hasTabbar(path)) {
|
||||
uni.switchTab({ url: path });
|
||||
return;
|
||||
}
|
||||
let url: string = "";
|
||||
if (Object.keys(params)) {
|
||||
url = `${path}?${paramsToQuery(params)}`;
|
||||
}
|
||||
if (type == 1) {
|
||||
uni.navigateTo({ url: url });
|
||||
return;
|
||||
}
|
||||
if (type == 2) {
|
||||
uni.redirectTo({ url: url });
|
||||
return;
|
||||
}
|
||||
if (type == 3) {
|
||||
uni.reLaunch({ url: url });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 路由返回
|
||||
function _back(page = 1) {
|
||||
if (hasHistory()) {
|
||||
uni.navigateBack({
|
||||
delta: page,
|
||||
});
|
||||
} else {
|
||||
_jump(__TABBAR__[0], {});
|
||||
}
|
||||
}
|
||||
|
||||
export function jump(...args: any) {
|
||||
throttle({ interval: 500 }, () => _jump(...args))();
|
||||
}
|
||||
|
||||
export function back(...args: any) {
|
||||
throttle({ interval: 500 }, () => _back(...args))();
|
||||
}
|
||||
|
||||
export function hasHistory() {
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getCurrentPage() {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1];
|
||||
}
|
||||
|
||||
export function hasTabbar(str: string = "") {
|
||||
if (!str) {
|
||||
const { route } = getCurrentPage();
|
||||
if (route) {
|
||||
str = `/${route}`;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(__TABBAR__) && __TABBAR__.indexOf(str) !== -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
@font-face {
|
||||
font-family: "iconfont";
|
||||
/* Project id 4740692 */
|
||||
src: url('//at.alicdn.com/t/c/font_4740692_ozdkialdak.woff2?t=1731275285379') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_4740692_ozdkialdak.woff?t=1731275285379') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4740692_ozdkialdak.ttf?t=1731275285379') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-xiangxiajiantouxiao:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.icon-jiebang:before {
|
||||
content: "\ee1e";
|
||||
}
|
||||
|
||||
.icon-bangding:before {
|
||||
content: "\ee1f";
|
||||
}
|
||||
|
||||
.icon-youjiantou:before {
|
||||
content: "\e928";
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
@font-face {
|
||||
font-family: 'sheepicon';
|
||||
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAA7QAAsAAAAAH7gAAA6AAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACFGAquMKcBATYCJANYCy4ABCAFhGcHghMbGBszo8LGASAo3ovsvzzgDulfoYNbbIQFfeT6cUXKcnp8h6BAxr+OT0PaNfv10KZBODSIpU3sSo3EFhur478eSgk9tB9t3u5+1bOm2u/adRPIVAjdKiUTIiF5p0G7H4liOw9t80+OPDw84PjYX/CsAiMBxaj6kzAy38TIGQsWkbpo6xcu2kX11lw1dxwop50cColKPds3ntdm7TMQ5O5+/WLKMIHXtiAAiNIW12xIQlaYu4Gc3QMp9L00hYPmlmYUAIc59ZTtJbdsO9j2NSK25QLAc0DyxvKXihAq8ZKMoATv/f9zrzYZAHkEYdZa9V6Swn15H145H9pmnAJChmzHijgpJuV0xK6bIiH5+Bk9o+tkhfLTTSUJvfZo7JSOMoWx6YxH4gujkBDUd70OAql9x8x4+frt0KaW2NHmkBOFuKBmcLpD7kZznJkSu0PZHhMdAphlPpPwmn5/fbYxAiNokCc6ffJsOicffk0Me+0L7OXaXBW0bUAALNhes1htvv+VDWGbyikru3MPQOwrBkZTwKn1Plz08HMihm2VwHXeg0GnV6k1WqWCEwAi/AfPSqQyOTFQh+TKAVpqwGCvSnMxpv/A6ED/g9GDeqxChQlGDSowGlADRgtqwShBQzAK0AgMBxqDEYAmIMjRFATQDATQHASztAChCi1BAK1AAK1BAG3AEGgL8Zz0w7gU0MZTgOiRpBsy5RLFCuaCL5xPM0NODq8d2THAOMI4ubYfquKsiEWTK4eh6dLW43a8zOpckM+TjbnHdU1JVlrnxSTBlFHdSMq3EylMQYzHhjubO3aq7d5GobB13euVUhgAmWHC58lnTFrRDCqvztCU9YlrJOoRWxI1TTQmWeFHZ+NL4AbKEAGCSnvFWkQNs09QG4oOPwoNpWp3Igca3Ijt0enb5js0+TSgbcvz9Tps1wO/MfvfIdQN/VsKcnWwUYOTaKVfTMxPs3q66vjgClvbbTYEuoS6SS5OV0RrAdWvsMVLfXlLmcs1lWJhTSVP8rDHqbV5o6jTHIuLhmAA1flCHbspEhMFy864PMEQhCNNHWbhxhk258Iu6jgCf6sh0ILiPgCqts3YJqh/EKNO8AGsmKhmQdqr9at9GVEMbJSj53lt1snRPWqDSpyZxmMEVc9HzZ0oopAvsoXrKhGBfk3BDIABwH1B02U+xWzmM0Djl1mO4vw3BOHrXBr9hNHDMUQKzjQEqXwuDxZcWHRh2VGXJKqPeJO3/AZkVx9v27Q7tlvHnW6av6EgDxCdwsmuOGqBQEBJBve5cgEh+yxKeSj/iGUn9BMLq5EdXKYuW/OBjXp3vKOnKnHtPV8vhm+bYvfmscPd+A5r7efmxFbv1k97dX6Fwuo5nKA8Wg1unahNCDpxH5f4F1e1Avm+J3bkirM/B1AhHkJb4kpDSglrbCUS6Cqxa1VFyupCJ0dNYejan0vb1A8Ka/myLMlEUoiihO6nZuJk5AaJtJSPvodqG/gaybU0QfZtKrRS3O/bidlgV8Z3pKbI4cCo4yVvkkw7RTJ2UNK77Q9PQhPqxex+dB+QEx7whgM5Y3QV/RGOSYliqX7GSzpdq7OoBVTx2PrTMd1+Wd0fdauWJKmBIx995D0bDjNEGwnjvgBQpdML/A1fy1wmurOjsCdwpCei9v3dXPCw/mp6J3bRRPB9J/BPW50dhLadQYN6abeMHYYgl9xP6njrAKPFNiKR9DGX6VaI2bLFUmKKtfsOj5cmUYXWlagKhkFbOArjBptIBaP1RcxULU6yoVsDKN8lLIfQaRX9PIOvgxNsqg9RSccoe+H7LT3aJsOJLTGSgm4BWABvm1gL3igAG/BBHSyADIgUsk69ltYJBhd7rAa1Xrwt0eiCWKPL3U1zN6i0FvTdrbJ3m2qtoG5oQPdWD0c3QfV5WptcB/F2Srp2aHFbOoYcoXuPuXi3f1HUvfenYkQnUUiOVRNj8+RnXgePrgPANUIc4A4Hg0uvD2/GvuCAuhBH/xE3OqRefM8W68GISmsunTYDetfdZgWGjiCjXjBE6u5JiRBELt7MmrRUTYfm/KSFVmKVIk6ozxbI3d3lguxvgpzOJvfIEXzNGfMeXrXvhea5ZO7ZAtKZsKwE+3geP84hcdE+gR8fx090UCXaS3AUtUX7yDCWl2Avygo7dSpsetpwKhhWgO7PndKvF+9a5hyra1mZohfrXOYk1dWpaGZGnujv/usNLOx3nLOW3wpbC+hGcUd8RsgPEFesSP9ftz/f67st6ZdqBeh7ryItSaFS6Cb0HieoRRQZ6X7g4ZrLDbm5kYGmUXz1yOrUwo91/49qMisALeESc954nrnE2ma67UPDEjplso2V3Z9xJ2EJKLAxM80k0Vih2JxokvYv7944UaFIN04z+ZdhTGyZeixzl7i4SNxl74D64hIXmfu7nF1O4RidtL90C50cGO/ruzVOLjt0yMdfaVD8GJ0QyZFETU42avH0mk6RmTg5FEaePkXaPs5Lc2XdYvXOX6wGfcly7aZvvea2Zfl8ALePB1OVvjRmCPsN7421NaQzdRVUqBeiuhZke1mEdqkhOK4a2Av6KuvYS7wlmyXbd7E9AvQ2y4PbKRHXQ1QuBNHyn9wjcXRSdWBQ2su62Z+SBkECP+MgnuVUW7G4CkBV/HGsTxsbw7T7sLAdtqY2R2tci6M9RrO5Scexfq3SJK1+zFbZWlrcSZX5Ootarw3phjEYpl4sBq3Rt5C2El8Tt5JiiDAlui2OYWsgjI9RlLUwJSXMBq1h79WmD1G5+LRBd2cW0hn/oOO27jPw+NbNTciKF1vT7EpMw4zCLIvfjcTJQhFlclguEcGTGhmXzyCTr5Gx88Wh5zzKNeWacUwR6YAjie5N96eLueLlUHkDwv/qo3RCy3Yc+P/q96nzy6qNdz3K0CpE/MWoJ9qW8FtOppYZh+qGRkEEsFaUJZRnuuV4fBdqr0B+DwCsdeIIvTy3+x4Fdlxpab2yfYqI74MYyszySDnEFIc0x+pDcB6cw5EaZg4RxR3Og51H5XKnjiFk4/v5pmmm+MXTNRUKZzo7hR1Zh4w0VomyPQTZwrxGCL9To3HQ4zzLI0+Ue/ZXlV0rUM1LSeVVtfFqUpKreO3dyGQhGFWBU17W1XEpRbxao0Zq3urstlfJe9IaP3lyunrifTS+J9nU3ywwTmhRCFEEDd1oUjw62X6hVsDulmbA0G5Y0KBApcvYinqv9GoqzgC6UcCx+amf39Hg2akQRc+to3PO4usH+Psd+c07FtKDwmjF8CnlyKmiYo+hdXzfXRAconJWOY2PH1G5qIKD7dAEdoJAQn1qbqGHQkY1wUkVUmWprrMwudrU3KL4PJdSKg9JqjEil1N1Bwss9azyLdfpQsCJhxFIjEGhNJXAwhj1vREaEhiJSY4gUsQ9+u7vaydqL68WPnayDAkgsQauI2EscPrq8qXbxvPGLz9+mrec//r1VryXb52+RvxoXzMfExMfM9/NRu31Mbn1N/eLRvFei4T5+cJGIWVpEublCZuB0KANZSh+F4N77vuD5uhXxFHlmOkI4g1RtX+lLC3kNctDQ53ms8ZkjVSDSAC2F4aFHQnhz2jqm/3w1POwQwlk2sIWGs1Hd+Szv4htPWdQfmNzbeqq1uuBN3jYd59PrYsnu8bf/7NNTu6xHmf0zuoYHQoyVlQyOebh645NtqY2EyzyWEOzNs6cxffvfbvUllafm7131z1FrOQR3BJwD0lG9yf4012g8whSrhMIYRGMFtwNUizHmeDkY9mCjkQ3sxhOCUgixQBcZ5x0ZOtGa2AMP4L/UUfMcr8hu7PCpqPT3sau7BhfJduTHQkKixuENuQH2AQ8/rSSNMHW5dNA2Etm+jtogjx7niSDaTEl4F1ChpcMQpU4sZo0Zp0xVEohtKnErSuyKX9MFCF/ZSwwyF7xdIlYYrfMhkLsb46YYSP20Q2XnLyf2bZXBANC3sIUtA3afiuP5U6eIRoQchemIVsSXc4wezTH27ZxKyMTIdlC0wu5jqwZl+e2Pt4+9jAOmYFR9fUwQ2NGlzBdsoe7p7CEGzbIA5wY52ku+oydLzL3dEyD8UDWCRiILoYB19KhGT//u3DlvUeGtja3054Y3FMdtrhmfeHK5/9TziIy6igWMVcaTL67hukZXi94bHBt70iZWSFcnMOUAFzEfqijsXKWwLanNivfnRXVzu4dLjcth33mYMoM7ANjFYuOPlD6gAUHHoOBqFspk1um/AZdWE3dXt1Cr2qh1nxQarSzqNPCzj/agiljFi/nKw5WzkwfyNjpbUW69vVJjKn4E8YoUOWOBwB/AjwFJtCVwFaBLsp23BqW+T5wP2j/+7PvBn91U9LQbLsbbACIaoK4LUh/BSpA+Hd0W3B/5LsqTcP9I5n613r7t723oWOKzeQthy34HSxJi7+a0x47/I9/yTqIf8yEVd/5UfI/5KRmczL6To5oBqR+Eng7qfV1XVydtPSz2tXFi43IAljW2EVwGLu5OJA5hBBjjxantpz0nuk8A6RMgFXPsVhqbxYbpbdgMfYdOIz9sjgwmiDE2H+LU9cqBpZZjWsLToxa0KjioQJQtHYLvdJvdJdac9HOf+QUsF3t81024xUjsg6bdHUHEassU6NevCfDuibVMpUIknuR9rjdWpZNDtR0ToxalssbpXhsHICi1Zf0z/eN7lJrluT/FU6OYF+5spfbSfCvcpSqcU2Kp6s7EFuxag5spzfKS1fBepRKSsvWSoSRP+craO3R1tezsnLevKu5CGROu1pJ92PYZOUUKFSkWMkvum87V6FSlWrFK0GJ4iVIlJ0BmiDDQqcAA08NDhpinIFHqLI6RJwKLine truncated
|
||||
}
|
||||
|
||||
[class*='sicon-'] {
|
||||
font-family: 'sheepicon';
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sicon-back:before {
|
||||
content: '\e706';
|
||||
}
|
||||
|
||||
.sicon-home:before {
|
||||
content: '\e70c';
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
@import "font/sheepicon";
|
||||
@import "font/iconfont";
|
||||
|
||||
view,
|
||||
text,
|
||||
scroll-view {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
view,
|
||||
text {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
navigator {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
page {
|
||||
// 解决ios滑动不流畅
|
||||
-webkit-overflow-scrolling: touch;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
//英文文本不换行
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
color: transparent;
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './request'
|
||||
@@ -0,0 +1,158 @@
|
||||
import Request from "luch-request";
|
||||
import { isEmpty } from "radash";
|
||||
|
||||
import { userApi } from "@@/api";
|
||||
import { router, constant } from "@@/index";
|
||||
|
||||
const options = {
|
||||
// 显示操作成功消息 默认不显示
|
||||
showSuccess: false,
|
||||
// 成功提醒 默认使用后端返回值
|
||||
successMsg: "",
|
||||
// 显示失败消息 默认显示
|
||||
showError: true,
|
||||
// 失败提醒 默认使用后端返回信息
|
||||
errorMsg: "",
|
||||
// 显示请求时loading模态框 默认显示
|
||||
showLoading: true,
|
||||
// loading提醒文字
|
||||
loadingMsg: "加载中",
|
||||
// 需要授权才能请求 默认放开
|
||||
auth: false,
|
||||
};
|
||||
|
||||
let LoadingInstance = {
|
||||
target: null,
|
||||
count: 0,
|
||||
};
|
||||
|
||||
function closeLoading() {
|
||||
if (LoadingInstance.count > 0) LoadingInstance.count--;
|
||||
if (LoadingInstance.count === 0) uni.hideLoading();
|
||||
}
|
||||
const http = new Request({
|
||||
baseURL: __APP_SERVICE__,
|
||||
timeout: 8000,
|
||||
method: "GET",
|
||||
header: {
|
||||
Accept: "*/*",
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
Authorization: "",
|
||||
"tenant-id": "",
|
||||
},
|
||||
withCredentials: true,
|
||||
custom: options,
|
||||
});
|
||||
|
||||
http.interceptors.request.use(
|
||||
(config) => {
|
||||
const tokenKey = constant.storageConstant.TOKEN;
|
||||
const tokenStorage = uni.getStorageSync(tokenKey);
|
||||
const config_header: any = config?.header;
|
||||
config_header["tenant-id"] = "1";
|
||||
if (tokenStorage && tokenStorage.accessToken && config_header) {
|
||||
config_header.Authorization = tokenStorage.accessToken;
|
||||
}
|
||||
if (config.custom && config.custom.showLoading) {
|
||||
LoadingInstance.count++;
|
||||
LoadingInstance.count === 1 &&
|
||||
uni.showLoading({
|
||||
title: config.custom.loadingMsg,
|
||||
mask: true,
|
||||
fail: () => {
|
||||
uni.hideLoading();
|
||||
},
|
||||
});
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
http.interceptors.response.use(
|
||||
async (response) => {
|
||||
response.config &&
|
||||
response.config.custom &&
|
||||
response.config.custom.showLoading &&
|
||||
closeLoading();
|
||||
if (response.data.code !== 0) {
|
||||
if (
|
||||
response.config &&
|
||||
response.config.custom &&
|
||||
response.config.custom.showError
|
||||
) {
|
||||
switch (response.data.code) {
|
||||
case 401:
|
||||
setTimeout(() => {
|
||||
const tokenKey = constant.storageConstant.TOKEN;
|
||||
const userInfoKey = constant.storageConstant.USERINFO;
|
||||
uni.removeStorageSync(tokenKey);
|
||||
uni.removeStorageSync(userInfoKey);
|
||||
router.jump("/pages/tabbar/my", {}, 2);
|
||||
}, 500);
|
||||
break;
|
||||
case 500:
|
||||
uni.showToast({
|
||||
title: response.data.message || "服务器开小差啦,请稍后再试~",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
break;
|
||||
}
|
||||
return Promise.resolve(response.data);
|
||||
}
|
||||
}
|
||||
if (
|
||||
response.data.code === 200 &&
|
||||
response.data.message !== "" &&
|
||||
response.config &&
|
||||
response.config.custom &&
|
||||
response.config.custom.showSuccess
|
||||
) {
|
||||
uni.showToast({
|
||||
title: response.config.custom.successMsg || response.data.message,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
const tokenKey = constant.storageConstant.TOKEN;
|
||||
const tokenStorage = uni.getStorageSync(tokenKey);
|
||||
if (
|
||||
!isEmpty(tokenStorage) &&
|
||||
response.config.url != "/sys/auth/refresh-token"
|
||||
) {
|
||||
const { refreshToken, expiresTime } = tokenStorage;
|
||||
const now = new Date().getTime();
|
||||
const remain = expiresTime - now;
|
||||
if (remain <= 1000 * 60 * 20) {
|
||||
const _pa = { refreshToken: refreshToken };
|
||||
const refreshData = await userApi.refreshToken(_pa);
|
||||
console.log(refreshData, "refreshData");
|
||||
if (!isEmpty(refreshData.data)) {
|
||||
uni.setStorageSync(tokenKey, refreshData.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(response.data);
|
||||
},
|
||||
(error) => {
|
||||
let errorMessage = "";
|
||||
if (error !== undefined) {
|
||||
if (errorMessage) {
|
||||
uni.showToast({
|
||||
title: errorMessage || "",
|
||||
icon: "none",
|
||||
mask: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
const request = (config: any) => {
|
||||
return http.middleware(config);
|
||||
};
|
||||
|
||||
export default request;
|
||||
Reference in new issue
Block a user