Browse Source

修改bug

master
岳鹏龙 7 months ago
parent
commit
37623bdb47
  1. 9
      src/api/shop.ts
  2. 303
      src/pages/index/active/activeDetail.vue
  3. 3
      src/pages/index/bnbdetail.vue
  4. 1599
      src/pages/index/index.vue
  5. 2
      src/pages/index/shoppage/annualReport.vue
  6. 25
      src/pages/index/shoppage/interactiveZone.vue
  7. 3
      src/pages/index/shoppage/jrbDetail.vue
  8. 12
      src/pages/index/shoppage/makeAssistance.vue
  9. 3
      src/pages/index/shoppage/mediaDetail.vue
  10. 21
      src/pages/index/shoppage/mediaHelp.vue
  11. 3
      src/pages/index/shoppage/qyDetail.vue
  12. 20
      src/pages/index/shoppage/qyHelp.vue
  13. 3
      src/pages/index/shoppage/qyhelpDetail.vue
  14. 161
      src/pages/index/shoppage/shopHelp.vue
  15. 3
      src/pages/index/shoppage/shophelpDetail.vue
  16. 3
      src/pages/index/ztdetail.vue
  17. 2
      src/pages/learningCenter/index.vue
  18. BIN
      src/static/img/nianb.png
  19. 13
      src/uni_modules/uv-parse/changelog.md
  20. 576
      src/uni_modules/uv-parse/components/uv-parse/node/node.vue
  21. 1335
      src/uni_modules/uv-parse/components/uv-parse/parser.js
  22. 498
      src/uni_modules/uv-parse/components/uv-parse/uv-parse.vue
  23. 87
      src/uni_modules/uv-parse/package.json
  24. 21
      src/uni_modules/uv-parse/readme.md
  25. 224
      src/uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js
  26. 19
      src/uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js
  27. 1
      src/uni_modules/uv-parse/static/app-plus/uv-parse/local.html
  28. 76
      src/uni_modules/uv-ui-tools/changelog.md
  29. 6
      src/uni_modules/uv-ui-tools/components/uv-ui-tools/uv-ui-tools.vue
  30. 79
      src/uni_modules/uv-ui-tools/index.js
  31. 7
      src/uni_modules/uv-ui-tools/index.scss
  32. 34
      src/uni_modules/uv-ui-tools/libs/config/config.js
  33. 32
      src/uni_modules/uv-ui-tools/libs/css/color.scss
  34. 100
      src/uni_modules/uv-ui-tools/libs/css/common.scss
  35. 23
      src/uni_modules/uv-ui-tools/libs/css/components.scss
  36. 111
      src/uni_modules/uv-ui-tools/libs/css/variable.scss
  37. 40
      src/uni_modules/uv-ui-tools/libs/css/vue.scss
  38. 134
      src/uni_modules/uv-ui-tools/libs/function/colorGradient.js
  39. 29
      src/uni_modules/uv-ui-tools/libs/function/debounce.js
  40. 167
      src/uni_modules/uv-ui-tools/libs/function/digit.js
  41. 734
      src/uni_modules/uv-ui-tools/libs/function/index.js
  42. 75
      src/uni_modules/uv-ui-tools/libs/function/platform.js
  43. 287
      src/uni_modules/uv-ui-tools/libs/function/test.js
  44. 30
      src/uni_modules/uv-ui-tools/libs/function/throttle.js
  45. 132
      src/uni_modules/uv-ui-tools/libs/luch-request/adapters/index.js
  46. 51
      src/uni_modules/uv-ui-tools/libs/luch-request/core/InterceptorManager.js
  47. 201
      src/uni_modules/uv-ui-tools/libs/luch-request/core/Request.js
  48. 20
      src/uni_modules/uv-ui-tools/libs/luch-request/core/buildFullPath.js
  49. 33
      src/uni_modules/uv-ui-tools/libs/luch-request/core/defaults.js
  50. 6
      src/uni_modules/uv-ui-tools/libs/luch-request/core/dispatchRequest.js
  51. 126
      src/uni_modules/uv-ui-tools/libs/luch-request/core/mergeConfig.js
  52. 16
      src/uni_modules/uv-ui-tools/libs/luch-request/core/settle.js
  53. 64
      src/uni_modules/uv-ui-tools/libs/luch-request/helpers/buildURL.js
  54. 14
      src/uni_modules/uv-ui-tools/libs/luch-request/helpers/combineURLs.js
  55. 14
      src/uni_modules/uv-ui-tools/libs/luch-request/helpers/isAbsoluteURL.js
  56. 197
      src/uni_modules/uv-ui-tools/libs/luch-request/index.d.ts
  57. 2
      src/uni_modules/uv-ui-tools/libs/luch-request/index.js
  58. 135
      src/uni_modules/uv-ui-tools/libs/luch-request/utils.js
  59. 264
      src/uni_modules/uv-ui-tools/libs/luch-request/utils/clone.js
  60. 13
      src/uni_modules/uv-ui-tools/libs/mixin/button.js
  61. 172
      src/uni_modules/uv-ui-tools/libs/mixin/mixin.js
  62. 8
      src/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js
  63. 13
      src/uni_modules/uv-ui-tools/libs/mixin/mpShare.js
  64. 47
      src/uni_modules/uv-ui-tools/libs/mixin/openType.js
  65. 59
      src/uni_modules/uv-ui-tools/libs/mixin/touch.js
  66. 216
      src/uni_modules/uv-ui-tools/libs/util/dayjs.js
  67. 126
      src/uni_modules/uv-ui-tools/libs/util/route.js
  68. 81
      src/uni_modules/uv-ui-tools/package.json
  69. 23
      src/uni_modules/uv-ui-tools/readme.md
  70. 43
      src/uni_modules/uv-ui-tools/theme.scss

9
src/api/shop.ts

@ -25,6 +25,15 @@ export function storeInfo(id: string) {
}) })
} }
//开店帮分类内容列表
export function storemainList(data: any) {
return request.http({
url: '/api/huiqitong/store/list',
method: 'GET',
data
})
}
//金融帮信息列表 //金融帮信息列表
export function financeList(data: any) { export function financeList(data: any) {
return request.http({ return request.http({

303
src/pages/index/active/activeDetail.vue

@ -1,24 +1,24 @@
<template> <template>
<image style="width: 100%; height: 352rpx" :src="baseurl + detaildata.index_pic" mode=""></image> <image style="width: 100%; height: 352rpx" :src="baseurl + detaildata.index_pic" mode=""></image>
<view class="container" :style="{height: detaildata.form?'calc(100vh - 552rpx)': 'calc(100vh - 352rpx)'}"> <view class="container" :style="{height: detaildata.form?'calc(100vh - 552rpx)': 'calc(100vh - 352rpx)'}">
<text class="headtxt">{{ detaildata.title }}</text> <text class="headtxt">{{ detaildata.title }}</text>
<text class="address">活动地点{{ detaildata.address }}</text> <text class="address">活动地点{{ detaildata.address }}</text>
<view class="adressnum"> <view class="adressnum">
<text class="time">活动时间{{ detaildata.activity_time }}</text> <text class="time">活动时间{{ detaildata.activity_time }}</text>
<view class="ydl"> <view class="ydl">
<text class="time">阅读量</text> <text class="time">阅读量</text>
<text class="value"> <text class="value">
{{ detaildata.count }} {{ detaildata.count }}
</text> </text>
</view> </view>
</view> </view>
<view class="titlepart"> <view class="titlepart">
<view class="icon"></view> <view class="icon"></view>
<text class="title">活动详情</text> <text class="title">活动详情</text>
</view> </view>
<u-parse :content="detaildata.content"></u-parse> <uvparse :content="detaildata.content"></uvparse>
<text class="certetime">{{ detaildata.create_time }}</text> <text class="certetime">{{ detaildata.create_time }}</text>
</view> </view>
<view class="baombut" v-if="detaildata.form"> <view class="baombut" v-if="detaildata.form">
<view class="buttt" @click="gotij(detaildata.id, detaildata.form)"> <view class="buttt" @click="gotij(detaildata.id, detaildata.form)">
立即报名 立即报名
@ -27,140 +27,153 @@
</template> </template>
<script setup> <script setup>
import { onLoad } from '@dcloudio/uni-app' import {
import { ref } from 'vue' onLoad
import { activityInfo } from '@/api/index' } from '@dcloudio/uni-app'
import {
ref
} from 'vue'
import {
activityInfo
} from '@/api/index'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/') const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/')
const detaildata = ref({}) const detaildata = ref({})
const gotij = (id,item) => { const gotij = (id, item) => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/index/subform?form='+JSON.stringify(item)+ '&id='+id +'&type='+'hd' url: '/pages/index/subform?form=' + JSON.stringify(item) + '&id=' + id + '&type=' + 'hd'
}) })
} }
onLoad(async (param) => {
await activityInfo(param.id).then((res) => { onLoad(async (param) => {
if (res.code === 1) { await activityInfo(param.id).then((res) => {
detaildata.value = res.data if (res.code === 1) {
} detaildata.value = res.data
}) }
}) })
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.container { .container {
background-color: #ffffff; background-color: #ffffff;
height: calc(100vh - 552rpx); height: calc(100vh - 552rpx);
width: 100%; width: 100%;
padding: 40rpx 40rpx 0 40rpx; padding: 40rpx 40rpx 0 40rpx;
box-sizing: border-box; box-sizing: border-box;
overflow-y: auto; overflow-y: auto;
.titlepart { .titlepart {
margin-top: 40rpx; margin-top: 40rpx;
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 30rpx; margin-bottom: 30rpx;
.icon { .icon {
width: 12rpx; width: 12rpx;
height: 32rpx; height: 32rpx;
border-radius: 0rpx 32rpx 32rpx 0rpx; border-radius: 0rpx 32rpx 32rpx 0rpx;
/* 蓝色渐变 */ /* 蓝色渐变 */
background: linear-gradient(0deg, #007fff 0%, #99ccff 100%); background: linear-gradient(0deg, #007fff 0%, #99ccff 100%);
} }
.title { .title {
margin-left: 18rpx; margin-left: 18rpx;
font-family: Source Han Sans; font-family: Source Han Sans;
font-size: 30rpx; font-size: 30rpx;
font-weight: 350; font-weight: 350;
line-height: 32rpx; line-height: 32rpx;
text-align: justify; text-align: justify;
/* 浏览器可能不支持 */ /* 浏览器可能不支持 */
display: flex; display: flex;
align-items: center; align-items: center;
letter-spacing: normal; letter-spacing: normal;
color: #0072ff; color: #0072ff;
} }
} }
.headtxt { .headtxt {
font-family: Source Han Sans; font-family: Source Han Sans;
font-size: 36rpx; font-size: 36rpx;
font-weight: normal; font-weight: normal;
line-height: 48rpx; line-height: 48rpx;
display: flex; display: flex;
align-items: center; align-items: center;
letter-spacing: normal; letter-spacing: normal;
color: #0c092a; color: #0c092a;
} }
.address { .address {
margin-top: 28rpx; margin-top: 28rpx;
font-family: Source Han Sans; font-family: Source Han Sans;
font-size: 28rpx; font-size: 28rpx;
font-weight: 350; font-weight: 350;
line-height: 48rpx; line-height: 48rpx;
display: flex; display: flex;
align-items: center; align-items: center;
letter-spacing: normal; letter-spacing: normal;
color: #858494; color: #858494;
} }
.adressnum { .adressnum {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
.time { .time {
font-family: Source Han Sans; font-family: Source Han Sans;
font-size: 28rpx; font-size: 28rpx;
font-weight: 350; font-weight: 350;
line-height: 48rpx; line-height: 48rpx;
display: flex; display: flex;
align-items: center; align-items: center;
letter-spacing: normal; letter-spacing: normal;
color: #858494; color: #858494;
} }
.ydl {
display: flex; .ydl {
align-items: center; display: flex;
.time { align-items: center;
font-family: Source Han Sans;
font-size: 28rpx; .time {
font-weight: 350; font-family: Source Han Sans;
line-height: 48rpx; font-size: 28rpx;
display: flex; font-weight: 350;
align-items: center; line-height: 48rpx;
letter-spacing: normal; display: flex;
color: #858494; align-items: center;
} letter-spacing: normal;
.value { color: #858494;
font-family: Source Han Sans; }
font-size: 28rpx;
font-weight: 350; .value {
line-height: 48rpx; font-family: Source Han Sans;
text-align: right; font-size: 28rpx;
display: flex; font-weight: 350;
align-items: center; line-height: 48rpx;
letter-spacing: normal; text-align: right;
color: #007fff; display: flex;
} align-items: center;
} letter-spacing: normal;
} color: #007fff;
.certetime { }
font-family: Roboto; }
font-size: 28rpx; }
font-weight: normal;
line-height: 80rpx; .certetime {
letter-spacing: normal; font-family: Roboto;
color: #a1a1a1; font-size: 28rpx;
} font-weight: normal;
} line-height: 80rpx;
.baombut { letter-spacing: normal;
color: #a1a1a1;
}
}
.baombut {
width: 100%; width: 100%;
padding: 36rpx 0; padding: 36rpx 0;
display: flex; display: flex;

3
src/pages/index/bnbdetail.vue

@ -8,7 +8,7 @@
<text class="title">{{ titleData.title }}</text> <text class="title">{{ titleData.title }}</text>
</view> </view>
<!-- <image style="width: 100%;height: 436rpx;margin-top: 48rpx;" src="@/static/img/Bitmap.png" mode=""></image> --> <!-- <image style="width: 100%;height: 436rpx;margin-top: 48rpx;" src="@/static/img/Bitmap.png" mode=""></image> -->
<rich-text :nodes="titleData.content"></rich-text> <uvparse :content="titleData.content"></uvparse>
<view class="fbtime">{{ titleData.create_time }}</view> <view class="fbtime">{{ titleData.create_time }}</view>
</view> </view>
</template> </template>
@ -23,6 +23,7 @@
import { import {
ReportDetail ReportDetail
} from '@/api/shop' } from '@/api/shop'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const titleData = ref([]) const titleData = ref([])

1599
src/pages/index/index.vue

File diff suppressed because it is too large

2
src/pages/index/shoppage/annualReport.vue

@ -7,7 +7,7 @@
<view class="main"> <view class="main">
<scroll-view scroll-y="auto" class="hyonne" @scrolltolower="onloadmore"> <scroll-view scroll-y="auto" class="hyonne" @scrolltolower="onloadmore">
<view class="ztone" v-for="(item, index) in ztList" :key="index" @click="godetail(item.id)"> <view class="ztone" v-for="(item, index) in ztList" :key="index" @click="godetail(item.id)">
<image style="width: 80rpx; height: 80rpx; flex: 1" src="@/static/img/nianb.png" mode="widthFix"></image> <image style="width: 120rpx; height: 120rpx;" src="@/static/img/nianb.png" mode="scaleToFill"></image>
<view class="rightpart"> <view class="rightpart">
<view class="splace"> <view class="splace">
{{ item.title }} {{ item.title }}

25
src/pages/index/shoppage/interactiveZone.vue

@ -84,14 +84,20 @@ const list1 = reactive([
{ {
name: '企业帮', name: '企业帮',
id: 4 id: 4
},
{
name: '法律帮',
id: 5
} }
]) ])
const typeid = ref(1) const typeid = ref(0)
const typename = ref('')
// //
const click = async (item) => { const click = async (item) => {
page.value = 1 page.value = 1
typeid.value = item.id typeid.value = item.id
typename.value = item.name
datalist.value = [] datalist.value = []
await getpostsList() await getpostsList()
} }
@ -124,7 +130,7 @@ const scrolltolower = async () => {
const goxzhdzq = () => { const goxzhdzq = () => {
uni.navigateTo({ uni.navigateTo({
url: '/pages/index/shoppage/makeAssistance' url: '/pages/index/shoppage/makeAssistance?id='+typeid.value+'&name='+typename.value
}) })
} }
@ -170,11 +176,24 @@ const pldzClick = (postid, like, id) => {
} }
onLoad(async (e) => { onLoad(async (e) => {
typeid.value = list1[e.tabid].id
typename.value = list1[e.tabid].name
current.value = e.tabid current.value = e.tabid
page.value = 1 page.value = 1
datalist.value = [] datalist.value = []
await getpostsList() await getpostsList()
console.log(userStore.cometype) })
onShow(async()=> {
if(datalist.value.length !== 0) {
page.value = 1
datalist.value = []
await postsList({ page: page.value, limit: limit.value, type_id: typeid.value }).then((res) => {
if (res.code === 1) {
datalist.value = [...datalist.value, ...res.data.data]
}
})
}
}) })
</script> </script>

3
src/pages/index/shoppage/jrbDetail.vue

@ -6,7 +6,7 @@
<text class="title">产品说明</text> <text class="title">产品说明</text>
</view> </view>
<image style="width: 100%; height: 286rpx; margin-top: 40rpx" :src="baseurl + listdata.index_pic" mode=""></image> <image style="width: 100%; height: 286rpx; margin-top: 40rpx" :src="baseurl + listdata.index_pic" mode=""></image>
<u-parse :content="listdata.content"></u-parse> <uvparse :content="listdata.content"></uvparse>
<view class="titlepart"> <view class="titlepart">
<view class="icon"></view> <view class="icon"></view>
<text class="title">联系方式</text> <text class="title">联系方式</text>
@ -26,6 +26,7 @@
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue' import { ref } from 'vue'
import { financeInfo } from '@/api/shop' import { financeInfo } from '@/api/shop'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/') const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/')

12
src/pages/index/shoppage/makeAssistance.vue

@ -44,7 +44,7 @@ import { addPosts } from '@/api/shop'
const list = ref([ const list = ref([
{ {
name: '店帮', name: '店帮',
id: 1 id: 1
}, },
{ {
@ -58,6 +58,10 @@ const list = ref([
{ {
name: '企业帮', name: '企业帮',
id: 4 id: 4
},
{
name: '法律帮',
id: 5
} }
]) ])
@ -93,6 +97,12 @@ const submit = () => {
}) })
} }
} }
onLoad((e)=> {
console.log(e);
xzid.value = e.id
xzvalue.value = e.name
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

3
src/pages/index/shoppage/mediaDetail.vue

@ -14,7 +14,7 @@
<text class="title">{{ userData.introduce ? '个人介绍' : '助企案例' }}</text> <text class="title">{{ userData.introduce ? '个人介绍' : '助企案例' }}</text>
</view> </view>
<text class="message" v-if="userData.introduce">{{ userData.introduce }}</text> <text class="message" v-if="userData.introduce">{{ userData.introduce }}</text>
<u-parse v-if="userData.content" :content="userData.content"></u-parse> <uvparse v-if="userData.content" :content="userData.content"></uvparse>
<view class="titlepart"> <view class="titlepart">
<view class="icon"></view> <view class="icon"></view>
<text class="title">联系方式</text> <text class="title">联系方式</text>
@ -48,6 +48,7 @@ import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue' import { ref } from 'vue'
import { memberCenterInfo } from '@/api/memberCenter' import { memberCenterInfo } from '@/api/memberCenter'
import { mediumInfo } from '@/api/shop' import { mediumInfo } from '@/api/shop'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/') const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/')

21
src/pages/index/shoppage/mediaHelp.vue

@ -2,8 +2,8 @@
<view class="container"> <view class="container">
<u-navbar title="媒体帮" placeholder="true" bg-color="#F1F3F9" :auto-back="true"></u-navbar> <u-navbar title="媒体帮" placeholder="true" bg-color="#F1F3F9" :auto-back="true"></u-navbar>
<u-search @search="search" @clickIcon="clickIcon" @clear="clear" shape="square" placeholder="请输入搜索内容" <u-search @search="search" @clickIcon="clickIcon" @clear="clear" shape="square" placeholder="请输入搜索内容"
placeholder-color="#a4c7ff" v-model="keyword" search-icon="/static/img/search.png" search-icon-size="14" placeholder-color="#A9D4FF" v-model="keyword" search-icon="/static/img/search.png" search-icon-size="14"
:show-action="false" height="40" margin="40rpx 24rpx 24rpx 24rpx"></u-search> :show-action="false" height="40" margin="40rpx 24rpx 24rpx 24rpx" bg-color="#FFFFFF"></u-search>
<scroll-view scroll-y="auto" class="mtlist" @scrolltolower="onloadmore"> <scroll-view scroll-y="auto" class="mtlist" @scrolltolower="onloadmore">
<view class="mtone" v-for="(item, index) in mtList" :key="index" @click="gomtbdetail(item.id)"> <view class="mtone" v-for="(item, index) in mtList" :key="index" @click="gomtbdetail(item.id)">
<view class="headpart"> <view class="headpart">
@ -51,6 +51,8 @@
mediumList mediumList
} from '@/api/shop' } from '@/api/shop'
import liuDragButton from '@/uni_modules/liu-drag-button/components/liu-drag-button/liu-drag-button.vue' import liuDragButton from '@/uni_modules/liu-drag-button/components/liu-drag-button/liu-drag-button.vue'
import useUserStore from '@/store/user'
const userStore = useUserStore()
const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/') const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/')
@ -92,9 +94,18 @@
} }
const gofbxx = () => { const gofbxx = () => {
uni.navigateTo({ console.log(userStore.userInfo);
url: '/pages/index/shoppage/mtbmakeMessage' if(userStore.userInfo.moblie) {
}) uni.navigateTo({
url: '/pages/index/shoppage/mtbmakeMessage'
})
} else {
uni.showToast({
title: '请登录后操作!',
icon:'error'
})
}
} }
const getmediumList = async (name, page, limit) => { const getmediumList = async (name, page, limit) => {

3
src/pages/index/shoppage/qyDetail.vue

@ -14,7 +14,7 @@
<view class="icon"></view> <view class="icon"></view>
<text class="title">个人介绍</text> <text class="title">个人介绍</text>
</view> </view>
<u-parse v-if="userData.introduction" :content="userData.introduction"></u-parse> <uvparse v-if="userData.introduction" :content="userData.introduction"></uvparse>
<view class="titlepart"> <view class="titlepart">
<view class="icon"></view> <view class="icon"></view>
<text class="title">联系电话</text> <text class="title">联系电话</text>
@ -43,6 +43,7 @@
import { import {
enterpriseInfo enterpriseInfo
} from '@/api/shop' } from '@/api/shop'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/') const baseurl = ref(import.meta.env.VITE_APP_BASE_URL + '/')

20
src/pages/index/shoppage/qyHelp.vue

@ -2,9 +2,9 @@
<u-navbar title="企业帮" placeholder="true" bg-color="#F1F3F9" :auto-back="true"></u-navbar> <u-navbar title="企业帮" placeholder="true" bg-color="#F1F3F9" :auto-back="true"></u-navbar>
<view class="container"> <view class="container">
<u-search @search="search" @clickIcon="clickIcon" shape="square" placeholder="请输入搜索内容" <u-search @search="search" @clickIcon="clickIcon" shape="square" placeholder="请输入搜索内容"
placeholder-color="#a4c7ff" v-model="keyword" search-icon="/static/img/search.png" search-icon-size="14" placeholder-color="#A9D4FF" v-model="keyword" search-icon="/static/img/search.png" search-icon-size="14"
:show-action="false" height="40" margin="40rpx 24rpx 24rpx 24rpx"></u-search> :show-action="false" height="40" margin="40rpx 24rpx 24rpx 24rpx"></u-search>
<zh-scroll ref="zhscrollRel" :scroll-list="goods" :mote="'two'" :search-val="keyword" @tabId="lefttabId"> <zh-scroll ref="zhscrollRel" :scroll-list="goods" :mote="'two'" @tabId="lefttabId">
<scroll-view scroll-y class="reightpp" @scrolltolower="onloadmore" v-if="current === 1"> <scroll-view scroll-y class="reightpp" @scrolltolower="onloadmore" v-if="current === 1">
<view class="ztone" v-for="(item, index) in rightData" :key="index" @click="godetail(item.id)"> <view class="ztone" v-for="(item, index) in rightData" :key="index" @click="godetail(item.id)">
<image style="width: 80rpx; height: 80rpx; flex: 1;border-radius: 16rpx;" <image style="width: 80rpx; height: 80rpx; flex: 1;border-radius: 16rpx;"
@ -18,7 +18,7 @@
<image style="width: 32rpx; height: 44rpx" src="@/static/img/Icon.png" mode="aspectFill"></image> <image style="width: 32rpx; height: 44rpx" src="@/static/img/Icon.png" mode="aspectFill"></image>
</view> </view>
</scroll-view> </scroll-view>
<scroll-view scroll-y class="reightpp" v-else-if="current === 2"> <scroll-view scroll-y class="reightpp" @scrolltolower="onloadmore" v-else-if="current === 2">
<view class="wkbox"> <view class="wkbox">
<view class="wkone" v-for="(item, index) in rightData" :key="index"> <view class="wkone" v-for="(item, index) in rightData" :key="index">
<view class="tittt"> <view class="tittt">
@ -36,7 +36,7 @@
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
<scroll-view scroll-y class="reightpp" v-else> <scroll-view scroll-y class="reightpp" @scrolltolower="onloadmore" v-else>
<view class="list"> <view class="list">
<!-- category begin --> <!-- category begin -->
<view class="category"> <view class="category">
@ -124,9 +124,15 @@
} }
const gohdzq = () => { const gohdzq = () => {
uni.navigateTo({ if(current.value === 1) {
url: '/pages/index/shoppage/interactiveZone?tabid=' + 3 uni.navigateTo({
}) url: '/pages/index/shoppage/interactiveZone?tabid=' + 4
})
} else {
uni.navigateTo({
url: '/pages/index/shoppage/interactiveZone?tabid=' + 3
})
}
} }
const lefttabId = (id) => { const lefttabId = (id) => {

3
src/pages/index/shoppage/qyhelpDetail.vue

@ -14,7 +14,7 @@
<view class="icon"></view> <view class="icon"></view>
<text class="nrtitle">内容详情</text> <text class="nrtitle">内容详情</text>
</view> </view>
<u-parse :content="xdbData.content"></u-parse> <uvparse :content="xdbData.content"></uvparse>
</view> </view>
</template> </template>
@ -22,6 +22,7 @@
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue' import { ref } from 'vue'
import { enterpriseInfo } from '@/api/shop' import { enterpriseInfo } from '@/api/shop'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const xdbData = ref({}) const xdbData = ref({})

161
src/pages/index/shoppage/shopHelp.vue

@ -2,9 +2,35 @@
<u-navbar title="开店帮" placeholder="true" bg-color="#F1F3F9" :auto-back="true"></u-navbar> <u-navbar title="开店帮" placeholder="true" bg-color="#F1F3F9" :auto-back="true"></u-navbar>
<view class="container"> <view class="container">
<u-search @search="search" @clickIcon="clickIcon" shape="square" placeholder="请输入搜索内容" <u-search @search="search" @clickIcon="clickIcon" shape="square" placeholder="请输入搜索内容"
placeholder-color="#a4c7ff" v-model="keyword" search-icon="/static/img/search.png" search-icon-size="14" placeholder-color="#A9D4FF" v-model="keyword" search-icon="/static/img/search.png" search-icon-size="14"
:show-action="false" height="40" margin="40rpx 24rpx 24rpx 24rpx"></u-search> :show-action="false" height="40" margin="40rpx 24rpx 24rpx 24rpx"></u-search>
<zh-scroll ref="zhscrollRel" :scroll-list="goods" :mote="'one'" :search-val="keyword"></zh-scroll> <zh-scroll ref="zhscrollRel" :scroll-list="goods" :mote="'two'" @tabId="lefttabId">
<scroll-view scroll-y class="reightpp" @scrolltolower="onloadmore">
<view class="list">
<!-- category begin -->
<view class="category">
<view class="items">
<!-- 商品 begin -->
<view class="good" v-for="(good, key) in rightData" :key="key" @click="goxdbdetail(good.id)">
<slot name="custom">
<!-- <image :src="good.images" class="image"></image> -->
<!-- <view class="title"> -->
<!-- <image :src="baseurl+good.icon_path" class="icon"></image> -->
<text class="name">{{ good.title }}</text>
<!-- </view> -->
<image class="lefticon" src="@/static/img/icon2.png" mode=""></image>
<!-- <view class="right"> -->
<!-- <text class="tips">{{ good.content }}</text> -->
<!-- </view> -->
</slot>
</view>
<!-- 商品 end -->
</view>
</view>
<!-- category end -->
</view>
</scroll-view>
</zh-scroll>
</view> </view>
<liuDragButton :width-px="'145rpx'"> <liuDragButton :width-px="'145rpx'">
<view class="boxhd" @click="gohdzq"> <view class="boxhd" @click="gohdzq">
@ -22,7 +48,8 @@
import zhScroll from '@/components/zh-scroll/zh-scroll.vue' import zhScroll from '@/components/zh-scroll/zh-scroll.vue'
import { import {
storeList storeList,
storemainList
} from '@/api/shop' } from '@/api/shop'
import liuDragButton from '@/uni_modules/liu-drag-button/components/liu-drag-button/liu-drag-button.vue' import liuDragButton from '@/uni_modules/liu-drag-button/components/liu-drag-button/liu-drag-button.vue'
@ -31,14 +58,56 @@
// //
const keyword = ref('') const keyword = ref('')
const current = ref(1)
const page = ref(1)
const limit = ref(8)
const goods = ref([]) const goods = ref([])
const rightData = ref([])
const search = (val) => { const search = (val) => {
zhscrollRel.value.searchleMenuTap(keyword.value) page.value = 1
limit.value = 8
getRightdata()
} }
const clickIcon = () => { const clickIcon = () => {
zhscrollRel.value.searchleMenuTap(keyword.value) page.value = 1
limit.value = 8
getRightdata()
}
const lefttabId = (id) => {
keyword.value = ''
page.value = 1
limit.value = 8
current.value = id
getRightdata()
}
const onloadmore = async () => {
page.value++
let params = {
store_category_id: current.value,
title: keyword.value,
page: page.value,
limit: limit.value
}
await storemainList(params).then((res) => {
rightData.value = [...rightData.value, ...res.data.data]
})
}
const getRightdata = async () => {
let param = {
store_category_id: current.value,
title: keyword.value,
page: page.value,
limit: limit.value
}
await storemainList(param).then((res) => {
rightData.value = res.data.data
})
} }
const gohdzq = () => { const gohdzq = () => {
@ -47,10 +116,17 @@
}) })
} }
onShow(async () => { const goxdbdetail = (id) => {
uni.navigateTo({
url: '/pages/index/shoppage/shophelpDetail?id=' + id
})
}
onLoad(async () => {
await storeList({}).then((res) => { await storeList({}).then((res) => {
goods.value = res.data goods.value = res.data
}) })
await getRightdata()
}) })
</script> </script>
@ -87,4 +163,77 @@
margin-right: -14rpx; margin-right: -14rpx;
} }
} }
.reightpp {
width: 100%;
padding: 30rpx;
box-sizing: border-box;
gap: 20rpx;
.list {
width: 100%;
font-size: 28rpx;
padding-bottom: 130rpx;
.category {
width: 100%;
.title {
padding: 10rpx 0;
display: flex;
align-items: center;
color: #0c092a;
.icon {
width: 38rpx;
height: 38rpx;
}
.fristtitle {
margin-left: 20rpx;
font-family: Source Han Sans;
font-size: 28rpx;
font-weight: 530;
line-height: 48rpx;
display: flex;
align-items: center;
letter-spacing: normal;
color: #0c092a;
}
}
}
.items {
display: flex;
flex-direction: column;
padding-bottom: -30rpx;
:deep(.good) {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30rpx;
padding-left: 40rpx;
.name {
max-width: 100%;
margin-left: 10rpx;
font-family: Source Han Sans;
font-size: 28rpx;
font-weight: 300;
color: #606266;
overflow: hidden; /* 隐藏溢出内容 */
white-space: nowrap; /* 禁止换行 */
text-overflow: ellipsis; /* 显示省略号 */
}
.icon {
width: 38rpx;
height: 38rpx;
}
.lefticon {
width: 16rpx;
height: 28rpx;
margin-right: 6rpx;
}
}
}
}
}
</style> </style>

3
src/pages/index/shoppage/shophelpDetail.vue

@ -14,7 +14,7 @@
<view class="icon"></view> <view class="icon"></view>
<text class="nrtitle">内容详情</text> <text class="nrtitle">内容详情</text>
</view> </view>
<u-parse :content="xdbData.content"></u-parse> <uvparse :content="xdbData.content"></uvparse>
</view> </view>
</template> </template>
@ -22,6 +22,7 @@
import { onLoad } from '@dcloudio/uni-app' import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue' import { ref } from 'vue'
import { storeInfo } from '@/api/shop' import { storeInfo } from '@/api/shop'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const xdbData = ref({}) const xdbData = ref({})

3
src/pages/index/ztdetail.vue

@ -8,7 +8,7 @@
<text class="title">{{ titleData.title }}</text> <text class="title">{{ titleData.title }}</text>
</view> </view>
<!-- <image style="width: 100%;height: 436rpx;margin-top: 48rpx;" src="@/static/img/Bitmap.png" mode=""></image> --> <!-- <image style="width: 100%;height: 436rpx;margin-top: 48rpx;" src="@/static/img/Bitmap.png" mode=""></image> -->
<rich-text :nodes="titleData.content"></rich-text> <uvparse :content="titleData.content"></uvparse>
<view class="fbtime">{{ titleData.create_time }}</view> <view class="fbtime">{{ titleData.create_time }}</view>
</view> </view>
<view class="baombut" v-if="titleData.form"> <view class="baombut" v-if="titleData.form">
@ -29,6 +29,7 @@
specialListInfo, specialListInfo,
bannerInfo bannerInfo
} from '@/api/index' } from '@/api/index'
import uvparse from '@/uni_modules/uv-parse/components/uv-parse/uv-parse.vue'
const titleData = ref([]) const titleData = ref([])

2
src/pages/learningCenter/index.vue

@ -48,7 +48,7 @@
@clear="clear" @clear="clear"
shape="square" shape="square"
placeholder="请输入搜索内容" placeholder="请输入搜索内容"
placeholder-color="#088cff" placeholder-color="#A9D4FF"
v-model="keyword" v-model="keyword"
search-icon="/static/img/search.png" search-icon="/static/img/search.png"
search-icon-size="14" search-icon-size="14"

BIN
src/static/img/nianb.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 B

After

Width:  |  Height:  |  Size: 2.3 KiB

13
src/uni_modules/uv-parse/changelog.md

@ -0,0 +1,13 @@
## 1.0.4(2023-07-17)
1. 优化文档
2. 优化其他
## 1.0.3(2023-06-19)
1. 修复nvue模式下不显示的BUG
## 1.0.2(2023-06-02)
1. 修复可能存在的BUG
2. 优化
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-parse 富文本解析器

576
src/uni_modules/uv-parse/components/uv-parse/node/node.vue

@ -0,0 +1,576 @@
<template>
<view :id="attrs.id" :class="'_block _'+name+' '+attrs.class" :style="attrs.style">
<block v-for="(n, i) in childs" v-bind:key="i">
<!-- 图片 -->
<!-- 占位图 -->
<image v-if="n.name==='img'&&!n.t&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
<!-- 显示图片 -->
<!-- #ifdef H5 || (APP-PLUS && VUE2) -->
<img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- #ifndef H5 || (APP-PLUS && VUE2) -->
<!-- 表格中的图片使用 rich-text 防止大小不正确 -->
<rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="[{attrs:{style:n.attrs.style,src:n.attrs.src},name:'img'}]" :data-i="i" @tap.stop="imgTap" />
<!-- #endif -->
<!-- #ifndef H5 || APP-PLUS -->
<image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- #ifdef APP-PLUS && VUE3 -->
<image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
<!-- #endif -->
<!-- 文本 -->
<!-- #ifdef MP-WEIXIN -->
<text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
<text v-else-if="n.text" decode>{{n.text}}</text>
<!-- #endif -->
<text v-else-if="n.name==='br'">\n</text>
<!-- 链接 -->
<view v-else-if="n.name==='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
<node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
</view>
<!-- 视频 -->
<!-- #ifdef APP-PLUS -->
<view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" @vplay.stop="play" />
<!-- #endif -->
<!-- #ifndef APP-PLUS -->
<video v-else-if="n.name==='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :object-fit="n.attrs['object-fit']" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
<!-- #endif -->
<!-- #ifdef H5 || APP-PLUS -->
<iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
<embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
<!-- #endif -->
<!-- #ifndef MP-TOUTIAO || ((H5 || APP-PLUS) && VUE3) -->
<!-- 音频 -->
<audio v-else-if="n.name==='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
<!-- #endif -->
<view v-else-if="(n.name==='table'&&n.c)||n.name==='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style">
<node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
<view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
<node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
<block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
<view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
<node :childs="tr.children" :opts="opts" />
</view>
<view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
<view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
<node :childs="td.children" :opts="opts" />
</view>
</view>
</block>
</view>
</view>
<!-- 富文本 -->
<!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
<rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :user-select="opts[4]" :nodes="[n]" />
<!-- #endif -->
<!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
<rich-text v-else-if="!n.c" :id="n.attrs.id" :style="'display:inline;'+n.f" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
<!-- #endif -->
<!-- 继续递归 -->
<view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
<node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
</view>
<node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
</block>
</view>
</template>
<script module="handler" lang="wxs">
//
var inlineTags = {
abbr: true,
b: true,
big: true,
code: true,
del: true,
em: true,
i: true,
ins: true,
label: true,
q: true,
small: true,
span: true,
strong: true,
sub: true,
sup: true
}
/**
* @description 判断是否为行内标签
*/
module.exports = {
isInline: function (tagName, style) {
return inlineTags[tagName] || (style || '').indexOf('display:inline') !== -1
}
}
</script>
<script>
import node from './node'
export default {
name: 'node',
options: {
// #ifdef MP-WEIXIN
virtualHost: true,
// #endif
// #ifdef MP-TOUTIAO
addGlobalClass: false
// #endif
},
data () {
return {
ctrl: {},
// #ifdef MP-WEIXIN
isiOS: uni.getSystemInfoSync().system.includes('iOS')
// #endif
}
},
props: {
name: String,
attrs: {
type: Object,
default () {
return {}
}
},
childs: Array,
opts: Array
},
components: {
// #ifndef (H5 || APP-PLUS) && VUE3
node
// #endif
},
mounted () {
this.$nextTick(() => {
for (this.root = this.$parent; this.root.$options.name !== 'uv-parse'; this.root = this.root.$parent);
})
// #ifdef H5 || APP-PLUS
if (this.opts[0]) {
let i
for (i = this.childs.length; i--;) {
if (this.childs[i].name === 'img') break
}
if (i !== -1) {
this.observer = uni.createIntersectionObserver(this).relativeToViewport({
top: 500,
bottom: 500
})
this.observer.observe('._img', res => {
if (res.intersectionRatio) {
this.$set(this.ctrl, 'load', 1)
this.observer.disconnect()
}
})
}
}
// #endif
},
beforeDestroy () {
// #ifdef H5 || APP-PLUS
if (this.observer) {
this.observer.disconnect()
}
// #endif
},
methods:{
// #ifdef MP-WEIXIN
toJSON () { return this },
// #endif
/**
* @description 播放视频事件
* @param {Event} e
*/
play (e) {
this.root.$emit('play')
// #ifndef APP-PLUS
if (this.root.pauseVideo) {
let flag = false
const id = e.target.id
for (let i = this.root._videos.length; i--;) {
if (this.root._videos[i].id === id) {
flag = true
} else {
this.root._videos[i].pause() //
}
}
//
if (!flag) {
const ctx = uni.createVideoContext(id
// #ifndef MP-BAIDU
, this
// #endif
)
ctx.id = id
if (this.root.playbackRate) {
ctx.playbackRate(this.root.playbackRate)
}
this.root._videos.push(ctx)
}
}
// #endif
},
/**
* @description 图片点击事件
* @param {Event} e
*/
imgTap (e) {
const node = this.childs[e.currentTarget.dataset.i]
if (node.a) {
this.linkTap(node.a)
return
}
if (node.attrs.ignore) return
// #ifdef H5 || APP-PLUS
node.attrs.src = node.attrs.src || node.attrs['data-src']
// #endif
this.root.$emit('imgtap', node.attrs)
//
if (this.root.previewImg) {
uni.previewImage({
// #ifdef MP-WEIXIN
showmenu: this.root.showImgMenu,
// #endif
// #ifdef MP-ALIPAY
enablesavephoto: this.root.showImgMenu,
enableShowPhotoDownload: this.root.showImgMenu,
// #endif
current: parseInt(node.attrs.i),
urls: this.root.imgList
})
}
},
/**
* @description 图片长按
*/
imgLongTap (e) {
// #ifdef APP-PLUS
const attrs = this.childs[e.currentTarget.dataset.i].attrs
if (this.opts[3] && !attrs.ignore) {
uni.showActionSheet({
itemList: ['保存图片'],
success: () => {
const save = path => {
uni.saveImageToPhotosAlbum({
filePath: path,
success () {
uni.showToast({
title: '保存成功'
})
}
})
}
if (this.root.imgList[attrs.i].startsWith('http')) {
uni.downloadFile({
url: this.root.imgList[attrs.i],
success: res => save(res.tempFilePath)
})
} else {
save(this.root.imgList[attrs.i])
}
}
})
}
// #endif
},
/**
* @description 图片加载完成事件
* @param {Event} e
*/
imgLoad (e) {
const i = e.currentTarget.dataset.i
/* #ifndef H5 || (APP-PLUS && VUE2) */
if (!this.childs[i].w) {
//
this.$set(this.ctrl, i, e.detail.width)
} else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
//
this.$set(this.ctrl, i, 1)
}
this.checkReady()
},
/**
* @description 检查是否所有图片加载完毕
*/
checkReady () {
if (this.root && !this.root.lazyLoad) {
this.root._unloadimgs -= 1
if (!this.root._unloadimgs) {
setTimeout(() => {
this.root.getRect().then(rect => {
this.root.$emit('ready', rect)
}).catch(() => {
this.root.$emit('ready', {})
})
}, 350)
}
}
},
/**
* @description 链接点击事件
* @param {Event} e
*/
linkTap (e) {
const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
const attrs = node.attrs || e
const href = attrs.href
this.root.$emit('linktap', Object.assign({
innerText: this.root.getText(node.children || []) //
}, attrs))
if (href) {
if (href[0] === '#') {
//
this.root.navigateTo(href.substring(1)).catch(() => { })
} else if (href.split('?')[0].includes('://')) {
//
if (this.root.copyLink) {
// #ifdef H5
window.open(href)
// #endif
// #ifdef MP
uni.setClipboardData({
data: href,
success: () =>
uni.showToast({
title: '链接已复制'
})
})
// #endif
// #ifdef APP-PLUS
plus.runtime.openWeb(href)
// #endif
}
} else {
//
uni.navigateTo({
url: href,
fail () {
uni.switchTab({
url: href,
fail () { }
})
}
})
}
}
},
/**
* @description 错误事件
* @param {Event} e
*/
mediaError (e) {
const i = e.currentTarget.dataset.i
const node = this.childs[i]
//
if (node.name === 'video' || node.name === 'audio') {
let index = (this.ctrl[i] || 0) + 1
if (index > node.src.length) {
index = 0
}
if (index < node.src.length) {
this.$set(this.ctrl, i, index)
return
}
} else if (node.name === 'img') {
// #ifdef H5 && VUE3
if (this.opts[0] && !this.ctrl.load) return
// #endif
//
if (this.opts[2]) {
this.$set(this.ctrl, i, -1)
}
this.checkReady()
}
if (this.root) {
this.root.$emit('error', {
source: node.name,
attrs: node.attrs,
// #ifndef H5 && VUE3
errMsg: e.detail.errMsg
// #endif
})
}
}
}
}
</script>
<style>
/* a 标签默认效果 */
._a {
padding: 1.5px 0 1.5px 0;
color: #366092;
word-break: break-all;
}
/* a 标签点击态效果 */
._hover {
text-decoration: underline;
opacity: 0.7;
}
/* 图片默认效果 */
._img {
max-width: 100%;
-webkit-touch-callout: none;
}
/* 内部样式 */
._block {
display: block;
}
._b,
._strong {
font-weight: bold;
}
._code {
font-family: monospace;
}
._del {
text-decoration: line-through;
}
._em,
._i {
font-style: italic;
}
._h1 {
font-size: 2em;
}
._h2 {
font-size: 1.5em;
}
._h3 {
font-size: 1.17em;
}
._h5 {
font-size: 0.83em;
}
._h6 {
font-size: 0.67em;
}
._h1,
._h2,
._h3,
._h4,
._h5,
._h6 {
display: block;
font-weight: bold;
}
._image {
height: 1px;
}
._ins {
text-decoration: underline;
}
._li {
display: list-item;
}
._ol {
list-style-type: decimal;
}
._ol,
._ul {
display: block;
padding-left: 40px;
margin: 1em 0;
}
._q::before {
content: '"';
}
._q::after {
content: '"';
}
._sub {
font-size: smaller;
vertical-align: sub;
}
._sup {
font-size: smaller;
vertical-align: super;
}
._thead,
._tbody,
._tfoot {
display: table-row-group;
}
._tr {
display: table-row;
}
._td,
._th {
display: table-cell;
vertical-align: middle;
}
._th {
font-weight: bold;
text-align: center;
}
._ul {
list-style-type: disc;
}
._ul ._ul {
margin: 0;
list-style-type: circle;
}
._ul ._ul ._ul {
list-style-type: square;
}
._abbr,
._b,
._code,
._del,
._em,
._i,
._ins,
._label,
._q,
._span,
._strong,
._sub,
._sup {
display: inline;
}
/* #ifdef APP-PLUS */
._video {
width: 300px;
height: 225px;
}
/* #endif */
</style>

1335
src/uni_modules/uv-parse/components/uv-parse/parser.js

File diff suppressed because it is too large

498
src/uni_modules/uv-parse/components/uv-parse/uv-parse.vue

@ -0,0 +1,498 @@
<template>
<view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="containerStyle">
<slot v-if="!nodes[0]" />
<!-- #ifndef APP-PLUS-NVUE -->
<node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]" name="span" />
<!-- #endif -->
<!-- #ifdef APP-PLUS-NVUE -->
<web-view ref="web" src="/uni_modules/uv-parse/static/app-plus/uv-parse/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
<!-- #endif -->
</view>
</template>
<script>
/**
* uv-parse v1.0.3
* @description 富文本组件
* @tutorial https://www.uvui.cn/components/parse.html
* @property {String} container-style 容器的样式
* @property {String} content 用于渲染的 html 字符串
* @property {Boolean} copy-link 是否允许外部链接被点击时自动复制
* @property {String} domain 主域名用于拼接链接
* @property {String} error-img 图片出错时的占位图链接
* @property {Boolean} lazy-load 是否开启图片懒加载
* @property {string} loading-img 图片加载过程中的占位图链接
* @property {Boolean} pause-video 是否在播放一个视频时自动暂停其他视频
* @property {Boolean} preview-img 是否允许图片被点击时自动预览
* @property {Boolean} scroll-table 是否给每个表格添加一个滚动层使其能单独横向滚动
* @property {Boolean | String} selectable 是否开启长按复制
* @property {Boolean} set-title 是否将 title 标签的内容设置到页面标题
* @property {Boolean} show-img-menu 是否允许图片被长按时显示菜单
* @property {Object} tag-style 标签的默认样式
* @property {Boolean | Number} use-anchor 是否使用锚点链接
* @event {Function} load dom 结构加载完毕时触发
* @event {Function} ready 所有图片加载完毕时触发
* @event {Function} imgtap 图片被点击时触发
* @event {Function} linktap 链接被点击时触发
* @event {Function} play 音视频播放时触发
* @event {Function} error 媒体加载出错时触发
*/
// #ifndef APP-PLUS-NVUE
import node from './node/node'
// #endif
import Parser from './parser'
const plugins=[]
// #ifdef APP-PLUS-NVUE
const dom = weex.requireModule('dom')
// #endif
export default {
name: 'uv-parse',
data () {
return {
nodes: [],
// #ifdef APP-PLUS-NVUE
height: 3
// #endif
}
},
props: {
containerStyle: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
copyLink: {
type: [Boolean, String],
default: true
},
domain: String,
errorImg: {
type: String,
default: ''
},
lazyLoad: {
type: [Boolean, String],
default: false
},
loadingImg: {
type: String,
default: ''
},
pauseVideo: {
type: [Boolean, String],
default: true
},
previewImg: {
type: [Boolean, String],
default: true
},
scrollTable: [Boolean, String],
selectable: [Boolean, String],
setTitle: {
type: [Boolean, String],
default: true
},
showImgMenu: {
type: [Boolean, String],
default: true
},
tagStyle: Object,
useAnchor: [Boolean, Number]
},
// #ifdef VUE3
emits: ['load', 'ready', 'imgtap', 'linktap', 'play', 'error'],
// #endif
// #ifndef APP-PLUS-NVUE
components: {
node
},
// #endif
watch: {
content (content) {
this.setContent(content)
}
},
created () {
this.plugins = []
for (let i = plugins.length; i--;) {
this.plugins.push(new plugins[i](this))
}
},
mounted () {
if (this.content && !this.nodes.length) {
this.setContent(this.content)
}
},
beforeDestroy () {
this._hook('onDetached')
},
methods: {
/**
* @description 将锚点跳转的范围限定在一个 scroll-view
* @param {Object} page scroll-view 所在页面的示例
* @param {String} selector scroll-view 的选择器
* @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
*/
in (page, selector, scrollTop) {
// #ifndef APP-PLUS-NVUE
if (page && selector && scrollTop) {
this._in = {
page,
selector,
scrollTop
}
}
// #endif
},
/**
* @description 锚点跳转
* @param {String} id 要跳转的锚点 id
* @param {Number} offset 跳转位置的偏移量
* @returns {Promise}
*/
navigateTo (id, offset) {
return new Promise((resolve, reject) => {
if (!this.useAnchor) {
reject(Error('Anchor is disabled'))
return
}
offset = offset || parseInt(this.useAnchor) || 0
// #ifdef APP-PLUS-NVUE
if (!id) {
dom.scrollToElement(this.$refs.web, {
offset
})
resolve()
} else {
this._navigateTo = {
resolve,
reject,
offset
}
this.$refs.web.evalJs('uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' + id + ')||{}).offsetTop}})')
}
// #endif
// #ifndef APP-PLUS-NVUE
let deep = ' '
// #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
deep = '>>>'
// #endif
const selector = uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this._in ? this._in.page : this)
// #endif
.select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect()
if (this._in) {
selector.select(this._in.selector).scrollOffset()
.select(this._in.selector).boundingClientRect()
} else {
// scroll-view
selector.selectViewport().scrollOffset() //
}
selector.exec(res => {
if (!res[0]) {
reject(Error('Label not found'))
return
}
const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
if (this._in) {
// scroll-view
this._in.page[this._in.scrollTop] = scrollTop
} else {
//
uni.pageScrollTo({
scrollTop,
duration: 300
})
}
resolve()
})
// #endif
})
},
/**
* @description 获取文本内容
* @return {String}
*/
getText (nodes) {
let text = '';
(function traversal (nodes) {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
if (node.type === 'text') {
text += node.text.replace(/&amp;/g, '&')
} else if (node.name === 'br') {
text += '\n'
} else {
//
const isBlock = node.name === 'p' || node.name === 'div' || node.name === 'tr' || node.name === 'li' || (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
if (isBlock && text && text[text.length - 1] !== '\n') {
text += '\n'
}
//
if (node.children) {
traversal(node.children)
}
if (isBlock && text[text.length - 1] !== '\n') {
text += '\n'
} else if (node.name === 'td' || node.name === 'th') {
text += '\t'
}
}
}
})(nodes || this.nodes)
return text
},
/**
* @description 获取内容大小和位置
* @return {Promise}
*/
getRect () {
return new Promise((resolve, reject) => {
uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
.select('#_root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject(Error('Root label not found')))
})
},
/**
* @description 暂停播放媒体
*/
pauseMedia () {
for (let i = (this._videos || []).length; i--;) {
this._videos[i].pause()
}
// #ifdef APP-PLUS
const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
// #ifndef APP-PLUS-NVUE
let page = this.$parent
while (!page.$scope) page = page.$parent
page.$scope.$getAppWebview().evalJS(command)
// #endif
// #ifdef APP-PLUS-NVUE
this.$refs.web.evalJs(command)
// #endif
// #endif
},
/**
* @description 设置媒体播放速率
* @param {Number} rate 播放速率
*/
setPlaybackRate (rate) {
this.playbackRate = rate
for (let i = (this._videos || []).length; i--;) {
this._videos[i].playbackRate(rate)
}
// #ifdef APP-PLUS
const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' + rate
// #ifndef APP-PLUS-NVUE
let page = this.$parent
while (!page.$scope) page = page.$parent
page.$scope.$getAppWebview().evalJS(command)
// #endif
// #ifdef APP-PLUS-NVUE
this.$refs.web.evalJs(command)
// #endif
// #endif
},
/**
* @description 设置内容
* @param {String} content html 内容
* @param {Boolean} append 是否在尾部追加
*/
setContent (content, append) {
if (!append || !this.imgList) {
this.imgList = []
}
const nodes = new Parser(this).parse(content)
// #ifdef APP-PLUS-NVUE
if (this._ready) {
this._set(nodes, append)
}
// #endif
this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
// #ifndef APP-PLUS-NVUE
this._videos = []
this.$nextTick(() => {
this._hook('onLoad')
this.$emit('load')
})
if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
// 350ms
let height = 0
const callback = rect => {
if (!rect || !rect.height) rect = {}
// 350ms ready
if (rect.height === height) {
this.$emit('ready', rect)
} else {
height = rect.height
setTimeout(() => {
this.getRect().then(callback).catch(callback)
}, 350)
}
}
this.getRect().then(callback).catch(callback)
} else {
//
if (!this.imgList._unloadimgs) {
this.getRect().then(rect => {
this.$emit('ready', rect)
}).catch(() => {
this.$emit('ready', {})
})
}
}
// #endif
},
/**
* @description 调用插件钩子函数
*/
_hook (name) {
for (let i = plugins.length; i--;) {
if (this.plugins[i][name]) {
this.plugins[i][name]()
}
}
},
// #ifdef APP-PLUS-NVUE
/**
* @description 设置内容
*/
_set (nodes, append) {
this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes).replace(/%22/g, '') + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
},
/**
* @description 接收到 web-view 消息
*/
_onMessage (e) {
const message = e.detail.data[0]
switch (message.action) {
// web-view
case 'onJSBridgeReady':
this._ready = true
if (this.nodes) {
this._set(this.nodes)
}
break
// dom
case 'onLoad':
this.height = message.height
this._hook('onLoad')
this.$emit('load')
break
//
case 'onReady':
this.getRect().then(res => {
this.$emit('ready', res)
}).catch(() => {
this.$emit('ready', {})
})
break
//
case 'onHeightChange':
this.height = message.height
break
//
case 'onImgTap':
this.$emit('imgtap', message.attrs)
if (this.previewImg) {
uni.previewImage({
current: parseInt(message.attrs.i),
urls: this.imgList
})
}
break
//
case 'onLinkTap': {
const href = message.attrs.href
this.$emit('linktap', message.attrs)
if (href) {
//
if (href[0] === '#') {
if (this.useAnchor) {
dom.scrollToElement(this.$refs.web, {
offset: message.offset
})
}
} else if (href.includes('://')) {
//
if (this.copyLink) {
plus.runtime.openWeb(href)
}
} else {
uni.navigateTo({
url: href,
fail () {
uni.switchTab({
url: href
})
}
})
}
}
break
}
case 'onPlay':
this.$emit('play')
break
//
case 'getOffset':
if (typeof message.offset === 'number') {
dom.scrollToElement(this.$refs.web, {
offset: message.offset + this._navigateTo.offset
})
this._navigateTo.resolve()
} else {
this._navigateTo.reject(Error('Label not found'))
}
break
//
case 'onClick':
this.$emit('tap')
this.$emit('click')
break
//
case 'onError':
this.$emit('error', {
source: message.source,
attrs: message.attrs
})
}
}
// #endif
}
}
</script>
<style>
/* #ifndef APP-PLUS-NVUE */
/* 根节点样式 */
._root {
padding: 1px 0;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
}
/* 长按复制 */
._select {
user-select: text;
}
/* #endif */
</style>

87
src/uni_modules/uv-parse/package.json

@ -0,0 +1,87 @@
{
"id": "uv-parse",
"displayName": "uv-parse 富文本解析器 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.4",
"description": "uv-parse 该组件一般用于富文本解析场景,比如解析文章内容,商品详情,带原生HTML标签的各类字符串等,此组件和uni-app官方的rich-text组件功能有重合之处,但是也有不同的地方。",
"keywords": [
"uv-parse",
"uvui",
"uv-ui",
"parse",
"富文本"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

21
src/uni_modules/uv-parse/readme.md

@ -0,0 +1,21 @@
## Parse 富文本解析器
> **组件名:uv-parse**
该组件一般用于富文本解析场景,比如解析文章内容,商品详情,带原生`HTML`标签的各类字符串等,此组件和`uni-app`官方的`rich-text`组件功能有重合之处,但是也有不同的地方。
该插件只提供富文本的解析,该功能已经足够丰富。如果需要富文本的编辑,可使用`uniapp`官方提供的组件。
# <a href="https://www.uvui.cn/components/parse.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

224
src/uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js

@ -0,0 +1,224 @@
'use strict'
// 等待初始化完毕
document.addEventListener('UniAppJSBridgeReady', () => {
document.body.onclick = function () {
return uni.postMessage({
data: {
action: 'onClick'
}
})
}
uni.postMessage({
data: {
action: 'onJSBridgeReady'
}
})
})
let options
let medias = []
/**
* @description 获取标签的所有属性
* @param {Element} ele
*/
function getAttrs(ele) {
const attrs = Object.create(null)
for (let i = ele.attributes.length; i--;) {
attrs[ele.attributes[i].name] = ele.attributes[i].value
}
return attrs
}
/**
* @description 图片加载出错
*/
function onImgError() {
if (options[1]) {
this.src = options[1]
this.onerror = null
} // 取消监听点击
this.onclick = null
this.ontouchstart = null
uni.postMessage({
data: {
action: 'onError',
source: 'img',
attrs: getAttrs(this)
}
})
}
/**
* @description 创建 dom 结构
* @param {object[]} nodes 节点数组
* @param {Element} parent 父节点
* @param {string} namespace 命名空间
*/
function createDom(nodes, parent, namespace) {
const _loop = function _loop(i) {
const node = nodes[i]
let ele = void 0
if (!node.type || node.type == 'node') {
let { name } = node // svg 需要设置 namespace
if (name == 'svg') namespace = 'http://www.w3.org/2000/svg'
if (name == 'html' || name == 'body') name = 'div' // 创建标签
if (!namespace) ele = document.createElement(name); else ele = document.createElementNS(namespace, name) // 设置属性
for (const item in node.attrs) {
ele.setAttribute(item, node.attrs[item])
} // 递归创建子节点
if (node.children) createDom(node.children, ele, namespace) // 处理图片
if (name == 'img') {
if (!ele.src && ele.getAttribute('data-src')) ele.src = ele.getAttribute('data-src')
if (!node.attrs.ignore) {
// 监听图片点击事件
ele.onclick = function (e) {
e.stopPropagation()
uni.postMessage({
data: {
action: 'onImgTap',
attrs: getAttrs(this)
}
})
}
}
if (options[2]) {
image = new Image()
image.src = ele.src
ele.src = options[2]
image.onload = function () {
ele.src = this.src
}
image.onerror = function () {
ele.onerror()
}
}
ele.onerror = onImgError
} // 处理链接
else if (name == 'a') {
ele.addEventListener('click', function (e) {
e.stopPropagation()
e.preventDefault() // 阻止默认跳转
const href = this.getAttribute('href')
let offset
if (href && href[0] == '#') offset = (document.getElementById(href.substr(1)) || {}).offsetTop
uni.postMessage({
data: {
action: 'onLinkTap',
attrs: getAttrs(this),
offset
}
})
}, true)
} // 处理音视频
else if (name == 'video' || name == 'audio') {
medias.push(ele)
if (!node.attrs.autoplay) {
if (!node.attrs.controls) ele.setAttribute('controls', 'true') // 空白图占位
if (!node.attrs.poster) ele.setAttribute('poster', "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'/>")
}
if (options[3]) {
ele.onplay = function () {
for (let _i = 0; _i < medias.length; _i++) {
if (medias[_i] != this) medias[_i].pause()
}
}
}
ele.onerror = function () {
uni.postMessage({
data: {
action: 'onError',
source: name,
attrs: getAttrs(this)
}
})
}
} // 处理表格
else if (name == 'table' && options[4] && !ele.style.cssText.includes('inline')) {
const div = document.createElement('div')
div.style.overflow = 'auto'
div.appendChild(ele)
ele = div
} else if (name == 'svg') namespace = void 0
} else ele = document.createTextNode(node.text.replace(/&amp;/g, '&'))
parent.appendChild(ele)
}
for (let i = 0; i < nodes.length; i++) {
var image
_loop(i)
}
} // 设置 html 内容
window.setContent = function (nodes, opts, append) {
const ele = document.getElementById('content') // 背景颜色
if (opts[0]) document.body.bgColor = opts[0] // 长按复制
if (!opts[5]) ele.style.userSelect = 'none'
if (!append) {
ele.innerHTML = '' // 不追加则先清空
medias = []
}
options = opts
const fragment = document.createDocumentFragment()
createDom(nodes, fragment)
ele.appendChild(fragment) // 触发事件
let height = ele.scrollHeight
uni.postMessage({
data: {
action: 'onLoad',
height
}
})
clearInterval(window.timer)
let ready = false
window.timer = setInterval(() => {
if (ele.scrollHeight != height) {
height = ele.scrollHeight
uni.postMessage({
data: {
action: 'onHeightChange',
height
}
})
} else if (!ready) {
ready = true
uni.postMessage({
data: {
action: 'onReady'
}
})
}
}, 350)
} // 回收计时器
window.onunload = function () {
clearInterval(window.timer)
}

19
src/uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js

@ -0,0 +1,19 @@
!(function (e, n) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = n() : typeof define === 'function' && define.amd ? define(n) : (e = e || self).uni = n() }(this, (() => {
'use strict'
try { const e = {}; Object.defineProperty(e, 'passive', { get() { !0 } }), window.addEventListener('test-passive', null, e) } catch (e) {} const n = Object.prototype.hasOwnProperty; function t(e, t) { return n.call(e, t) } const i = []; const a = function (e, n) { const t = { options: { timestamp: +new Date() }, name: e, arg: n }; if (window.__dcloud_weex_postMessage || window.__dcloud_weex_) { if (e === 'postMessage') { const a = { data: [n] }; return window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessage(a) : window.__dcloud_weex_.postMessage(JSON.stringify(a)) } const o = { type: 'WEB_INVOKE_APPSERVICE', args: { data: t, webviewIds: i } }; window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessageToService(o) : window.__dcloud_weex_.postMessageToService(JSON.stringify(o)) } if (!window.plus) return window.parent.postMessage({ type: 'WEB_INVOKE_APPSERVICE', data: t, pageId: '' }, '*'); if (i.length === 0) { const r = plus.webview.currentWebview(); if (!r) throw new Error('plus.webview.currentWebview() is undefined'); const d = r.parent(); let s = ''; s = d ? d.id : r.id, i.push(s) } if (plus.webview.getWebviewById('__uniapp__service'))plus.webview.postMessageToUniNView({ type: 'WEB_INVOKE_APPSERVICE', args: { data: t, webviewIds: i } }, '__uniapp__service'); else { const w = JSON.stringify(t); plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat('WEB_INVOKE_APPSERVICE', '",').concat(w, ',').concat(JSON.stringify(i), ');')) } }; const o = {
navigateTo() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('navigateTo', { url: encodeURI(n) }) }, navigateBack() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.delta; a('navigateBack', { delta: parseInt(n) || 1 }) }, switchTab() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('switchTab', { url: encodeURI(n) }) }, reLaunch() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('reLaunch', { url: encodeURI(n) }) }, redirectTo() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('redirectTo', { url: encodeURI(n) }) }, getEnv(e) { window.plus ? e({ plus: !0 }) : e({ h5: !0 }) }, postMessage() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; a('postMessage', e.data || {}) }
}; const r = /uni-app/i.test(navigator.userAgent); const d = /Html5Plus/i.test(navigator.userAgent); const s = /complete|loaded|interactive/; const w = window.my && navigator.userAgent.indexOf('AlipayClient') > -1; const u = window.swan && window.swan.webView && /swan/i.test(navigator.userAgent); const c = window.qq && window.qq.miniProgram && /QQ/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent); const g = window.tt && window.tt.miniProgram && /toutiaomicroapp/i.test(navigator.userAgent); const v = window.wx && window.wx.miniProgram && /micromessenger/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent); const p = window.qa && /quickapp/i.test(navigator.userAgent); for (var l, _ = function () { window.UniAppJSBridge = !0, document.dispatchEvent(new CustomEvent('UniAppJSBridgeReady', { bubbles: !0, cancelable: !0 })) }, f = [function (e) { if (r || d) return window.__dcloud_weex_postMessage || window.__dcloud_weex_ ? document.addEventListener('DOMContentLoaded', e) : window.plus && s.test(document.readyState) ? setTimeout(e, 0) : document.addEventListener('plusready', e), o }, function (e) { if (v) return window.WeixinJSBridge && window.WeixinJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('WeixinJSBridgeReady', e), window.wx.miniProgram }, function (e) { if (c) return window.QQJSBridge && window.QQJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('QQJSBridgeReady', e), window.qq.miniProgram }, function (e) {
if (w) {
document.addEventListener('DOMContentLoaded', e); const n = window.my; return {
navigateTo: n.navigateTo, navigateBack: n.navigateBack, switchTab: n.switchTab, reLaunch: n.reLaunch, redirectTo: n.redirectTo, postMessage: n.postMessage, getEnv: n.getEnv
}
}
}, function (e) { if (u) return document.addEventListener('DOMContentLoaded', e), window.swan.webView }, function (e) { if (g) return document.addEventListener('DOMContentLoaded', e), window.tt.miniProgram }, function (e) {
if (p) {
window.QaJSBridge && window.QaJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('QaJSBridgeReady', e); const n = window.qa; return {
navigateTo: n.navigateTo, navigateBack: n.navigateBack, switchTab: n.switchTab, reLaunch: n.reLaunch, redirectTo: n.redirectTo, postMessage: n.postMessage, getEnv: n.getEnv
}
}
}, function (e) { return document.addEventListener('DOMContentLoaded', e), o }], m = 0; m < f.length && !(l = f[m](_)); m++);l || (l = {}); const E = typeof uni !== 'undefined' ? uni : {}; if (!E.navigateTo) for (const b in l)t(l, b) && (E[b] = l[b]); return E.webView = l, E
})))

1
src/uni_modules/uv-parse/static/app-plus/uv-parse/local.html

@ -0,0 +1 @@
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><style>body,html{width:100%;height:100%;overflow:hidden}body{margin:0}video{width:300px;height:225px}img{max-width:100%;-webkit-touch-callout:none}@keyframes show{0%{opacity:0}100%{opacity:1}}</style></head><body><div id="content"></div><script type="text/javascript" src="./js/uni.webview.min.js"></script><script type="text/javascript" src="./js/handler.js"></script></body>

76
src/uni_modules/uv-ui-tools/changelog.md

@ -0,0 +1,76 @@
## 1.1.25(2024-01-20)
1.1.20版本更新
## 1.1.24(2023-12-21)
1. luch-request更新
## 1.1.23(2023-12-12)
1. 1.1.19版本
## 1.1.22(2023-11-28)
1. 优化
## 1.1.21(2023-11-10)
1. 1.1.17版本
## 1.1.20(2023-10-30)
1. 1.1.16版本
## 1.1.19(2023-10-13)
1. 兼容vue3
## 1.1.18(2023-10-12)
1. 1.1.15版本
## 1.1.17(2023-09-27)
1. 1.1.14版本发布
## 1.1.16(2023-09-15)
1. 1.1.13版本发布
## 1.1.15(2023-09-15)
1. 更新button.js相关按钮支持open-type="agreePrivacyAuthorization"
## 1.1.14(2023-09-14)
1. 优化dayjs
## 1.1.13(2023-09-13)
1. 优化,$uv中增加unit参数,方便组件中使用
## 1.1.12(2023-09-10)
1. 升级版本
## 1.1.11(2023-09-04)
1. 1.1.11版本
## 1.1.10(2023-08-31)
1. 修复customStyle和customClass存在冲突的问题
## 1.1.9(2023-08-27)
1. 版本升级
2. 优化
## 1.1.8(2023-08-24)
1. 版本升级
## 1.1.7(2023-08-22)
1. 版本升级
## 1.1.6(2023-08-18)
uvui版本:1.1.6
## 1.0.15(2023-08-14)
1. 更新uvui版本号
## 1.0.13(2023-08-06)
1. 优化
## 1.0.12(2023-08-06)
1. 修改版本号
## 1.0.11(2023-08-06)
1. 路由增加events参数
2. 路由拦截修复
## 1.0.10(2023-08-01)
1. 优化
## 1.0.9(2023-06-28)
优化openType.js
## 1.0.8(2023-06-15)
1. 修改支付宝报错的BUG
## 1.0.7(2023-06-07)
1. 解决微信小程序使用uvui提示 Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors
2. 解决上述提示,需要在uni.scss配置$uvui-nvue-style: false; 然后在APP.vue下面引入uvui内置的基础样式:@import '@/uni_modules/uv-ui-tools/index.scss';
## 1.0.6(2023-06-04)
1. uv-ui-tools 优化工具组件,兼容更多功能
2. 小程序分享功能优化等
## 1.0.5(2023-06-02)
1. 修改扩展使用mixin中方法的问题
## 1.0.4(2023-05-23)
1. 兼容百度小程序修改bem函数
## 1.0.3(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.2(2023-05-10)
1. 增加Http请求封装
2. 优化
## 1.0.1(2023-05-04)
1. 修改名称及备注
## 1.0.0(2023-05-04)
1. uv-ui工具集首次发布

6
src/uni_modules/uv-ui-tools/components/uv-ui-tools/uv-ui-tools.vue

@ -0,0 +1,6 @@
<template>
</template>
<script>
</script>
<style>
</style>

79
src/uni_modules/uv-ui-tools/index.js

@ -0,0 +1,79 @@
// 全局挂载引入http相关请求拦截插件
import Request from './libs/luch-request'
// 引入全局mixin
import mixin from './libs/mixin/mixin.js'
// 小程序特有的mixin
import mpMixin from './libs/mixin/mpMixin.js'
// #ifdef MP
import mpShare from './libs/mixin/mpShare.js'
// #endif
// 路由封装
import route from './libs/util/route.js'
// 公共工具函数
import * as index from './libs/function/index.js'
// 防抖方法
import debounce from './libs/function/debounce.js'
// 节流方法
import throttle from './libs/function/throttle.js'
// 规则检验
import * as test from './libs/function/test.js'
// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制
import * as colorGradient from './libs/function/colorGradient.js'
// 配置信息
import config from './libs/config/config.js'
// 平台
import platform from './libs/function/platform'
const $uv = {
route,
config,
test,
date: index.timeFormat, // 另名date
...index,
colorGradient: colorGradient.colorGradient,
hexToRgb: colorGradient.hexToRgb,
rgbToHex: colorGradient.rgbToHex,
colorToRgba: colorGradient.colorToRgba,
http: new Request(),
debounce,
throttle,
platform,
mixin,
mpMixin
}
uni.$uv = $uv;
const install = (Vue,options={}) => {
// #ifndef APP-NVUE
const cloneMixin = index.deepClone(mixin);
delete cloneMixin?.props?.customClass;
delete cloneMixin?.props?.customStyle;
Vue.mixin(cloneMixin);
// #ifdef MP
if(options.mpShare){
Vue.mixin(mpShare);
}
// #endif
// #endif
// #ifdef VUE2
// 时间格式化,同时两个名称,date和timeFormat
Vue.filter('timeFormat', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
Vue.filter('date', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
// 将多久以前的方法,注入到全局过滤器
Vue.filter('timeFrom', (timestamp, format) => uni.$uv.timeFrom(timestamp, format));
// 同时挂载到uni和Vue.prototype中
// #ifndef APP-NVUE
// 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的
Vue.prototype.$uv = $uv;
// #endif
// #endif
// #ifdef VUE3
Vue.config.globalProperties.$uv = $uv;
// #endif
}
export default {
install
}

7
src/uni_modules/uv-ui-tools/index.scss

@ -0,0 +1,7 @@
// 引入公共基础类
@import "./libs/css/common.scss";
// 非nvue的样式
/* #ifndef APP-NVUE */
@import "./libs/css/vue.scss";
/* #endif */

34
src/uni_modules/uv-ui-tools/libs/config/config.js

@ -0,0 +1,34 @@
// 此版本发布于2024-01-20
const version = '1.1.20'
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.log(`\n %c uvui V${version} https://www.uvui.cn/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px;');
}
export default {
v: version,
version,
// 主题名称
type: [
'primary',
'success',
'info',
'error',
'warning'
],
// 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持
color: {
'uv-primary': '#2979ff',
'uv-warning': '#ff9900',
'uv-success': '#19be6b',
'uv-error': '#fa3534',
'uv-info': '#909399',
'uv-main-color': '#303133',
'uv-content-color': '#606266',
'uv-tips-color': '#909399',
'uv-light-color': '#c0c4cc'
},
// 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx
unit: 'px'
}

32
src/uni_modules/uv-ui-tools/libs/css/color.scss

@ -0,0 +1,32 @@
$uv-main-color: #303133 !default;
$uv-content-color: #606266 !default;
$uv-tips-color: #909193 !default;
$uv-light-color: #c0c4cc !default;
$uv-border-color: #dadbde !default;
$uv-bg-color: #f3f4f6 !default;
$uv-disabled-color: #c8c9cc !default;
$uv-primary: #3c9cff !default;
$uv-primary-dark: #398ade !default;
$uv-primary-disabled: #9acafc !default;
$uv-primary-light: #ecf5ff !default;
$uv-warning: #f9ae3d !default;
$uv-warning-dark: #f1a532 !default;
$uv-warning-disabled: #f9d39b !default;
$uv-warning-light: #fdf6ec !default;
$uv-success: #5ac725 !default;
$uv-success-dark: #53c21d !default;
$uv-success-disabled: #a9e08f !default;
$uv-success-light: #f5fff0;
$uv-error: #f56c6c !default;
$uv-error-dark: #e45656 !default;
$uv-error-disabled: #f7b2b2 !default;
$uv-error-light: #fef0f0 !default;
$uv-info: #909399 !default;
$uv-info-dark: #767a82 !default;
$uv-info-disabled: #c4c6c9 !default;
$uv-info-light: #f4f4f5 !default;

100
src/uni_modules/uv-ui-tools/libs/css/common.scss

@ -0,0 +1,100 @@
// 超出行数自动显示行尾省略号最多5行
// 来自uvui的温馨提示当您在控制台看到此报错说明需要在App.vue的style标签加上lang="scss"
@for $i from 1 through 5 {
.uv-line-#{$i} {
/* #ifdef APP-NVUE */
// nvue下可以直接使用lines属性这是weex特有样式
lines: $i;
text-overflow: ellipsis;
overflow: hidden;
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
// vue下单行和多行显示省略号需要单独处理
@if $i == '1' {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} @else {
display: -webkit-box!important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: $i;
-webkit-box-orient: vertical!important;
}
/* #endif */
}
}
$uv-bordercolor: #dadbde;
@if variable-exists(uv-border-color) {
$uv-bordercolor: $uv-border-color;
}
// 此处加上!important并非随意乱用而是因为目前*.nvue页面编译到H5时
// App.vue的样式会被uni-app的view元素的自带border属性覆盖导致无效
// 综上这是uni-app的缺陷导致我们为了多端兼容而必须要加上!important
// 移动端兼容性较好直接使用0.5px去实现细边框不使用伪元素形式实现
.uv-border {
border-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-style: solid;
}
.uv-border-top {
border-top-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
}
.uv-border-left {
border-left-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-left-style: solid;
}
.uv-border-right {
border-right-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-right-style: solid;
}
.uv-border-bottom {
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-bottom-style: solid;
}
.uv-border-top-bottom {
border-top-width: 0.5px!important;
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
border-bottom-style: solid;
}
// 去除button的所有默认样式让其表现跟普通的viewtext元素一样
.uv-reset-button {
padding: 0;
background-color: transparent;
/* #ifndef APP-PLUS */
font-size: inherit;
line-height: inherit;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.uv-reset-button::after {
border: none;
}
/* #endif */
.uv-hover-class {
opacity: 0.7;
}

23
src/uni_modules/uv-ui-tools/libs/css/components.scss

@ -0,0 +1,23 @@
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
/* #ifndef APP-NVUE */
// 由于uvui是基于nvue环境进行开发的此环境中普通元素默认为flex-direction: column;
// 所以在非nvue中需要对元素进行重置为flex-direction: column; 否则可能会表现异常
$uvui-nvue-style: true !default;
@if $uvui-nvue-style == true {
view, scroll-view, swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
}
/* #endif */

111
src/uni_modules/uv-ui-tools/libs/css/variable.scss

@ -0,0 +1,111 @@
// 超出行数自动显示行尾省略号最多5行
// 来自uvui的温馨提示当您在控制台看到此报错说明需要在App.vue的style标签加上lang="scss"
@if variable-exists(show-lines) {
@for $i from 1 through 5 {
.uv-line-#{$i} {
/* #ifdef APP-NVUE */
// nvue下可以直接使用lines属性这是weex特有样式
lines: $i;
text-overflow: ellipsis;
overflow: hidden;
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
// vue下单行和多行显示省略号需要单独处理
@if $i == '1' {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} @else {
display: -webkit-box!important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-line-clamp: $i;
-webkit-box-orient: vertical!important;
}
/* #endif */
}
}
}
@if variable-exists(show-border) {
$uv-bordercolor: #dadbde;
@if variable-exists(uv-border-color) {
$uv-bordercolor: $uv-border-color;
}
// 此处加上!important并非随意乱用而是因为目前*.nvue页面编译到H5时
// App.vue的样式会被uni-app的view元素的自带border属性覆盖导致无效
// 综上这是uni-app的缺陷导致我们为了多端兼容而必须要加上!important
// 移动端兼容性较好直接使用0.5px去实现细边框不使用伪元素形式实现
@if variable-exists(show-border-surround) {
.uv-border {
border-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-style: solid;
}
}
@if variable-exists(show-border-top) {
.uv-border-top {
border-top-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
}
}
@if variable-exists(show-border-left) {
.uv-border-left {
border-left-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-left-style: solid;
}
}
@if variable-exists(show-border-right) {
.uv-border-right {
border-right-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-right-style: solid;
}
}
@if variable-exists(show-border-bottom) {
.uv-border-bottom {
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-bottom-style: solid;
}
}
@if variable-exists(show-border-top-bottom) {
.uv-border-top-bottom {
border-top-width: 0.5px!important;
border-bottom-width: 0.5px!important;
border-color: $uv-bordercolor!important;
border-top-style: solid;
border-bottom-style: solid;
}
}
}
@if variable-exists(show-reset-button) {
// 去除button的所有默认样式让其表现跟普通的viewtext元素一样
.uv-reset-button {
padding: 0;
background-color: transparent;
/* #ifndef APP-PLUS */
font-size: inherit;
line-height: inherit;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.uv-reset-button::after {
border: none;
}
/* #endif */
}
@if variable-exists(show-hover) {
.uv-hover-class {
opacity: 0.7;
}
}

40
src/uni_modules/uv-ui-tools/libs/css/vue.scss

@ -0,0 +1,40 @@
// 历遍生成4个方向的底部安全区
@each $d in top, right, bottom, left {
.uv-safe-area-inset-#{$d} {
padding-#{$d}: 0;
padding-#{$d}: constant(safe-area-inset-#{$d});
padding-#{$d}: env(safe-area-inset-#{$d});
}
}
//提升H5端uni.toast()的层级避免被uvui的modal等遮盖
/* #ifdef H5 */
uni-toast {
z-index: 10090;
}
uni-toast .uni-toast {
z-index: 10090;
}
/* #endif */
// 隐藏scroll-view的滚动条
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
$uvui-nvue-style: true !default;
@if $uvui-nvue-style == false {
view, scroll-view, swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
}

134
src/uni_modules/uv-ui-tools/libs/function/colorGradient.js

@ -0,0 +1,134 @@
/**
* 求两个颜色之间的渐变值
* @param {string} startColor 开始的颜色
* @param {string} endColor 结束的颜色
* @param {number} step 颜色等分的份额
* */
function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
const startRGB = hexToRgb(startColor, false) // 转换为rgb数组模式
const startR = startRGB[0]
const startG = startRGB[1]
const startB = startRGB[2]
const endRGB = hexToRgb(endColor, false)
const endR = endRGB[0]
const endG = endRGB[1]
const endB = endRGB[2]
const sR = (endR - startR) / step // 总差值
const sG = (endG - startG) / step
const sB = (endB - startB) / step
const colorArr = []
for (let i = 0; i < step; i++) {
// 计算每一步的hex值
let hex = rgbToHex(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB
* i + startB))})`)
// 确保第一个颜色值为startColor的值
if (i === 0) hex = rgbToHex(startColor)
// 确保最后一个颜色值为endColor的值
if (i === step - 1) hex = rgbToHex(endColor)
colorArr.push(hex)
}
return colorArr
}
// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
function hexToRgb(sColor, str = true) {
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
sColor = String(sColor).toLowerCase()
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
let sColorNew = '#'
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
}
sColor = sColorNew
}
// 处理六位的颜色值
const sColorChange = []
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
}
if (!str) {
return sColorChange
}
return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})`
} if (/^(rgb|RGB)/.test(sColor)) {
const arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
return arr.map((val) => Number(val))
}
return sColor
}
// 将rgb表示方式转换为hex表示方式
function rgbToHex(rgb) {
const _this = rgb
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
if (/^(rgb|RGB)/.test(_this)) {
const aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
let strHex = '#'
for (let i = 0; i < aColor.length; i++) {
let hex = Number(aColor[i]).toString(16)
hex = String(hex).length == 1 ? `${0}${hex}` : hex // 保证每个rgb的值为2位
if (hex === '0') {
hex += hex
}
strHex += hex
}
if (strHex.length !== 7) {
strHex = _this
}
return strHex
} if (reg.test(_this)) {
const aNum = _this.replace(/#/, '').split('')
if (aNum.length === 6) {
return _this
} if (aNum.length === 3) {
let numHex = '#'
for (let i = 0; i < aNum.length; i += 1) {
numHex += (aNum[i] + aNum[i])
}
return numHex
}
} else {
return _this
}
}
/**
* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba2552552550.5字符串
* sHex为传入的十六进制的色值
* alpha为rgba的透明度
*/
function colorToRgba(color, alpha) {
color = rgbToHex(color)
// 十六进制颜色值的正则表达式
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
/* 16进制颜色转为RGB格式 */
let sColor = String(color).toLowerCase()
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
let sColorNew = '#'
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
}
sColor = sColorNew
}
// 处理六位的颜色值
const sColorChange = []
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
}
// return sColorChange.join(',')
return `rgba(${sColorChange.join(',')},${alpha})`
}
return sColor
}
export {
colorGradient,
hexToRgb,
rgbToHex,
colorToRgba
}

29
src/uni_modules/uv-ui-tools/libs/function/debounce.js

@ -0,0 +1,29 @@
let timeout = null
/**
* 防抖原理一定时间内只有最后一次操作再过wait毫秒后才执行函数
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function debounce(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout)
// 立即执行,此类情况一般用不到
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) typeof func === 'function' && func()
} else {
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(() => {
typeof func === 'function' && func()
}, wait)
}
}
export default debounce

167
src/uni_modules/uv-ui-tools/libs/function/digit.js

@ -0,0 +1,167 @@
let _boundaryCheckingState = true; // 是否进行越界检查的全局开关
/**
* 把错误的数据转正
* @private
* @example strip(0.09999999999999998)=0.1
*/
function strip(num, precision = 15) {
return +parseFloat(Number(num).toPrecision(precision));
}
/**
* Return digits length of a number
* @private
* @param {*number} num Input number
*/
function digitLength(num) {
// Get digit length of e
const eSplit = num.toString().split(/[eE]/);
const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
return len > 0 ? len : 0;
}
/**
* 把小数转成整数,如果是小数则放大成整数
* @private
* @param {*number} num 输入数
*/
function float2Fixed(num) {
if (num.toString().indexOf('e') === -1) {
return Number(num.toString().replace('.', ''));
}
const dLen = digitLength(num);
return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
}
/**
* 检测数字是否越界如果越界给出提示
* @private
* @param {*number} num 输入数
*/
function checkBoundary(num) {
if (_boundaryCheckingState) {
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
console.warn(`${num} 超出了精度限制,结果可能不正确`);
}
}
}
/**
* 把递归操作扁平迭代化
* @param {number[]} arr 要操作的数字数组
* @param {function} operation 迭代操作
* @private
*/
function iteratorOperation(arr, operation) {
const [num1, num2, ...others] = arr;
let res = operation(num1, num2);
others.forEach((num) => {
res = operation(res, num);
});
return res;
}
/**
* 高精度乘法
* @export
*/
export function times(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, times);
}
const [num1, num2] = nums;
const num1Changed = float2Fixed(num1);
const num2Changed = float2Fixed(num2);
const baseNum = digitLength(num1) + digitLength(num2);
const leftValue = num1Changed * num2Changed;
checkBoundary(leftValue);
return leftValue / Math.pow(10, baseNum);
}
/**
* 高精度加法
* @export
*/
export function plus(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, plus);
}
const [num1, num2] = nums;
// 取最大的小数位
const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
// 把小数都转为整数然后再计算
return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
}
/**
* 高精度减法
* @export
*/
export function minus(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, minus);
}
const [num1, num2] = nums;
const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
}
/**
* 高精度除法
* @export
*/
export function divide(...nums) {
if (nums.length > 2) {
return iteratorOperation(nums, divide);
}
const [num1, num2] = nums;
const num1Changed = float2Fixed(num1);
const num2Changed = float2Fixed(num2);
checkBoundary(num1Changed);
checkBoundary(num2Changed);
// 重要,这里必须用strip进行修正
return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
}
/**
* 四舍五入
* @export
*/
export function round(num, ratio) {
const base = Math.pow(10, ratio);
let result = divide(Math.round(Math.abs(times(num, base))), base);
if (num < 0 && result !== 0) {
result = times(result, -1);
}
// 位数不足则补0
return result;
}
/**
* 是否进行边界检查默认开启
* @param flag 标记开关true 为开启false 为关闭默认为 true
* @export
*/
export function enableBoundaryChecking(flag = true) {
_boundaryCheckingState = flag;
}
export default {
times,
plus,
minus,
divide,
round,
enableBoundaryChecking,
};

734
src/uni_modules/uv-ui-tools/libs/function/index.js

@ -0,0 +1,734 @@
import { number, empty } from './test.js'
import { round } from './digit.js'
/**
* @description 如果value小于min取min如果value大于max取max
* @param {number} min
* @param {number} max
* @param {number} value
*/
function range(min = 0, max = 0, value = 0) {
return Math.max(min, Math.min(max, Number(value)))
}
/**
* @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx"取出其数值部分如果是"xxxrpx"还需要用过uni.upx2px进行转换
* @param {number|string} value 用户传递值的px值
* @param {boolean} unit
* @returns {number|string}
*/
function getPx(value, unit = false) {
if (number(value)) {
return unit ? `${value}px` : Number(value)
}
// 如果带有rpx,先取出其数值部分,再转为px值
if (/(rpx|upx)$/.test(value)) {
return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value)))
}
return unit ? `${parseInt(value)}px` : parseInt(value)
}
/**
* @description 进行延时以达到可以简写代码的目的 比如: await uni.$uv.sleep(20)将会阻塞20ms
* @param {number} value 堵塞时间 单位ms 毫秒
* @returns {Promise} 返回promise
*/
function sleep(value = 30) {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, value)
})
}
/**
* @description 运行期判断平台
* @returns {string} 返回所在平台(小写)
* @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台
*/
function os() {
return uni.getSystemInfoSync().platform.toLowerCase()
}
/**
* @description 获取系统信息同步接口
* @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
*/
function sys() {
return uni.getSystemInfoSync()
}
/**
* @description 取一个区间数
* @param {Number} min 最小值
* @param {Number} max 最大值
*/
function random(min, max) {
if (min >= 0 && max > 0 && max >= min) {
const gab = max - min + 1
return Math.floor(Math.random() * gab + min)
}
return 0
}
/**
* @param {Number} len uuid的长度
* @param {Boolean} firstU 将返回的首字母置为"u"
* @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
*/
function guid(len = 32, firstU = true, radix = null) {
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
const uuid = []
radix = radix || chars.length
if (len) {
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
} else {
let r
// rfc4122标准要求返回的uuid中,某些位为固定的字符
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
uuid[14] = '4'
for (let i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random() * 16
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]
}
}
}
// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
if (firstU) {
uuid.shift()
return `u${uuid.join('')}`
}
return uuid.join('')
}
/**
* @description 获取父组件的参数因为支付宝小程序不支持provide/inject的写法
this.$parent在非H5中可以准确获取到父组件但是在H5中需要多次this.$parent.$parent.xxx
这里默认值等于undefined有它的含义因为最顶层元素(组件)的$parent就是undefined意味着不传name
(默认为undefined)就是查找最顶层的$parent
* @param {string|undefined} name 父组件的参数名
*/
function $parent(name = undefined) {
let parent = this.$parent
// 通过while历遍,这里主要是为了H5需要多层解析的问题
while (parent) {
// 父组件
if (parent.$options && parent.$options.name !== name) {
// 如果组件的name不相等,继续上一级寻找
parent = parent.$parent
} else {
return parent
}
}
return false
}
/**
* @description 样式转换
* 对象转字符串或者字符串转对象
* @param {object | string} customStyle 需要转换的目标
* @param {String} target 转换的目的object-转为对象string-转为字符串
* @returns {object|string}
*/
function addStyle(customStyle, target = 'object') {
// 字符串转字符串,对象转对象情形,直接返回
if (empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' &&
typeof(customStyle) === 'string') {
return customStyle
}
// 字符串转对象
if (target === 'object') {
// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
customStyle = trim(customStyle)
// 根据";"将字符串转为数组形式
const styleArray = customStyle.split(';')
const style = {}
// 历遍数组,拼接成对象
for (let i = 0; i < styleArray.length; i++) {
// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
if (styleArray[i]) {
const item = styleArray[i].split(':')
style[trim(item[0])] = trim(item[1])
}
}
return style
}
// 这里为对象转字符串形式
let string = ''
for (const i in customStyle) {
// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
string += `${key}:${customStyle[i]};`
}
// 去除两端空格
return trim(string)
}
/**
* @description 添加单位如果有rpxupx%px等单位结尾或者值为auto直接返回否则加上px单位结尾
* @param {string|number} value 需要添加单位的值
* @param {string} unit 添加的单位名 比如px
*/
function addUnit(value = 'auto', unit = uni?.$uv?.config?.unit ? uni?.$uv?.config?.unit : 'px') {
value = String(value)
// 用uvui内置验证规则中的number判断是否为数值
return number(value) ? `${value}${unit}` : value
}
/**
* @description 深度克隆
* @param {object} obj 需要深度克隆的对象
* @param cache 缓存
* @returns {*} 克隆后的对象或者原值不是对象
*/
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
let clone;
if (obj instanceof Date) {
clone = new Date(obj.getTime());
} else if (obj instanceof RegExp) {
clone = new RegExp(obj);
} else if (obj instanceof Map) {
clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
} else if (obj instanceof Set) {
clone = new Set(Array.from(obj, value => deepClone(value, cache)));
} else if (Array.isArray(obj)) {
clone = obj.map(value => deepClone(value, cache));
} else if (Object.prototype.toString.call(obj) === '[object Object]') {
clone = Object.create(Object.getPrototypeOf(obj));
cache.set(obj, clone);
for (const [key, value] of Object.entries(obj)) {
clone[key] = deepClone(value, cache);
}
} else {
clone = Object.assign({}, obj);
}
cache.set(obj, clone);
return clone;
}
/**
* @description JS对象深度合并
* @param {object} target 需要拷贝的对象
* @param {object} source 拷贝的来源对象
* @returns {object|boolean} 深度合并后的对象或者false入参有不是对象
*/
function deepMerge(target = {}, source = {}) {
target = deepClone(target)
if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
for (const prop in source) {
if (!source.hasOwnProperty(prop)) continue;
const sourceValue = source[prop];
const targetValue = merged[prop];
if (sourceValue instanceof Date) {
merged[prop] = new Date(sourceValue);
} else if (sourceValue instanceof RegExp) {
merged[prop] = new RegExp(sourceValue);
} else if (sourceValue instanceof Map) {
merged[prop] = new Map(sourceValue);
} else if (sourceValue instanceof Set) {
merged[prop] = new Set(sourceValue);
} else if (typeof sourceValue === 'object' && sourceValue !== null) {
merged[prop] = deepMerge(targetValue, sourceValue);
} else {
merged[prop] = sourceValue;
}
}
return merged;
}
/**
* @description error提示
* @param {*} err 错误内容
*/
function error(err) {
// 开发环境才提示,生产环境不会提示
if (process.env.NODE_ENV === 'development') {
console.error(`uvui提示:${err}`)
}
}
/**
* @description 打乱数组
* @param {array} array 需要打乱的数组
* @returns {array} 打乱后的数组
*/
function randomArray(array = []) {
// 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
return array.sort(() => Math.random() - 0.5)
}
// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序
// 所以这里做一个兼容polyfill的兼容处理
if (!String.prototype.padStart) {
// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
String.prototype.padStart = function(maxLength, fillString = ' ') {
if (Object.prototype.toString.call(fillString) !== '[object String]') {
throw new TypeError(
'fillString must be String'
)
}
const str = this
// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
if (str.length >= maxLength) return String(str)
const fillLength = maxLength - str.length
let times = Math.ceil(fillLength / fillString.length)
while (times >>= 1) {
fillString += fillString
if (times === 1) {
fillString += fillString
}
}
return fillString.slice(0, fillLength) + str
}
}
/**
* @description 格式化时间
* @param {String|Number} dateTime 需要格式化的时间戳
* @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
* @returns {string} 返回格式化后的字符串
*/
function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
let date
// 若传入时间为假值,则取当前时间
if (!dateTime) {
date = new Date()
}
// 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容)
else if (/^\d{10}$/.test(dateTime?.toString().trim())) {
date = new Date(dateTime * 1000)
}
// 若用户传入字符串格式时间戳,new Date无法解析,需做兼容
else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
date = new Date(Number(dateTime))
}
// 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间
// 处理 '2022-07-10 01:02:03',跳过 '2022-07-10T01:02:03'
else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) {
date = new Date(dateTime.replace(/-/g, '/'))
}
// 其他都认为符合 RFC 2822 规范
else {
date = new Date(dateTime)
}
const timeSource = {
'y': date.getFullYear().toString(), // 年
'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
'd': date.getDate().toString().padStart(2, '0'), // 日
'h': date.getHours().toString().padStart(2, '0'), // 时
'M': date.getMinutes().toString().padStart(2, '0'), // 分
's': date.getSeconds().toString().padStart(2, '0') // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
}
for (const key in timeSource) {
const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
if (ret) {
// 年可能只需展示两位
const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
}
}
return formatStr
}
/**
* @description 时间戳转为多久之前
* @param {String|Number} timestamp 时间戳
* @param {String|Boolean} format
* 格式化规则如果为时间格式字符串超出一定时间范围返回固定的时间格式
* 如果为布尔值false无论什么时间都返回多久以前的格式
* @returns {string} 转化后的内容
*/
function timeFrom(timestamp = null, format = 'yyyy-mm-dd') {
if (timestamp == null) timestamp = Number(new Date())
timestamp = parseInt(timestamp)
// 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
if (timestamp.toString().length == 10) timestamp *= 1000
let timer = (new Date()).getTime() - timestamp
timer = parseInt(timer / 1000)
// 如果小于5分钟,则返回"刚刚",其他以此类推
let tips = ''
switch (true) {
case timer < 300:
tips = '刚刚'
break
case timer >= 300 && timer < 3600:
tips = `${parseInt(timer / 60)}分钟前`
break
case timer >= 3600 && timer < 86400:
tips = `${parseInt(timer / 3600)}小时前`
break
case timer >= 86400 && timer < 2592000:
tips = `${parseInt(timer / 86400)}天前`
break
default:
// 如果format为false,则无论什么时间戳,都显示xx之前
if (format === false) {
if (timer >= 2592000 && timer < 365 * 86400) {
tips = `${parseInt(timer / (86400 * 30))}个月前`
} else {
tips = `${parseInt(timer / (86400 * 365))}年前`
}
} else {
tips = timeFormat(timestamp, format)
}
}
return tips
}
/**
* @description 去除空格
* @param String str 需要去除空格的字符串
* @param String pos both(左右)|left|right|all 默认both
*/
function trim(str, pos = 'both') {
str = String(str)
if (pos == 'both') {
return str.replace(/^\s+|\s+$/g, '')
}
if (pos == 'left') {
return str.replace(/^\s*/, '')
}
if (pos == 'right') {
return str.replace(/(\s*$)/g, '')
}
if (pos == 'all') {
return str.replace(/\s+/g, '')
}
return str
}
/**
* @description 对象转url参数
* @param {object} data,对象
* @param {Boolean} isPrefix,是否自动加上"?"
* @param {string} arrayFormat 规则 indices|brackets|repeat|comma
*/
function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {
const prefix = isPrefix ? '?' : ''
const _result = []
if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'
for (const key in data) {
const value = data[key]
// 去掉为空的参数
if (['', undefined, null].indexOf(value) >= 0) {
continue
}
// 如果值为数组,另行处理
if (value.constructor === Array) {
// e.g. {ids: [1, 2, 3]}
switch (arrayFormat) {
case 'indices':
// 结果: ids[0]=1&ids[1]=2&ids[2]=3
for (let i = 0; i < value.length; i++) {
_result.push(`${key}[${i}]=${value[i]}`)
}
break
case 'brackets':
// 结果: ids[]=1&ids[]=2&ids[]=3
value.forEach((_value) => {
_result.push(`${key}[]=${_value}`)
})
break
case 'repeat':
// 结果: ids=1&ids=2&ids=3
value.forEach((_value) => {
_result.push(`${key}=${_value}`)
})
break
case 'comma':
// 结果: ids=1,2,3
let commaStr = ''
value.forEach((_value) => {
commaStr += (commaStr ? ',' : '') + _value
})
_result.push(`${key}=${commaStr}`)
break
default:
value.forEach((_value) => {
_result.push(`${key}[]=${_value}`)
})
}
} else {
_result.push(`${key}=${value}`)
}
}
return _result.length ? prefix + _result.join('&') : ''
}
/**
* 显示消息提示框
* @param {String} title 提示的内容长度与 icon 取值有关
* @param {Number} duration 提示的延迟时间单位毫秒默认2000
*/
function toast(title, duration = 2000) {
uni.showToast({
title: String(title),
icon: 'none',
duration
})
}
/**
* @description 根据主题type值,获取对应的图标
* @param {String} type 主题名称,primary|info|error|warning|success
* @param {boolean} fill 是否使用fill填充实体的图标
*/
function type2icon(type = 'success', fill = false) {
// 如果非预置值,默认为success
if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success'
let iconName = ''
// 目前(2019-12-12),info和primary使用同一个图标
switch (type) {
case 'primary':
iconName = 'info-circle'
break
case 'info':
iconName = 'info-circle'
break
case 'error':
iconName = 'close-circle'
break
case 'warning':
iconName = 'error-circle'
break
case 'success':
iconName = 'checkmark-circle'
break
default:
iconName = 'checkmark-circle'
}
// 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的
if (fill) iconName += '-fill'
return iconName
}
/**
* @description 数字格式化
* @param {number|string} number 要格式化的数字
* @param {number} decimals 保留几位小数
* @param {string} decimalPoint 小数点符号
* @param {string} thousandsSeparator 千分位符号
* @returns {string} 格式化后的数字
*/
function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') {
number = (`${number}`).replace(/[^0-9+-Ee.]/g, '')
const n = !isFinite(+number) ? 0 : +number
const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator
const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint
let s = ''
s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.')
const re = /(-?\d+)(\d{3})/
while (re.test(s[0])) {
s[0] = s[0].replace(re, `$1${sep}$2`)
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || ''
s[1] += new Array(prec - s[1].length + 1).join('0')
}
return s.join(dec)
}
/**
* @description 获取duration值
* 如果带有ms或者s直接返回如果大于一定值认为是ms单位小于一定值认为是s单位
* 比如以30位阈值那么300大于30可以理解为用户想要的是300ms而不是想花300s去执行一个动画
* @param {String|number} value 比如: "1s"|"100ms"|1|100
* @param {boolean} unit 提示: 如果是false 默认返回number
* @return {string|number}
*/
function getDuration(value, unit = true) {
const valueNum = parseInt(value)
if (unit) {
if (/s$/.test(value)) return value
return value > 30 ? `${value}ms` : `${value}s`
}
if (/ms$/.test(value)) return valueNum
if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000
return valueNum
}
/**
* @description 日期的月或日补零操作
* @param {String} value 需要补零的值
*/
function padZero(value) {
return `00${value}`.slice(-2)
}
/**
* @description 在uv-form的子组件内容发生变化或者失去焦点时尝试通知uv-form执行校验方法
* @param {*} instance
* @param {*} event
*/
function formValidate(instance, event) {
const formItem = $parent.call(instance, 'uv-form-item')
const form = $parent.call(instance, 'uv-form')
// 如果发生变化的input或者textarea等,其父组件中有uv-form-item或者uv-form等,就执行form的validate方法
// 同时将form-item的pros传递给form,让其进行精确对象验证
if (formItem && form) {
form.validateField(formItem.prop, () => {}, event)
}
}
/**
* @description 获取某个对象下的属性用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式
* @param {object} obj 对象
* @param {string} key 需要获取的属性字段
* @returns {*}
*/
function getProperty(obj, key) {
if (!obj) {
return
}
if (typeof key !== 'string' || key === '') {
return ''
}
if (key.indexOf('.') !== -1) {
const keys = key.split('.')
let firstObj = obj[keys[0]] || {}
for (let i = 1; i < keys.length; i++) {
if (firstObj) {
firstObj = firstObj[keys[i]]
}
}
return firstObj
}
return obj[key]
}
/**
* @description 设置对象的属性值如果'a.b.c'的形式进行设置
* @param {object} obj 对象
* @param {string} key 需要设置的属性
* @param {string} value 设置的值
*/
function setProperty(obj, key, value) {
if (!obj) {
return
}
// 递归赋值
const inFn = function(_obj, keys, v) {
// 最后一个属性key
if (keys.length === 1) {
_obj[keys[0]] = v
return
}
// 0~length-1个key
while (keys.length > 1) {
const k = keys[0]
if (!_obj[k] || (typeof _obj[k] !== 'object')) {
_obj[k] = {}
}
const key = keys.shift()
// 自调用判断是否存在属性,不存在则自动创建对象
inFn(_obj[k], keys, v)
}
}
if (typeof key !== 'string' || key === '') {
} else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作
const keys = key.split('.')
inFn(obj, keys, value)
} else {
obj[key] = value
}
}
/**
* @description 获取当前页面路径
*/
function page() {
const pages = getCurrentPages();
const route = pages[pages.length - 1]?.route;
// 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组
return `/${route ? route : ''}`
}
/**
* @description 获取当前路由栈实例数组
*/
function pages() {
const pages = getCurrentPages()
return pages
}
/**
* 获取页面历史栈指定层实例
* @param back {number} [0] - 0或者负数表示获取历史栈的哪一层0表示获取当前页面实例-1 表示获取上一个页面实例默认0
*/
function getHistoryPage(back = 0) {
const pages = getCurrentPages()
const len = pages.length
return pages[len - 1 + back]
}
/**
* @description 修改uvui内置属性值
* @param {object} props 修改内置props属性
* @param {object} config 修改内置config属性
* @param {object} color 修改内置color属性
* @param {object} zIndex 修改内置zIndex属性
*/
function setConfig({
props = {},
config = {},
color = {},
zIndex = {}
}) {
const {
deepMerge,
} = uni.$uv
uni.$uv.config = deepMerge(uni.$uv.config, config)
uni.$uv.props = deepMerge(uni.$uv.props, props)
uni.$uv.color = deepMerge(uni.$uv.color, color)
uni.$uv.zIndex = deepMerge(uni.$uv.zIndex, zIndex)
}
export {
range,
getPx,
sleep,
os,
sys,
random,
guid,
$parent,
addStyle,
addUnit,
deepClone,
deepMerge,
error,
randomArray,
timeFormat,
timeFrom,
trim,
queryParams,
toast,
type2icon,
priceFormat,
getDuration,
padZero,
formValidate,
getProperty,
setProperty,
page,
pages,
getHistoryPage,
setConfig
}

75
src/uni_modules/uv-ui-tools/libs/function/platform.js

@ -0,0 +1,75 @@
/**
* 注意
* 此部分内容在vue-cli模式下需要在vue.config.js加入如下内容才有效
* module.exports = {
* transpileDependencies: ['uview-v2']
* }
*/
let platform = 'none'
// #ifdef VUE3
platform = 'vue3'
// #endif
// #ifdef VUE2
platform = 'vue2'
// #endif
// #ifdef APP-PLUS
platform = 'plus'
// #endif
// #ifdef APP-NVUE
platform = 'nvue'
// #endif
// #ifdef H5
platform = 'h5'
// #endif
// #ifdef MP-WEIXIN
platform = 'weixin'
// #endif
// #ifdef MP-ALIPAY
platform = 'alipay'
// #endif
// #ifdef MP-BAIDU
platform = 'baidu'
// #endif
// #ifdef MP-TOUTIAO
platform = 'toutiao'
// #endif
// #ifdef MP-QQ
platform = 'qq'
// #endif
// #ifdef MP-KUAISHOU
platform = 'kuaishou'
// #endif
// #ifdef MP-360
platform = '360'
// #endif
// #ifdef MP
platform = 'mp'
// #endif
// #ifdef QUICKAPP-WEBVIEW
platform = 'quickapp-webview'
// #endif
// #ifdef QUICKAPP-WEBVIEW-HUAWEI
platform = 'quickapp-webview-huawei'
// #endif
// #ifdef QUICKAPP-WEBVIEW-UNION
platform = 'quckapp-webview-union'
// #endif
export default platform

287
src/uni_modules/uv-ui-tools/libs/function/test.js

@ -0,0 +1,287 @@
/**
* 验证电子邮箱格式
*/
function email(value) {
return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value)
}
/**
* 验证手机格式
*/
function mobile(value) {
return /^1([3589]\d|4[5-9]|6[1-2,4-7]|7[0-8])\d{8}$/.test(value)
}
/**
* 验证URL格式
*/
function url(value) {
return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/
.test(value)
}
/**
* 验证日期格式
*/
function date(value) {
if (!value) return false
// 判断是否数值或者字符串数值(意味着为时间戳),转为数值,否则new Date无法识别字符串时间戳
if (number(value)) value = +value
return !/Invalid|NaN/.test(new Date(value).toString())
}
/**
* 验证ISO类型的日期格式
*/
function dateISO(value) {
return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
}
/**
* 验证十进制数字
*/
function number(value) {
return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value)
}
/**
* 验证字符串
*/
function string(value) {
return typeof value === 'string'
}
/**
* 验证整数
*/
function digits(value) {
return /^\d+$/.test(value)
}
/**
* 验证身份证号码
*/
function idCard(value) {
return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value
)
}
/**
* 是否车牌号
*/
function carNo(value) {
// 新能源车牌
const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/
// 旧车牌
const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/
if (value.length === 7) {
return creg.test(value)
} if (value.length === 8) {
return xreg.test(value)
}
return false
}
/**
* 金额,只允许2位小数
*/
function amount(value) {
// 金额,只允许保留两位小数
return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value)
}
/**
* 中文
*/
function chinese(value) {
const reg = /^[\u4e00-\u9fa5]+$/gi
return reg.test(value)
}
/**
* 只能输入字母
*/
function letter(value) {
return /^[a-zA-Z]*$/.test(value)
}
/**
* 只能是字母或者数字
*/
function enOrNum(value) {
// 英文或者数字
const reg = /^[0-9a-zA-Z]*$/g
return reg.test(value)
}
/**
* 验证是否包含某个值
*/
function contains(value, param) {
return value.indexOf(param) >= 0
}
/**
* 验证一个值范围[min, max]
*/
function range(value, param) {
return value >= param[0] && value <= param[1]
}
/**
* 验证一个长度范围[min, max]
*/
function rangeLength(value, param) {
return value.length >= param[0] && value.length <= param[1]
}
/**
* 是否固定电话
*/
function landline(value) {
const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/
return reg.test(value)
}
/**
* 判断是否为空
*/
function empty(value) {
switch (typeof value) {
case 'undefined':
return true
case 'string':
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
break
case 'boolean':
if (!value) return true
break
case 'number':
if (value === 0 || isNaN(value)) return true
break
case 'object':
if (value === null || value.length === 0) return true
for (const i in value) {
return false
}
return true
}
return false
}
/**
* 是否json字符串
*/
function jsonString(value) {
if (typeof value === 'string') {
try {
const obj = JSON.parse(value)
if (typeof obj === 'object' && obj) {
return true
}
return false
} catch (e) {
return false
}
}
return false
}
/**
* 是否数组
*/
function array(value) {
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
}
return Object.prototype.toString.call(value) === '[object Array]'
}
/**
* 是否对象
*/
function object(value) {
return Object.prototype.toString.call(value) === '[object Object]'
}
/**
* 是否短信验证码
*/
function code(value, len = 6) {
return new RegExp(`^\\d{${len}}$`).test(value)
}
/**
* 是否函数方法
* @param {Object} value
*/
function func(value) {
return typeof value === 'function'
}
/**
* 是否promise对象
* @param {Object} value
*/
function promise(value) {
return object(value) && func(value.then) && func(value.catch)
}
/**
* @param {Object} value
*/
function image(value) {
const newValue = value.split('?')[0]
const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i
return IMAGE_REGEXP.test(newValue)
}
/**
* 是否视频格式
* @param {Object} value
*/
function video(value) {
const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i
return VIDEO_REGEXP.test(value)
}
/**
* 是否为正则对象
* @param {Object}
* @return {Boolean}
*/
function regExp(o) {
return o && Object.prototype.toString.call(o) === '[object RegExp]'
}
export {
email,
mobile,
url,
date,
dateISO,
number,
digits,
idCard,
carNo,
amount,
chinese,
letter,
enOrNum,
contains,
range,
rangeLength,
empty,
jsonString,
landline,
object,
array,
code,
func,
promise,
video,
image,
regExp,
string
}

30
src/uni_modules/uv-ui-tools/libs/function/throttle.js

@ -0,0 +1,30 @@
let timer; let
flag
/**
* 节流原理在一定时间内只能触发一次
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function throttle(func, wait = 500, immediate = true) {
if (immediate) {
if (!flag) {
flag = true
// 如果是立即执行,则在wait毫秒内开始时执行
typeof func === 'function' && func()
timer = setTimeout(() => {
flag = false
}, wait)
}
} else if (!flag) {
flag = true
// 如果是非立即执行,则在wait毫秒内的结束处执行
timer = setTimeout(() => {
flag = false
typeof func === 'function' && func()
}, wait)
}
}
export default throttle

132
src/uni_modules/uv-ui-tools/libs/luch-request/adapters/index.js

@ -0,0 +1,132 @@
import buildURL from '../helpers/buildURL'
import buildFullPath from '../core/buildFullPath'
import settle from '../core/settle'
import {isUndefined} from "../utils"
/**
* 返回可选值存在的配置
* @param {Array} keys - 可选值数组
* @param {Object} config2 - 配置
* @return {{}} - 存在的配置项
*/
const mergeKeys = (keys, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
return config
}
export default (config) => {
return new Promise((resolve, reject) => {
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params, config.paramsSerializer)
const _config = {
url: fullPath,
header: config.header,
complete: (response) => {
config.fullPath = fullPath
response.config = config
response.rawData = response.data
try {
let jsonParseHandle = false
const forcedJSONParsingType = typeof config.forcedJSONParsing
if (forcedJSONParsingType === 'boolean') {
jsonParseHandle = config.forcedJSONParsing
} else if (forcedJSONParsingType === 'object') {
const includesMethod = config.forcedJSONParsing.include || []
jsonParseHandle = includesMethod.includes(config.method)
}
// 对可能字符串不是json 的情况容错
if (jsonParseHandle && typeof response.data === 'string') {
response.data = JSON.parse(response.data)
}
// eslint-disable-next-line no-empty
} catch (e) {
}
settle(resolve, reject, response)
}
}
let requestTask
if (config.method === 'UPLOAD') {
delete _config.header['content-type']
delete _config.header['Content-Type']
let otherConfig = {
// #ifdef MP-ALIPAY
fileType: config.fileType,
// #endif
filePath: config.filePath,
name: config.name
}
const optionalKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef H5
'file',
// #endif
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
'formData'
]
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
} else if (config.method === 'DOWNLOAD') {
const optionalKeys = [
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
// #ifdef MP
'filePath',
// #endif
]
requestTask = uni.downloadFile({..._config, ...mergeKeys(optionalKeys, config)})
} else {
const optionalKeys = [
'data',
'method',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
// #ifdef MP-WEIXIN
'enableHttp2',
'enableQuic',
// #endif
// #ifdef MP-TOUTIAO || MP-WEIXIN
'enableCache',
// #endif
// #ifdef MP-WEIXIN
'enableHttpDNS',
'httpDNSServiceId',
'enableChunked',
'forceCellularNetwork',
// #endif
// #ifdef MP-ALIPAY
'enableCookie',
// #endif
// #ifdef MP-BAIDU
'cloudCache',
'defer'
// #endif
]
requestTask = uni.request({..._config, ...mergeKeys(optionalKeys, config)})
}
if (config.getTask) {
config.getTask(requestTask, config)
}
})
}

51
src/uni_modules/uv-ui-tools/libs/luch-request/core/InterceptorManager.js

@ -0,0 +1,51 @@
'use strict'
function InterceptorManager() {
this.handlers = []
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
})
return this.handlers.length - 1
}
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null
}
}
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
this.handlers.forEach(h => {
if (h !== null) {
fn(h)
}
})
}
export default InterceptorManager

201
src/uni_modules/uv-ui-tools/libs/luch-request/core/Request.js

@ -0,0 +1,201 @@
/**
* @Class Request
* @description luch-request http请求插件
* @Author lu-ch
* @Email webwork.s@qq.com
* 文档: https://www.quanzhan.co/luch-request/
* github: https://github.com/lei-mu/luch-request
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
*/
import dispatchRequest from './dispatchRequest'
import InterceptorManager from './InterceptorManager'
import mergeConfig from './mergeConfig'
import defaults from './defaults'
import { isPlainObject } from '../utils'
import clone from '../utils/clone'
export default class Request {
/**
* @param {Object} arg - 全局配置
* @param {String} arg.baseURL - 全局根路径
* @param {Object} arg.header - 全局header
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
* @param {String} arg.dataType = [json] - 全局默认的dataType
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType支付宝小程序不支持
* @param {Object} arg.custom - 全局默认的自定义参数
* @param {Number} arg.timeout - 全局默认的超时时间单位 ms默认60000H5(HBuilderX 2.9.9+)APP(HBuilderX 2.9.9+)微信小程序2.10.0支付宝小程序
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书默认true.仅App安卓端支持HBuilderX 2.3.3+
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证cookies默认false仅H5支持HBuilderX 2.6.15+
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4默认false App-Android 支持 (HBuilderX 2.8.0+)
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器默认statusCode >= 200 && statusCode < 300
*/
constructor(arg = {}) {
if (!isPlainObject(arg)) {
arg = {}
console.warn('设置全局参数必须接收一个Object')
}
this.config = clone({...defaults, ...arg})
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig(f) {
this.config = f(this.config)
}
middleware(config) {
config = mergeConfig(this.config, config)
let chain = [dispatchRequest, undefined]
let promise = Promise.resolve(config)
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected)
})
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected)
})
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise
}
/**
* @Function
* @param {Object} config - 请求配置项
* @prop {String} options.url - 请求路径
* @prop {Object} options.data - 请求参数
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json会尝试对返回的数据做一次 JSON.parse
* @prop {Object} [options.header = config.header] - 请求header
* @prop {Object} [options.method = config.method] - 请求方法
* @returns {Promise<unknown>}
*/
request(config = {}) {
return this.middleware(config)
}
get(url, options = {}) {
return this.middleware({
url,
method: 'GET',
...options
})
}
post(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY || MP-KUAISHOU || MP-JD
put(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
connect(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
head(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
trace(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload(url, config = {}) {
config.url = url
config.method = 'UPLOAD'
return this.middleware(config)
}
download(url, config = {}) {
config.url = url
config.method = 'DOWNLOAD'
return this.middleware(config)
}
get version () {
return '3.1.0'
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/

20
src/uni_modules/uv-ui-tools/libs/luch-request/core/buildFullPath.js

@ -0,0 +1,20 @@
'use strict'
import isAbsoluteURL from '../helpers/isAbsoluteURL'
import combineURLs from '../helpers/combineURLs'
/**
* Creates a new URL by combining the baseURL with the requestedURL,
* only when the requestedURL is not already an absolute URL.
* If the requestURL is absolute, this function returns the requestedURL untouched.
*
* @param {string} baseURL The base URL
* @param {string} requestedURL Absolute or relative URL to combine
* @returns {string} The combined full path
*/
export default function buildFullPath(baseURL, requestedURL) {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL)
}
return requestedURL
}

33
src/uni_modules/uv-ui-tools/libs/luch-request/core/defaults.js

@ -0,0 +1,33 @@
/**
* 默认的全局配置
*/
export default {
baseURL: '',
header: {},
method: 'GET',
dataType: 'json',
paramsSerializer: null,
// #ifndef MP-ALIPAY
responseType: 'text',
// #endif
custom: {},
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
timeout: 60000,
// #endif
// #ifdef APP-PLUS
sslVerify: true,
// #endif
// #ifdef H5
withCredentials: false,
// #endif
// #ifdef APP-PLUS
firstIpv4: false,
// #endif
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300
},
// 是否尝试将响应数据json化
forcedJSONParsing: true
}

6
src/uni_modules/uv-ui-tools/libs/luch-request/core/dispatchRequest.js

@ -0,0 +1,6 @@
import adapter from '../adapters/index'
export default (config) => {
return adapter(config)
}

126
src/uni_modules/uv-ui-tools/libs/luch-request/core/mergeConfig.js

@ -0,0 +1,126 @@
import {deepMerge, isUndefined} from '../utils'
/**
* 合并局部配置优先的配置如果局部有该配置项则用局部如果全局有该配置项则用全局
* @param {Array} keys - 配置项
* @param {Object} globalsConfig - 当前的全局配置
* @param {Object} config2 - 局部配置
* @return {{}}
*/
const mergeKeys = (keys, globalsConfig, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
} else if (!isUndefined(globalsConfig[prop])) {
config[prop] = globalsConfig[prop]
}
})
return config
}
/**
*
* @param globalsConfig - 当前实例的全局配置
* @param config2 - 当前的局部配置
* @return - 合并后的配置
*/
export default (globalsConfig, config2 = {}) => {
const method = config2.method || globalsConfig.method || 'GET'
let config = {
baseURL: config2.baseURL || globalsConfig.baseURL || '',
method: method,
url: config2.url || '',
params: config2.params || {},
custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
header: deepMerge(globalsConfig.header || {}, config2.header || {})
}
const defaultToConfig2Keys = ['getTask', 'validateStatus', 'paramsSerializer', 'forcedJSONParsing']
config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
// eslint-disable-next-line no-empty
if (method === 'DOWNLOAD') {
const downloadKeys = [
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
// #ifdef MP
'filePath',
// #endif
]
config = {...config, ...mergeKeys(downloadKeys, globalsConfig, config2)}
} else if (method === 'UPLOAD') {
delete config.header['content-type']
delete config.header['Content-Type']
const uploadKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef MP-ALIPAY
'fileType',
// #endif
// #ifdef H5
'file',
// #endif
'filePath',
'name',
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
'timeout',
// #endif
'formData',
]
uploadKeys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
// #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
config['timeout'] = globalsConfig['timeout']
}
// #endif
} else {
const defaultsKeys = [
'data',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
// #ifdef MP-WEIXIN
'enableHttp2',
'enableQuic',
// #endif
// #ifdef MP-TOUTIAO || MP-WEIXIN
'enableCache',
// #endif
// #ifdef MP-WEIXIN
'enableHttpDNS',
'httpDNSServiceId',
'enableChunked',
'forceCellularNetwork',
// #endif
// #ifdef MP-ALIPAY
'enableCookie',
// #endif
// #ifdef MP-BAIDU
'cloudCache',
'defer'
// #endif
]
config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
}
return config
}

16
src/uni_modules/uv-ui-tools/libs/luch-request/core/settle.js

@ -0,0 +1,16 @@
/**
* Resolve or reject a Promise based on response status.
*
* @param {Function} resolve A function that resolves the promise.
* @param {Function} reject A function that rejects the promise.
* @param {object} response The response.
*/
export default function settle(resolve, reject, response) {
const validateStatus = response.config.validateStatus
const status = response.statusCode
if (status && (!validateStatus || validateStatus(status))) {
resolve(response)
} else {
reject(response)
}
}

64
src/uni_modules/uv-ui-tools/libs/luch-request/helpers/buildURL.js

@ -0,0 +1,64 @@
'use strict'
import * as utils from './../utils'
function encode(val) {
return encodeURIComponent(val).replace(/%40/gi, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ',').replace(/%20/g, '+').replace(/%5B/gi, '[').replace(/%5D/gi, ']')
}
/**
* Build a URL by appending params to the end
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @returns {string} The formatted url
*/
export default function buildURL(url, params, paramsSerializer) {
/*eslint no-param-reassign:0*/
if (!params) {
return url
}
var serializedParams
if (paramsSerializer) {
serializedParams = paramsSerializer(params)
} else if (utils.isURLSearchParams(params)) {
serializedParams = params.toString()
} else {
var parts = []
utils.forEach(params, function serialize(val, key) {
if (val === null || typeof val === 'undefined') {
return
}
if (utils.isArray(val)) {
key = key + '[]'
} else {
val = [val]
}
utils.forEach(val, function parseValue(v) {
if (utils.isDate(v)) {
v = v.toISOString()
} else if (utils.isObject(v)) {
v = JSON.stringify(v)
}
parts.push(encode(key) + '=' + encode(v))
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
var hashmarkIndex = url.indexOf('#')
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}

14
src/uni_modules/uv-ui-tools/libs/luch-request/helpers/combineURLs.js

@ -0,0 +1,14 @@
'use strict'
/**
* Creates a new URL by combining the specified URLs
*
* @param {string} baseURL The base URL
* @param {string} relativeURL The relative URL
* @returns {string} The combined URL
*/
export default function combineURLs(baseURL, relativeURL) {
return relativeURL
? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
: baseURL
}

14
src/uni_modules/uv-ui-tools/libs/luch-request/helpers/isAbsoluteURL.js

@ -0,0 +1,14 @@
'use strict'
/**
* Determines whether the specified URL is absolute
*
* @param {string} url The URL to test
* @returns {boolean} True if the specified URL is absolute, otherwise false
*/
export default function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
}

197
src/uni_modules/uv-ui-tools/libs/luch-request/index.d.ts

@ -0,0 +1,197 @@
export type HttpTask = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask;
export type HttpRequestTask = UniApp.RequestTask;
export type HttpUploadTask = UniApp.UploadTask;
export type HttpDownloadTask = UniApp.DownloadTask;
export type HttpMethod =
"GET"
| "POST"
| "PUT"
| "DELETE"
| "CONNECT"
| "HEAD"
| "OPTIONS"
| "TRACE"
| "UPLOAD"
| "DOWNLOAD";
export type HttpRequestHeader = Record<string, string>;
export type HttpParams = Record<string, any>;
export type HttpData = Record<string, any>;
export type HttpResponseType = 'arraybuffer' | 'text';
export type HttpCustom = Record<string, any>;
export type HttpFileType = 'image' | 'video' | 'audio';
export type HttpFormData = Record<string, any>;
export type HttpResponseHeader = Record<string, string> & {
"set-cookie"?: string[]
};
export interface HttpRequestConfig<T = HttpTask> {
/** @desc 请求服务器接口地址 */
url?: string;
/** @desc 请求方式,默认为 GET */
method?: HttpMethod;
/** @desc 请求基地址 */
baseURL?: string;
/** @desc 请求头信息,不能设置 Referer,App、H5 端会自动带上 cookie,且 H5 端不可手动修改 */
header?: HttpRequestHeader;
/** @desc 请求查询参数,自动拼接为查询字符串 */
params?: HttpParams;
/** @desc 请求体参数 */
data?: HttpData;
/** @desc 超时时间,单位 ms,默认为 60000,仅 H5 (HBuilderX 2.9.9+)、APP (HBuilderX 2.9.9+)、微信小程序 (2.10.0)、支付宝小程序支持 */
timeout?: number;
/** @desc 跨域请求时是否携带凭证 (cookies),默认为 false,仅 H5 (HBuilderX 2.6.15+) 支持 */
withCredentials?: boolean;
/** @desc 设置响应的数据类型,支付宝小程序不支持 */
responseType?: HttpResponseType;
/** @desc 全局自定义验证器 */
validateStatus?: ((statusCode: number) => boolean) | null;
/** params 参数自定义处理 */
paramsSerializer?: (params: AnyObject) => string | void;
/** @desc 默认为 json,如果设为 json,会尝试对返回的数据做一次 JSON.parse */
dataType?: string;
/** @desc DNS 解析时是否优先使用 ipv4,默认为 false,仅 App-Android (HBuilderX 2.8.0+) 支持 */
firstIpv4?: boolean;
/** @desc 是否验证 SSL 证书,默认为 true,仅 App-Android (HBuilderX 2.3.3+) 支持 */
sslVerify?: boolean;
/** @desc 开启 http2;微信小程序 */
enableHttp2?: boolean;
/** @desc 开启 quic;微信小程序 */
enableQuic?: boolean;
/** @desc 开启 cache;微信小程序、字节跳动小程序 2.31.0+ */
enableCache?: boolean;
/** @desc 开启 httpDNS;微信小程序 */
enableHttpDNS?: boolean;
/** @desc httpDNS 服务商;微信小程序 */
httpDNSServiceId?: string;
/** @desc 开启 transfer-encoding chunked;微信小程序 */
enableChunked?: boolean;
/** @desc wifi下使用移动网络发送请求;微信小程序 */
forceCellularNetwork?: boolean;
/** @desc 开启后可在headers中编辑cookie;支付宝小程序 10.2.33+ */
enableCookie?: boolean;
/** @desc 是否开启云加速;百度小程序 3.310.11+ */
cloudCache?: boolean | object;
/** @desc 控制当前请求是否延时至首屏内容渲染后发送;百度小程序 3.310.11+ */
defer?: boolean;
/** @desc 自定义参数 */
custom?: HttpCustom;
/** @desc 返回当前请求的 task 和 options,不要在这里修改 options */
getTask?: (task: T, options: HttpRequestConfig<T>) => void;
/** @desc 需要上传的文件列表,使用 files 时,filePath 和 name 不生效,仅支持 App、H5 (2.6.15+) */
files?: { name?: string; file?: File; uri: string; }[];
/** @desc 文件类型,仅支付宝小程序支持且为必填项 */
fileType?: HttpFileType;
/** @desc 要上传的文件对象,仅 H5 (2.6.15+) 支持 */
file?: File;
/** @desc 要上传文件资源的路径,使用 files 时,filePath 和 name 不生效 */
filePath?: string;
/** @desc 文件对应的 key,开发者在服务器端通过这个 key 可以获取到文件二进制内容,使用 files 时,filePath 和 name 不生效 */
name?: string;
/** @desc 请求中其他额外的 form data */
formData?: HttpFormData;
}
export interface HttpResponse<T = any, D = HttpTask> {
data: T;
statusCode: number;
header: HttpResponseHeader;
config: HttpRequestConfig<D>;
cookies: string[];
errMsg: string;
rawData: any;
}
export interface HttpUploadResponse<T = any, D = HttpTask> {
data: T;
statusCode: number;
config: HttpRequestConfig<D>;
errMsg: string;
rawData: any;
}
export interface HttpDownloadResponse extends HttpResponse {
tempFilePath: string;
apFilePath?: string;
filePath?: string;
fileContent?: string;
}
export interface HttpError<T = any, D = HttpTask> {
data?: T;
statusCode?: number;
header?: HttpResponseHeader;
config: HttpRequestConfig<D>;
cookies?: string[];
errMsg: string;
}
export interface HttpPromise<T = any> extends Promise<HttpResponse<T>> {
}
export interface HttpInterceptorManager<V, E = V> {
use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: E) => T | Promise<E>): void;
eject(id: number): void;
}
export abstract class HttpRequestAbstract {
constructor(config?: HttpRequestConfig);
interceptors: {
request: HttpInterceptorManager<HttpRequestConfig>;
response: HttpInterceptorManager<HttpResponse, HttpError>;
}
request<T = any, R = HttpResponse<T>, D = HttpRequestTask>(config: HttpRequestConfig<D>): Promise<R>;
get<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
delete<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
head<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
options<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
post<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
put<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
config: HttpRequestConfig;
setConfig<D = HttpTask>(onSend: (config: HttpRequestConfig<D>) => HttpRequestConfig<D>): void;
connect<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
trace<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
upload<T = any, R = HttpUploadResponse<T>, D = HttpUploadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
download<T = any, R = HttpDownloadResponse<T>, D = HttpDownloadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
middleware<T = any, R = HttpResponse<T>, D = HttpTask>(config: HttpRequestConfig<D>): Promise<R>;
}
declare class HttpRequest extends HttpRequestAbstract {
}
export default HttpRequest;

2
src/uni_modules/uv-ui-tools/libs/luch-request/index.js

@ -0,0 +1,2 @@
import Request from './core/Request'
export default Request

135
src/uni_modules/uv-ui-tools/libs/luch-request/utils.js

@ -0,0 +1,135 @@
'use strict'
// utils is a library of generic helper functions non-specific to axios
var toString = Object.prototype.toString
/**
* Determine if a value is an Array
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
export function isArray (val) {
return toString.call(val) === '[object Array]'
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
export function isObject (val) {
return val !== null && typeof val === 'object'
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
export function isDate (val) {
return toString.call(val) === '[object Date]'
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
export function isURLSearchParams (val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
export function forEach (obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
/*eslint no-param-reassign:0*/
obj = [obj]
}
if (isArray(obj)) {
// Iterate over array values
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj)
}
} else {
// Iterate over object keys
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj)
}
}
}
}
/**
* 是否为boolean
* @param val
* @returns {boolean}
*/
export function isBoolean(val) {
return typeof val === 'boolean'
}
/**
* 是否为真正的对象{} new Object
* @param {any} obj - 检测的对象
* @returns {boolean}
*/
export function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
/**
* Function equal to merge with the difference being that no reference
* to original objects is kept.
*
* @see merge
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
export function deepMerge(/* obj1, obj2, obj3, ... */) {
let result = {}
function assignValue(val, key) {
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = deepMerge(result[key], val)
} else if (typeof val === 'object') {
result[key] = deepMerge({}, val)
} else {
result[key] = val
}
}
for (let i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue)
}
return result
}
export function isUndefined (val) {
return typeof val === 'undefined'
}

264
src/uni_modules/uv-ui-tools/libs/luch-request/utils/clone.js

@ -0,0 +1,264 @@
/* eslint-disable */
var clone = (function() {
'use strict';
function _instanceof(obj, type) {
return type != null && obj instanceof type;
}
var nativeMap;
try {
nativeMap = Map;
} catch(_) {
// maybe a reference error because no `Map`. Give it a dummy value that no
// value will ever be an instanceof.
nativeMap = function() {};
}
var nativeSet;
try {
nativeSet = Set;
} catch(_) {
nativeSet = function() {};
}
var nativePromise;
try {
nativePromise = Promise;
} catch(_) {
nativePromise = function() {};
}
/**
* Clones (copies) an Object using deep copying.
*
* This function supports circular references by default, but if you are certain
* there are no circular references in your object, you can save some CPU time
* by calling clone(obj, false).
*
* Caution: if `circular` is false and `parent` contains circular references,
* your program may enter an infinite loop and crash.
*
* @param `parent` - the object to be cloned
* @param `circular` - set to true if the object to be cloned may contain
* circular references. (optional - true by default)
* @param `depth` - set to a number if the object is only to be cloned to
* a particular depth. (optional - defaults to Infinity)
* @param `prototype` - sets the prototype to be used when cloning an object.
* (optional - defaults to parent prototype).
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
* should be cloned as well. Non-enumerable properties on the prototype
* chain will be ignored. (optional - false by default)
*/
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
if (typeof circular === 'object') {
depth = circular.depth;
prototype = circular.prototype;
includeNonEnumerable = circular.includeNonEnumerable;
circular = circular.circular;
}
// maintain two arrays for circular references, where corresponding parents
// and children have the same index
var allParents = [];
var allChildren = [];
var useBuffer = typeof Buffer != 'undefined';
if (typeof circular == 'undefined')
circular = true;
if (typeof depth == 'undefined')
depth = Infinity;
// recurse this function so we don't reset allParents and allChildren
function _clone(parent, depth) {
// cloning null always returns null
if (parent === null)
return null;
if (depth === 0)
return parent;
var child;
var proto;
if (typeof parent != 'object') {
return parent;
}
if (_instanceof(parent, nativeMap)) {
child = new nativeMap();
} else if (_instanceof(parent, nativeSet)) {
child = new nativeSet();
} else if (_instanceof(parent, nativePromise)) {
child = new nativePromise(function (resolve, reject) {
parent.then(function(value) {
resolve(_clone(value, depth - 1));
}, function(err) {
reject(_clone(err, depth - 1));
});
});
} else if (clone.__isArray(parent)) {
child = [];
} else if (clone.__isRegExp(parent)) {
child = new RegExp(parent.source, __getRegExpFlags(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (clone.__isDate(parent)) {
child = new Date(parent.getTime());
} else if (useBuffer && Buffer.isBuffer(parent)) {
if (Buffer.from) {
// Node.js >= 5.10.0
child = Buffer.from(parent);
} else {
// Older Node.js versions
child = new Buffer(parent.length);
parent.copy(child);
}
return child;
} else if (_instanceof(parent, Error)) {
child = Object.create(parent);
} else {
if (typeof prototype == 'undefined') {
proto = Object.getPrototypeOf(parent);
child = Object.create(proto);
}
else {
child = Object.create(prototype);
proto = prototype;
}
}
if (circular) {
var index = allParents.indexOf(parent);
if (index != -1) {
return allChildren[index];
}
allParents.push(parent);
allChildren.push(child);
}
if (_instanceof(parent, nativeMap)) {
parent.forEach(function(value, key) {
var keyChild = _clone(key, depth - 1);
var valueChild = _clone(value, depth - 1);
child.set(keyChild, valueChild);
});
}
if (_instanceof(parent, nativeSet)) {
parent.forEach(function(value) {
var entryChild = _clone(value, depth - 1);
child.add(entryChild);
});
}
for (var i in parent) {
var attrs = Object.getOwnPropertyDescriptor(parent, i);
if (attrs) {
child[i] = _clone(parent[i], depth - 1);
}
try {
var objProperty = Object.getOwnPropertyDescriptor(parent, i);
if (objProperty.set === 'undefined') {
// no setter defined. Skip cloning this property
continue;
}
child[i] = _clone(parent[i], depth - 1);
} catch(e){
if (e instanceof TypeError) {
// when in strict mode, TypeError will be thrown if child[i] property only has a getter
// we can't do anything about this, other than inform the user that this property cannot be set.
continue
} else if (e instanceof ReferenceError) {
//this may happen in non strict mode
continue
}
}
}
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(parent);
for (var i = 0; i < symbols.length; i++) {
// Don't need to worry about cloning a symbol because it is a primitive,
// like a number or string.
var symbol = symbols[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
continue;
}
child[symbol] = _clone(parent[symbol], depth - 1);
Object.defineProperty(child, symbol, descriptor);
}
}
if (includeNonEnumerable) {
var allPropertyNames = Object.getOwnPropertyNames(parent);
for (var i = 0; i < allPropertyNames.length; i++) {
var propertyName = allPropertyNames[i];
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
if (descriptor && descriptor.enumerable) {
continue;
}
child[propertyName] = _clone(parent[propertyName], depth - 1);
Object.defineProperty(child, propertyName, descriptor);
}
}
return child;
}
return _clone(parent, depth);
}
/**
* Simple flat clone using prototype, accepts only objects, usefull for property
* override on FLAT configuration object (no nested props).
*
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
* works.
*/
clone.clonePrototype = function clonePrototype(parent) {
if (parent === null)
return null;
var c = function () {};
c.prototype = parent;
return new c();
};
// private utility functions
function __objToStr(o) {
return Object.prototype.toString.call(o);
}
clone.__objToStr = __objToStr;
function __isDate(o) {
return typeof o === 'object' && __objToStr(o) === '[object Date]';
}
clone.__isDate = __isDate;
function __isArray(o) {
return typeof o === 'object' && __objToStr(o) === '[object Array]';
}
clone.__isArray = __isArray;
function __isRegExp(o) {
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
}
clone.__isRegExp = __isRegExp;
function __getRegExpFlags(re) {
var flags = '';
if (re.global) flags += 'g';
if (re.ignoreCase) flags += 'i';
if (re.multiline) flags += 'm';
return flags;
}
clone.__getRegExpFlags = __getRegExpFlags;
return clone;
})();
export default clone

13
src/uni_modules/uv-ui-tools/libs/mixin/button.js

@ -0,0 +1,13 @@
export default {
props: {
lang: String,
sessionFrom: String,
sendMessageTitle: String,
sendMessagePath: String,
sendMessageImg: String,
showMessageCard: Boolean,
appParameter: String,
formType: String,
openType: String
}
}

172
src/uni_modules/uv-ui-tools/libs/mixin/mixin.js

@ -0,0 +1,172 @@
import * as index from '../function/index.js';
import * as test from '../function/test.js';
import route from '../util/route.js';
import debounce from '../function/debounce.js';
import throttle from '../function/throttle.js';
export default {
// 定义每个组件都可能需要用到的外部样式以及类名
props: {
// 每个组件都有的父组件传递的样式,可以为字符串或者对象形式
customStyle: {
type: [Object, String],
default: () => ({})
},
customClass: {
type: String,
default: ''
},
// 跳转的页面路径
url: {
type: String,
default: ''
},
// 页面跳转的类型
linkType: {
type: String,
default: 'navigateTo'
}
},
data() {
return {}
},
onLoad() {
// getRect挂载到$uv上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出
this.$uv.getRect = this.$uvGetRect
},
created() {
// 组件当中,只有created声明周期,为了能在组件使用,故也在created中将方法挂载到$uv
this.$uv.getRect = this.$uvGetRect
},
computed: {
$uv() {
return {
...index,
test,
route,
debounce,
throttle,
unit: uni?.$uv?.config?.unit
}
},
/**
* 生成bem规则类名
* 由于微信小程序H5nvue之间绑定class的差异无法通过:class="[bem()]"的形式进行同用
* 故采用如下折中做法最后返回的是数组一般平台或字符串支付宝和字节跳动平台类似['a', 'b', 'c']'a b c'的形式
* @param {String} name 组件名称
* @param {Array} fixed 一直会存在的类名
* @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名
* @returns {Array|string}
*/
bem() {
return function(name, fixed, change) {
// 类名前缀
const prefix = `uv-${name}--`
const classes = {}
if (fixed) {
fixed.map((item) => {
// 这里的类名,会一直存在
classes[prefix + this[item]] = true
})
}
if (change) {
change.map((item) => {
// 这里的类名,会根据this[item]的值为true或者false,而进行添加或者移除某一个类
this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item])
})
}
return Object.keys(classes)
// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
// #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK || MP-BAIDU
.join(' ')
// #endif
}
}
},
methods: {
// 跳转某一个页面
openPage(urlKey = 'url') {
const url = this[urlKey]
if (url) {
// 执行类似uni.navigateTo的方法
uni[this.linkType]({
url
})
}
},
// 查询节点信息
// 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
// 解决办法为在组件根部再套一个没有任何作用的view元素
$uvGetRect(selector, all) {
return new Promise((resolve) => {
uni.createSelectorQuery()
.in(this)[all ? 'selectAll' : 'select'](selector)
.boundingClientRect((rect) => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect)
}
if (!all && rect) {
resolve(rect)
}
})
.exec()
})
},
getParentData(parentName = '') {
// 避免在created中去定义parent变量
if (!this.parent) this.parent = {}
// 这里的本质原理是,通过获取父组件实例(也即类似uv-radio的父组件uv-radio-group的this)
// 将父组件this中对应的参数,赋值给本组件(uv-radio的this)的parentData对象中对应的属性
// 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
// 此处并不会自动更新子组件的数据,而是依赖父组件uv-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取
this.parent = this.$uv.$parent.call(this, parentName)
if (this.parent.children) {
// 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中
this.parent.children.indexOf(this) === -1 && this.parent.children.push(this)
}
if (this.parent && this.parentData) {
// 历遍parentData中的属性,将parent中的同名属性赋值给parentData
Object.keys(this.parentData).map((key) => {
this.parentData[key] = this.parent[key]
})
}
},
// 阻止事件冒泡
preventEvent(e) {
e && typeof(e.stopPropagation) === 'function' && e.stopPropagation()
},
// 空操作
noop(e) {
this.preventEvent(e)
}
},
onReachBottom() {
uni.$emit('uvOnReachBottom')
},
beforeDestroy() {
// 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
// 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
if (this.parent && test.array(this.parent.children)) {
// 组件销毁时,移除父组件中的children数组中对应的实例
const childrenList = this.parent.children
childrenList.map((child, index) => {
// 如果相等,则移除
if (child === this) {
childrenList.splice(index, 1)
}
})
}
},
// 兼容vue3
unmounted() {
if (this.parent && test.array(this.parent.children)) {
// 组件销毁时,移除父组件中的children数组中对应的实例
const childrenList = this.parent.children
childrenList.map((child, index) => {
// 如果相等,则移除
if (child === this) {
childrenList.splice(index, 1)
}
})
}
}
}

8
src/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js

@ -0,0 +1,8 @@
export default {
// #ifdef MP-WEIXIN
// 将自定义节点设置成虚拟的(去掉自定义组件包裹层),更加接近Vue组件的表现,能更好的使用flex属性
options: {
virtualHost: true
}
// #endif
}

13
src/uni_modules/uv-ui-tools/libs/mixin/mpShare.js

@ -0,0 +1,13 @@
export default {
onLoad() {
// 设置默认的转发参数
uni.$uv.mpShare = {
title: '', // 默认为小程序名称
path: '', // 默认为当前页面路径
imageUrl: '' // 默认为当前页面的截图
}
},
onShareAppMessage() {
return uni.$uv.mpShare
}
}

47
src/uni_modules/uv-ui-tools/libs/mixin/openType.js

@ -0,0 +1,47 @@
export default {
props: {
openType: String
},
emits: ['getphonenumber','getuserinfo','error','opensetting','launchapp','contact','chooseavatar','addgroupapp','chooseaddress','subscribe','login','im'],
methods: {
onGetPhoneNumber(event) {
this.$emit('getphonenumber', event.detail)
},
onGetUserInfo(event) {
this.$emit('getuserinfo', event.detail)
},
onError(event) {
this.$emit('error', event.detail)
},
onOpenSetting(event) {
this.$emit('opensetting', event.detail)
},
onLaunchApp(event) {
this.$emit('launchapp', event.detail)
},
onContact(event) {
this.$emit('contact', event.detail)
},
onChooseavatar(event) {
this.$emit('chooseavatar', event.detail)
},
onAgreeprivacyauthorization(event) {
this.$emit('agreeprivacyauthorization', event.detail)
},
onAddgroupapp(event) {
this.$emit('addgroupapp', event.detail)
},
onChooseaddress(event) {
this.$emit('chooseaddress', event.detail)
},
onSubscribe(event) {
this.$emit('subscribe', event.detail)
},
onLogin(event) {
this.$emit('login', event.detail)
},
onIm(event) {
this.$emit('im', event.detail)
}
}
}

59
src/uni_modules/uv-ui-tools/libs/mixin/touch.js

@ -0,0 +1,59 @@
const MIN_DISTANCE = 10
function getDirection(x, y) {
if (x > y && x > MIN_DISTANCE) {
return 'horizontal'
}
if (y > x && y > MIN_DISTANCE) {
return 'vertical'
}
return ''
}
export default {
methods: {
getTouchPoint(e) {
if (!e) {
return {
x: 0,
y: 0
}
} if (e.touches && e.touches[0]) {
return {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
} if (e.changedTouches && e.changedTouches[0]) {
return {
x: e.changedTouches[0].pageX,
y: e.changedTouches[0].pageY
}
}
return {
x: e.clientX || 0,
y: e.clientY || 0
}
},
resetTouchStatus() {
this.direction = ''
this.deltaX = 0
this.deltaY = 0
this.offsetX = 0
this.offsetY = 0
},
touchStart(event) {
this.resetTouchStatus()
const touch = this.getTouchPoint(event)
this.startX = touch.x
this.startY = touch.y
},
touchMove(event) {
const touch = this.getTouchPoint(event)
this.deltaX = touch.x - this.startX
this.deltaY = touch.y - this.startY
this.offsetX = Math.abs(this.deltaX)
this.offsetY = Math.abs(this.deltaY)
this.direction = this.direction || getDirection(this.offsetX, this.offsetY)
}
}
}

216
src/uni_modules/uv-ui-tools/libs/util/dayjs.js

@ -0,0 +1,216 @@
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var require_dayjs_min = __commonJS({
"uvuidayjs"(exports, module) {
!function(t, e) {
"object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e();
}(exports, function() {
"use strict";
var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", f = "month", h = "quarter", c = "year", d = "date", l = "Invalid Date", $ = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) {
var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100;
return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]";
} }, m = function(t2, e2, n2) {
var r2 = String(t2);
return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2;
}, v = { s: m, z: function(t2) {
var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60;
return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0");
}, m: function t2(e2, n2) {
if (e2.date() < n2.date())
return -t2(n2, e2);
var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, f), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), f);
return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0);
}, a: function(t2) {
return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2);
}, p: function(t2) {
return { M: f, y: c, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: h }[t2] || String(t2 || "").toLowerCase().replace(/s$/, "");
}, u: function(t2) {
return void 0 === t2;
} }, g = "en", D = {};
D[g] = M;
var p = function(t2) {
return t2 instanceof _;
}, S = function t2(e2, n2, r2) {
var i2;
if (!e2)
return g;
if ("string" == typeof e2) {
var s2 = e2.toLowerCase();
D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2);
var u2 = e2.split("-");
if (!i2 && u2.length > 1)
return t2(u2[0]);
} else {
var a2 = e2.name;
D[a2] = e2, i2 = a2;
}
return !r2 && i2 && (g = i2), i2 || !r2 && g;
}, w = function(t2, e2) {
if (p(t2))
return t2.clone();
var n2 = "object" == typeof e2 ? e2 : {};
return n2.date = t2, n2.args = arguments, new _(n2);
}, O = v;
O.l = S, O.i = p, O.w = function(t2, e2) {
return w(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset });
};
var _ = function() {
function M2(t2) {
this.$L = S(t2.locale, null, true), this.parse(t2);
}
var m2 = M2.prototype;
return m2.parse = function(t2) {
this.$d = function(t3) {
var e2 = t3.date, n2 = t3.utc;
if (null === e2)
return new Date(NaN);
if (O.u(e2))
return new Date();
if (e2 instanceof Date)
return new Date(e2);
if ("string" == typeof e2 && !/Z$/i.test(e2)) {
var r2 = e2.match($);
if (r2) {
var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3);
return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2);
}
}
return new Date(e2);
}(t2), this.$x = t2.x || {}, this.init();
}, m2.init = function() {
var t2 = this.$d;
this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds();
}, m2.$utils = function() {
return O;
}, m2.isValid = function() {
return !(this.$d.toString() === l);
}, m2.isSame = function(t2, e2) {
var n2 = w(t2);
return this.startOf(e2) <= n2 && n2 <= this.endOf(e2);
}, m2.isAfter = function(t2, e2) {
return w(t2) < this.startOf(e2);
}, m2.isBefore = function(t2, e2) {
return this.endOf(e2) < w(t2);
}, m2.$g = function(t2, e2, n2) {
return O.u(t2) ? this[e2] : this.set(n2, t2);
}, m2.unix = function() {
return Math.floor(this.valueOf() / 1e3);
}, m2.valueOf = function() {
return this.$d.getTime();
}, m2.startOf = function(t2, e2) {
var n2 = this, r2 = !!O.u(e2) || e2, h2 = O.p(t2), l2 = function(t3, e3) {
var i2 = O.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2);
return r2 ? i2 : i2.endOf(a);
}, $2 = function(t3, e3) {
return O.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2);
}, y2 = this.$W, M3 = this.$M, m3 = this.$D, v2 = "set" + (this.$u ? "UTC" : "");
switch (h2) {
case c:
return r2 ? l2(1, 0) : l2(31, 11);
case f:
return r2 ? l2(1, M3) : l2(0, M3 + 1);
case o:
var g2 = this.$locale().weekStart || 0, D2 = (y2 < g2 ? y2 + 7 : y2) - g2;
return l2(r2 ? m3 - D2 : m3 + (6 - D2), M3);
case a:
case d:
return $2(v2 + "Hours", 0);
case u:
return $2(v2 + "Minutes", 1);
case s:
return $2(v2 + "Seconds", 2);
case i:
return $2(v2 + "Milliseconds", 3);
default:
return this.clone();
}
}, m2.endOf = function(t2) {
return this.startOf(t2, false);
}, m2.$set = function(t2, e2) {
var n2, o2 = O.p(t2), h2 = "set" + (this.$u ? "UTC" : ""), l2 = (n2 = {}, n2[a] = h2 + "Date", n2[d] = h2 + "Date", n2[f] = h2 + "Month", n2[c] = h2 + "FullYear", n2[u] = h2 + "Hours", n2[s] = h2 + "Minutes", n2[i] = h2 + "Seconds", n2[r] = h2 + "Milliseconds", n2)[o2], $2 = o2 === a ? this.$D + (e2 - this.$W) : e2;
if (o2 === f || o2 === c) {
var y2 = this.clone().set(d, 1);
y2.$d[l2]($2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d;
} else
l2 && this.$d[l2]($2);
return this.init(), this;
}, m2.set = function(t2, e2) {
return this.clone().$set(t2, e2);
}, m2.get = function(t2) {
return this[O.p(t2)]();
}, m2.add = function(r2, h2) {
var d2, l2 = this;
r2 = Number(r2);
var $2 = O.p(h2), y2 = function(t2) {
var e2 = w(l2);
return O.w(e2.date(e2.date() + Math.round(t2 * r2)), l2);
};
if ($2 === f)
return this.set(f, this.$M + r2);
if ($2 === c)
return this.set(c, this.$y + r2);
if ($2 === a)
return y2(1);
if ($2 === o)
return y2(7);
var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[$2] || 1, m3 = this.$d.getTime() + r2 * M3;
return O.w(m3, this);
}, m2.subtract = function(t2, e2) {
return this.add(-1 * t2, e2);
}, m2.format = function(t2) {
var e2 = this, n2 = this.$locale();
if (!this.isValid())
return n2.invalidDate || l;
var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = O.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, f2 = n2.months, h2 = function(t3, n3, i3, s3) {
return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3);
}, c2 = function(t3) {
return O.s(s2 % 12 || 12, t3, "0");
}, d2 = n2.meridiem || function(t3, e3, n3) {
var r3 = t3 < 12 ? "AM" : "PM";
return n3 ? r3.toLowerCase() : r3;
}, $2 = { YY: String(this.$y).slice(-2), YYYY: this.$y, M: a2 + 1, MM: O.s(a2 + 1, 2, "0"), MMM: h2(n2.monthsShort, a2, f2, 3), MMMM: h2(f2, a2), D: this.$D, DD: O.s(this.$D, 2, "0"), d: String(this.$W), dd: h2(n2.weekdaysMin, this.$W, o2, 2), ddd: h2(n2.weekdaysShort, this.$W, o2, 3), dddd: o2[this.$W], H: String(s2), HH: O.s(s2, 2, "0"), h: c2(1), hh: c2(2), a: d2(s2, u2, true), A: d2(s2, u2, false), m: String(u2), mm: O.s(u2, 2, "0"), s: String(this.$s), ss: O.s(this.$s, 2, "0"), SSS: O.s(this.$ms, 3, "0"), Z: i2 };
return r2.replace(y, function(t3, e3) {
return e3 || $2[t3] || i2.replace(":", "");
});
}, m2.utcOffset = function() {
return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
}, m2.diff = function(r2, d2, l2) {
var $2, y2 = O.p(d2), M3 = w(r2), m3 = (M3.utcOffset() - this.utcOffset()) * e, v2 = this - M3, g2 = O.m(this, M3);
return g2 = ($2 = {}, $2[c] = g2 / 12, $2[f] = g2, $2[h] = g2 / 3, $2[o] = (v2 - m3) / 6048e5, $2[a] = (v2 - m3) / 864e5, $2[u] = v2 / n, $2[s] = v2 / e, $2[i] = v2 / t, $2)[y2] || v2, l2 ? g2 : O.a(g2);
}, m2.daysInMonth = function() {
return this.endOf(f).$D;
}, m2.$locale = function() {
return D[this.$L];
}, m2.locale = function(t2, e2) {
if (!t2)
return this.$L;
var n2 = this.clone(), r2 = S(t2, e2, true);
return r2 && (n2.$L = r2), n2;
}, m2.clone = function() {
return O.w(this.$d, this);
}, m2.toDate = function() {
return new Date(this.valueOf());
}, m2.toJSON = function() {
return this.isValid() ? this.toISOString() : null;
}, m2.toISOString = function() {
return this.$d.toISOString();
}, m2.toString = function() {
return this.$d.toUTCString();
}, M2;
}(), T = _.prototype;
return w.prototype = T, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", f], ["$y", c], ["$D", d]].forEach(function(t2) {
T[t2[1]] = function(e2) {
return this.$g(e2, t2[0], t2[1]);
};
}), w.extend = function(t2, e2) {
return t2.$i || (t2(e2, _, w), t2.$i = true), w;
}, w.locale = S, w.isDayjs = p, w.unix = function(t2) {
return w(1e3 * t2);
}, w.en = D[g], w.Ls = D, w.p = {}, w;
});
}
});
export default require_dayjs_min();

126
src/uni_modules/uv-ui-tools/libs/util/route.js

@ -0,0 +1,126 @@
/**
* 路由跳转方法该方法相对于直接使用uni.xxx的好处是使用更加简单快捷
* 并且带有路由拦截功能
*/
import { queryParams, deepMerge, page } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
class Router {
constructor() {
// 原始属性定义
this.config = {
type: 'navigateTo',
url: '',
delta: 1, // navigateBack页面后退时,回退的层数
params: {}, // 传递的参数
animationType: 'pop-in', // 窗口动画,只在APP有效
animationDuration: 300, // 窗口动画持续时间,单位毫秒,只在APP有效
intercept: false ,// 是否需要拦截
events: {} // 页面间通信接口,用于监听被打开页面发送到当前页面的数据。hbuilderx 2.8.9+ 开始支持。
}
// 因为route方法是需要对外赋值给另外的对象使用,同时route内部有使用this,会导致route失去上下文
// 这里在构造函数中进行this绑定
this.route = this.route.bind(this)
}
// 判断url前面是否有"/",如果没有则加上,否则无法跳转
addRootPath(url) {
return url[0] === '/' ? url : `/${url}`
}
// 整合路由参数
mixinParam(url, params) {
url = url && this.addRootPath(url)
// 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
// 如果有url中有get参数,转换后无需带上"?"
let query = ''
if (/.*\/.*\?.*=.*/.test(url)) {
// object对象转为get类型的参数
query = queryParams(params, false)
// 因为已有get参数,所以后面拼接的参数需要带上"&"隔开
return url += `&${query}`
}
// 直接拼接参数,因为此处url中没有后面的query参数,也就没有"?/&"之类的符号
query = queryParams(params)
return url += query
}
// 对外的方法名称
async route(options = {}, params = {}) {
// 合并用户的配置和内部的默认配置
let mergeConfig = {}
if (typeof options === 'string') {
// 如果options为字符串,则为route(url, params)的形式
mergeConfig.url = this.mixinParam(options, params)
mergeConfig.type = 'navigateTo'
} else {
mergeConfig = deepMerge(this.config, options)
// 否则正常使用mergeConfig中的url和params进行拼接
mergeConfig.url = this.mixinParam(options.url, options.params)
}
// 如果本次跳转的路径和本页面路径一致,不执行跳转,防止用户快速点击跳转按钮,造成多次跳转同一个页面的问题
if (mergeConfig.url === page()) return
if (params.intercept) {
mergeConfig.intercept = params.intercept
}
// params参数也带给拦截器
mergeConfig.params = params
// 合并内外部参数
mergeConfig = deepMerge(this.config, mergeConfig)
// 判断用户是否定义了拦截器
if (typeof mergeConfig.intercept === 'function') {
// 定一个promise,根据用户执行resolve(true)或者resolve(false)来决定是否进行路由跳转
const isNext = await new Promise((resolve, reject) => {
mergeConfig.intercept(mergeConfig, resolve)
})
// 如果isNext为true,则执行路由跳转
isNext && this.openPage(mergeConfig)
} else {
this.openPage(mergeConfig)
}
}
// 执行路由跳转
openPage(config) {
// 解构参数
const {
url,
type,
delta,
animationType,
animationDuration,
events
} = config
if (config.type == 'navigateTo' || config.type == 'to') {
uni.navigateTo({
url,
animationType,
animationDuration,
events
})
}
if (config.type == 'redirectTo' || config.type == 'redirect') {
uni.redirectTo({
url
})
}
if (config.type == 'switchTab' || config.type == 'tab') {
uni.switchTab({
url
})
}
if (config.type == 'reLaunch' || config.type == 'launch') {
uni.reLaunch({
url
})
}
if (config.type == 'navigateBack' || config.type == 'back') {
uni.navigateBack({
delta
})
}
}
}
export default (new Router()).route

81
src/uni_modules/uv-ui-tools/package.json

@ -0,0 +1,81 @@
{
"id": "uv-ui-tools",
"displayName": "uv-ui-tools 工具集 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.1.25",
"description": "uv-ui-tools,集成工具库,强大的Http请求封装,清晰的文档说明,开箱即用。方便使用,可以全局使用",
"keywords": [
"uv-ui-tools,uv-ui组件库,工具集,uvui,uView2.x"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}

23
src/uni_modules/uv-ui-tools/readme.md

@ -0,0 +1,23 @@
## uv-ui-tools 工具集
> **组件名:uv-ui-tools**
uv-ui工具集成,包括网络Http请求、便捷工具、节流防抖、对象操作、时间格式化、路由跳转、全局唯一标识符、规则校验等等。
该组件推荐配合[uv-ui组件库](https://www.uvui.cn/components/intro.html)使用,单独下载也可以在自己项目中使用,需要做相应的配置,可查看文档。强烈推荐使用[uv-ui组件库](https://www.uvui.cn/components/intro.html),导入组件都会自动导入`uv-ui-tools`。需要在自己的项目中使用请参考[扩展配置](https://www.uvui.cn/components/setting.html)。
uv-ui破釜沉舟之兼容vue3+2、app、h5、多端小程序的uni-app生态框架,大部分组件基于uView2.x,在经过改进后全面支持vue3,部分组件做了进一步的优化,修复大量BUG,支持单独导入,方便开发者选择导入需要的组件。开箱即用,灵活配置。
# <a href="https://www.uvui.cn/js/intro.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

43
src/uni_modules/uv-ui-tools/theme.scss

@ -0,0 +1,43 @@
// 此文件为uvUI的主题变量这些变量目前只能通过uni.scss引入才有效另外由于
// uni.scss中引入的样式会同时混入到全局样式文件和单独每一个页面的样式中造成微信程序包太大
// 故uni.scss只建议放scss变量名相关样式其他的样式可以通过main.js或者App.vue引入
$uv-main-color: #303133;
$uv-content-color: #606266;
$uv-tips-color: #909193;
$uv-light-color: #c0c4cc;
$uv-border-color: #dadbde;
$uv-bg-color: #f3f4f6;
$uv-disabled-color: #c8c9cc;
$uv-primary: #3c9cff;
$uv-primary-dark: #398ade;
$uv-primary-disabled: #9acafc;
$uv-primary-light: #ecf5ff;
$uv-warning: #f9ae3d;
$uv-warning-dark: #f1a532;
$uv-warning-disabled: #f9d39b;
$uv-warning-light: #fdf6ec;
$uv-success: #5ac725;
$uv-success-dark: #53c21d;
$uv-success-disabled: #a9e08f;
$uv-success-light: #f5fff0;
$uv-error: #f56c6c;
$uv-error-dark: #e45656;
$uv-error-disabled: #f7b2b2;
$uv-error-light: #fef0f0;
$uv-info: #909399;
$uv-info-dark: #767a82;
$uv-info-disabled: #c4c6c9;
$uv-info-light: #f4f4f5;
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
Loading…
Cancel
Save