评论商品接口有问题
This commit is contained in:
@@ -1,36 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="43px" height="42px" viewBox="0 0 43 42" version="1.1">
|
||||
<title>编组 3</title>
|
||||
<defs>
|
||||
<path d="M52.1440033,262.168279 C53.7071313,260.607213 56.2397907,260.608885 57.8008563,262.172013 L57.8008563,262.172013 L71.61,275.999572 L301,276 C308.731986,276 315,282.268014 315,290 L315,449 C315,456.731986 308.731986,463 301,463 L29,463 C21.2680135,463 15,456.731986 15,449 L15,290 C15,282.268014 21.2680135,276 29,276 L38.33,275.999572 L52.1402692,262.172013 L52.1402692,262.172013 Z" id="path-1"></path>
|
||||
<filter x="-6.7%" y="-7.9%" width="113.3%" height="119.8%" filterUnits="objectBoundingBox" id="filter-2">
|
||||
<feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="6" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="信息页-发起聊天" transform="translate(-47.000000, -311.000000)">
|
||||
<rect fill="#000000" x="0" y="0" width="750" height="1624"></rect>
|
||||
<rect id="矩形" fill="#FFFFFF" x="0" y="0" width="750" height="1624"></rect>
|
||||
<rect id="矩形" fill-opacity="0.1" fill="#FFFFFF" x="0" y="284" width="750" height="144"></rect>
|
||||
<g id="编组-5" transform="translate(32.000000, 298.000000)">
|
||||
<g id="位图" transform="translate(0.000000, 8.000000)">
|
||||
<rect id="矩形" fill="#006EFF" x="0" y="0" width="96" height="96" rx="8"></rect>
|
||||
<path d="M42.4156863,59.7575758 C42.5411765,59.6363636 42.7921569,59.6363636 42.917647,59.7575758 C42.917647,59.7575758 44.172549,60.8484849 47.0588235,60.8484849 C47.6862744,60.8484849 48.1882353,60.8484849 48.8156863,60.7272727 C52.9568628,60.2424242 53.9607843,58.5454545 53.9607843,58.5454545 C54.0862745,58.4242424 54.3372549,58.3030303 54.4627451,58.4242424 C54.5882353,58.5454545 54.7137255,58.6666666 54.7137255,58.9090909 C53.8352941,61.3333333 51.5764706,63.1515151 48.9411764,63.5151515 L48.0627451,63.5151515 C45.6784314,63.5151515 43.4196078,62.3030302 42.1647058,60.3636364 C42.2901961,60 42.2901961,59.8787878 42.4156863,59.7575758 L42.4156863,59.7575758 Z M47.5607843,21.3333333 C58.6039216,21.3333333 67.7647059,28.8484848 70.1490196,38.7878788 L70.2745098,38.7878788 C72.7843137,38.7878788 74.6666667,40.7272727 74.6666667,43.0303031 L74.6666667,51.5151515 C74.6666667,53.3333333 73.5372549,54.7878788 71.9058823,55.3939394 C70.1490196,64.969697 61.4901961,71.2727273 50.6980392,72.1212121 C50.1960784,73.5757576 48.8156863,74.6666667 47.0588235,74.6666667 C44.9254902,74.6666667 43.2941176,73.0909091 43.2941176,71.030303 C43.2941176,68.969697 45.0509803,67.3939394 47.0588235,67.3939394 C48.6901961,67.3939394 50.0705882,68.3636363 50.572549,69.6969697 C59.8588235,68.9696969 67.3882353,63.8787878 69.2705882,55.6363636 C68.1411764,55.3939394 67.1372549,54.7878788 66.5098039,53.8181818 C64.8784314,58.5454545 61.4901961,62.5454545 57.0980392,64.8484849 L56.972549,64.9696969 L56.5960784,64.9696969 C56.4915032,64.8686868 56.474074,64.8518518 56.4711692,64.7789001 L56.4705882,64.7272727 C56.4705882,64.6060606 56.4705882,64.6060606 56.5960784,64.4848485 C60.9882353,60.6060606 61.2392157,56.8484849 60.7372549,51.2727273 C60.3607843,46.4242424 57.3490196,41.8181818 55.9686275,39.7575758 L55.8431372,39.8787879 C55.717647,39.7575757 55.5921569,39.6363636 55.4666667,39.6363636 C55.2156863,39.6363636 55.0901961,39.7575758 54.9647058,40 C54.8392156,41.4545455 54.462745,42.4242424 54.2117647,42.9090909 C53.2078431,44.9696969 51.5764706,46.3030303 49.1921569,46.6666667 C45.427451,47.2727273 41.6627451,48.1212121 38.0235294,48.7272727 C35.6392156,49.0909091 33.6313725,51.6363636 34.1333333,54.3030303 C34.9926684,59.9947299 38.974432,64.1784384 40.2044901,65.1519921 L40.2153333,65.1603333 L40.2039215,65.1515151 C40.1568627,65.1212121 40.0941176,65.0909091 40.0313725,65.0909091 C39.9058824,65.0909091 39.9058823,65.2121212 39.7803922,65.2121212 C39.6549019,65.3333333 39.5294118,65.5757576 39.5294117,65.5757576 C34.6352941,63.5151515 30.745098,59.5151516 28.8627451,54.6666667 C28.1098039,55.6363636 26.8549019,56.2424243 25.6,56.2424242 C23.2156862,56.2424242 21.3333333,54.3030303 21.3333333,52 L21.3333333,43.6363636 C21.3333333,41.5757576 22.8392157,39.8787879 24.8470588,39.5151515 C26.8549019,29.2121212 36.2666666,21.3333333 47.5607843,21.3333333 Z M47.5607843,25.0909091 C38.0235294,25.0909091 29.9921569,31.8787879 28.4862745,40.7272727 C28.6117647,40.8484849 28.8627451,40.969697 28.9882352,41.2121212 C31.8745097,33.939394 39.1529411,28.7272727 47.6862745,28.7272727 C56.2196078,28.7272727 63.4980392,33.939394 66.3843137,41.3333333 L66.7607843,40.6060606 C65.0039216,31.7575757 57.0980392,25.0909091 47.5607843,25.0909091 Z" id="形状" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="形状结合">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
|
||||
<path stroke="#E0E0E0" stroke-width="2" d="M54.8888733,262.000133 C55.625226,261.979907 56.3673182,262.22911 56.9545645,262.748115 L56.9545645,262.748115 L71.1955708,276.999572 L301,277 C304.589851,277 307.839851,278.455075 310.192388,280.807612 C312.544925,283.160149 314,286.410149 314,290 L314,290 L314,449 C314,452.589851 312.544925,455.839851 310.192388,458.192388 C307.839851,460.544925 304.589851,462 301,462 L301,462 L29,462 C25.4101491,462 22.1601491,460.544925 19.8076118,458.192388 C17.4550746,455.839851 16,452.589851 16,449 L16,449 L16,290 C16,286.410149 17.4550746,283.160149 19.8076118,280.807612 C22.1601491,278.455075 25.4101491,277 29.0000458,277 L29.0000458,277 L38.7445991,276.999553 L52.8469102,262.879585 C53.4135391,262.313702 54.1483154,262.020475 54.8888733,262.000133 Z" stroke-linejoin="square" fill="#FFFFFF" fill-rule="evenodd"></path>
|
||||
</g>
|
||||
<g id="编组-3" transform="translate(47.000000, 305.000000)" stroke="#666666" stroke-width="4">
|
||||
<g id="编组-24" transform="translate(6.142857, 11.000000)">
|
||||
<path d="M18.7142857,22 C21.4757095,22 23.9757095,23.1192881 25.7853535,24.9289322 C27.5949976,26.7385763 28.7142857,29.2385763 28.7142857,32 L28.7142857,32 L2,32 L2.0057986,31.6562146 C2.09061716,29.145562 3.10093106,26.8695956 4.70635331,25.1587023 L4.92893219,24.9289322 C6.73857625,23.1192881 9.23857625,22 12,22 L12,22 Z" id="矩形"></path>
|
||||
<ellipse id="椭圆形" cx="15.3571429" cy="9" rx="7.21428571" ry="7"></ellipse>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1768479239176" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9970" xmlns:xlink="http://www.w3.org/1999/xlink" width="80" height="80"><path d="M277.333333 303.978667A245.056 245.056 0 0 1 520.384 57.536a245.056 245.056 0 0 1 243.050667 246.442667 245.034667 245.034667 0 0 1-243.050667 246.357333A245.034667 245.034667 0 0 1 277.333333 303.978667z m74.133334 0a170.496 170.496 0 0 0 168.917333 171.690666 171.669333 171.669333 0 0 0 0-343.317333 170.496 170.496 0 0 0-168.917333 171.648z" p-id="9971" fill="#2c2c2c"></path><path d="M832 896v-21.333333a213.333333 213.333333 0 0 0-213.333333-213.333334H405.333333A213.333333 213.333333 0 0 0 192 874.666667v21.333333h640m61.738667 64H130.261333A280.981333 280.981333 0 0 1 128 924.437333V874.666667a277.333333 277.333333 0 0 1 277.333333-277.333334h213.333334a277.333333 277.333333 0 0 1 277.333333 277.333334v49.770666A281.066667 281.066667 0 0 1 893.738667 960z" p-id="9972" fill="#2c2c2c"></path></svg>
|
||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 1.1 KiB |
1
TUIKit/assets/icon/scan.svg
Normal file
1
TUIKit/assets/icon/scan.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1768479451365" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12034" xmlns:xlink="http://www.w3.org/1999/xlink" width="80" height="80"><path d="M68.96 472.268h887.08v80.126H68.96v-80.126z m78.272 402.656V687.627H68.96v241.39c0 14.538 11.52 26.371 26.09 26.371h260.906v-80.125H147.232v-0.339z m730.536 0V687.627h78.272v241.39c0 14.538-11.52 26.371-26.09 26.371H669.043v-80.125h208.724v-0.339zM147.232 150.076v187.297H68.96V95.983c0-14.538 11.52-26.371 26.09-26.371h260.906v80.125H147.232v0.339z m730.536 0v187.297h78.272V95.983c0-14.538-11.52-26.371-26.09-26.371H669.043v80.125h208.724v0.339z" p-id="12035" fill="#2c2c2c"></path></svg>
|
||||
|
After Width: | Height: | Size: 822 B |
@@ -28,9 +28,13 @@
|
||||
import Icon from '../../common/Icon.vue'
|
||||
import More from '../../../assets/icon/more.svg'
|
||||
import backSVG from '../../../assets/icon/back.svg'
|
||||
import { useUI } from '../../../../utils/use-ui'
|
||||
import { endUserService } from '../../../../api/my-index'
|
||||
|
||||
const { showDialog, showToast } = useUI()
|
||||
|
||||
const emits = defineEmits(['openGroupManagement'])
|
||||
const props = defineProps(['isGroup'])
|
||||
const props = defineProps(['isGroup', 'serviceID'])
|
||||
|
||||
const currentConversation = ref<IConversationModel>()
|
||||
const typingStatus = ref(false)
|
||||
@@ -86,8 +90,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
function back() {
|
||||
uni.navigateBack()
|
||||
const back = async () => {
|
||||
if (props.serviceID) {
|
||||
const show = await showDialog('提示', '确定要退出当前会话吗?')
|
||||
if (show) {
|
||||
await endUserService(props.serviceID)
|
||||
await showToast('结束服务成功')
|
||||
uni.navigateBack()
|
||||
}
|
||||
} else {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
<ChatHeader
|
||||
:isGroup="isGroup"
|
||||
:headerExtensionList="headerExtensionList"
|
||||
:serviceID="serviceID"
|
||||
@closeChat="closeChat"
|
||||
@openGroupManagement="handleGroup"
|
||||
/>
|
||||
|
||||
<Forward @toggleMultipleSelectMode="toggleMultipleSelectMode" />
|
||||
<MessageList
|
||||
ref="messageListRef"
|
||||
@@ -120,6 +122,7 @@
|
||||
import { initChat, logout } from './entry-chat-only.ts'
|
||||
|
||||
onLoad(options => {
|
||||
serviceID.value = options?.id || ''
|
||||
initChat(options)
|
||||
})
|
||||
|
||||
@@ -136,7 +139,8 @@
|
||||
// @End uniapp use Chat only
|
||||
|
||||
const emits = defineEmits(['closeChat'])
|
||||
|
||||
/** 客服 id */
|
||||
const serviceID = ref('')
|
||||
const groupID = ref(undefined)
|
||||
const isGroup = ref(false)
|
||||
const isNotInGroup = ref(false)
|
||||
@@ -163,7 +167,7 @@
|
||||
}
|
||||
uni.onWindowResize(windowResizeCallback)
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(e => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdate
|
||||
})
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
<template>
|
||||
<div
|
||||
:ref="convHeaderRef"
|
||||
class="tui-conversation-header"
|
||||
>
|
||||
<div :ref="convHeaderRef" class="tui-conversation-header">
|
||||
<Navigation title="消息">
|
||||
<template
|
||||
#right
|
||||
>
|
||||
<template #right>
|
||||
<div v-show="!isGlobalSearching" class="menu-container">
|
||||
<ul
|
||||
v-if="menuList.length > 0"
|
||||
class="list"
|
||||
>
|
||||
<ul v-if="menuList.length > 0" class="list">
|
||||
<li
|
||||
v-for="(item, index) in menuList"
|
||||
:key="index"
|
||||
class="list-item"
|
||||
>
|
||||
<main
|
||||
class="list-item-item"
|
||||
@click.stop="handleMenu(item)"
|
||||
>
|
||||
<main class="list-item-item" @click.stop="handleMenu(item)">
|
||||
<Icon
|
||||
v-if="item.icon"
|
||||
class="list-item-icon"
|
||||
@@ -36,7 +25,7 @@
|
||||
<li
|
||||
v-for="(childrenItem, childrenIndex) in showChildren"
|
||||
:key="childrenIndex"
|
||||
class="list-item"
|
||||
class="list-item top-right_box"
|
||||
@click="handleMenu(childrenItem)"
|
||||
>
|
||||
<Icon
|
||||
@@ -56,64 +45,106 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { StoreName, TUIStore } from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import { computed, ref, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import Navigation from '../../common/Navigation/index.vue';
|
||||
import Server, { IMenuItem } from './server';
|
||||
import type { ISearchingStatus } from '../../TUISearch/type';
|
||||
import {
|
||||
StoreName,
|
||||
TUIStore
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import {
|
||||
computed,
|
||||
ref,
|
||||
onMounted,
|
||||
onUnmounted
|
||||
} from '../../../adapter-vue'
|
||||
import Icon from '../../common/Icon.vue'
|
||||
import Navigation from '../../common/Navigation/index.vue'
|
||||
import Server, { IMenuItem } from './server'
|
||||
import type { ISearchingStatus } from '../../TUISearch/type'
|
||||
import { navigateTo } from '../../../../utils/router'
|
||||
|
||||
const showChildren = ref<IMenuItem[]>([]);
|
||||
const convHeaderRef = ref<HTMLElement | undefined>();
|
||||
const isGlobalSearching = ref(false);
|
||||
const showChildren = ref<IMenuItem[]>([])
|
||||
const convHeaderRef = ref<HTMLElement | undefined>()
|
||||
const isGlobalSearching = ref(false)
|
||||
|
||||
const menuList = computed(() => {
|
||||
return Server.getInstance().getMenu();
|
||||
});
|
||||
const menuList = computed(() => {
|
||||
return Server.getInstance().getMenu()
|
||||
})
|
||||
|
||||
const onCurrentSearchingStatusChange = (data: ISearchingStatus) => {
|
||||
isGlobalSearching.value = data.searchType === 'global' && data.isSearching;
|
||||
if (isGlobalSearching.value) {
|
||||
closeChildren();
|
||||
const onCurrentSearchingStatusChange = (data: ISearchingStatus) => {
|
||||
isGlobalSearching.value =
|
||||
data.searchType === 'global' && data.isSearching
|
||||
if (isGlobalSearching.value) {
|
||||
closeChildren()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
showChildren.value = [];
|
||||
TUIStore.watch(StoreName.SEARCH, {
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange,
|
||||
});
|
||||
});
|
||||
onMounted(() => {
|
||||
showChildren.value = []
|
||||
TUIStore.watch(StoreName.SEARCH, {
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.SEARCH, {
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange,
|
||||
});
|
||||
});
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.SEARCH, {
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange
|
||||
})
|
||||
})
|
||||
|
||||
const handleMenu = (item: IMenuItem) => {
|
||||
const { data: { children }, listener = { onClicked: () => {} } } = item;
|
||||
if (children) {
|
||||
showChildren.value = showChildren.value.length > 0 ? [] : children;
|
||||
} else {
|
||||
listener.onClicked(item);
|
||||
closeChildren();
|
||||
const handleMenu = (item: IMenuItem) => {
|
||||
const {
|
||||
data: { children },
|
||||
listener = { onClicked: () => {} }
|
||||
} = item
|
||||
if (children) {
|
||||
let listData = children
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// 扫一扫
|
||||
const scanItem = {
|
||||
data: { name: 'isScan' },
|
||||
icon: '/TUIKit/assets/icon/scan.svg',
|
||||
text: '扫一扫',
|
||||
listener: {
|
||||
onClicked: () => {}
|
||||
}
|
||||
}
|
||||
listData = [scanItem, ...listData]
|
||||
// #endif
|
||||
showChildren.value = showChildren.value.length > 0 ? [] : listData
|
||||
} else {
|
||||
if (item.data.name === 'isScan') {
|
||||
uni.scanCode({
|
||||
onlyFromCamera: false,
|
||||
scanType: ['qrCode'],
|
||||
success: (res: any) => {
|
||||
navigateTo(res.result)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
listener.onClicked(item)
|
||||
}
|
||||
closeChildren()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const closeChildren = () => {
|
||||
showChildren.value = [];
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
closeChildren,
|
||||
});
|
||||
const closeChildren = () => {
|
||||
showChildren.value = []
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
closeChildren
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.menu-container {
|
||||
position: relative;
|
||||
}
|
||||
.menu-container {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped src="../style/index.scss"></style>
|
||||
<style lang="scss" scoped>
|
||||
.top-right_box {
|
||||
width: 180rpx;
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -34,6 +34,15 @@ export const getProductCommentList = data => {
|
||||
})
|
||||
}
|
||||
|
||||
/** 添加商品评论 */
|
||||
export const addProductComment = data => {
|
||||
return http({
|
||||
url: '/api/service/productReview',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增订单 */
|
||||
export const addOrder = data => {
|
||||
return http({
|
||||
|
||||
@@ -275,3 +275,19 @@ export const getUserServiceList = () => {
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 查询空闲客服 */
|
||||
export const getUserServiceFree = () => {
|
||||
return http({
|
||||
url: '/api/service/customerStaff/listFree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/** 结束客服对话 */
|
||||
export const endUserService = id => {
|
||||
return http({
|
||||
url: `/api/service/customerStaff/end/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<script setup>
|
||||
const props = defineProps()
|
||||
const props = defineProps({
|
||||
position: {
|
||||
type: String,
|
||||
default: 'fixed'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="bottom-view">
|
||||
<view :style="{ position: props.position }" class="bottom-view">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bottom-view {
|
||||
position: fixed;
|
||||
// position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
// 确认密码
|
||||
confirmPassword: '',
|
||||
// 邀请码
|
||||
invitationCode: '54321',
|
||||
invitationCode: '',
|
||||
agreement: true
|
||||
})
|
||||
|
||||
@@ -79,6 +79,11 @@
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.invitationCode) {
|
||||
showToast('请输入邀请码')
|
||||
return
|
||||
}
|
||||
|
||||
const data = {
|
||||
type: isPhone.value ? 2 : 1,
|
||||
mobile: formData.name,
|
||||
|
||||
13
pages.json
13
pages.json
@@ -276,7 +276,8 @@
|
||||
{
|
||||
"path": "pages/mall/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商城"
|
||||
"navigationBarTitleText": "商城",
|
||||
"navigationBarBackgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -289,7 +290,15 @@
|
||||
{
|
||||
"path": "pages/mall/comment",
|
||||
"style": {
|
||||
"navigationBarTitleText": "评价"
|
||||
"navigationBarTitleText": "评价",
|
||||
"navigationBarBackgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mall/add-comment",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加评价",
|
||||
"navigationBarBackgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
|
||||
.address-form {
|
||||
:deep(.uni-easyinput__content) {
|
||||
border-radius: 34rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
.discover-box {
|
||||
padding: 32rpx 24rpx;
|
||||
.card-box {
|
||||
padding: 20rpx 32rpx;
|
||||
padding: 20rpx 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
@@ -161,6 +161,22 @@
|
||||
getData()
|
||||
}
|
||||
|
||||
const formatNumberWithWan = num => {
|
||||
if (num < 10000) {
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
// 保留小数:根据需求可调整 toFixed 的位数
|
||||
let wan = num / 10000
|
||||
|
||||
// 如果是整数万,不显示小数;否则保留两位小数(或你想要的位数)
|
||||
if (wan % 1 === 0) {
|
||||
return wan + '万'
|
||||
} else {
|
||||
return wan.toFixed(2).replace(/\.?0+$/, '') + '万' // 去掉不必要的尾随零
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
const now = new Date()
|
||||
currentYear.value = now.getFullYear()
|
||||
@@ -184,7 +200,7 @@
|
||||
<view class="public-header—box">
|
||||
<view class="integral-box">
|
||||
<text>我的积分</text>
|
||||
<text>{{ userInfo.totalPoints }}</text>
|
||||
<text>{{ formatNumberWithWan(userInfo.totalPoints) }}</text>
|
||||
</view>
|
||||
<image
|
||||
src="/static/images/discover/calendar.png"
|
||||
|
||||
70
pages/mall/add-comment.vue
Normal file
70
pages/mall/add-comment.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script setup>
|
||||
import CardInput from '../my-index/components/card-input.vue'
|
||||
import { reactive } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { addProductComment } from '../../api/mall'
|
||||
import { useUI } from '../../utils/use-ui'
|
||||
import { navigateBack } from '../../utils/router'
|
||||
|
||||
const { showToast } = useUI()
|
||||
|
||||
const formData = reactive({
|
||||
content: '',
|
||||
rating: 5,
|
||||
isAnonymous: false,
|
||||
imageUrls: '',
|
||||
productId: ''
|
||||
})
|
||||
|
||||
const switchChange = ({ detail }) => {
|
||||
formData.isAnonymous = detail.value
|
||||
}
|
||||
|
||||
const onAdd = async () => {
|
||||
const data = {
|
||||
...formData,
|
||||
isAnonymous: formData.isAnonymous ? 1 : 0
|
||||
}
|
||||
await addProductComment(data)
|
||||
await showToast('添加成功', 'success')
|
||||
navigateBack()
|
||||
}
|
||||
|
||||
onLoad(e => {
|
||||
formData.productId = e.productId
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<CardInput :is-input="false" title="评论内容">
|
||||
<textarea v-model="formData.content" placeholder="请输入评论内容" />
|
||||
</CardInput>
|
||||
|
||||
<CardInput :is-input="false" title="评分">
|
||||
<template #right>
|
||||
<uni-rate v-model="formData.rating" />
|
||||
</template>
|
||||
</CardInput>
|
||||
|
||||
<CardInput :is-input="false" title="是否匿名">
|
||||
<template #right>
|
||||
<switch style="transform: scale(0.8)" @change="switchChange" />
|
||||
</template>
|
||||
</CardInput>
|
||||
|
||||
<CardInput :is-input="false" title="添加图片">
|
||||
<cb-file-picker v-model="formData.imageUrls"></cb-file-picker>
|
||||
</CardInput>
|
||||
|
||||
<bottom-view>
|
||||
<cb-button @click="onAdd">确认添加</cb-button>
|
||||
</bottom-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,8 @@
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import { getProductCommentList } from '@/api/mall'
|
||||
import { navigateTo } from '../../utils/router'
|
||||
|
||||
const topNav = ref([
|
||||
{ name: '全部', id: '1' },
|
||||
@@ -11,61 +14,97 @@
|
||||
const formData = reactive({
|
||||
type: '1'
|
||||
})
|
||||
const productId = ref('')
|
||||
const listData = ref([])
|
||||
const paging = ref(null)
|
||||
|
||||
const onTop = id => {
|
||||
formData.type = id
|
||||
}
|
||||
|
||||
const getList = async (pageNum, pageSize) => {
|
||||
try {
|
||||
const res = await getProductCommentList({
|
||||
productId: productId.value,
|
||||
pageNum,
|
||||
pageSize
|
||||
})
|
||||
paging.value.complete(res.rows)
|
||||
} catch (err) {
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(e => {
|
||||
productId.value = e.productId
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="comment-box">
|
||||
<view class="top-options">
|
||||
<view
|
||||
v-for="(item, index) in topNav"
|
||||
:key="index"
|
||||
:class="{ active: item.id === formData.type }"
|
||||
class="text"
|
||||
@click="onTop(item.id)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="listData"
|
||||
:default-page-size="15"
|
||||
safe-area-inset-bottom
|
||||
use-safe-area-placeholder
|
||||
:auto="false"
|
||||
@query="getList"
|
||||
>
|
||||
<view class="comment-box">
|
||||
<!-- <view class="top-options">
|
||||
<view
|
||||
v-for="(item, index) in topNav"
|
||||
:key="index"
|
||||
:class="{ active: item.id === formData.type }"
|
||||
class="text"
|
||||
@click="onTop(item.id)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 卡片位置 -->
|
||||
<view class="card-box">
|
||||
<image
|
||||
src="/static/images/public/random1.png"
|
||||
mode="scaleToFill"
|
||||
class="avatar"
|
||||
></image>
|
||||
<!-- 右边 -->
|
||||
<view class="right-box">
|
||||
<text class="name">名字</text>
|
||||
<view class="rate-box">
|
||||
<view class="date">
|
||||
<text>2022-2-2</text>
|
||||
<text>重庆市</text>
|
||||
<!-- 卡片位置 -->
|
||||
<view v-for="item in listData" :key="item.id" class="card-box">
|
||||
<image
|
||||
:src="item.avatar || '/static/images/public/random1.png'"
|
||||
mode="scaleToFill"
|
||||
class="avatar"
|
||||
></image>
|
||||
<!-- 右边 -->
|
||||
<view class="right-box">
|
||||
<view class="name_box">
|
||||
<text class="name">{{ item.userName }}</text>
|
||||
<uni-rate readonly :size="24" :value="item.rating" />
|
||||
</view>
|
||||
<view class="star">
|
||||
<view class="rate-box">
|
||||
<view class="date">
|
||||
<text>{{ item.createTime }}</text>
|
||||
<!-- <text>重庆市</text> -->
|
||||
</view>
|
||||
<!-- <view class="star">
|
||||
<view class="like">
|
||||
<uni-icons
|
||||
type="hand-up"
|
||||
size="16"
|
||||
color="#74747480"
|
||||
></uni-icons>
|
||||
22
|
||||
<text v-if="item.likeCount">{{ item.likeCount }}</text>
|
||||
</view>
|
||||
<uni-icons
|
||||
type="chat"
|
||||
size="16"
|
||||
color="#74747480"
|
||||
></uni-icons>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
<text class="content">
|
||||
视频讲的真好,全面升级,讲的真的全面,老师 徒弟吗?
|
||||
</text>
|
||||
<view class="img-box">
|
||||
<text class="content">
|
||||
{{ item.content }}
|
||||
</text>
|
||||
<!-- <view class="img-box">
|
||||
<image
|
||||
v-for="item in 5"
|
||||
src="/static/images/public/random1.png"
|
||||
@@ -79,10 +118,21 @@
|
||||
<text>内容</text>
|
||||
</view>
|
||||
<text class="expand">共三条回复></text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 底部按钮 -->
|
||||
<template #bottom>
|
||||
<bottom-view position="absolute">
|
||||
<cb-button
|
||||
@click="navigateTo('/pages/mall/add-comment', { productId })"
|
||||
>
|
||||
确认添加
|
||||
</cb-button>
|
||||
</bottom-view>
|
||||
</template>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -96,6 +146,7 @@
|
||||
.avatar {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 64rpx;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
@@ -109,10 +160,16 @@
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
.name_box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.name {
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.rate-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { formatRMB } from '@/utils'
|
||||
import { getUserAddress } from '@/api'
|
||||
import { getUserPayPwd } from '@/api/my-index'
|
||||
import { navigateTo, redirectTo } from '@/utils/router'
|
||||
import { navigateTo, navigateBack, redirectTo } from '@/utils/router'
|
||||
import { useUI } from '@/utils/use-ui'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
const viewData = ref({})
|
||||
|
||||
/** 分享二维码进入 拼团ID */
|
||||
const groupId = ref('')
|
||||
/** 单价 */
|
||||
const priceData = ref(0)
|
||||
const formData = reactive({
|
||||
@@ -40,6 +42,7 @@
|
||||
loading: true
|
||||
})
|
||||
const tixian = ref(null)
|
||||
const originalPrice = ref(0)
|
||||
|
||||
/** 获取用户地址 */
|
||||
const userRess = async () => {
|
||||
@@ -62,9 +65,13 @@
|
||||
const getData = async productId => {
|
||||
const res = await getProductDetail(productId)
|
||||
viewData.value = res.data
|
||||
const { id, price, stockQuantity } = res.data.skuList.find(
|
||||
v => v.isDefault == 1
|
||||
)
|
||||
const {
|
||||
id,
|
||||
price,
|
||||
stockQuantity,
|
||||
originalPrice: nub
|
||||
} = res.data.skuList.find(v => v.isDefault == 1)
|
||||
originalPrice.value = nub
|
||||
formData.maxNum = stockQuantity
|
||||
formData.spec = id
|
||||
priceData.value = price
|
||||
@@ -83,6 +90,7 @@
|
||||
formData.total = item.price
|
||||
priceData.value = item.price
|
||||
formData.maxNum = item.stockQuantity
|
||||
originalPrice.value = item.originalPrice
|
||||
}
|
||||
|
||||
// 提交订单
|
||||
@@ -104,6 +112,7 @@
|
||||
|
||||
const submit = async e => {
|
||||
const data = {
|
||||
groupId: groupId.value,
|
||||
payPassword: e.join(''),
|
||||
addressId: topRessData.id,
|
||||
productId: viewData.value.id,
|
||||
@@ -114,10 +123,8 @@
|
||||
tixian.value.close()
|
||||
const res = await addOrder(data)
|
||||
await refreshUserInfo()
|
||||
redirectTo('/pages/shop-together/detail', {
|
||||
id: res.data.groupId,
|
||||
type: 'add'
|
||||
})
|
||||
await showToast('订单提交成功', 'success')
|
||||
navigateBack()
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
@@ -125,6 +132,7 @@
|
||||
})
|
||||
|
||||
onLoad(async e => {
|
||||
groupId.value = e?.groupId || ''
|
||||
await getData(e.productId)
|
||||
})
|
||||
</script>
|
||||
@@ -165,7 +173,7 @@
|
||||
</text>
|
||||
<view class="line-box">
|
||||
<view class="rmb-box">
|
||||
<text>¥{{ viewData.originalPrice || '' }}</text>
|
||||
<text>¥{{ originalPrice }}</text>
|
||||
<text>¥{{ priceData }}</text>
|
||||
</view>
|
||||
<!-- 添加数量 -->
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
const viewData = ref({})
|
||||
const productId = ref('')
|
||||
// 分享二维码进入
|
||||
const groupId = ref('')
|
||||
/** 评论数量 */
|
||||
const commentNum = ref(0)
|
||||
const getData = async productId => {
|
||||
@@ -39,12 +41,14 @@
|
||||
|
||||
const onConfirm = () => {
|
||||
navigateTo('/pages/mall/confirm-order', {
|
||||
productId: productId.value
|
||||
productId: productId.value,
|
||||
groupId: groupId.value
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(async e => {
|
||||
productId.value = e.productId
|
||||
groupId.value = e?.groupId || ''
|
||||
await getData(e.productId)
|
||||
await getComment(e.productId)
|
||||
})
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
topNavOptions.value = res.data
|
||||
}
|
||||
|
||||
const getListData = async (pageNum, pageSize) => {
|
||||
const getListData = async (pageNum, pageSize, state) => {
|
||||
if (state === 1) {
|
||||
cardList.value = []
|
||||
}
|
||||
try {
|
||||
const res = await getProductList({
|
||||
pageNum,
|
||||
@@ -38,7 +41,7 @@
|
||||
const onTop = value => {
|
||||
formData.type = value
|
||||
formData.name = ''
|
||||
getListData(1, formData.pageSize)
|
||||
getListData(1, formData.pageSize, 1)
|
||||
}
|
||||
|
||||
const onGo = item => {
|
||||
@@ -47,7 +50,6 @@
|
||||
|
||||
onLoad(async () => {
|
||||
await categoryList()
|
||||
// await getListData(1, formData.pageSize)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -67,7 +69,8 @@
|
||||
<view class="top-box">
|
||||
<cb-search
|
||||
v-model="formData.name"
|
||||
@search="getListData()"
|
||||
placeholder="搜索热门商品"
|
||||
@search="getListData(1, formData.pageSize, 1)"
|
||||
></cb-search>
|
||||
<view class="top-options">
|
||||
<view
|
||||
@@ -124,6 +127,7 @@
|
||||
@import '@/styles/top-selector.scss';
|
||||
.mall-list {
|
||||
.top-box {
|
||||
background: #ffffff;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.right-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,17 +1,48 @@
|
||||
<script setup>
|
||||
import { navigateTo } from '@/utils/router'
|
||||
// import { onLoad } from '@dcloudio/uni-app'
|
||||
// import { ref } from 'vue'
|
||||
// import { getUserServiceList } from '@/api/my-index'
|
||||
import TUIChatEngine, {
|
||||
TUIConversationService,
|
||||
TUIFriendService
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { getUserServiceFree } from '@/api/my-index'
|
||||
|
||||
// const list = ref([])
|
||||
// const getList = async () => {
|
||||
// const res = await getUserServiceList()
|
||||
// list.value = res.data
|
||||
// }
|
||||
// onLoad(() => {
|
||||
// getList()
|
||||
// })
|
||||
const customerData = ref({})
|
||||
const getList = async () => {
|
||||
const res = await getUserServiceFree()
|
||||
customerData.value = res?.data || {}
|
||||
}
|
||||
|
||||
const handleSwitchConversation = async () => {
|
||||
// 在这里可以添加提交验证信息的逻辑
|
||||
let source = 'AddSource_Type_Web' // 来源渠道
|
||||
// #ifdef H5
|
||||
source = 'AddSource_Type_H5'
|
||||
// #endif
|
||||
|
||||
// 判断是否为 App(5+ App)
|
||||
// #ifdef APP-PLUS
|
||||
source = 'AddSource_Type_App'
|
||||
// #endif
|
||||
await TUIFriendService.addFriend({
|
||||
to: customerData.value.imUserId,
|
||||
source,
|
||||
remark: customerData.value.nickname,
|
||||
type: TUIChatEngine.TYPES.SNS_ADD_TYPE_BOTH
|
||||
})
|
||||
TUIConversationService.switchConversation(
|
||||
`C2C${customerData.value.imUserId}`
|
||||
)
|
||||
TUIGlobal?.navigateTo({
|
||||
url: `/TUIKit/components/TUIChat/index?id=${customerData.value.id}`
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -28,7 +59,13 @@
|
||||
>
|
||||
第三方客服
|
||||
</button>
|
||||
<!-- <button>APP客服</button> -->
|
||||
<button
|
||||
v-if="customerData?.imUserId"
|
||||
class="btn"
|
||||
@click="handleSwitchConversation"
|
||||
>
|
||||
APP客服
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -70,16 +107,16 @@
|
||||
background: linear-gradient(0deg, #00d993 0%, #00d9c5 100%);
|
||||
border-radius: 16rpx;
|
||||
box-sizing: border-box;
|
||||
&:nth-child(2) {
|
||||
background: #ffffff;
|
||||
color: #00d993;
|
||||
border: 2rpx solid #00d993;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: #ffffff;
|
||||
color: #00d993;
|
||||
border: 2rpx solid #00d993;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { useAuthUser } from '@/composables/useAuthUser'
|
||||
|
||||
const { userInfo } = useAuthUser()
|
||||
const { userInfo, tencentUserSig } = useAuthUser()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -19,7 +19,13 @@
|
||||
<text>ID: {{ userInfo.userId }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="code-img">二维码</view>
|
||||
<view class="code-img">
|
||||
<l-qrcode
|
||||
:value="`/pages/adduser/index?id=${tencentUserSig.userId}`"
|
||||
size="240"
|
||||
/>
|
||||
</view>
|
||||
<text class="bottom-text">邀请码:{{ userInfo.invitationCode }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -30,6 +36,13 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.bottom-text {
|
||||
font-size: 38rpx;
|
||||
color: #333333;
|
||||
margin-top: 10rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.top-img {
|
||||
width: 480rpx;
|
||||
display: flex;
|
||||
@@ -60,7 +73,9 @@
|
||||
}
|
||||
|
||||
.code-img {
|
||||
background: rgb(165, 136, 136);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 480rpx;
|
||||
height: 480rpx;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
|
||||
const onShare = () => {
|
||||
// productId
|
||||
navigateTo('/pages/shop-together/share')
|
||||
navigateTo('/pages/shop-together/share', {
|
||||
id: viewData.value.id,
|
||||
productId: viewData.value.productId
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(e => {
|
||||
@@ -39,10 +42,12 @@
|
||||
<view class="state-box">发起人</view>
|
||||
<view class="left-box">
|
||||
<image
|
||||
src="https://wx1.sinaimg.cn/mw690/92eeb099gy1i29hl0ne80j21jk2bcash.jpg"
|
||||
mode="scaleToFill"
|
||||
v-if="topUser?.avatar"
|
||||
:src="topUser?.avatar"
|
||||
mode="aspectFill"
|
||||
class="avatar"
|
||||
></image>
|
||||
<uni-icons v-else type="contact-filled" size="64"></uni-icons>
|
||||
<view class="name-box">
|
||||
<text>{{ topUser.userName }}</text>
|
||||
<text>ID:{{ topUser.userId }}</text>
|
||||
@@ -68,7 +73,12 @@
|
||||
class="left-img"
|
||||
></image>
|
||||
<view class="right-content">
|
||||
<text class="product-name">{{ viewData.productName }}</text>
|
||||
<view>
|
||||
<text class="product-name">{{ viewData.productName }}</text>
|
||||
<text v-if="viewData.activityType" class="num-box">
|
||||
{{ viewData.activityType }} 人拼团
|
||||
</text>
|
||||
</view>
|
||||
<view class="line-box">
|
||||
<view class="rmb-box">
|
||||
<text></text>
|
||||
|
||||
@@ -39,7 +39,12 @@
|
||||
class="left-img"
|
||||
></image>
|
||||
<view class="right-content">
|
||||
<text class="product-name">{{ item.productName }}</text>
|
||||
<view>
|
||||
<text class="product-name">{{ item.productName }}</text>
|
||||
<text v-if="item.activityType" class="num-box">
|
||||
{{ item.activityType }}人拼团
|
||||
</text>
|
||||
</view>
|
||||
<view class="line-box">
|
||||
<view class="rmb-box">
|
||||
<text></text>
|
||||
|
||||
@@ -1,15 +1,39 @@
|
||||
<script setup></script>
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { useUI } from '../../utils/use-ui'
|
||||
|
||||
const { showToast } = useUI()
|
||||
|
||||
const groupId = ref('')
|
||||
const productId = ref('')
|
||||
const qrcodeRef = ref(null)
|
||||
|
||||
|
||||
onLoad(e => {
|
||||
groupId.value = e.id
|
||||
productId.value = e.productId
|
||||
// /pages/mall/detail
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="share-box">
|
||||
<nav-bar title="拼单" targetColor="transparent"></nav-bar>
|
||||
<view class="code-box">
|
||||
<view class="code-content">
|
||||
<view class="code">二维码</view>
|
||||
<view class="btn-box">
|
||||
<text>分享二维码</text>
|
||||
<text>保存到本地</text>
|
||||
<view class="code">
|
||||
<l-qrcode
|
||||
ref="qrcodeRef"
|
||||
:value="`/pages/mall/detail?productId=${productId}&groupId=${groupId}`"
|
||||
size="160"
|
||||
/>
|
||||
</view>
|
||||
<!-- <view class="btn-box">
|
||||
<text>分享二维码</text>
|
||||
<text @click="onCode">保存到本地</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -26,7 +50,7 @@
|
||||
background-repeat: no-repeat;
|
||||
|
||||
.code-box {
|
||||
padding-top: 20vh;
|
||||
padding-top: 24vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -40,15 +64,17 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.code {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 332rpx;
|
||||
height: 332rpx;
|
||||
background: rgb(202, 118, 118);
|
||||
}
|
||||
.btn-box {
|
||||
margin-top: 60rpx;
|
||||
width: 332rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
|
||||
@@ -109,6 +109,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
* 退出登录(不带提示)
|
||||
*/
|
||||
const logout = async () => {
|
||||
if (!userInfo.value) return
|
||||
try {
|
||||
userInfo.value = null
|
||||
await userLogout()
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.num-box {
|
||||
font-size: 28rpx;
|
||||
color: #7c7c7c;
|
||||
}
|
||||
|
||||
.line-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
49
uni_modules/lime-qrcode/changelog.md
Normal file
49
uni_modules/lime-qrcode/changelog.md
Normal file
@@ -0,0 +1,49 @@
|
||||
## 0.2.4(2025-09-22)
|
||||
- fix: uniappx ios报错
|
||||
## 0.2.3(2025-06-13)
|
||||
- fix: 修复uniapp x 安卓问题
|
||||
## 0.2.2(2025-06-13)
|
||||
- fix: 修复uniapp x ios问题
|
||||
## 0.2.1(2025-05-24)
|
||||
- feat: 支持uniapp x 鸿蒙next
|
||||
## 0.2.0(2025-02-09)
|
||||
- chore: 更新文档
|
||||
## 0.1.9(2024-11-24)
|
||||
- feat: 支持uniapp x 微信小程序
|
||||
## 0.1.8(2024-08-05)
|
||||
- fix: 修复因shared升级导致微信小程序可能不生效的问题
|
||||
## 0.1.7(2024-05-15)
|
||||
- fix: 修复uvue因缺少依赖无法使用
|
||||
## 0.1.6(2024-05-09)
|
||||
- fix: 修复vue2因type问题导致无法使用
|
||||
## 0.1.5(2024-04-14)
|
||||
- fix: 修复缺少依赖
|
||||
## 0.1.4(2024-04-10)
|
||||
- chore: 更新文档
|
||||
## 0.1.3(2024-04-01)
|
||||
- chore: 兼容uniapp x ios(app-js)
|
||||
## 0.1.2(2023-12-14)
|
||||
- fix: uvue 引入 API 自定义包出错
|
||||
## 0.1.1(2023-12-11)
|
||||
- chore: uvue的二维码API独立,需要单独下载
|
||||
## 0.1.0(2023-12-07)
|
||||
- fix: 修复因utssdk目录导致无法运行
|
||||
## 0.0.9(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.8(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.7(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.6(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.5(2023-07-30)
|
||||
- fix: 修复再次生成前没有清空,导致图形叠加
|
||||
## 0.0.4(2023-07-27)
|
||||
- fix: 修复相同尺寸无法再次生成
|
||||
## 0.0.3(2023-06-09)
|
||||
- feat: 支持通过`@vue/composition-api`在`vue2`上使用
|
||||
- chore: 更新文档
|
||||
## 0.0.2(2023-06-08)
|
||||
- chore: 更新文档
|
||||
## 0.0.1(2023-06-08)
|
||||
- 首次
|
||||
180
uni_modules/lime-qrcode/components/l-qrcode/ios/index.uts
Normal file
180
uni_modules/lime-qrcode/components/l-qrcode/ios/index.uts
Normal file
@@ -0,0 +1,180 @@
|
||||
// export * from '../qrcode'
|
||||
import { qrcodegen } from './qrcodegen'
|
||||
|
||||
// export * from '@/uni_modules/lime-qrcodegen/utssdk/interface';
|
||||
// import { type QRCodePropsTypes , type ImageSettings, type QRCodeCallback } from '@/uni_modules/lime-qrcodegen/utssdk/interface'
|
||||
export type QRCodePropsTypes = {
|
||||
value?: string
|
||||
size?: number
|
||||
fgColor?: string
|
||||
level?: string
|
||||
marginSize: number
|
||||
includeMargin: boolean
|
||||
imageSettings?: ImageSettings
|
||||
}
|
||||
export type ImageSettings = {
|
||||
width: number
|
||||
height: number
|
||||
x?: number
|
||||
y?: number
|
||||
src?: string
|
||||
excavate: boolean
|
||||
}
|
||||
export type QRCodeCallback = (cells : boolean[][]) => void
|
||||
|
||||
|
||||
const Ecc = qrcodegen.QrCode.Ecc
|
||||
const QrCode = qrcodegen.QrCode
|
||||
type Modules = boolean[][];
|
||||
type Excavation = { x : number; y : number; w : number; h : number };
|
||||
type ImageSettingExcavation = {
|
||||
x: number
|
||||
y: number
|
||||
h: number
|
||||
w: number
|
||||
excavation: Excavation
|
||||
}
|
||||
const ERROR_LEVEL_MAP = {
|
||||
L: Ecc.LOW,
|
||||
M: Ecc.MEDIUM,
|
||||
Q: Ecc.QUARTILE,
|
||||
H: Ecc.HIGH,
|
||||
};
|
||||
|
||||
const DEFAULT_SIZE:number = 128;
|
||||
const DEFAULT_LEVEL:string = 'L';
|
||||
// const DEFAULT_BGCOLOR:string = '#FFFFFF';
|
||||
const DEFAULT_FGCOLOR:string = '#000000';
|
||||
const DEFAULT_INCLUDEMARGIN:boolean = false;
|
||||
|
||||
const SPEC_MARGIN_SIZE : number = 4;
|
||||
const DEFAULT_MARGIN_SIZE : number = 0;
|
||||
|
||||
// This is *very* rough estimate of max amount of QRCode allowed to be covered.
|
||||
// It is "wrong" in a lot of ways (area is a terrible way to estimate, it
|
||||
// really should be number of modules covered), but if for some reason we don't
|
||||
// get an explicit height or width, I'd rather default to something than throw.
|
||||
const DEFAULT_IMG_SCALE = 0.1;
|
||||
|
||||
// We could just do this in generatePath, except that we want to support
|
||||
// non-Path2D canvas, so we need to keep it an explicit step.
|
||||
function excavateModules(modules : Modules, excavation : Excavation) : Modules {
|
||||
const ox = excavation.x
|
||||
const oy = excavation.y
|
||||
const oh = excavation.h
|
||||
const ow = excavation.w
|
||||
return modules.slice().map((row, y):boolean[] => {
|
||||
if (y < oy || y >= oy + oh) {
|
||||
return row;
|
||||
}
|
||||
return row.map((cell, x):boolean => {
|
||||
if (x < ox || x >= ox + ow) {
|
||||
return cell;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getImageSettings(
|
||||
cells : Modules,
|
||||
size : number,
|
||||
margin : number,
|
||||
imageSettings: ImageSettings | null
|
||||
) : ImageSettingExcavation | null {
|
||||
if (imageSettings == null) {
|
||||
return null;
|
||||
}
|
||||
// const result : UTSJSONObject = {}
|
||||
const numCells = cells.length + margin * 2;
|
||||
const defaultSize = Math.floor(size * DEFAULT_IMG_SCALE);
|
||||
const scale = numCells / size;
|
||||
|
||||
const width = imageSettings.width
|
||||
const height = imageSettings.height
|
||||
const ox = imageSettings.x
|
||||
const oy = imageSettings.y
|
||||
const excavate = imageSettings.excavate
|
||||
|
||||
const w = (width > 0 ? width : defaultSize) * scale;
|
||||
const h = (height > 0 ? height: defaultSize) * scale;
|
||||
const x = ox == null ? cells.length / 2 - w / 2 : ox * scale;
|
||||
const y = oy == null ? cells.length / 2 - h / 2 : oy * scale;
|
||||
|
||||
let excavation: Excavation = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 0,
|
||||
h: 0,
|
||||
};
|
||||
if (excavate) {
|
||||
let floorX = Math.floor(x);
|
||||
let floorY = Math.floor(y);
|
||||
let ceilW = Math.ceil(w + x - floorX);
|
||||
let ceilH = Math.ceil(h + y - floorY);
|
||||
// excavation = { x: floorX, y: floorY, w: ceilW, h: ceilH };
|
||||
excavation.x = floorX
|
||||
excavation.y = floorY
|
||||
excavation.w = ceilW
|
||||
excavation.h = ceilH
|
||||
}
|
||||
|
||||
return { x, y, h, w, excavation } as ImageSettingExcavation;
|
||||
}
|
||||
function getMarginSize(includeMargin : boolean, marginSize: number|null) : number {
|
||||
if (marginSize != null) {
|
||||
return Math.floor(marginSize);
|
||||
}
|
||||
return includeMargin ? SPEC_MARGIN_SIZE : DEFAULT_MARGIN_SIZE;
|
||||
}
|
||||
export class QRCodeCanvas {
|
||||
ctx : DrawableContext
|
||||
constructor(ctx : DrawableContext) {
|
||||
this.ctx = ctx
|
||||
}
|
||||
render(props : QRCodePropsTypes, cb : QRCodeCallback | null=null) {
|
||||
const ctx = this.ctx
|
||||
const value = props.value
|
||||
const size = props.size ?? DEFAULT_SIZE
|
||||
const level = props.level ?? DEFAULT_LEVEL
|
||||
const fgColor = props.fgColor ?? DEFAULT_FGCOLOR
|
||||
const includeMargin = props.includeMargin || DEFAULT_INCLUDEMARGIN
|
||||
const marginSize = props.marginSize
|
||||
const imageSettings = props.imageSettings
|
||||
if (value == null || value == '') {
|
||||
return;
|
||||
}
|
||||
let cells = QrCode.encodeText(value, ERROR_LEVEL_MAP[level] as Ecc).getModules();
|
||||
const margin = getMarginSize(includeMargin, marginSize);
|
||||
const numCells = cells.length + margin * 2;
|
||||
const scale = (size / numCells);
|
||||
const calculatedImageSettings = getImageSettings(
|
||||
cells,
|
||||
size,
|
||||
margin,
|
||||
imageSettings
|
||||
)
|
||||
const haveImageToRender = calculatedImageSettings != null
|
||||
const excavation: Excavation | null = haveImageToRender ? calculatedImageSettings?.excavation : null
|
||||
|
||||
if(haveImageToRender && excavation != null) {
|
||||
cells = excavateModules(cells, excavation);
|
||||
}
|
||||
ctx.reset()
|
||||
// ctx.clearRect(0, 0, size, size)
|
||||
// ctx.fillStyle = bgColor;
|
||||
// ctx.fillRect(0, 0, numCells, numCells);
|
||||
ctx.fillStyle = fgColor;
|
||||
cells.forEach(function (row, rdx) {
|
||||
row.forEach(function (cell, cdx) {
|
||||
if (cell) {
|
||||
ctx.fillRect((cdx + margin) * scale, (rdx + margin) * scale, scale, scale);
|
||||
}
|
||||
});
|
||||
});
|
||||
ctx.update()
|
||||
if(cb != null){
|
||||
cb(cells)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
300
uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue
Normal file
300
uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<!-- #ifndef APP || WEB -->
|
||||
<canvas :style="styles" v-if="use2d" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas>
|
||||
<canvas :style="styles" v-else :canvas-id="canvasId" :id="canvasId"></canvas>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP -->
|
||||
<view class="l-qrcode" ref="drawableRef" :style="[styles]">
|
||||
<image class="l-qrcode__icon" v-if="icon" :src="icon" :style="[iconStyle]"></image>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef WEB -->
|
||||
<view class="l-qrcode" ref="drawableRef" :style="[styles]"></view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
<script lang="uts" setup>
|
||||
/**
|
||||
* QRCode 二维码组件
|
||||
* @description 用于生成二维码图形,支持自定义图标和样式配置
|
||||
* <br>插件类型:LQrcodeComponentPublicInstance
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-qrcode
|
||||
*
|
||||
* @property {string} value 二维码内容(支持文本/URL等)
|
||||
* @property {string} icon 中心图标路径(支持本地/网络路径)
|
||||
* @property {number | string} size 二维码尺寸
|
||||
* @property {number | string} iconSize 中心图标尺寸
|
||||
* @property {number} marginSize 二维码外边距(默认:0)
|
||||
* @property {string} color 二维码颜色(默认:#000000)
|
||||
* @property {string} bgColor 背景颜色(默认:#ffffff)
|
||||
* @property {boolean} bordered 显示边框(默认:false)
|
||||
* @property {'L' | 'M' | 'Q' | 'H'} errorLevel 容错等级(默认:'H')
|
||||
* @value L 可恢复7%的数据
|
||||
* @value M 可恢复15%的数据
|
||||
* @value Q 可恢复25%的数据
|
||||
* @value H 可恢复30%的数据
|
||||
* @property {boolean} useCanvasToTempFilePath 使用canvas生成临时路径(H5端可能需要)
|
||||
* @property {boolean} use2d 启用2D上下文渲染(性能优化,默认:false)
|
||||
*/
|
||||
import { type PropType, nextTick } from 'vue'
|
||||
// #ifndef APP
|
||||
import { createImage } from '@/uni_modules/lime-shared/createImage'
|
||||
import { getCanvas, isCanvas2d } from './useCanvas'
|
||||
import { QRCodeCanvas } from './qrcode.js';
|
||||
import { QRCodePropsTypes , ImageSettings } from './type'
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID || APP-HARMONY
|
||||
import { QRCodeCanvas, type QRCodePropsTypes , type ImageSettings } from '@/uni_modules/lime-qrcodegen'
|
||||
// #endif
|
||||
// #ifdef APP-IOS
|
||||
import { QRCodeCanvas, type QRCodePropsTypes , type ImageSettings } from './ios'
|
||||
// #endif
|
||||
// import { addUnit } from '@/uni_modules/lime-shared/addUnit'
|
||||
// import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
// import { toBoolean } from '@/uni_modules/lime-shared/toBoolean'
|
||||
import { addUnit, unitConvert } from './utils'
|
||||
import { LQrcodeFailCallback, LQrcodeCompleteCallback, LQrcodeSuccessCallback} from './type'
|
||||
|
||||
const name = 'l-qrcode'
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String
|
||||
},
|
||||
icon: {
|
||||
type: String
|
||||
},
|
||||
// #ifdef APP-ANDROID
|
||||
size: {
|
||||
type: Object,
|
||||
default: 160
|
||||
},
|
||||
iconSize: {
|
||||
type: Object,
|
||||
default: 40
|
||||
},
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 160
|
||||
},
|
||||
iconSize: {
|
||||
type: [Number, String],
|
||||
default: 40
|
||||
},
|
||||
// #endif
|
||||
marginSize: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#000'
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
errorLevel: {
|
||||
type: String as PropType<'L' | 'M' | 'Q' | 'H'>,
|
||||
default: 'M' // 'L' | 'M' | 'Q' | 'H'
|
||||
},
|
||||
useCanvasToTempFilePath: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
use2d: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
// status: {
|
||||
// type: String as PropType<'active'|'expired'|'loading'>,
|
||||
// default: 'active' // active | expired | loading
|
||||
// }
|
||||
})
|
||||
const emits = defineEmits(['success'])
|
||||
const context = getCurrentInstance();
|
||||
const canvasId = `l-qrcode${context!.uid}`
|
||||
const styles = computed<Map<string, any>>(():Map<string, any>=>{
|
||||
const style = new Map<string, any>()
|
||||
const size = addUnit(props.size);
|
||||
if(size!=null){
|
||||
style.set('width', size)
|
||||
style.set('height', size)
|
||||
}
|
||||
style.set('background', props.bgColor)
|
||||
return style
|
||||
})
|
||||
// #ifdef APP
|
||||
const iconStyle = computed<Map<string, any>>(():Map<string, any>=>{
|
||||
const style = new Map<string, any>()
|
||||
const size = addUnit(props.iconSize);
|
||||
// if(size!=null){
|
||||
style.set('width', size)
|
||||
style.set('height', size)
|
||||
// }
|
||||
return style
|
||||
})
|
||||
// #endif
|
||||
const drawableRef = ref<UniElement|null>(null);
|
||||
// #ifndef APP
|
||||
let canvas:HTMLCanvasElement|null = null
|
||||
// #endif
|
||||
let qrcode:QRCodeCanvas|null = null
|
||||
|
||||
const canvasToTempFilePath = (options: UTSJSONObject)=>{
|
||||
const format = options.getString('format') ?? 'png';
|
||||
const fail = options.get('fail') as LQrcodeFailCallback | null;
|
||||
const complete = options.get('complete') as LQrcodeCompleteCallback | null;
|
||||
const success = options.get('success') as LQrcodeSuccessCallback | null;
|
||||
// #ifdef APP
|
||||
const newOptions = {
|
||||
format,
|
||||
fail,
|
||||
complete,
|
||||
success,
|
||||
} as TakeSnapshotOptions
|
||||
drawableRef.value!.takeSnapshot(newOptions)
|
||||
// #endif
|
||||
// #ifdef WEB
|
||||
success?.({
|
||||
tempFilePath: canvas?.toDataURL('image/'+format)
|
||||
})
|
||||
// #endif
|
||||
|
||||
}
|
||||
const render = ()=>{
|
||||
const param:QRCodePropsTypes = {
|
||||
value: props.value,
|
||||
size: unitConvert(props.size),
|
||||
fgColor: props.color,
|
||||
level: ['L', 'M', 'Q', 'H'].includes(props.errorLevel) ? props.errorLevel : 'M',
|
||||
marginSize: props.marginSize,
|
||||
includeMargin: props.bordered,
|
||||
imageSettings: null,
|
||||
} as QRCodePropsTypes
|
||||
// #ifdef APP-HARMONY
|
||||
param.value = props.value
|
||||
param.size = unitConvert(props.size)
|
||||
param.fgColor = props.color
|
||||
param.level = ['L', 'M', 'Q', 'H'].includes(props.errorLevel) ? props.errorLevel : 'M'
|
||||
param.marginSize = props.marginSize
|
||||
param.includeMargin = props.bordered
|
||||
param.imageSettings = null
|
||||
// #endif
|
||||
if(props.icon != null){
|
||||
// if(toBoolean(props.iconSize) && toBoolean(props.icon)){
|
||||
const size = unitConvert(props.iconSize)
|
||||
param.imageSettings = {
|
||||
src: props.icon,
|
||||
width: size,
|
||||
height: size,
|
||||
excavate: true
|
||||
} as ImageSettings
|
||||
|
||||
// #ifdef APP-HARMONY
|
||||
param.imageSettings.src = props.icon
|
||||
param.imageSettings.width = size
|
||||
param.imageSettings.height = size
|
||||
param.imageSettings.excavate = true
|
||||
// #endif
|
||||
}
|
||||
qrcode?.render(param)
|
||||
if(props.useCanvasToTempFilePath){
|
||||
setTimeout(()=>{
|
||||
canvasToTempFilePath({
|
||||
success: (res: TakeSnapshotSuccess)=>{
|
||||
emits('success', res.tempFilePath)
|
||||
}
|
||||
})
|
||||
},100)
|
||||
}
|
||||
}
|
||||
defineExpose({
|
||||
canvasToTempFilePath
|
||||
})
|
||||
onMounted(()=>{
|
||||
nextTick(()=>{
|
||||
// #ifdef APP
|
||||
requestAnimationFrame(()=> {
|
||||
drawableRef.value?.getBoundingClientRectAsync()?.then(res => {
|
||||
const ctx = drawableRef.value!.getDrawableContext();
|
||||
qrcode = new QRCodeCanvas(ctx!)
|
||||
watchEffect(()=>{
|
||||
render()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// #endif
|
||||
// #ifdef WEB
|
||||
canvas = document.createElement('canvas')
|
||||
canvas!.style.width = '100%'
|
||||
canvas!.style.height = '100%'
|
||||
drawableRef.value!.appendChild(canvas!)
|
||||
qrcode = new QRCodeCanvas(canvas, {
|
||||
pixelRatio: uni.getSystemInfoSync().pixelRatio,
|
||||
createImage: () => {
|
||||
const image = new Image();
|
||||
// @ts-ignore
|
||||
image.crossOrigin = 'anonymous';
|
||||
return image;
|
||||
}
|
||||
})
|
||||
watchEffect(()=>{
|
||||
render()
|
||||
})
|
||||
// #endif
|
||||
// #ifndef APP || WEB
|
||||
getCanvas(canvasId, { context }).then(res => {
|
||||
canvas = res;
|
||||
qrcode = new QRCodeCanvas(res, {
|
||||
path2D: false,
|
||||
pixelRatio: isCanvas2d && props.use2d ? uni.getSystemInfoSync().pixelRatio : 1,
|
||||
createImage
|
||||
})
|
||||
watchEffect(()=>{
|
||||
render()
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
// #ifdef WEB
|
||||
canvas?.remove();
|
||||
// #endif
|
||||
qrcode = null;
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.l-qrcode {
|
||||
position: relative;
|
||||
background-color: aqua;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&-mask {
|
||||
position: absolute;
|
||||
// inset: 0;
|
||||
// inset-block-start: 0;
|
||||
// inset-inline-start: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
line-height: 1.5714285714285714;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
249
uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue
Normal file
249
uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue
Normal file
@@ -0,0 +1,249 @@
|
||||
<template>
|
||||
<view class="l-qrcode" :style="[styles]">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<canvas :style="styles" v-if="use2d" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas>
|
||||
<canvas :style="styles" v-else :canvas-id="canvasId" :id="canvasId"></canvas>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<web-view
|
||||
ref="qrcodeRef"
|
||||
@pagefinish="onFinished"
|
||||
@error="onError"
|
||||
@onPostMessage="onMessage"
|
||||
:style="styles" src="/uni_modules/lime-qrcode/hybrid/html/index.html?v=1"></web-view>
|
||||
<!-- #endif -->
|
||||
<!-- <view class="l-qrcode-mask" v-if="['loading', 'expired'].includes(props.status)">
|
||||
<l-loading v-if="props.status == 'loading'"></l-loading>
|
||||
<view class="l-qrcode-expired" v-if="props.status == 'expired'">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* QRCode 二维码组件
|
||||
* @description 用于生成二维码图形,支持自定义图标和样式配置
|
||||
* <br>插件类型:LQrcodeComponentPublicInstance
|
||||
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-qrcode
|
||||
*
|
||||
* @property {string} value 二维码内容(支持文本/URL等)
|
||||
* @property {string} icon 中心图标路径(支持本地/网络路径)
|
||||
* @property {number | string} size 二维码尺寸
|
||||
* @property {number | string} iconSize 中心图标尺寸
|
||||
* @property {number} marginSize 二维码外边距(默认:0)
|
||||
* @property {string} color 二维码颜色(默认:#000000)
|
||||
* @property {string} bgColor 背景颜色(默认:#ffffff)
|
||||
* @property {boolean} bordered 显示边框(默认:false)
|
||||
* @property {'L' | 'M' | 'Q' | 'H'} errorLevel 容错等级(默认:'H')
|
||||
* @value L 可恢复7%的数据
|
||||
* @value M 可恢复15%的数据
|
||||
* @value Q 可恢复25%的数据
|
||||
* @value H 可恢复30%的数据
|
||||
* @property {boolean} useCanvasToTempFilePath 使用canvas生成临时路径(H5端可能需要)
|
||||
* @property {boolean} use2d 启用2D上下文渲染(性能优化,默认:false)
|
||||
*/
|
||||
import { computed, defineComponent, getCurrentInstance, watch, onUnmounted, onMounted } from '@/uni_modules/lime-shared/vue';
|
||||
import QRCodeProps from './props'
|
||||
// #ifndef APP-NVUE
|
||||
import { getCanvas, isCanvas2d } from './useCanvas'
|
||||
import { QRCodeCanvas } from './qrcode.js';
|
||||
// #endif
|
||||
import { addUnit } from '@/uni_modules/lime-shared/addUnit'
|
||||
import { createImage } from '@/uni_modules/lime-shared/createImage'
|
||||
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
import { isBase64 } from '@/uni_modules/lime-shared/isBase64'
|
||||
import { pathToBase64 } from '@/uni_modules/lime-shared/pathToBase64'
|
||||
import { debounce } from '@/uni_modules/lime-shared/debounce'
|
||||
const name = 'l-qrcode'
|
||||
export default defineComponent({
|
||||
name,
|
||||
props: QRCodeProps,
|
||||
emits: ['success'],
|
||||
setup(props, {emit}) {
|
||||
const context = getCurrentInstance();
|
||||
const canvasId = `l-qrcode${context.uid}`
|
||||
const styles = computed(() => `width: ${addUnit(props.size)}; height: ${addUnit(props.size)};`)
|
||||
let qrcode = null
|
||||
let canvas = null
|
||||
const qrCodeProps = computed(() => {
|
||||
const { value, icon, size, color, bgColor, bordered, iconSize, errorLevel, marginSize } = props
|
||||
const imageSettings = {
|
||||
src: icon,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
height: unitConvert(iconSize),
|
||||
width: unitConvert(iconSize),
|
||||
excavate: true,
|
||||
}
|
||||
return {
|
||||
value,
|
||||
size: unitConvert(size),
|
||||
level: errorLevel,
|
||||
bgColor,
|
||||
fgColor: color,
|
||||
imageSettings: icon ? imageSettings : undefined,
|
||||
includeMargin: bordered,
|
||||
marginSize: marginSize ?? 0
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
const stacks = new Map()
|
||||
// #endif
|
||||
const canvasToTempFilePath = debounce((args: UniNamespace.CanvasToTempFilePathRes) => {
|
||||
if(!canvas) return
|
||||
// #ifndef APP-NVUE
|
||||
const copyArgs = Object.assign({
|
||||
canvasId,
|
||||
canvas: null
|
||||
}, args)
|
||||
|
||||
if (isCanvas2d && props.use2d) {
|
||||
// copyArgs.canvas = canvas
|
||||
const tempFilePath = canvas.toDataURL();
|
||||
copyArgs.success({
|
||||
tempFilePath
|
||||
})
|
||||
return
|
||||
}
|
||||
if ('toTempFilePath' in canvas) {
|
||||
canvas.toTempFilePath(copyArgs)
|
||||
} else {
|
||||
uni.canvasToTempFilePath(copyArgs, context);
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
if(!stacks.size) {
|
||||
const flie = 'file-' + Math.random();
|
||||
const stack = {args, time: +new Date()}
|
||||
stacks.set(`${flie}`, stack)
|
||||
canvas.toDataURL(flie)
|
||||
setTimeout(() => {
|
||||
const stack = stacks.get(flie)
|
||||
if(stack && 'fail' in stack.args) {
|
||||
stack.args.fail({
|
||||
error: '超时'
|
||||
})
|
||||
stacks.delete(flie)
|
||||
}
|
||||
},5000)
|
||||
}
|
||||
// #endif
|
||||
})
|
||||
const useCanvasToTempFilePath = () => {
|
||||
if(props.useCanvasToTempFilePath) {
|
||||
canvasToTempFilePath({
|
||||
success(res: UniNamespace.CanvasToTempFilePathRes) {
|
||||
emit('success', res.tempFilePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
const onFinished = () => {
|
||||
const { pixelRatio } = uni.getSystemInfoSync()
|
||||
canvas = {
|
||||
toDataURL(flie: string) {
|
||||
const ref: any = context.refs['qrcodeRef'];
|
||||
if(ref) {
|
||||
ref?.evalJS(`toDataURL('${flie}')`)
|
||||
}
|
||||
}
|
||||
};
|
||||
qrcode = {
|
||||
async render(props: any) {
|
||||
const ref: any = context.refs['qrcodeRef'];
|
||||
const { src } = props.imageSettings || { };
|
||||
if(!ref) return
|
||||
if(src && !isBase64(src) && !/^http/.test(src) && /^\/static/.test(src)) {
|
||||
props.imageSettings.src = await pathToBase64(src)
|
||||
}
|
||||
const _props = JSON.stringify(Object.assign({}, props, {pixelRatio}));
|
||||
ref?.evalJS(`render(${_props})`);
|
||||
}
|
||||
}
|
||||
qrcode.render(qrCodeProps.value)
|
||||
useCanvasToTempFilePath()
|
||||
}
|
||||
const onError = () => {
|
||||
console.warn('lime-qrcode 加载失败')
|
||||
}
|
||||
const onMessage = (e: any) => {
|
||||
const {detail:{data: [res]}} = e
|
||||
if(res.event == 'toDataURL') {
|
||||
const {file, image, msg} = res.data;
|
||||
const stack = stacks.get(file)
|
||||
if(stack && image && 'success' in stack.args) {
|
||||
stack.args.success({tempFilePath: image})
|
||||
stacks.delete(file)
|
||||
} else if(stack && 'fails' in stack.args) {
|
||||
stack.args.fail({error: msg})
|
||||
stacks.delete(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
const propsWatch = watch(props, () => {
|
||||
if (qrcode) {
|
||||
qrcode.render(qrCodeProps.value)
|
||||
useCanvasToTempFilePath()
|
||||
}
|
||||
})
|
||||
onMounted(() => {
|
||||
// #ifndef APP-NVUE
|
||||
getCanvas(canvasId, { context }).then(res => {
|
||||
canvas = res;
|
||||
qrcode = new QRCodeCanvas(res, {
|
||||
path2D: false,
|
||||
pixelRatio: isCanvas2d && props.use2d ? uni.getSystemInfoSync().pixelRatio : 1,
|
||||
createImage
|
||||
})
|
||||
qrcode.render(qrCodeProps.value)
|
||||
useCanvasToTempFilePath()
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
onUnmounted(() => {
|
||||
propsWatch && propsWatch()
|
||||
})
|
||||
return {
|
||||
canvasId,
|
||||
styles,
|
||||
props,
|
||||
canvasToTempFilePath,
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
onFinished,
|
||||
onError,
|
||||
onMessage
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.l-qrcode {
|
||||
position: relative;
|
||||
|
||||
&-mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
// inset-block-start: 0;
|
||||
// inset-inline-start: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
line-height: 1.5714285714285714;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
40
uni_modules/lime-qrcode/components/l-qrcode/props.ts
Normal file
40
uni_modules/lime-qrcode/components/l-qrcode/props.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
// @ts-nocheck
|
||||
// import type { PropType } from './vue'
|
||||
export default {
|
||||
value: String,
|
||||
icon: String,
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 160
|
||||
},
|
||||
iconSize: {
|
||||
type: [Number, String],
|
||||
default: 40
|
||||
},
|
||||
marginSize: Number,
|
||||
color: {
|
||||
type: String,
|
||||
default: '#000'
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
errorLevel: {
|
||||
type: String as PropType<'L'|'M'|'Q'|'H'>,
|
||||
default: 'M' // 'L' | 'M' | 'Q' | 'H'
|
||||
},
|
||||
useCanvasToTempFilePath: Boolean,
|
||||
use2d: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
// status: {
|
||||
// type: String as PropType<'active'|'expired'|'loading'>,
|
||||
// default: 'active' // active | expired | loading
|
||||
// }
|
||||
}
|
||||
6
uni_modules/lime-qrcode/components/l-qrcode/qrcode.js
Normal file
6
uni_modules/lime-qrcode/components/l-qrcode/qrcode.js
Normal file
File diff suppressed because one or more lines are too long
48
uni_modules/lime-qrcode/components/l-qrcode/type.ts
Normal file
48
uni_modules/lime-qrcode/components/l-qrcode/type.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
// @ts-nocheck
|
||||
export type ImageSettings = {
|
||||
width: number
|
||||
height: number
|
||||
x?: number
|
||||
y?: number
|
||||
excavate: boolean
|
||||
}
|
||||
export type QRCodePropsTypes = {
|
||||
value?: string
|
||||
size?: number
|
||||
fgColor?: string
|
||||
level?: string
|
||||
marginSize: number
|
||||
includeMargin: boolean
|
||||
imageSettings?: ImageSettings
|
||||
}
|
||||
|
||||
export type QRCodeCallback = (cells : boolean[][]) => void
|
||||
|
||||
export type Excavation = {
|
||||
x: number
|
||||
y: number
|
||||
h: number
|
||||
w: number
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 成功回调函数定义
|
||||
*/
|
||||
export type TakeSnapshotSuccessCallback = (res: TakeSnapshotSuccess) => void
|
||||
/**
|
||||
* 失败回调函数定义
|
||||
*/
|
||||
export type TakeSnapshotFailCallback = (res: TakeSnapshotFail) => void
|
||||
/**
|
||||
* 完成回调函数定义
|
||||
*/
|
||||
export type TakeSnapshotCompleteCallback = (res: any) => void
|
||||
|
||||
|
||||
export type LQrcodeFailCallback = TakeSnapshotFailCallback
|
||||
export type LQrcodeCompleteCallback = TakeSnapshotCompleteCallback
|
||||
export type LQrcodeSuccessCallback = TakeSnapshotSuccessCallback
|
||||
78
uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts
Normal file
78
uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
// @ts-nocheck
|
||||
import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue'
|
||||
import { getRect } from '@/uni_modules/lime-shared/getRect'
|
||||
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
|
||||
export const isCanvas2d = canIUseCanvas2d()
|
||||
|
||||
export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) {
|
||||
let { context } = options
|
||||
// #ifdef MP || VUE2
|
||||
if (context.proxy) context = context.proxy
|
||||
// #endif
|
||||
return getRect('#' + canvasId, context, isCanvas2d).then(res => {
|
||||
if(res.node){
|
||||
return res.node
|
||||
} else {
|
||||
const ctx = uni.createCanvasContext(canvasId, context)
|
||||
return {
|
||||
getContext(type: string) {
|
||||
if(type == '2d') {
|
||||
return ctx
|
||||
}
|
||||
},
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
}
|
||||
// #ifdef H5
|
||||
// canvas.value = context.proxy.$el.querySelector('#'+ canvasId)
|
||||
// #endif
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// #ifndef H5 || APP-NVUE
|
||||
class Image {
|
||||
currentSrc: string | null = null
|
||||
naturalHeight: number = 0
|
||||
naturalWidth: number = 0
|
||||
width: number = 0
|
||||
height: number = 0
|
||||
tagName: string = 'IMG'
|
||||
path: any = ''
|
||||
crossOrigin: any = ''
|
||||
referrerPolicy: any = ''
|
||||
onload: () => void
|
||||
onerror: () => void
|
||||
constructor() {}
|
||||
set src(src) {
|
||||
this.currentSrc = src
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
this.path = res.path
|
||||
this.naturalWidth = this.width = res.width
|
||||
this.naturalHeight = this.height = res.height
|
||||
this.onload()
|
||||
},
|
||||
fail: () => {
|
||||
this.onerror()
|
||||
}
|
||||
})
|
||||
}
|
||||
get src() {
|
||||
return this.currentSrc
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
export function createImage(canvas: WechatMiniprogram.Canvas) {
|
||||
if(canvas && canvas.createImage) {
|
||||
return canvas.createImage()
|
||||
} else if(typeof window != 'undefined' && window.Image) {
|
||||
return new window.Image()
|
||||
}
|
||||
// #ifndef H5 || APP-NVUE
|
||||
return new Image()
|
||||
// #endif
|
||||
}
|
||||
35
uni_modules/lime-qrcode/components/l-qrcode/utils.uts
Normal file
35
uni_modules/lime-qrcode/components/l-qrcode/utils.uts
Normal file
@@ -0,0 +1,35 @@
|
||||
export function addUnit(value: any|null):string{
|
||||
if(value == null){
|
||||
return ''
|
||||
}
|
||||
value = `${value}`
|
||||
return /^(-)?\d+(\\.\d+)?$/.test(value) ? `${value}px` : value
|
||||
}
|
||||
|
||||
export function unitConvert(value: any|null): number{
|
||||
if(typeof value == 'number'){
|
||||
return value as number
|
||||
}
|
||||
if(typeof value == 'string'){
|
||||
value = `${value}`
|
||||
if(/^(-)?\d+(\\.\d+)?$/.test(value)){
|
||||
return parseFloat(value);
|
||||
}
|
||||
|
||||
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g;
|
||||
const results = reg.exec(value);
|
||||
if (results == null) {
|
||||
return 0;
|
||||
}
|
||||
const unit = results[3];
|
||||
const v = parseFloat(value);
|
||||
if (unit == 'rpx') {
|
||||
const { windowWidth } = uni.getWindowInfo()
|
||||
return windowWidth / 750 * v;
|
||||
}
|
||||
if (unit == 'px') {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
146
uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue
Normal file
146
uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text ultra">QRCode</text>
|
||||
<text class="demo-block__desc-text">能够将文本转换生成二维码的组件,支持自定义配色和 Logo 配置</text>
|
||||
<view class="demo-block__body">
|
||||
<view class="demo-block card">
|
||||
<text class="demo-block__title-text large">基础</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" :use2d="false"></l-qrcode>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block card">
|
||||
<text class="demo-block__title-text large">icon</text>
|
||||
<view class="demo-block__body">
|
||||
<image v-if="image !=''" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<view style="flex-direction: row; justify-content: space-between">
|
||||
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
<l-qrcode :useCanvasToTempFilePath="true" @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
</view>
|
||||
<button type="primary" style="margin-top: 20rpx;" @click="onClick">生成图片</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block card">
|
||||
<text class="demo-block__title-text large">颜色</text>
|
||||
<view class="demo-block__body">
|
||||
<view style="flex-direction: row; justify-content: space-between">
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgba(82,196,26,1)"></l-qrcode>
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgba(22,119,255,1)" bgColor="rgb(245,245,245)"></l-qrcode>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block card">
|
||||
<text class="demo-block__title-text large">纠错比例</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
|
||||
<button type="primary" style="margin-top: 20rpx;" @click="onToggle">切换纠错等级:{{levels[index]}}</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block card">
|
||||
<text class="demo-block__title-text large">动态</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode>
|
||||
<button type="primary" style="margin-top: 20rpx;" @click="update">更新</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
// import {ComponentPublicInstance} from 'vue'
|
||||
export default {
|
||||
name: 'lime-qrcode',
|
||||
data() {
|
||||
return {
|
||||
text: 'qcoon.com.cn',
|
||||
image: '',
|
||||
index: 0,
|
||||
levels: ['L', 'M', 'Q', 'H']
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
success(src: string) {
|
||||
console.log(`src`, src)
|
||||
},
|
||||
update() {
|
||||
this.text = `qcoon.cn?v=${Math.random()}`
|
||||
},
|
||||
onToggle() {
|
||||
this.index++
|
||||
this.index = this.index % this.levels.length
|
||||
},
|
||||
onClick() {
|
||||
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
|
||||
el.canvasToTempFilePath({
|
||||
success:(res: TakeSnapshotSuccess)=>{
|
||||
this.image = res.tempFilePath
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
$card-bg-color: var(--doc-card-bg-color, white);
|
||||
// $page-bg-color: var(--doc-page-bg-color, #f5f5f5);
|
||||
$title-color: var(--doc-title-color, #000000E6);
|
||||
$summary-color: var(--doc-summary-color, #00000099);
|
||||
|
||||
.demo-block {
|
||||
margin: 32px 10px 0;
|
||||
overflow: visible;
|
||||
&.card{
|
||||
background-color: $card-bg-color;
|
||||
transition-property: background-color;
|
||||
// transition-duration: 300ms;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
&__title {
|
||||
margin: 0;
|
||||
margin-top: 8px;
|
||||
&-text {
|
||||
color: $summary-color;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
|
||||
&.large {
|
||||
color: $title-color;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 26px;
|
||||
}
|
||||
&.ultra {
|
||||
color: $title-color;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__desc-text {
|
||||
color: $summary-color;
|
||||
margin: 8px 16px 0 0;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
&__body {
|
||||
margin: 16px 0;
|
||||
overflow: visible;
|
||||
.demo-block {
|
||||
// margin-top: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
141
uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue
Normal file
141
uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text ultra">QRCode 二维码</text>
|
||||
<text class="demo-block__desc-text">生成二维码</text>
|
||||
<view class="demo-block__body">
|
||||
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">基础</text>
|
||||
<view class="demo-block__body">
|
||||
<view style="display: flex; gap: 10px">
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode :use2d="false" ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
<!-- <l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode> -->
|
||||
</view>
|
||||
<button @click="onClick">生成图片</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">颜色</text>
|
||||
<view class="demo-block__body">
|
||||
<view style="display: flex; gap: 10px">
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">纠错比例</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
|
||||
<button @click="onToggle">切换纠错等级:{{levels[index]}}</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">动态</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode>
|
||||
<button @click="update">更新</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {ref, defineComponent} from '@/uni_modules/lime-shared/vue'
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const qrcodeRef = ref(null)
|
||||
const image = ref(null)
|
||||
const text = ref('qcoon.com.cn')
|
||||
const levels = ['L', 'M', 'Q', 'H']
|
||||
let index = ref(0)
|
||||
const onToggle = () => {
|
||||
index.value++
|
||||
index.value = index.value % levels.length
|
||||
}
|
||||
const onClick = () => {
|
||||
if(qrcodeRef.value) {
|
||||
qrcodeRef.value.canvasToTempFilePath({
|
||||
success(res) {
|
||||
image.value = res.tempFilePath
|
||||
console.log('success:::', res)
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const success = (res) => {
|
||||
console.log('res', res)
|
||||
}
|
||||
|
||||
const update = () =>{
|
||||
text.value = `qcoon.cn?v=${Math.random()}`
|
||||
}
|
||||
|
||||
return {
|
||||
levels,
|
||||
index,
|
||||
image,
|
||||
text,
|
||||
qrcodeRef,
|
||||
onClick,
|
||||
update,
|
||||
success,
|
||||
onToggle,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
.demo-block {
|
||||
margin: 32px 10px 0;
|
||||
overflow: visible;
|
||||
&.card{
|
||||
background-color: white;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
&__title {
|
||||
margin: 0;
|
||||
margin-top: 8px;
|
||||
&-text {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
display: flex;
|
||||
&.large {
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 26px;
|
||||
}
|
||||
&.ultra {
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__desc-text {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
margin: 8px 16px 0 0;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
&__body {
|
||||
margin: 16px 0;
|
||||
overflow: visible;
|
||||
.demo-block {
|
||||
// margin-top: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
77
uni_modules/lime-qrcode/hybrid/html/index.html
Normal file
77
uni_modules/lime-qrcode/hybrid/html/index.html
Normal file
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>lime-qrcode</title>
|
||||
<style>
|
||||
html,body,canvas {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
/* background-color: rgba(255,0,0,0.1) */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="lime-qrcode"></canvas>
|
||||
<script type="text/javascript" src="./uni.webview.1.5.3.js"></script>
|
||||
<script type="text/javascript" src="./qrcode.min.js"></script>
|
||||
<script>
|
||||
var canvas = document.querySelector('#lime-qrcode')
|
||||
var pixelRatio = window.devicePixelRatio || 1
|
||||
function appendWatermark(image) {
|
||||
emit('append', mark.toDataURL())
|
||||
}
|
||||
|
||||
var qrcode = new lime.QRCodeCanvas(canvas, {
|
||||
pixelRatio,
|
||||
})
|
||||
function render(props) {
|
||||
if(props.pixelRatio) {
|
||||
pixelRatio = props.pixelRatio
|
||||
}
|
||||
if(qrcode) {
|
||||
qrcode.render(props)
|
||||
}
|
||||
}
|
||||
function toDataURL(file) {
|
||||
if(qrcode && canvas) {
|
||||
try{
|
||||
const image = canvas.toDataURL()
|
||||
emit('toDataURL', {
|
||||
file,
|
||||
image
|
||||
})
|
||||
}catch(e){
|
||||
emit('toDataURL', {
|
||||
file,
|
||||
msg: e
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
function emit(event, data) {
|
||||
postMessage({
|
||||
event,
|
||||
data
|
||||
});
|
||||
};
|
||||
function postMessage(data) {
|
||||
uni.postMessage({
|
||||
data
|
||||
});
|
||||
};
|
||||
// render({
|
||||
// content: ['Lime UI'],
|
||||
// // rotate: -22,
|
||||
// // baseSize: 2,
|
||||
// // fontGap: 3
|
||||
// })
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
6
uni_modules/lime-qrcode/hybrid/html/qrcode.min.js
vendored
Normal file
6
uni_modules/lime-qrcode/hybrid/html/qrcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js
Normal file
1
uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js
Normal file
File diff suppressed because one or more lines are too long
108
uni_modules/lime-qrcode/package.json
Normal file
108
uni_modules/lime-qrcode/package.json
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"id": "lime-qrcode",
|
||||
"displayName": "qrcode 二维码生成",
|
||||
"version": "0.2.4",
|
||||
"description": "一款全平台通用的二维码生成插件,支持uniapp/uniappx",
|
||||
"keywords": [
|
||||
"qrcode",
|
||||
"qr",
|
||||
"uvue",
|
||||
"生成图片",
|
||||
"二维码"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"uni-app": "^4.41",
|
||||
"uni-app-x": "^4.61"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": "305716444"
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"darkmode": "x",
|
||||
"i18n": "x",
|
||||
"widescreen": "x"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"lime-shared"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "√",
|
||||
"aliyun": "√",
|
||||
"alipay": "√"
|
||||
},
|
||||
"client": {
|
||||
"uni-app": {
|
||||
"vue": {
|
||||
"vue2": "√",
|
||||
"vue3": "√"
|
||||
},
|
||||
"web": {
|
||||
"safari": "√",
|
||||
"chrome": "√"
|
||||
},
|
||||
"app": {
|
||||
"vue": "√",
|
||||
"nvue": "√",
|
||||
"android": {
|
||||
"extVersion": "",
|
||||
"minVersion": "21"
|
||||
},
|
||||
"ios": "√",
|
||||
"harmony": "√"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "√",
|
||||
"alipay": "√",
|
||||
"toutiao": "√",
|
||||
"baidu": "√",
|
||||
"kuaishou": "√",
|
||||
"jd": "√",
|
||||
"harmony": "-",
|
||||
"qq": "√",
|
||||
"lark": "√"
|
||||
},
|
||||
"quickapp": {
|
||||
"huawei": "-",
|
||||
"union": "-"
|
||||
}
|
||||
},
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "√",
|
||||
"chrome": "√"
|
||||
},
|
||||
"app": {
|
||||
"android": {
|
||||
"extVersion": "",
|
||||
"minVersion": "21"
|
||||
},
|
||||
"ios": "√",
|
||||
"harmony": "√"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "√"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
166
uni_modules/lime-qrcode/readme - 副本.md
Normal file
166
uni_modules/lime-qrcode/readme - 副本.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# lime-qrcode 二维码
|
||||
- 一款全平台通用的二维码生成插件,支持uniapp/uniappx
|
||||
- uniappx app 需要导入[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)
|
||||
|
||||
|
||||
## 文档
|
||||
[qrcode【站点1】](https://limex.qcoon.cn/components/qrcode.html)
|
||||
[qrcode【站点2】](https://limeui.netlify.app/components/qrcode.html)
|
||||
[qrcode【站点3】](https://limeui.familyzone.top/components/qrcode.html)
|
||||
|
||||
|
||||
|
||||
## 安装
|
||||
在插件市场导入即可。
|
||||
|
||||
**注意**
|
||||
- uniappx app 需要导入**[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)**,
|
||||
- 非uniappx app可直接使用
|
||||
|
||||
## 代码演示
|
||||
|
||||
### 基础使用
|
||||
|
||||
```html
|
||||
<l-qrcode value="http://lime.qcoon.cn" />
|
||||
```
|
||||
|
||||
|
||||
### ICON
|
||||
- 带 Icon 的二维码
|
||||
|
||||
```html
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" icon="/static/logo.png" iconSize="70rpx"></l-qrcode>
|
||||
```
|
||||
|
||||
### 颜色
|
||||
- 通过设置 `color` 自定义二维码颜色,通过设置 `bgColor` 自定义背景颜色。
|
||||
|
||||
```html
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
|
||||
```
|
||||
|
||||
### 纠错比例
|
||||
- 通过设置 `errorLevel` 调整不同的容错等级。
|
||||
|
||||
```html
|
||||
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" errorLevel="H"></l-qrcode>
|
||||
```
|
||||
|
||||
### 生成图片
|
||||
如果是canvas 2d的环境生成的是base64
|
||||
- 1、通过调用插件的`canvasToTempFilePath`方法生成图片。
|
||||
|
||||
```html
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
<button @click="onClick">生成图片</button>
|
||||
```
|
||||
```js
|
||||
// vue3
|
||||
const qrcodeRef = ref(null)
|
||||
const onClick = () => {
|
||||
if(!qrcodeRef.value) return
|
||||
qrcodeRef.value.canvasToTempFilePath({
|
||||
success(res) {
|
||||
image.value = res.tempFilePath
|
||||
console.log('success:::', res)
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// vue2
|
||||
const el = this.$refs['qrcodeRef']
|
||||
el.canvasToTempFilePath({
|
||||
success:(res)=>{
|
||||
this.image = res.tempFilePath
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
|
||||
// uvue
|
||||
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
|
||||
el.canvasToTempFilePath({
|
||||
success:(res: TakeSnapshotSuccess)=>{
|
||||
this.image = res.tempFilePath
|
||||
},
|
||||
fail(err: TakeSnapshotFail) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
- 2、通过设置`useCanvasToTempFilePath`在`success`事件里接收图片地址
|
||||
|
||||
```html
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn"></l-qrcode>
|
||||
```
|
||||
```js
|
||||
const image = ref(null)
|
||||
const success = (img) => {
|
||||
image.value = img
|
||||
}
|
||||
```
|
||||
|
||||
### Vue2使用
|
||||
- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置
|
||||
- 关键代码是: 在main.js中 在vue2部分加上这一段即可
|
||||
|
||||
```js
|
||||
// main.js vue2
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
Vue.use(VueCompositionAPI)
|
||||
```
|
||||
另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录
|
||||
```cmd
|
||||
// \HBuilderX\plugins\compile-typescript
|
||||
yarn add typescript -D
|
||||
- or -
|
||||
npm install typescript -D
|
||||
```
|
||||
|
||||
### 查看示例
|
||||
- 导入后直接使用这个标签查看演示效果
|
||||
|
||||
```html
|
||||
// 代码位于 uni_modules/lime-qrcode/compoents/lime-qrcode
|
||||
<lime-qrcode />
|
||||
```
|
||||
|
||||
### 插件标签
|
||||
- 默认 l-qrcode 为 component
|
||||
- 默认 lime-qrcode 为 demo
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ |
|
||||
| value | 扫描后的文本 | <em>string</em> | `-` |
|
||||
| icon | 二维码中图片的地址 | <em>string</em> | `-` |
|
||||
| size | 二维码大小 | <em>number,string</em> | `160` |
|
||||
| iconSize | 二维码中图片的大小 | <em>number,string</em> | `40` |
|
||||
| color | 二维码颜色 | <em>string</em> | `-` |
|
||||
| bgColor | 二维码背景颜色 | <em>string</em> | `-` |
|
||||
| errorLevel | 二维码纠错等级 | `'L' | 'M' | 'Q' | 'H' ` | `M` |
|
||||
| marginSize | 边距码大小,默认为0码点 | <em>number</em> | `0` |
|
||||
|
||||
### 常见问题
|
||||
- icon 是网络地址时,H5和Nvue需要解决跨域问题,小程序需要配置download
|
||||
|
||||
## 打赏
|
||||
|
||||
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
|
||||

|
||||

|
||||
172
uni_modules/lime-qrcode/readme.md
Normal file
172
uni_modules/lime-qrcode/readme.md
Normal file
@@ -0,0 +1,172 @@
|
||||
# lime-qrcode 二维码组件
|
||||
一个功能丰富的二维码生成组件,用于生成自定义二维码。支持自定义大小、颜色、图标、边框等多种配置,可用于分享链接、支付码、名片等多种场景。组件提供了丰富的自定义选项,可以满足各种二维码生成需求。
|
||||
|
||||
> 插件依赖:`lime-shared`
|
||||
|
||||
## 文档链接
|
||||
📚 组件详细文档请访问以下站点:
|
||||
- [二维码组件文档 - 站点1](https://limex.qcoon.cn/components/qrcode.html)
|
||||
- [二维码组件文档 - 站点2](https://limeui.netlify.app/components/qrcode.html)
|
||||
- [二维码组件文档 - 站点3](https://limeui.familyzone.top/components/qrcode.html)
|
||||
|
||||
## 安装方法
|
||||
1. 在uni-app插件市场中搜索并导入`lime-qrcode`
|
||||
2. 导入后可能需要重新编译项目
|
||||
3. 在页面中使用`l-qrcode`组件
|
||||
|
||||
::: tip 注意🔔
|
||||
uniappx app 需要导入[【lime-qrcodegen】](https://ext.dcloud.net.cn/plugin?id=1583)这个依赖插件,它是收费的插件,普通授权则需要自定义基座,才能使用,如果您是UI组件库VIP则忽略。
|
||||
:::
|
||||
|
||||
|
||||
## 代码演示
|
||||
|
||||
### 基础使用
|
||||
|
||||
```html
|
||||
<l-qrcode value="http://lime.qcoon.cn" />
|
||||
```
|
||||
|
||||
|
||||
### ICON
|
||||
- 带 Icon 的二维码
|
||||
|
||||
```html
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" icon="/static/logo.png" iconSize="70rpx"></l-qrcode>
|
||||
```
|
||||
|
||||
### 颜色
|
||||
- 通过设置 `color` 自定义二维码颜色,通过设置 `bgColor` 自定义背景颜色。
|
||||
|
||||
```html
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
|
||||
```
|
||||
|
||||
### 纠错比例
|
||||
- 通过设置 `errorLevel` 调整不同的容错等级。
|
||||
|
||||
```html
|
||||
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" errorLevel="H"></l-qrcode>
|
||||
```
|
||||
|
||||
### 生成图片
|
||||
如果是canvas 2d的环境生成的是base64
|
||||
- 1、通过调用插件的`canvasToTempFilePath`方法生成图片。
|
||||
|
||||
```html
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
<button @click="onClick">生成图片</button>
|
||||
```
|
||||
```js
|
||||
// vue3
|
||||
const qrcodeRef = ref(null)
|
||||
const onClick = () => {
|
||||
if(!qrcodeRef.value) return
|
||||
qrcodeRef.value.canvasToTempFilePath({
|
||||
success(res) {
|
||||
image.value = res.tempFilePath
|
||||
console.log('success:::', res)
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// vue2
|
||||
const el = this.$refs['qrcodeRef']
|
||||
el.canvasToTempFilePath({
|
||||
success:(res)=>{
|
||||
this.image = res.tempFilePath
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
|
||||
// uvue
|
||||
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
|
||||
el.canvasToTempFilePath({
|
||||
success:(res: TakeSnapshotSuccess)=>{
|
||||
this.image = res.tempFilePath
|
||||
},
|
||||
fail(err: TakeSnapshotFail) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
- 2、通过设置`useCanvasToTempFilePath`在`success`事件里接收图片地址
|
||||
|
||||
```html
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn"></l-qrcode>
|
||||
```
|
||||
```js
|
||||
const image = ref(null)
|
||||
const success = (img) => {
|
||||
image.value = img
|
||||
}
|
||||
```
|
||||
|
||||
## 快速预览
|
||||
导入插件后,可以直接使用以下标签查看演示效果:
|
||||
|
||||
```html
|
||||
<!-- 代码位于 uni_modules/lime-qrcode/components/lime-qrcode -->
|
||||
<lime-qrcode />
|
||||
```
|
||||
|
||||
## 插件标签说明
|
||||
|
||||
| 标签名 | 说明 |
|
||||
| --- | --- |
|
||||
| `l-qrcode` | 组件标签 |
|
||||
| `lime-qrcode` | 演示标签 |
|
||||
|
||||
## Vue2使用说明
|
||||
main.js中添加以下代码:
|
||||
```js
|
||||
// vue2项目中使用
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
Vue.use(VueCompositionAPI)
|
||||
```
|
||||
|
||||
详细配置请参考官方文档:[Vue Composition API](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)
|
||||
|
||||
|
||||
## API文档
|
||||
|
||||
### Props 属性说明
|
||||
|
||||
| 属性名 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| value | 二维码内容 | _string_ | - |
|
||||
| size | 二维码大小 | _number \| string_ | `160` |
|
||||
| color | 二维码颜色 | _string_ | `#000` |
|
||||
| bgColor | 二维码背景色 | _string_ | `transparent` |
|
||||
| icon | 二维码中心图标 | _string_ | - |
|
||||
| iconSize | 图标大小 | _number \| string_ | `40` |
|
||||
| marginSize | 二维码外边距 | _number_ | - |
|
||||
| bordered | 是否显示边框 | _boolean_ | `true` |
|
||||
| errorLevel | 纠错级别,可选值为 `L`、`M`、`Q`、`H` | _string_ | `M` |
|
||||
| useCanvasToTempFilePath | 是否使用Canvas导出为临时文件 | _boolean_ | `false` |
|
||||
| use2d | 是否使用2D Canvas | _boolean_ | `true` |
|
||||
|
||||
### Events 事件
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| complete | 二维码生成完成时触发 | _path: string_ 临时文件路径(仅当 useCanvasToTempFilePath 为 true 时有效) |
|
||||
| error | 二维码生成失败时触发 | _error: Error_ 错误信息 |
|
||||
|
||||
## 支持与赞赏
|
||||
|
||||
如果你觉得本插件解决了你的问题,可以考虑支持作者:
|
||||
|
||||
| 支付宝赞助 | 微信赞助 |
|
||||
|------------|------------|
|
||||
|  |  |
|
||||
42
uni_modules/lime-shared/addUnit/index.ts
Normal file
42
uni_modules/lime-shared/addUnit/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
// @ts-nocheck
|
||||
import {isNumeric} from '../isNumeric'
|
||||
import {isDef} from '../isDef'
|
||||
/**
|
||||
* 给一个值添加单位(像素 px)
|
||||
* @param value 要添加单位的值,可以是字符串或数字
|
||||
* @returns 添加了单位的值,如果值为 null 则返回 null
|
||||
*/
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export function addUnit(value?: string | number): string | null {
|
||||
if (!isDef(value)) {
|
||||
return null;
|
||||
}
|
||||
value = String(value); // 将值转换为字符串
|
||||
// 如果值是数字,则在后面添加单位 "px",否则保持原始值
|
||||
return isNumeric(value) ? `${value}px` : value;
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
function addUnit(value: string): string|null
|
||||
function addUnit(value: number): string|null
|
||||
function addUnit(value: string|number|null): string|null {
|
||||
if (!isDef(value)) {
|
||||
return null;
|
||||
}
|
||||
value = `${value}` //value.toString(); // 将值转换为字符串
|
||||
|
||||
// 如果值是数字,则在后面添加单位 "px",否则保持原始值
|
||||
return isNumeric(value) ? `${value}px` : value;
|
||||
}
|
||||
export {addUnit}
|
||||
// #endif
|
||||
|
||||
|
||||
// console.log(addUnit(100)); // 输出: "100px"
|
||||
// console.log(addUnit("200")); // 输出: "200px"
|
||||
// console.log(addUnit("300px")); // 输出: "300px"(已经包含单位)
|
||||
// console.log(addUnit()); // 输出: undefined(值为 undefined)
|
||||
// console.log(addUnit(null)); // 输出: undefined(值为 null)
|
||||
82
uni_modules/lime-shared/animation/bezier.ts
Normal file
82
uni_modules/lime-shared/animation/bezier.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
export function cubicBezier(p1x : number, p1y : number, p2x : number, p2y : number):(x: number)=> number {
|
||||
const ZERO_LIMIT = 1e-6;
|
||||
// Calculate the polynomial coefficients,
|
||||
// implicit first and last control points are (0,0) and (1,1).
|
||||
const ax = 3 * p1x - 3 * p2x + 1;
|
||||
const bx = 3 * p2x - 6 * p1x;
|
||||
const cx = 3 * p1x;
|
||||
|
||||
const ay = 3 * p1y - 3 * p2y + 1;
|
||||
const by = 3 * p2y - 6 * p1y;
|
||||
const cy = 3 * p1y;
|
||||
|
||||
function sampleCurveDerivativeX(t : number) : number {
|
||||
// `ax t^3 + bx t^2 + cx t` expanded using Horner's rule
|
||||
return (3 * ax * t + 2 * bx) * t + cx;
|
||||
}
|
||||
|
||||
function sampleCurveX(t : number) : number {
|
||||
return ((ax * t + bx) * t + cx) * t;
|
||||
}
|
||||
|
||||
function sampleCurveY(t : number) : number {
|
||||
return ((ay * t + by) * t + cy) * t;
|
||||
}
|
||||
|
||||
// Given an x value, find a parametric value it came from.
|
||||
function solveCurveX(x : number) : number {
|
||||
let t2 = x;
|
||||
let derivative : number;
|
||||
let x2 : number;
|
||||
|
||||
// https://trac.webkit.org/browser/trunk/Source/WebCore/platform/animation
|
||||
// first try a few iterations of Newton's method -- normally very fast.
|
||||
// http://en.wikipedia.org/wikiNewton's_method
|
||||
for (let i = 0; i < 8; i++) {
|
||||
// f(t) - x = 0
|
||||
x2 = sampleCurveX(t2) - x;
|
||||
if (Math.abs(x2) < ZERO_LIMIT) {
|
||||
return t2;
|
||||
}
|
||||
derivative = sampleCurveDerivativeX(t2);
|
||||
// == 0, failure
|
||||
/* istanbul ignore if */
|
||||
if (Math.abs(derivative) < ZERO_LIMIT) {
|
||||
break;
|
||||
}
|
||||
t2 -= x2 / derivative;
|
||||
}
|
||||
|
||||
// Fall back to the bisection method for reliability.
|
||||
// bisection
|
||||
// http://en.wikipedia.org/wiki/Bisection_method
|
||||
let t1 = 1;
|
||||
/* istanbul ignore next */
|
||||
let t0 = 0;
|
||||
|
||||
/* istanbul ignore next */
|
||||
t2 = x;
|
||||
/* istanbul ignore next */
|
||||
while (t1 > t0) {
|
||||
x2 = sampleCurveX(t2) - x;
|
||||
if (Math.abs(x2) < ZERO_LIMIT) {
|
||||
return t2;
|
||||
}
|
||||
if (x2 > 0) {
|
||||
t1 = t2;
|
||||
} else {
|
||||
t0 = t2;
|
||||
}
|
||||
t2 = (t1 + t0) / 2;
|
||||
}
|
||||
|
||||
// Failure
|
||||
return t2;
|
||||
}
|
||||
|
||||
return function (x : number) : number {
|
||||
return sampleCurveY(solveCurveX(x));
|
||||
}
|
||||
|
||||
// return solve;
|
||||
}
|
||||
3
uni_modules/lime-shared/animation/ease.ts
Normal file
3
uni_modules/lime-shared/animation/ease.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import {cubicBezier} from './bezier';
|
||||
export let ease = cubicBezier(0.25, 0.1, 0.25, 1);
|
||||
export let linear = cubicBezier(0,0,1,1);
|
||||
12
uni_modules/lime-shared/animation/index.ts
Normal file
12
uni_modules/lime-shared/animation/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
// export * from './uvue.uts'
|
||||
export { Timeline, Animation } from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export * from './vue.ts'
|
||||
export { Timeline, Animation } from './vue.ts'
|
||||
// #endif
|
||||
108
uni_modules/lime-shared/animation/useTransition.ts
Normal file
108
uni_modules/lime-shared/animation/useTransition.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
// @ts-nocheck
|
||||
import type { ComponentPublicInstance, Ref } from 'vue'
|
||||
import { ease, linear } from './ease';
|
||||
import { Timeline, Animation } from './';
|
||||
export type UseTransitionOptions = {
|
||||
duration ?: number
|
||||
immediate ?: boolean
|
||||
context ?: ComponentPublicInstance
|
||||
}
|
||||
// #ifndef UNI-APP-X && APP
|
||||
import { ref, watch } from '@/uni_modules/lime-shared/vue'
|
||||
|
||||
export function useTransition(percent : Ref<number>|(() => number), options : UseTransitionOptions) : Ref<number> {
|
||||
const current = ref(0)
|
||||
const { immediate, duration = 300 } = options
|
||||
let tl:Timeline|null = null;
|
||||
let timer = -1
|
||||
const isFunction = typeof percent === 'function'
|
||||
watch(isFunction ? percent : () => percent.value, (v) => {
|
||||
if(tl == null){
|
||||
tl = new Timeline()
|
||||
}
|
||||
tl.start();
|
||||
tl.add(
|
||||
new Animation(
|
||||
current.value,
|
||||
v,
|
||||
duration,
|
||||
0,
|
||||
ease,
|
||||
nowValue => {
|
||||
current.value = nowValue
|
||||
clearTimeout(timer)
|
||||
if(current.value == v){
|
||||
timer = setTimeout(()=>{
|
||||
tl?.pause();
|
||||
tl = null
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}, { immediate })
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
type UseTransitionReturnType = Ref<number>
|
||||
export function useTransition(source : any, options : UseTransitionOptions) : UseTransitionReturnType {
|
||||
const outputRef : Ref<number> = ref(0)
|
||||
const immediate = options.immediate ?? false
|
||||
const duration = options.duration ?? 300
|
||||
const context = options.context //as ComponentPublicInstance | null
|
||||
let tl:Timeline|null = null;
|
||||
let timer = -1
|
||||
const watchFunc = (v : number) => {
|
||||
if(tl == null){
|
||||
tl = new Timeline()
|
||||
}
|
||||
tl!.start();
|
||||
tl!.add(
|
||||
new Animation(
|
||||
outputRef.value,
|
||||
v,
|
||||
duration,
|
||||
0,
|
||||
ease,
|
||||
nowValue => {
|
||||
outputRef.value = nowValue
|
||||
clearTimeout(timer)
|
||||
if(outputRef.value == v){
|
||||
timer = setTimeout(()=>{
|
||||
tl?.pause();
|
||||
tl = null
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if (context != null && typeof source == 'string') {
|
||||
context.$watch(source, watchFunc, { immediate } as WatchOptions)
|
||||
} else if(typeof source == 'function'){
|
||||
watch(source, watchFunc, { immediate })
|
||||
}
|
||||
// #ifdef APP-ANDROID
|
||||
else if(isRef(source) && source instanceof Ref<number>) {
|
||||
watch(source as Ref<number>, watchFunc, { immediate })
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
else if(isRef(source) && typeof source.value == 'number') {
|
||||
watch(source, watchFunc, { immediate })
|
||||
}
|
||||
// #endif
|
||||
|
||||
const stop = ()=>{
|
||||
|
||||
}
|
||||
return outputRef //as UseTransitionReturnType
|
||||
}
|
||||
|
||||
// #endif
|
||||
119
uni_modules/lime-shared/animation/uvue.uts
Normal file
119
uni_modules/lime-shared/animation/uvue.uts
Normal file
@@ -0,0 +1,119 @@
|
||||
// @ts-nocheck
|
||||
import { raf, cancelRaf} from '../raf'
|
||||
export class Timeline {
|
||||
state : string
|
||||
animations : Set<Animation> = new Set<Animation>()
|
||||
delAnimations : Animation[] = []
|
||||
startTimes : Map<Animation, number> = new Map<Animation, number>()
|
||||
pauseTime : number = 0
|
||||
pauseStart : number = Date.now()
|
||||
tickHandler : number = 0
|
||||
tickHandlers : number[] = []
|
||||
tick : (() => void) | null = null
|
||||
constructor() {
|
||||
this.state = 'Initiated';
|
||||
}
|
||||
start() {
|
||||
if (!(this.state == 'Initiated')) return;
|
||||
this.state = 'Started';
|
||||
|
||||
let startTime = Date.now();
|
||||
this.pauseTime = 0;
|
||||
this.tick = () => {
|
||||
let now = Date.now();
|
||||
this.animations.forEach((animation : Animation) => {
|
||||
let t:number;
|
||||
const ani = this.startTimes.get(animation)
|
||||
if (ani == null) return
|
||||
if (ani < startTime) {
|
||||
t = now - startTime - animation.delay - this.pauseTime;
|
||||
} else {
|
||||
t = now - ani - animation.delay - this.pauseTime;
|
||||
}
|
||||
if (t > animation.duration) {
|
||||
this.delAnimations.push(animation)
|
||||
// 不能在 foreach 里面 对 集合进行删除操作
|
||||
// this.animations.delete(animation);
|
||||
t = animation.duration;
|
||||
}
|
||||
if (t > 0) animation.run(t);
|
||||
})
|
||||
// 不能在 foreach 里面 对 集合进行删除操作
|
||||
while (this.delAnimations.length > 0) {
|
||||
const animation = this.delAnimations.pop();
|
||||
if (animation == null) return
|
||||
this.animations.delete(animation);
|
||||
}
|
||||
// cancelAnimationFrame(this.tickHandler);
|
||||
if (this.state != 'Started') return
|
||||
|
||||
this.tickHandler = raf(()=>{
|
||||
this.tick!()
|
||||
})
|
||||
|
||||
this.tickHandlers.push(this.tickHandler)
|
||||
}
|
||||
if(this.tick != null) {
|
||||
this.tick!()
|
||||
}
|
||||
|
||||
}
|
||||
pause() {
|
||||
if (!(this.state === 'Started')) return;
|
||||
this.state = 'Paused';
|
||||
this.pauseStart = Date.now();
|
||||
cancelRaf(this.tickHandler);
|
||||
// cancelRaf(this.tickHandler);
|
||||
}
|
||||
resume() {
|
||||
if (!(this.state === 'Paused')) return;
|
||||
this.state = 'Started';
|
||||
this.pauseTime += Date.now() - this.pauseStart;
|
||||
this.tick!();
|
||||
}
|
||||
reset() {
|
||||
this.pause();
|
||||
this.state = 'Initiated';
|
||||
this.pauseTime = 0;
|
||||
this.pauseStart = 0;
|
||||
this.animations.clear()
|
||||
this.delAnimations.clear()
|
||||
this.startTimes.clear()
|
||||
this.tickHandler = 0;
|
||||
}
|
||||
add(animation : Animation, startTime ?: number | null) {
|
||||
if (startTime == null) startTime = Date.now();
|
||||
this.animations.add(animation);
|
||||
this.startTimes.set(animation, startTime);
|
||||
}
|
||||
}
|
||||
|
||||
export class Animation {
|
||||
startValue : number
|
||||
endValue : number
|
||||
duration : number
|
||||
timingFunction : (t : number) => number
|
||||
delay : number
|
||||
template : (t : number) => void
|
||||
constructor(
|
||||
startValue : number,
|
||||
endValue : number,
|
||||
duration : number,
|
||||
delay : number,
|
||||
timingFunction : (t : number) => number,
|
||||
template : (v : number) => void) {
|
||||
this.startValue = startValue;
|
||||
this.endValue = endValue;
|
||||
this.duration = duration;
|
||||
this.timingFunction = timingFunction;
|
||||
this.delay = delay;
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
run(time : number) {
|
||||
let range = this.endValue - this.startValue;
|
||||
let progress = time / this.duration
|
||||
if(progress != 1) progress = this.timingFunction(progress)
|
||||
this.template(this.startValue + range * progress)
|
||||
}
|
||||
}
|
||||
123
uni_modules/lime-shared/animation/vue.ts
Normal file
123
uni_modules/lime-shared/animation/vue.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
// @ts-nocheck
|
||||
const TICK = Symbol('tick');
|
||||
const TICK_HANDLER = Symbol('tick-handler');
|
||||
const ANIMATIONS = Symbol('animations');
|
||||
const START_TIMES = Symbol('start-times');
|
||||
const PAUSE_START = Symbol('pause-start');
|
||||
const PAUSE_TIME = Symbol('pause-time');
|
||||
const _raf = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : function(cb: Function) {return setTimeout(cb, 1000/60)}
|
||||
const _caf = typeof cancelAnimationFrame !== 'undefined' ? cancelAnimationFrame: function(id: any) {clearTimeout(id)}
|
||||
|
||||
// const TICK = 'tick';
|
||||
// const TICK_HANDLER = 'tick-handler';
|
||||
// const ANIMATIONS = 'animations';
|
||||
// const START_TIMES = 'start-times';
|
||||
// const PAUSE_START = 'pause-start';
|
||||
// const PAUSE_TIME = 'pause-time';
|
||||
// const _raf = function(callback):number|null {return setTimeout(callback, 1000/60)}
|
||||
// const _caf = function(id: number):void {clearTimeout(id)}
|
||||
|
||||
export class Timeline {
|
||||
state: string
|
||||
constructor() {
|
||||
this.state = 'Initiated';
|
||||
this[ANIMATIONS] = new Set();
|
||||
this[START_TIMES] = new Map();
|
||||
}
|
||||
start() {
|
||||
if (!(this.state === 'Initiated')) return;
|
||||
this.state = 'Started';
|
||||
|
||||
let startTime = Date.now();
|
||||
this[PAUSE_TIME] = 0;
|
||||
this[TICK] = () => {
|
||||
let now = Date.now();
|
||||
this[ANIMATIONS].forEach((animation) => {
|
||||
let t: number;
|
||||
if (this[START_TIMES].get(animation) < startTime) {
|
||||
t = now - startTime - animation.delay - this[PAUSE_TIME];
|
||||
} else {
|
||||
t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME];
|
||||
}
|
||||
|
||||
if (t > animation.duration) {
|
||||
this[ANIMATIONS].delete(animation);
|
||||
t = animation.duration;
|
||||
}
|
||||
if (t > 0) animation.run(t);
|
||||
})
|
||||
// for (let animation of this[ANIMATIONS]) {
|
||||
// let t: number;
|
||||
// console.log('animation', animation)
|
||||
// if (this[START_TIMES].get(animation) < startTime) {
|
||||
// t = now - startTime - animation.delay - this[PAUSE_TIME];
|
||||
// } else {
|
||||
// t = now - this[START_TIMES].get(animation) - animation.delay - this[PAUSE_TIME];
|
||||
// }
|
||||
|
||||
// if (t > animation.duration) {
|
||||
// this[ANIMATIONS].delete(animation);
|
||||
// t = animation.duration;
|
||||
// }
|
||||
// if (t > 0) animation.run(t);
|
||||
// }
|
||||
this[TICK_HANDLER] = _raf(this[TICK]);
|
||||
};
|
||||
this[TICK]();
|
||||
}
|
||||
pause() {
|
||||
if (!(this.state === 'Started')) return;
|
||||
this.state = 'Paused';
|
||||
|
||||
this[PAUSE_START] = Date.now();
|
||||
_caf(this[TICK_HANDLER]);
|
||||
}
|
||||
resume() {
|
||||
if (!(this.state === 'Paused')) return;
|
||||
this.state = 'Started';
|
||||
|
||||
this[PAUSE_TIME] += Date.now() - this[PAUSE_START];
|
||||
this[TICK]();
|
||||
}
|
||||
reset() {
|
||||
this.pause();
|
||||
this.state = 'Initiated';
|
||||
this[PAUSE_TIME] = 0;
|
||||
this[PAUSE_START] = 0;
|
||||
this[ANIMATIONS] = new Set();
|
||||
this[START_TIMES] = new Map();
|
||||
this[TICK_HANDLER] = null;
|
||||
}
|
||||
add(animation: any, startTime?: number) {
|
||||
if (arguments.length < 2) startTime = Date.now();
|
||||
this[ANIMATIONS].add(animation);
|
||||
this[START_TIMES].set(animation, startTime);
|
||||
}
|
||||
}
|
||||
|
||||
export class Animation {
|
||||
startValue: number
|
||||
endValue: number
|
||||
duration: number
|
||||
timingFunction: (t: number) => number
|
||||
delay: number
|
||||
template: (t: number) => void
|
||||
constructor(startValue: number, endValue: number, duration: number, delay: number, timingFunction: (t: number) => number, template: (v: number) => void) {
|
||||
timingFunction = timingFunction || (v => v);
|
||||
template = template || (v => v);
|
||||
|
||||
this.startValue = startValue;
|
||||
this.endValue = endValue;
|
||||
this.duration = duration;
|
||||
this.timingFunction = timingFunction;
|
||||
this.delay = delay;
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
run(time: number) {
|
||||
let range = this.endValue - this.startValue;
|
||||
let progress = time / this.duration
|
||||
if(progress != 1) progress = this.timingFunction(progress)
|
||||
this.template(this.startValue + range * progress)
|
||||
}
|
||||
}
|
||||
3888
uni_modules/lime-shared/areaData/city-china.json
Normal file
3888
uni_modules/lime-shared/areaData/city-china.json
Normal file
File diff suppressed because it is too large
Load Diff
71
uni_modules/lime-shared/areaData/index.ts
Normal file
71
uni_modules/lime-shared/areaData/index.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
// @ts-nocheck
|
||||
import _areaList from './city-china.json';
|
||||
export const areaList = _areaList
|
||||
// #ifndef UNI-APP-X
|
||||
type UTSJSONObject = Record<string, string>
|
||||
// #endif
|
||||
// #ifdef UNI-APP-X
|
||||
type Object = UTSJSONObject
|
||||
// #endif
|
||||
type AreaList = {
|
||||
province_list : Map<string, string>;
|
||||
city_list : Map<string, string>;
|
||||
county_list : Map<string, string>;
|
||||
}
|
||||
// type CascaderOption = {
|
||||
// text : string;
|
||||
// value : string;
|
||||
// children ?: CascaderOption[];
|
||||
// };
|
||||
|
||||
const makeOption = (
|
||||
label : string,
|
||||
value : string,
|
||||
children ?: UTSJSONObject[],
|
||||
) : UTSJSONObject => ({
|
||||
label,
|
||||
value,
|
||||
children,
|
||||
});
|
||||
|
||||
|
||||
|
||||
export function useCascaderAreaData() : UTSJSONObject[] {
|
||||
const city = areaList['city_list'] as UTSJSONObject
|
||||
const county = areaList['county_list'] as UTSJSONObject
|
||||
const province = areaList['province_list'] as UTSJSONObject
|
||||
const provinceMap = new Map<string, UTSJSONObject>();
|
||||
Object.keys(province).forEach((code) => {
|
||||
provinceMap.set(code.slice(0, 2), makeOption(`${province[code]}`, code, []));
|
||||
});
|
||||
|
||||
const cityMap = new Map<string, UTSJSONObject>();
|
||||
|
||||
Object.keys(city).forEach((code) => {
|
||||
const option = makeOption(`${city[code]}`, code, []);
|
||||
cityMap.set(code.slice(0, 4), option);
|
||||
|
||||
const _province = provinceMap.get(code.slice(0, 2));
|
||||
if (_province != null) {
|
||||
(_province['children'] as UTSJSONObject[]).push(option)
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(county).forEach((code) => {
|
||||
const _city = cityMap.get(code.slice(0, 4));
|
||||
if (_city != null) {
|
||||
(_city['children'] as UTSJSONObject[]).push(makeOption(`${county[code]}`, code, null));
|
||||
}
|
||||
});
|
||||
|
||||
// #ifndef APP-ANDROID || APP-IOS
|
||||
return Array.from(provinceMap.values());
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID || APP-IOS
|
||||
const obj : UTSJSONObject[] = []
|
||||
provinceMap.forEach((value, code) => {
|
||||
obj.push(value)
|
||||
})
|
||||
return obj
|
||||
// #endif
|
||||
}
|
||||
10
uni_modules/lime-shared/arrayBufferToFile/index.ts
Normal file
10
uni_modules/lime-shared/arrayBufferToFile/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export * from './vue.ts'
|
||||
export { arrayBufferToFile } from './vue.ts'
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
// export * from './uvue.uts'
|
||||
export { arrayBufferToFile } from './uvue.uts'
|
||||
// #endif
|
||||
10
uni_modules/lime-shared/arrayBufferToFile/uvue.uts
Normal file
10
uni_modules/lime-shared/arrayBufferToFile/uvue.uts
Normal file
@@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
// import {platform} from '../platform'
|
||||
/**
|
||||
* buffer转路径
|
||||
* @param {Object} buffer
|
||||
*/
|
||||
// @ts-nocheck
|
||||
export function arrayBufferToFile(buffer: ArrayBuffer, name?: string, format?:string):Promise<(File|string)> {
|
||||
console.error('[arrayBufferToFile] 当前环境不支持')
|
||||
}
|
||||
63
uni_modules/lime-shared/arrayBufferToFile/vue.ts
Normal file
63
uni_modules/lime-shared/arrayBufferToFile/vue.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
// @ts-nocheck
|
||||
import {platform} from '../platform'
|
||||
/**
|
||||
* buffer转路径
|
||||
* @param {Object} buffer
|
||||
*/
|
||||
// @ts-nocheck
|
||||
export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP
|
||||
const fs = uni.getFileSystemManager()
|
||||
//自定义文件名
|
||||
if (!name && !format) {
|
||||
reject(new Error('ERROR_NAME_PARSE'))
|
||||
}
|
||||
const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
|
||||
let pre = platform()
|
||||
const filePath = `${pre.env.USER_DATA_PATH}/${fileName}`
|
||||
fs.writeFile({
|
||||
filePath,
|
||||
data: buffer,
|
||||
success() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
const file = new File([buffer], name, {
|
||||
type: format,
|
||||
});
|
||||
resolve(file)
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
const base64 = uni.arrayBufferToBase64(buffer)
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!name && !format) {
|
||||
reject(new Error('ERROR_NAME_PARSE'))
|
||||
}
|
||||
const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
|
||||
const filePath = `_doc/uniapp_temp/${fileNmae}`
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
12
uni_modules/lime-shared/arrayEqual/index.ts
Normal file
12
uni_modules/lime-shared/arrayEqual/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 精确比较两个数组是否相等(浅比较)
|
||||
* @param arr1 数组1
|
||||
* @param arr2 数组2
|
||||
* @returns 是否相等
|
||||
*/
|
||||
export function arrayEqual<T>(arr1 : T[], arr2 : T[]) : boolean {
|
||||
return (
|
||||
arr1.length == arr2.length &&
|
||||
arr1.every((val, i) : boolean => val == arr2[i])
|
||||
)
|
||||
}
|
||||
41
uni_modules/lime-shared/assignAtIndex/index.ts
Normal file
41
uni_modules/lime-shared/assignAtIndex/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 安全地在数组指定索引处赋值,兼容 Android 平台对稀疏数组的限制。
|
||||
*
|
||||
* 行为说明:
|
||||
* - 非 Android:等同于 arr[index] = value(可能产生稀疏数组)
|
||||
* - Android:若 index >= length,则先用 undefined 填充至 index,再赋值(紧凑数组,无空槽)
|
||||
*
|
||||
* @param arr - 目标数组(会被修改)
|
||||
* @param index - 非负整数索引
|
||||
* @param value - 要设置的值
|
||||
* @throws {Error} 如果 index 不是非负整数
|
||||
*/
|
||||
export function assignAtIndex<T>(arr : T[], index : number, value : T) : void {
|
||||
|
||||
// 输入校验
|
||||
if (index < 0) {
|
||||
throw new Error(`Index must be a non-negative integer, got ${index}`);
|
||||
}
|
||||
|
||||
// #ifndef APP-ANDROID
|
||||
// 标准环境:直接赋值(允许稀疏数组)
|
||||
arr[index] = value;
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-ANDROID
|
||||
// Android 环境:安全赋值,避免稀疏数组导致的异常
|
||||
if (index < arr.length) {
|
||||
arr[index] = value;
|
||||
} else {
|
||||
// 手动扩展数组,中间填充 undefined
|
||||
// 注意:使用 push(undefined) 而不是 arr[length++] = undefined,
|
||||
// 因为某些 Android 引擎对直接 length 赋值也敏感
|
||||
while (arr.length <= index) {
|
||||
arr.push(null as T);
|
||||
}
|
||||
|
||||
// arr.set(index, value)
|
||||
arr[index] = value;
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
13
uni_modules/lime-shared/base64ToArrayBuffer/index.ts
Normal file
13
uni_modules/lime-shared/base64ToArrayBuffer/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// @ts-nocheck
|
||||
// 未完成
|
||||
export function base64ToArrayBuffer(base64 : string) {
|
||||
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
|
||||
if (!format) {
|
||||
new Error('ERROR_BASE64SRC_PARSE')
|
||||
}
|
||||
if(uni.base64ToArrayBuffer) {
|
||||
return uni.base64ToArrayBuffer(bodyData)
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
11
uni_modules/lime-shared/base64ToPath/index.ts
Normal file
11
uni_modules/lime-shared/base64ToPath/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export * from './vue.ts'
|
||||
export { base64ToPath } from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
// export * from './uvue.uts'
|
||||
export { base64ToPath } from './uvue.uts'
|
||||
// #endif
|
||||
22
uni_modules/lime-shared/base64ToPath/uvue.uts
Normal file
22
uni_modules/lime-shared/base64ToPath/uvue.uts
Normal file
@@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
import { processFile, type ProcessFileOptions } from '@/uni_modules/lime-file-utils'
|
||||
|
||||
/**
|
||||
* base64转路径
|
||||
* @param {Object} base64
|
||||
*/
|
||||
export function base64ToPath(base64: string, filename: string | null = null):Promise<string> {
|
||||
return new Promise((resolve,reject) => {
|
||||
processFile({
|
||||
type: 'toDataURL',
|
||||
path: base64,
|
||||
filename,
|
||||
success(res: string){
|
||||
resolve(res)
|
||||
},
|
||||
fail(err){
|
||||
reject(err)
|
||||
}
|
||||
} as ProcessFileOptions)
|
||||
})
|
||||
}
|
||||
75
uni_modules/lime-shared/base64ToPath/vue.ts
Normal file
75
uni_modules/lime-shared/base64ToPath/vue.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
// @ts-nocheck
|
||||
import {platform} from '../platform'
|
||||
/**
|
||||
* base64转路径
|
||||
* @param {Object} base64
|
||||
*/
|
||||
export function base64ToPath(base64: string, filename?: string):Promise<string> {
|
||||
const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || [];
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP
|
||||
const fs = uni.getFileSystemManager()
|
||||
//自定义文件名
|
||||
if (!filename && !format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
// const time = new Date().getTime();
|
||||
const name = filename || `${new Date().getTime()}.${format}`;
|
||||
let pre = platform()
|
||||
const filePath = `${pre.env.USER_DATA_PATH}/${name}`
|
||||
fs.writeFile({
|
||||
filePath,
|
||||
data: base64.split(',')[1],
|
||||
encoding: 'base64',
|
||||
success() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// mime类型
|
||||
let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
|
||||
//base64 解码
|
||||
let byteString = atob(base64.split(',')[1]);
|
||||
//创建缓冲数组
|
||||
let arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
//创建视图
|
||||
let intArray = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
intArray[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
resolve(URL.createObjectURL(new Blob([intArray], {
|
||||
type: mimeString
|
||||
})))
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!filename && !format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
// const time = new Date().getTime();
|
||||
const name = filename || `${new Date().getTime()}.${format}`;
|
||||
const filePath = `_doc/uniapp_temp/${name}`
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
21
uni_modules/lime-shared/camelCase/index.ts
Normal file
21
uni_modules/lime-shared/camelCase/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 将字符串转换为 camelCase 或 PascalCase 风格的命名约定
|
||||
* @param str 要转换的字符串
|
||||
* @param isPascalCase 指示是否转换为 PascalCase 的布尔值,默认为 false
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function camelCase(str: string, isPascalCase: boolean = false): string {
|
||||
// 将字符串分割成单词数组
|
||||
let words: string[] = str.split(/[\s_-]+/);
|
||||
|
||||
// 将数组中的每个单词首字母大写(除了第一个单词)
|
||||
let camelCased: string[] = words.map((word, index):string => {
|
||||
if (index == 0 && !isPascalCase) {
|
||||
return word.toLowerCase(); // 第一个单词全小写
|
||||
}
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
});
|
||||
|
||||
// 将数组中的单词拼接成一个字符串
|
||||
return camelCased.join('');
|
||||
};
|
||||
67
uni_modules/lime-shared/canIUseCanvas2d/index.ts
Normal file
67
uni_modules/lime-shared/canIUseCanvas2d/index.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
|
||||
// #ifdef MP-ALIPAY
|
||||
interface My {
|
||||
SDKVersion: string
|
||||
}
|
||||
declare var my: My
|
||||
// #endif
|
||||
|
||||
function compareVersion(v1:string, v2:string) {
|
||||
let a1 = v1.split('.');
|
||||
let a2 = v2.split('.');
|
||||
const len = Math.max(a1.length, a2.length);
|
||||
|
||||
while (a1.length < len) {
|
||||
a1.push('0');
|
||||
}
|
||||
while (a2.length < len) {
|
||||
a2.push('0');
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(a1[i], 10);
|
||||
const num2 = parseInt(a2[i], 10);
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1;
|
||||
}
|
||||
if (num1 < num2) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function gte(version: string) {
|
||||
let {SDKVersion} = uni.getSystemInfoSync();
|
||||
// #ifdef MP-ALIPAY
|
||||
SDKVersion = my.SDKVersion
|
||||
// #endif
|
||||
return compareVersion(SDKVersion, version) >= 0;
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
/** 环境是否支持canvas 2d */
|
||||
export function canIUseCanvas2d(): boolean {
|
||||
// #ifdef MP-WEIXIN
|
||||
return gte('2.9.0');
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
return gte('2.7.0');
|
||||
// #endif
|
||||
// #ifdef MP-TOUTIAO
|
||||
return gte('1.78.0');
|
||||
// #endif
|
||||
// #ifdef UNI-APP-X && WEB || UNI-APP-X && APP
|
||||
return true;
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO
|
||||
return false
|
||||
// #endif
|
||||
|
||||
}
|
||||
111
uni_modules/lime-shared/capitalizedAmount/index.ts
Normal file
111
uni_modules/lime-shared/capitalizedAmount/index.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
// @ts-nocheck
|
||||
import { isString } from "../isString";
|
||||
import { isNumber } from "../isNumber";
|
||||
/**
|
||||
* 将金额转换为中文大写形式
|
||||
* @param {number | string} amount - 需要转换的金额,可以是数字或字符串
|
||||
* @returns {string} 转换后的中文大写金额
|
||||
*/
|
||||
export function capitalizedAmount(amount : number) : string
|
||||
export function capitalizedAmount(amount : string) : string
|
||||
export function capitalizedAmount(amount : any | null) : string {
|
||||
try {
|
||||
let _amountStr :string;
|
||||
let _amountNum :number = 0;
|
||||
// 如果输入是字符串,先将其转换为数字,并去除逗号
|
||||
if (typeof amount == 'string') {
|
||||
_amountNum = parseFloat((amount as string).replace(/,/g, ''));
|
||||
}
|
||||
if(isNumber(amount)) {
|
||||
_amountNum = amount as number
|
||||
}
|
||||
// 判断输入是否为有效的金额 || isNaN(amount)
|
||||
if (amount == null) throw new Error('不是有效的金额!');
|
||||
|
||||
let result = '';
|
||||
|
||||
// 处理负数情况
|
||||
if (_amountNum < 0) {
|
||||
result = '欠';
|
||||
_amountNum = Math.abs(_amountNum);
|
||||
}
|
||||
|
||||
// 金额不能超过千亿以上
|
||||
if (_amountNum >= 10e11) throw new Error('计算金额过大!');
|
||||
|
||||
// 保留两位小数并转换为字符串
|
||||
_amountStr = _amountNum.toFixed(2);
|
||||
|
||||
// 定义数字、单位和小数单位的映射
|
||||
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
const units = ['', '拾', '佰', '仟'];
|
||||
const bigUnits = ['', '万', '亿'];
|
||||
const decimalUnits = ['角', '分'];
|
||||
|
||||
// 分离整数部分和小数部分
|
||||
const amountArray = _amountStr.split('.');
|
||||
let integerPart = amountArray[0]; // string| number[]
|
||||
const decimalPart = amountArray[1];
|
||||
|
||||
// 处理整数部分
|
||||
if (integerPart != '0') {
|
||||
let _integerPart = integerPart.split('').map((item):number => parseInt(item));
|
||||
|
||||
// 将整数部分按四位一级进行分组
|
||||
const levels = _integerPart.reverse().reduce((prev:string[][], item, index):string[][] => {
|
||||
// const level = prev?.[0]?.length < 4 ? prev[0] : [];
|
||||
const level = prev.length > 0 && prev[0].length < 4 ? prev[0]: []
|
||||
|
||||
const value = item == 0 ? digits[item] : digits[item] + units[index % 4];
|
||||
|
||||
level.unshift(value);
|
||||
|
||||
if (level.length == 1) {
|
||||
prev.unshift(level);
|
||||
} else {
|
||||
prev[0] = level;
|
||||
}
|
||||
|
||||
return prev;
|
||||
}, [] as string[][]);
|
||||
// 将分组后的整数部分转换为中文大写形式
|
||||
result += levels.reduce((prev, item, index):string => {
|
||||
let _level = bigUnits[levels.length - index - 1];
|
||||
let _item = item.join('').replace(/(零)\1+/g, '$1');
|
||||
|
||||
if (_item == '零') {
|
||||
_level = '';
|
||||
_item = '';
|
||||
} else if (_item.endsWith('零')) {
|
||||
_item = _item.slice(0, _item.length - 1);
|
||||
}
|
||||
|
||||
return prev + _item + _level;
|
||||
}, '');
|
||||
} else {
|
||||
result += '零';
|
||||
}
|
||||
|
||||
// 添加元
|
||||
result += '元';
|
||||
|
||||
// 处理小数部分
|
||||
if (decimalPart != '00') {
|
||||
if (result == '零元') result = '';
|
||||
|
||||
for (let i = 0; i < decimalPart.length; i++) {
|
||||
const digit = parseInt(decimalPart.charAt(i));
|
||||
|
||||
if (digit != 0) {
|
||||
result += digits[digit] + decimalUnits[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result += '整';
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error : Error) {
|
||||
return error.message;
|
||||
}
|
||||
};
|
||||
101
uni_modules/lime-shared/changelog.md
Normal file
101
uni_modules/lime-shared/changelog.md
Normal file
@@ -0,0 +1,101 @@
|
||||
## 0.4.7(2025-12-27)
|
||||
- feat: 增加`getWindowInfo`
|
||||
## 0.4.6(2025-12-26)
|
||||
- fix: 修复useVModel可选问题
|
||||
## 0.4.5(2025-12-10)
|
||||
- feat: 增加useVModel
|
||||
## 0.4.4(2025-12-07)
|
||||
- fix: 修复csstoobj uniappx安卓正则问题
|
||||
## 0.4.3(2025-12-02)
|
||||
- feat: 增加`arrayEqual`,`findLastIndex`
|
||||
## 0.4.2(2025-09-26)
|
||||
- feat: 增加`cssToObj`
|
||||
## 0.4.1(2025-06-13)
|
||||
- fix:测试hbx4.71更新
|
||||
## 0.4.0(2025-06-04)
|
||||
- fix:测试hbx4.71更新
|
||||
## 0.3.9(2025-06-04)
|
||||
- fix: 因hbx4.71更新导致mac用户下载的文件被重命名,故退回4.66
|
||||
## 0.3.8(2025-06-03)
|
||||
- fix: 修复`while`在vue2报错的问题
|
||||
## 0.3.7(2025-05-21)
|
||||
- fix: 修复`merge`在vue2报错的问题
|
||||
## 0.3.6(2025-05-17)
|
||||
- fix: 修复`raf`导错目录的问题
|
||||
## 0.3.5(2025-05-16)
|
||||
- feat: 增加`isIP`
|
||||
- feat: 增加`merge`
|
||||
- feat: 增加`isByteLength`
|
||||
- feat: 增加`isRegExp`
|
||||
## 0.3.4(2025-04-27)
|
||||
- fix: 修复exif缺少isBase64的问题
|
||||
## 0.3.3(2025-04-14)
|
||||
- fix: 修复4.61上的类型问题
|
||||
## 0.3.2(2025-04-14)
|
||||
- fix: 修复4.61上的类型问题
|
||||
## 0.3.1(2025-03-28)
|
||||
- fix: 修复getRect在nvue上的问题
|
||||
## 0.3.0(2025-02-27)
|
||||
- fix: 修复部分函数无法在uniappx上运行的问题
|
||||
## 0.2.9(2025-02-19)
|
||||
- chore: 更新文档
|
||||
## 0.2.8(2025-02-11)
|
||||
- chore: 更新文档
|
||||
## 0.2.7(2025-01-17)
|
||||
- fix: 针对canvas 平台判断优化
|
||||
## 0.2.6(2025-01-09)
|
||||
- feat: 增加`areaData`中国省市区数据
|
||||
## 0.2.5(2025-01-07)
|
||||
- fix: animation在app上类型问题
|
||||
## 0.2.4(2025-01-04)
|
||||
- feat: getRect类型问题
|
||||
## 0.2.3(2025-01-01)
|
||||
- chore: unitConvert使用uni.rpx2px
|
||||
## 0.2.2(2024-12-11)
|
||||
- chore: 动画使用`requestAnimationFrame`
|
||||
## 0.2.1(2024-11-20)
|
||||
- feat: 增加`characterLimit`
|
||||
## 0.2.0(2024-11-14)
|
||||
- fix: vue2的类型问题
|
||||
## 0.1.9(2024-11-14)
|
||||
- feat: 增加`shuffle`
|
||||
## 0.1.8(2024-10-08)
|
||||
- fix: vue2 条件编译 // #ifdef APP-IOS || APP-ANDROID 会生效
|
||||
## 0.1.7(2024-09-23)
|
||||
- fix: raf 类型跟随版本变更
|
||||
## 0.1.6(2024-07-24)
|
||||
- fix: vue2 app ts需要明确的后缀,所有补全
|
||||
- chore: 减少依赖
|
||||
## 0.1.5(2024-07-21)
|
||||
- feat: 删除 Hooks
|
||||
- feat: 兼容uniappx
|
||||
## 0.1.4(2023-09-05)
|
||||
- feat: 增加 Hooks `useIntersectionObserver`
|
||||
- feat: 增加 `floatAdd`
|
||||
- feat: 因为本人插件兼容 vue2 需要使用 `composition-api`,故增加vue文件代码插件的条件编译
|
||||
## 0.1.3(2023-08-13)
|
||||
- feat: 增加 `camelCase`
|
||||
## 0.1.2(2023-07-17)
|
||||
- feat: 增加 `getClassStr`
|
||||
## 0.1.1(2023-07-06)
|
||||
- feat: 增加 `isNumeric`, 区别于 `isNumber`
|
||||
## 0.1.0(2023-06-30)
|
||||
- fix: `clamp`忘记导出了
|
||||
## 0.0.9(2023-06-27)
|
||||
- feat: 增加`arrayBufferToFile`
|
||||
## 0.0.8(2023-06-19)
|
||||
- feat: 增加`createAnimation`、`clamp`
|
||||
## 0.0.7(2023-06-08)
|
||||
- chore: 更新注释
|
||||
## 0.0.6(2023-06-08)
|
||||
- chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖
|
||||
## 0.0.5(2023-06-03)
|
||||
- chore: 更新注释
|
||||
## 0.0.4(2023-05-22)
|
||||
- feat: 增加`range`,`exif`,`selectComponent`
|
||||
## 0.0.3(2023-05-08)
|
||||
- feat: 增加`fillZero`,`debounce`,`throttle`,`random`
|
||||
## 0.0.2(2023-05-05)
|
||||
- chore: 更新文档
|
||||
## 0.0.1(2023-05-05)
|
||||
- 无
|
||||
63
uni_modules/lime-shared/characterLimit/index.ts
Normal file
63
uni_modules/lime-shared/characterLimit/index.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 计算字符串字符的长度并可以截取字符串。
|
||||
* @param char 传入字符串(maxcharacter条件下,一个汉字表示两个字符)
|
||||
* @param max 规定最大字符串长度
|
||||
* @returns 当没有传入maxCharacter/maxLength 时返回字符串字符长度,当传入maxCharacter/maxLength时返回截取之后的字符串和长度。
|
||||
*/
|
||||
export type CharacterLengthResult = {
|
||||
length : number;
|
||||
characters : string;
|
||||
}
|
||||
// #ifdef APP-ANDROID
|
||||
type ChartType = any
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
type ChartType = string | number
|
||||
// #endif
|
||||
|
||||
export function characterLimit(type : string, char : ChartType, max : number) : CharacterLengthResult {
|
||||
const str = `${char}`;
|
||||
|
||||
if (str.length == 0) {
|
||||
return {
|
||||
length: 0,
|
||||
characters: '',
|
||||
} as CharacterLengthResult
|
||||
}
|
||||
|
||||
if (type == 'maxcharacter') {
|
||||
let len = 0;
|
||||
for (let i = 0; i < str.length; i += 1) {
|
||||
let currentStringLength : number// = 0;
|
||||
const code = str.charCodeAt(i)!
|
||||
if (code > 127 || code == 94) {
|
||||
currentStringLength = 2;
|
||||
} else {
|
||||
currentStringLength = 1;
|
||||
}
|
||||
if (len + currentStringLength > max) {
|
||||
return {
|
||||
length: len,
|
||||
characters: str.slice(0, i),
|
||||
} as CharacterLengthResult
|
||||
}
|
||||
len += currentStringLength;
|
||||
}
|
||||
return {
|
||||
length: len,
|
||||
characters: str,
|
||||
} as CharacterLengthResult
|
||||
} else if (type == 'maxlength') {
|
||||
const length = str.length > max ? max : str.length;
|
||||
return {
|
||||
length: length,
|
||||
characters: str.slice(0, length),
|
||||
} as CharacterLengthResult
|
||||
}
|
||||
|
||||
return {
|
||||
length: str.length,
|
||||
characters: str,
|
||||
} as CharacterLengthResult
|
||||
};
|
||||
16
uni_modules/lime-shared/clamp/index.ts
Normal file
16
uni_modules/lime-shared/clamp/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 将一个值限制在指定的范围内
|
||||
* @param val 要限制的值
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @returns 限制后的值
|
||||
*/
|
||||
export function clamp(val: number, min: number, max: number): number {
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
|
||||
// console.log(clamp(5 ,0, 10)); // 输出: 5(在范围内,不做更改)
|
||||
// console.log(clamp(-5 ,0, 10)); // 输出: 0(小于最小值,被限制为最小值)
|
||||
// console.log(clamp(15 ,0, 10)); // 输出: 10(大于最大值,被限制为最大值)
|
||||
53
uni_modules/lime-shared/classNames/index.ts
Normal file
53
uni_modules/lime-shared/classNames/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
import { isNumber } from '../isNumber'
|
||||
import { isString } from '../isString'
|
||||
import { isDef } from '../isDef'
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 获取对象的类名字符串
|
||||
* @param obj - 需要处理的对象
|
||||
* @returns 由对象属性作为类名组成的字符串
|
||||
*/
|
||||
export function classNames<T>(obj : T) : string {
|
||||
let classNames : string[] = [];
|
||||
// #ifdef APP-ANDROID || APP-HARMONY
|
||||
if (obj instanceof UTSJSONObject) {
|
||||
(obj as UTSJSONObject).toMap().forEach((value, key) => {
|
||||
if (isDef(value)) {
|
||||
if (isNumber(value)) {
|
||||
classNames.push(key);
|
||||
}
|
||||
if (isString(value) && value !== '') {
|
||||
classNames.push(key);
|
||||
}
|
||||
if (typeof value == 'boolean' && (value as boolean)) {
|
||||
classNames.push(key);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID || APP-HARMONY
|
||||
// 遍历对象的属性
|
||||
for (let key in obj) {
|
||||
// 检查属性确实属于对象自身且其值为true
|
||||
if ((obj as any).hasOwnProperty(key) && obj[key]) {
|
||||
// 将属性名添加到类名数组中
|
||||
classNames.push(key);
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// 将类名数组用空格连接成字符串并返回
|
||||
return classNames.join(' ');
|
||||
}
|
||||
|
||||
|
||||
// 示例
|
||||
// const obj = { foo: true, bar: false, baz: true };
|
||||
// const classNameStr = stringify(obj);
|
||||
// console.log(classNameStr); // 输出: "foo baz"
|
||||
12
uni_modules/lime-shared/cloneDeep/index.ts
Normal file
12
uni_modules/lime-shared/cloneDeep/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef APP-ANDROID
|
||||
// export * from './uvue.uts'
|
||||
export { cloneDeep } from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef APP-ANDROID
|
||||
// export * from './vue.ts'
|
||||
export { cloneDeep } from './vue.ts'
|
||||
// #endif
|
||||
39
uni_modules/lime-shared/cloneDeep/uvue.uts
Normal file
39
uni_modules/lime-shared/cloneDeep/uvue.uts
Normal file
@@ -0,0 +1,39 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 深度克隆一个对象或数组
|
||||
* @param obj 要克隆的对象或数组
|
||||
* @returns 克隆后的对象或数组
|
||||
*/
|
||||
export function cloneDeep<T>(obj : any) : T {
|
||||
if (obj instanceof Set) {
|
||||
const set = new Set<any>();
|
||||
obj.forEach((item : any) => {
|
||||
set.add(item)
|
||||
})
|
||||
return set as T;
|
||||
}
|
||||
if (obj instanceof Map) {
|
||||
const map = new Map<any, any>();
|
||||
obj.forEach((value : any, key : any) => {
|
||||
map.set(key, value)
|
||||
})
|
||||
return map as T;
|
||||
}
|
||||
|
||||
if (obj instanceof RegExp) {
|
||||
return new RegExp(obj) as T;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return (obj as any[]).map((item : any):any => item) as T;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as T;
|
||||
}
|
||||
|
||||
if (typeof obj == 'object') {
|
||||
return UTSJSONObject.assign<T>({}, toRaw(obj))!
|
||||
}
|
||||
return obj as T
|
||||
}
|
||||
103
uni_modules/lime-shared/cloneDeep/vue.ts
Normal file
103
uni_modules/lime-shared/cloneDeep/vue.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 深度克隆一个对象或数组
|
||||
* @param obj 要克隆的对象或数组
|
||||
* @returns 克隆后的对象或数组
|
||||
*/
|
||||
export function cloneDeep<T>(obj: any): T {
|
||||
// 如果传入的对象为空,返回空
|
||||
if (obj === null) {
|
||||
return null as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Set 类型,则将其转换为数组,并通过新的 Set 构造函数创建一个新的 Set 对象
|
||||
if (obj instanceof Set) {
|
||||
return new Set([...obj]) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Map 类型,则将其转换为数组,并通过新的 Map 构造函数创建一个新的 Map 对象
|
||||
if (obj instanceof Map) {
|
||||
return new Map([...obj]) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 WeakMap 类型,则直接用传入的 WeakMap 对象进行赋值
|
||||
if (obj instanceof WeakMap) {
|
||||
let weakMap = new WeakMap();
|
||||
weakMap = obj;
|
||||
return weakMap as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 WeakSet 类型,则直接用传入的 WeakSet 对象进行赋值
|
||||
if (obj instanceof WeakSet) {
|
||||
let weakSet = new WeakSet();
|
||||
weakSet = obj;
|
||||
return weakSet as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 RegExp 类型,则通过新的 RegExp 构造函数创建一个新的 RegExp 对象
|
||||
if (obj instanceof RegExp) {
|
||||
return new RegExp(obj) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 undefined 类型,则返回 undefined
|
||||
if (typeof obj === 'undefined') {
|
||||
return undefined as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是数组,则递归调用 cloneDeep 函数对数组中的每个元素进行克隆
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(cloneDeep) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Date 类型,则通过新的 Date 构造函数创建一个新的 Date 对象
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是普通对象,则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆
|
||||
if (typeof obj === 'object') {
|
||||
const newObj: any = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
newObj[key] = cloneDeep(value);
|
||||
}
|
||||
const symbolKeys = Object.getOwnPropertySymbols(obj);
|
||||
for (const key of symbolKeys) {
|
||||
newObj[key] = cloneDeep(obj[key]);
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
|
||||
// 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 示例使用
|
||||
|
||||
// // 克隆一个对象
|
||||
// const obj = { name: 'John', age: 30 };
|
||||
// const clonedObj = cloneDeep(obj);
|
||||
|
||||
// console.log(clonedObj); // 输出: { name: 'John', age: 30 }
|
||||
// console.log(clonedObj === obj); // 输出: false (副本与原对象是独立的)
|
||||
|
||||
// // 克隆一个数组
|
||||
// const arr = [1, 2, 3];
|
||||
// const clonedArr = cloneDeep(arr);
|
||||
|
||||
// console.log(clonedArr); // 输出: [1, 2, 3]
|
||||
// console.log(clonedArr === arr); // 输出: false (副本与原数组是独立的)
|
||||
|
||||
// // 克隆一个包含嵌套对象的对象
|
||||
// const person = {
|
||||
// name: 'Alice',
|
||||
// age: 25,
|
||||
// address: {
|
||||
// city: 'New York',
|
||||
// country: 'USA',
|
||||
// },
|
||||
// };
|
||||
// const clonedPerson = cloneDeep(person);
|
||||
|
||||
// console.log(clonedPerson); // 输出: { name: 'Alice', age: 25, address: { city: 'New York', country: 'USA' } }
|
||||
// console.log(clonedPerson === person); // 输出: false (副本与原对象是独立的)
|
||||
// console.log(clonedPerson.address === person.address); // 输出: false (嵌套对象的副本也是独立的)
|
||||
22
uni_modules/lime-shared/closest/index.ts
Normal file
22
uni_modules/lime-shared/closest/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 在给定数组中找到最接近目标数字的元素。
|
||||
* @param arr 要搜索的数字数组。
|
||||
* @param target 目标数字。
|
||||
* @returns 最接近目标数字的数组元素。
|
||||
*/
|
||||
export function closest(arr: number[], target: number):number {
|
||||
return arr.reduce((pre: number, cur: number):number =>
|
||||
Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur
|
||||
);
|
||||
}
|
||||
|
||||
// 示例
|
||||
// // 定义一个数字数组
|
||||
// const numbers = [1, 3, 5, 7, 9];
|
||||
|
||||
// // 在数组中找到最接近目标数字 6 的元素
|
||||
// const closestNumber = closest(numbers, 6);
|
||||
|
||||
// console.log(closestNumber); // 输出结果: 5
|
||||
407
uni_modules/lime-shared/components/lime-shared/lime-shared.vue
Normal file
407
uni_modules/lime-shared/components/lime-shared/lime-shared.vue
Normal file
@@ -0,0 +1,407 @@
|
||||
<template>
|
||||
<view id="shared" style="height: 500px; width: 300px; background-color: aqua;">
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
// #ifdef WEB
|
||||
import validator from 'validator'
|
||||
// #endif
|
||||
import { getRect, getAllRect } from '@/uni_modules/lime-shared/getRect'
|
||||
|
||||
import { camelCase } from '@/uni_modules/lime-shared/camelCase'
|
||||
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
|
||||
import { clamp } from '@/uni_modules/lime-shared/clamp'
|
||||
import { cloneDeep } from '@/uni_modules/lime-shared/cloneDeep'
|
||||
import { closest } from '@/uni_modules/lime-shared/closest'
|
||||
import { debounce } from '@/uni_modules/lime-shared/debounce'
|
||||
import { fillZero } from '@/uni_modules/lime-shared/fillZero'
|
||||
import { floatAdd } from '@/uni_modules/lime-shared/floatAdd'
|
||||
import { floatMul } from '@/uni_modules/lime-shared/floatMul'
|
||||
import { floatDiv } from '@/uni_modules/lime-shared/floatDiv'
|
||||
import { floatSub } from '@/uni_modules/lime-shared/floatSub'
|
||||
import { getClassStr } from '@/uni_modules/lime-shared/getClassStr'
|
||||
import { getCurrentPage } from '@/uni_modules/lime-shared/getCurrentPage'
|
||||
import { getStyleStr } from '@/uni_modules/lime-shared/getStyleStr'
|
||||
import { hasOwn } from '@/uni_modules/lime-shared/hasOwn'
|
||||
import { isBase64 } from '@/uni_modules/lime-shared/isBase64'
|
||||
import { isBrowser } from '@/uni_modules/lime-shared/isBrowser'
|
||||
import { isDef } from '@/uni_modules/lime-shared/isDef'
|
||||
import { isEmpty } from '@/uni_modules/lime-shared/isEmpty'
|
||||
import { isFunction } from '@/uni_modules/lime-shared/isFunction'
|
||||
import { isNumber } from '@/uni_modules/lime-shared/isNumber'
|
||||
import { isNumeric } from '@/uni_modules/lime-shared/isNumeric'
|
||||
import { isObject } from '@/uni_modules/lime-shared/isObject'
|
||||
import { isPromise } from '@/uni_modules/lime-shared/isPromise'
|
||||
import { isString } from '@/uni_modules/lime-shared/isString'
|
||||
import { kebabCase } from '@/uni_modules/lime-shared/kebabCase'
|
||||
import { raf, doubleRaf } from '@/uni_modules/lime-shared/raf'
|
||||
import { random } from '@/uni_modules/lime-shared/random'
|
||||
import { range } from '@/uni_modules/lime-shared/range'
|
||||
import { sleep } from '@/uni_modules/lime-shared/sleep'
|
||||
import { throttle } from '@/uni_modules/lime-shared/throttle'
|
||||
import { toArray } from '@/uni_modules/lime-shared/toArray'
|
||||
import { toBoolean } from '@/uni_modules/lime-shared/toBoolean'
|
||||
import { toNumber } from '@/uni_modules/lime-shared/toNumber'
|
||||
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
import { getCurrentInstance } from '@/uni_modules/lime-shared/vue'
|
||||
import { capitalizedAmount } from '@/uni_modules/lime-shared/capitalizedAmount'
|
||||
|
||||
import { obj2url } from '@/uni_modules/lime-shared/obj2url'
|
||||
import { isURL, type IsURLOptions } from '@/uni_modules/lime-shared/isURL'
|
||||
import { isIP } from '@/uni_modules/lime-shared/isIP'
|
||||
import { isDate, type IsDateOptions } from '@/uni_modules/lime-shared/isDate'
|
||||
import { isEmail } from '@/uni_modules/lime-shared/isEmail'
|
||||
import { isRegExp } from '@/uni_modules/lime-shared/isRegExp'
|
||||
import { isValidDomain, type IsValidDomainOptions } from '@/uni_modules/lime-shared/isValidDomain'
|
||||
import { merge } from '@/uni_modules/lime-shared/merge'
|
||||
import { isByteLength, type IsByteLengthOptions } from '@/uni_modules/lime-shared/isByteLength'
|
||||
|
||||
// #ifdef VUE2
|
||||
type UTSJSONObject = any
|
||||
// #endif
|
||||
|
||||
const context = getCurrentInstance()
|
||||
// getRect('#shared', context!).then(res =>{
|
||||
// console.log('res', res.bottom)
|
||||
// })
|
||||
// getAllRect('#shared', context).then(res =>{
|
||||
// console.log('res', res)
|
||||
// })
|
||||
|
||||
|
||||
// console.log('camelCase::', camelCase("hello world"));
|
||||
// console.log('camelCase::', camelCase("my_name_is_john", true));
|
||||
// console.log('canIUseCanvas2d::', canIUseCanvas2d());
|
||||
// console.log('clamp::', clamp(5 ,0, 10));
|
||||
// console.log('cloneDeep::', cloneDeep<UTSJSONObject>({a:5}));
|
||||
// console.log('closest::', closest([1, 3, 5, 7, 9], 6));
|
||||
|
||||
|
||||
|
||||
|
||||
// const saveData = (data: any) => {
|
||||
// // 模拟保存数据的操作
|
||||
// console.log(`Saving data: ${data}`);
|
||||
// }
|
||||
|
||||
// const debouncedSaveData = debounce(saveData, 500);
|
||||
// debouncedSaveData('Data 1');
|
||||
// debouncedSaveData('Data 2');
|
||||
|
||||
// console.log('fillZero', fillZero(1))
|
||||
// console.log('floatAdd', floatAdd(0.1, 0.2), floatAdd(1.05, 0.05), floatAdd(0.1, 0.7), floatAdd(0.0001, 0.0002), floatAdd(123.456 , 789.012))
|
||||
// console.log('floatMul', floatMul(0.1, 0.02), floatMul(1.0255, 100))
|
||||
// console.log('floatDiv', floatDiv(10.44, 100), floatDiv(1.0255, 100), floatDiv(5.419909340994699, 0.2))
|
||||
// console.log('floatSub', floatSub(0.4, 0.1), floatSub(1.0255, 100))
|
||||
// const now = () : number => System.nanoTime() / 1_000_000.0
|
||||
// console.log('capitalizedAmount', capitalizedAmount(0.4))
|
||||
// console.log('capitalizedAmount', capitalizedAmount(100))
|
||||
// console.log('capitalizedAmount', capitalizedAmount(100000000))
|
||||
// console.log('capitalizedAmount', capitalizedAmount('2023.04'))
|
||||
// console.log('capitalizedAmount', capitalizedAmount(-1024))
|
||||
// console.log('now', now(), Date.now())
|
||||
// console.log('getClassStr', getClassStr({hover: true}))
|
||||
// console.log('getStyleStr', getStyleStr({ color: 'red', fontSize: '16px', backgroundColor: '', border: null }))
|
||||
// console.log('hasOwn', hasOwn({a: true}, 'key'))
|
||||
// console.log('isBase64::', isBase64("SGVsbG8sIFdvcmxkIQ=="));
|
||||
// console.log('isBrowser::', isBrowser);
|
||||
// console.log('isDef::', isDef('6'));
|
||||
// console.log('isEmpty::', isEmpty({a: true}));
|
||||
|
||||
// const b = () =>{}
|
||||
// console.log('isFunction::', isFunction(b));
|
||||
// console.log('isNumber::', isNumber('6'));
|
||||
// console.log('isNumeric::', isNumeric('6'));
|
||||
// console.log('isObject::', isObject({}));
|
||||
|
||||
// const promise = ():Promise<boolean> => {
|
||||
// return new Promise((resolve) => {
|
||||
// resolve(true)
|
||||
// })
|
||||
// }
|
||||
// const a = promise()
|
||||
// console.log('isPromise::', isPromise(a));
|
||||
// console.log('isString::', isString('null'));
|
||||
// console.log('kebabCase::', kebabCase('my love'));
|
||||
// console.log('raf::', raf(()=>{
|
||||
// console.log('raf:::1')
|
||||
// }));
|
||||
// console.log('doubleRaf::', doubleRaf(()=>{
|
||||
// console.log('doubleRaf:::1')
|
||||
// }));
|
||||
// console.log('random', random(0, 10))
|
||||
// console.log('random', random(0, 1, 2))
|
||||
// console.log('range', range(0, 10, 2))
|
||||
// console.log('sleep', sleep(300).then(res => {
|
||||
// console.log('log')
|
||||
// }))
|
||||
|
||||
// const handleScroll = (a: string) => {
|
||||
// console.log("Scroll event handled!", a);
|
||||
// }
|
||||
|
||||
// // // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒
|
||||
// const throttledScroll = throttle(handleScroll, 500);
|
||||
// throttledScroll('5');
|
||||
// const page = getCurrentPage()
|
||||
// console.log('getCurrentPage::', page)
|
||||
|
||||
// console.log('toArray', toArray<number>(5))
|
||||
// console.log('toBoolean', toBoolean(5))
|
||||
// console.log('toNumber', toNumber('5'))
|
||||
// console.log('unitConvert', unitConvert('5'))
|
||||
|
||||
// uni.getImageInfo({
|
||||
// src: '/static/logo.png',
|
||||
// success(res) {
|
||||
// console.log('res', res)
|
||||
// }
|
||||
// })
|
||||
|
||||
// --------------------------
|
||||
// IPv4 验证示例
|
||||
// --------------------------
|
||||
|
||||
// 标准IPv4格式
|
||||
// console.log(isIP('192.168.1.1', 4)); // true
|
||||
// console.log(isIP('255.255.255.255', { version: 4 })); // true
|
||||
|
||||
// // 边界值验证
|
||||
// console.log(isIP('0.0.0.0', 4)); // true
|
||||
// console.log(isIP('223.255.255.255', '4')); // true
|
||||
|
||||
// // 非法IPv4案例
|
||||
// console.log(isIP('256.400.999.1', 4)); // false(数值超限)
|
||||
// console.log(isIP('192.168.01', 4)); // false(段数不足)
|
||||
|
||||
// // --------------------------
|
||||
// // IPv6 验证示例
|
||||
// // --------------------------
|
||||
|
||||
// // 标准IPv6格式
|
||||
// console.log(isIP('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 6)); // true
|
||||
// console.log(isIP('fe80::1%eth0', { version: 6 })); // true(带区域标识)
|
||||
|
||||
// // 压缩格式验证
|
||||
// console.log(isIP('2001:db8::1', '6')); // true(双冒号压缩)
|
||||
// console.log(isIP('::1', 6)); // true(本地环回简写)
|
||||
|
||||
// // IPv4混合格式
|
||||
// console.log(isIP('::ffff:192.168.1.1', 6)); // true(IPv4映射地址)
|
||||
|
||||
// // 非法IPv6案例
|
||||
// console.log(isIP('2001::gggg::1', 6)); // false(非法字符)
|
||||
// console.log(isIP('fe80::1%', 6)); // false(空区域标识)
|
||||
|
||||
// // --------------------------
|
||||
// // 自动版本检测
|
||||
// // --------------------------
|
||||
|
||||
// // 有效地址检测
|
||||
// console.log(isIP('172.16.254.1')); // true(自动识别IPv4)
|
||||
// console.log(isIP('2001:db8:3333:4444:5555:6666:7777:8888')); // true(自动识别IPv6)
|
||||
|
||||
// // 无效地址检测
|
||||
// console.log(isIP('192.168.1.256')); // false(无效IPv4)
|
||||
// console.log(isIP('2001::gggg::1')); // false(无效IPv6)
|
||||
|
||||
// // --------------------------
|
||||
// // 特殊场景
|
||||
// // --------------------------
|
||||
|
||||
// // 带前后空格处理
|
||||
// console.log(isIP(' 203.0.113.50 ', 4)); // true(自动trim)
|
||||
// console.log(isIP(' fe80::1%1 ', 6)); // true(自动trim)
|
||||
|
||||
// // 非法版本指定
|
||||
// console.log(isIP('192.168.1.1', 5)); // false(不存在IPv5)
|
||||
// console.log(isIP('2001:db8::1', 'ipv6')); // false(版本参数格式错误)
|
||||
|
||||
// // --------------------------
|
||||
// // 边界案例
|
||||
// // --------------------------
|
||||
|
||||
// // 最小/最大有效值
|
||||
// console.log(isIP('0.0.0.0', 4)); // true
|
||||
// console.log(isIP('255.255.255.255', 4)); // true
|
||||
// console.log(isIP('0000:0000:0000:0000:0000:0000:0000:0000', 6)); // true
|
||||
|
||||
// // 超长地址验证
|
||||
// console.log(isIP('192.168.1.1.1', 4)); // false(IPv4段数过多)
|
||||
// console.log(isIP('2001:db8:1:2:3:4:5:6:7', 6)); // false(IPv6段数过多)
|
||||
|
||||
// const original = { color: 'red' };
|
||||
// const merged = merge({ ...original }, { color: 'blue', size: 'M' });
|
||||
|
||||
// console.log('original', original); // 输出: { color: 'red' } (保持不变)
|
||||
// console.log('merged', merged); // 输出: { color: 'red', size: 'M' }
|
||||
|
||||
|
||||
type ColorType = {
|
||||
color?: string,
|
||||
size?: string,
|
||||
}
|
||||
|
||||
const merged2 = merge({ color: 'red' }, { size: 'M' } as ColorType);
|
||||
console.log('merged2:::', merged2)
|
||||
|
||||
// // 使用配置对象参数
|
||||
// console.log(isByteLength('hello', { min: 3, max: 7 } as ByteLengthOptions)); // true (5字节)
|
||||
// console.log(isByteLength('hello', { min: 6 } as ByteLengthOptions)); // false (5 < 6)
|
||||
// console.log(isByteLength('hello', { max: 4 } as ByteLengthOptions)); // false (5 > 4)
|
||||
|
||||
// // 使用独立参数(旧式调用)
|
||||
// console.log(isByteLength('hello', 3, 7)); // true
|
||||
// console.log(isByteLength('hello', 6)); // false
|
||||
// console.log(isByteLength('hello', null, 4)); // false
|
||||
|
||||
// =====================
|
||||
// 多字节字符处理示例
|
||||
// =====================
|
||||
|
||||
// 中文字符(UTF-8 每个汉字3字节)
|
||||
// console.log(isByteLength('中国', { min: 6 })); // true (2字 × 3字节 = 6)
|
||||
// console.log(isByteLength('中国', { max: 5 })); // false (6 > 5)
|
||||
|
||||
// // 表情符号(多数占用4字节)
|
||||
// console.log(isByteLength('🌟', { min: 4, max: 4 })); // true
|
||||
// console.log(isByteLength('👨👩👧👦', { max: 15 })); // false (家庭表情占25字节)
|
||||
|
||||
// // 混合字符集
|
||||
// console.log(isByteLength('aé🌟', { min: 7 })); // true
|
||||
|
||||
// // URL编码字符
|
||||
// console.log(isByteLength('%20', { min: 3 })); // true(实际字节长度3)
|
||||
// console.log(isByteLength('%E2%82%AC', { max: 3 })); // false(欧元符号编码为3字节)
|
||||
|
||||
// // 构造函数创建的正则表达式
|
||||
// console.log(isRegExp(new RegExp('hello'))); // true
|
||||
// console.log(isRegExp(new RegExp('\\d+', 'gi'))); // true
|
||||
|
||||
// // 字面量正则表达式
|
||||
// console.log(isRegExp(/abc/)); // true
|
||||
// console.log(isRegExp(/^[0-9]+$/gi)); // true
|
||||
|
||||
// // 字符串(含正则格式字符串)
|
||||
// console.log(isRegExp('/abc/')); // false
|
||||
// console.log(isRegExp('new RegExp("abc")')); // false
|
||||
|
||||
// console.log(isEmail('"John"@example.com')) // false(实际有效)
|
||||
// console.log(isEmail('中国@例子.中国')) // false(实际有效)
|
||||
|
||||
// // 简单键值对
|
||||
// console.log(obj2url({ name: '张三', age: 25 }));
|
||||
// // "name=%E5%BC%A0%E4%B8%89&age=25"
|
||||
// // 包含布尔值
|
||||
// console.log(obj2url({ active: true, admin: false }));
|
||||
// // "active=true&admin=false"
|
||||
|
||||
// // 数字处理
|
||||
// console.log(obj2url({ page: 1, limit: 10 }));
|
||||
// // "page=1&limit=10"
|
||||
|
||||
// 基础验证
|
||||
// console.log("example.com =>", isValidDomain("example.com")); // true
|
||||
// console.log("sub.example.co.uk =>", isValidDomain("sub.example.co.uk")); // true
|
||||
|
||||
// // 缺少TLD的情况
|
||||
// console.log("localhost =>", isValidDomain("localhost")); // false
|
||||
// console.log("localhost (不要求TLD) =>", isValidDomain("localhost", { requireTld: false } as DomainOptions)); // true
|
||||
|
||||
// // 带结尾点号的情况
|
||||
// console.log("example.com. =>", isValidDomain("example.com.")); // false
|
||||
// console.log("example.com. (允许结尾点号) =>", isValidDomain("example.com.", { allowTrailingDot: true } as DomainOptions)); // true
|
||||
|
||||
// // 带下划线的情况
|
||||
// console.log("my_site.com =>", isValidDomain("my_site.com")); // false
|
||||
// console.log("my_site.com (允许下划线) =>", isValidDomain("my_site.com", { allowUnderscore: true } as DomainOptions)); // true
|
||||
|
||||
// // 非法字符测试
|
||||
// console.log("含有空格的域名 =>", isValidDomain("exa mple.com")); // false
|
||||
// console.log("含有!的域名 =>", isValidDomain("exa!mple.com")); // false
|
||||
|
||||
// // 长度测试
|
||||
// const longPart = "a".repeat(64);
|
||||
// console.log(`超长部分 (${longPart.length}字符) =>`, isValidDomain(`${longPart}.com`)); // false
|
||||
|
||||
// // 连字符测试
|
||||
// console.log("以连字符开头 =>", isValidDomain("-example.com")); // false
|
||||
// console.log("以连字符结尾 =>", isValidDomain("example-.com")); // false
|
||||
|
||||
// // 国际化域名测试
|
||||
// console.log("中文域名 =>", isValidDomain("中国.中国")); // true
|
||||
// console.log("日文域名 =>", isValidDomain("ドメイン.テスト")); // true
|
||||
|
||||
// // 基础格式验证
|
||||
// console.log("1. 标准日期格式验证:");
|
||||
// console.log("2023/12/31 =>", isDate("2023/12/31")); // true
|
||||
// console.log("1999-01-01 =>", isDate("1999-01-01")); // true
|
||||
// console.log("02.28.2023 =>", isDate("02.28.2023", { delimiters: ['.'], format: 'MM.DD.YYYY' } as DateOptions)); // true (自定义分隔符)
|
||||
|
||||
// // 严格模式验证
|
||||
// console.log("2. 严格模式验证:");
|
||||
// console.log("严格匹配格式:", isDate("2023/02/28", { strictMode: true, format: "YYYY/MM/DD" }as DateOptions)); // true
|
||||
// console.log("长度不符:", isDate("2023/2/28", { strictMode: true, format: "YYYY/MM/DD" }as DateOptions)); // false
|
||||
// console.log("错误分隔符:", isDate("2023-02-28", { strictMode: true, format: "YYYY/MM/DD" }as DateOptions)); // false
|
||||
|
||||
// // 两位年份处理
|
||||
// console.log("3. 两位年份验证:");
|
||||
// console.log("23 -> 2023:", isDate("23/12/31", { format: "YY/MM/DD" } as DateOptions)); // true → 2023-12-31
|
||||
// console.log("87 -> 1987:", isDate("87-01-01", { format: "YY-MM-DD" } as DateOptions)); // true → 1987-01-01
|
||||
// console.log("负数年份:", isDate("-100/12/31", { format: "YYYY/MM/DD" } as DateOptions)); // false
|
||||
|
||||
// // 日期有效性验证
|
||||
// console.log("4. 无效日期检测:");
|
||||
// console.log("闰年2020-02-29:", isDate("2020/02/29")); // true
|
||||
// console.log("非闰年2023-02-29:", isDate("2023/02/29")); // false
|
||||
// console.log("月份溢出:", isDate("2023/13/01")); // false
|
||||
// console.log("日期溢出:", isDate("2023/12/32")); // false
|
||||
|
||||
// // Date对象验证
|
||||
// console.log("5. Date对象验证:");
|
||||
// console.log("有效Date对象:", isDate(new Date())); // true
|
||||
// console.log("无效Date对象:", isDate(new Date("invalid")), new Date("invalid")); // false
|
||||
// console.log("严格模式Date对象:", isDate(new Date(), { strictMode: true } as DateOptions)); // false
|
||||
|
||||
// // 自定义格式验证
|
||||
// console.log("6. 自定义格式测试:");
|
||||
// console.log("MM-DD-YYYY:", isDate("12-31-2023", { format: "MM-DD-YYYY" } as DateOptions)); // true
|
||||
// console.log("DD.MM.YY:", isDate("31.12.23", { format: "DD.MM.YY", delimiters: ['.'] } as DateOptions)); // true
|
||||
// console.log("中文分隔符:", isDate("2023年12月31日", {
|
||||
// format: "YYYY年MM月DD日",
|
||||
// strictMode: true,
|
||||
// delimiters: ['年', '月', '日']
|
||||
// } as DateOptions )); // true
|
||||
|
||||
|
||||
// 示例测试
|
||||
// console.log("示例1 标准HTTP URL:", isURL("http://example.com")); // true
|
||||
// console.log("示例2 需要端口时缺少端口:", isURL("https://example.com", { requirePort: true } as URLOptions)); // false
|
||||
// console.log("示例3 协议相对URL:", isURL("//example.com", { allowProtocolRelativeUrls: true })); // true
|
||||
console.log("示例4 IPv6地址:", isURL("http://[2001:db8::1]:8080", {})); // true
|
||||
// console.log("示例5 带认证信息被禁用:", isURL("user:pass@example.com", { disallowAuth: true })); // false
|
||||
console.log("示例6 查询参数被禁用:", isURL("http://example.com?q=test", { allowQueryComponents: true })); // false
|
||||
console.log("示例7 非字符串输入:", isURL(null, {})); // false
|
||||
console.log("示例8 邮件协议被排除:", isURL("mailto:test@example.com", {})); // false
|
||||
console.log("示例9 自定义协议:", isURL("ftp://files.example.com", { protocols: ["ftp"] })); // true
|
||||
console.log("示例10 白名单检查:", isURL("http://trusted.com", { hostWhitelist: ["trusted.com"] })); // true
|
||||
|
||||
|
||||
// #ifdef WEB
|
||||
// console.log('validator', validator.isURL())
|
||||
console.log("示例4 IPv6地址:", validator.isURL("http://[2001:db8::1]:8080", {})); // true
|
||||
console.log("示例6 查询参数被禁用:", validator.isURL("http://example.com?q=test", { allow_query_components: true })); // false
|
||||
console.log("示例8 邮件协议被排除:", validator.isURL("mailto:test@example.com", {})); // false
|
||||
console.log("示例9 自定义协议:", isURL("ftp://files.example.com", { protocols: ["ftp"] })); // true
|
||||
// #endif
|
||||
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
11
uni_modules/lime-shared/createAnimation/index.ts
Normal file
11
uni_modules/lime-shared/createAnimation/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X
|
||||
export * from './type.ts'
|
||||
// export * from './vue.ts'
|
||||
export { createAnimation } from './vue.ts'
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
// export * from './uvue.ts'
|
||||
export { createAnimation } from './uvue.uts'
|
||||
// #endif
|
||||
25
uni_modules/lime-shared/createAnimation/type.ts
Normal file
25
uni_modules/lime-shared/createAnimation/type.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export type CreateAnimationOptions = {
|
||||
/**
|
||||
* 动画持续时间,单位ms
|
||||
*/
|
||||
duration ?: number;
|
||||
/**
|
||||
* 定义动画的效果
|
||||
* - linear: 动画从头到尾的速度是相同的
|
||||
* - ease: 动画以低速开始,然后加快,在结束前变慢
|
||||
* - ease-in: 动画以低速开始
|
||||
* - ease-in-out: 动画以低速开始和结束
|
||||
* - ease-out: 动画以低速结束
|
||||
* - step-start: 动画第一帧就跳至结束状态直到结束
|
||||
* - step-end: 动画一直保持开始状态,最后一帧跳到结束状态
|
||||
*/
|
||||
timingFunction ?: string //'linear' | 'ease' | 'ease-in' | 'ease-in-out' | 'ease-out' | 'step-start' | 'step-end';
|
||||
/**
|
||||
* 动画延迟时间,单位 ms
|
||||
*/
|
||||
delay ?: number;
|
||||
/**
|
||||
* 设置transform-origin
|
||||
*/
|
||||
transformOrigin ?: string;
|
||||
}
|
||||
5
uni_modules/lime-shared/createAnimation/uvue.uts
Normal file
5
uni_modules/lime-shared/createAnimation/uvue.uts
Normal file
@@ -0,0 +1,5 @@
|
||||
// @ts-nocheck
|
||||
// export * from '@/uni_modules/lime-animateIt'
|
||||
export function createAnimation() {
|
||||
console.error('当前环境不支持,请使用:lime-animateIt')
|
||||
}
|
||||
148
uni_modules/lime-shared/createAnimation/vue.ts
Normal file
148
uni_modules/lime-shared/createAnimation/vue.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
// @ts-nocheck
|
||||
// nvue 需要在节点上设置ref或在export里传入
|
||||
// const animation = createAnimation({
|
||||
// ref: this.$refs['xxx'],
|
||||
// duration: 0,
|
||||
// timingFunction: 'linear'
|
||||
// })
|
||||
// animation.opacity(1).translate(x, y).step({duration})
|
||||
// animation.export(ref)
|
||||
|
||||
// 抹平nvue 与 uni.createAnimation的使用差距
|
||||
// 但是nvue动画太慢
|
||||
|
||||
|
||||
|
||||
import { type CreateAnimationOptions } from './type'
|
||||
// #ifdef APP-NVUE
|
||||
const nvueAnimation = uni.requireNativePlugin('animation')
|
||||
|
||||
type AnimationTypes = 'matrix' | 'matrix3d' | 'rotate' | 'rotate3d' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scale3d' | 'scaleX' | 'scaleY' | 'scaleZ' | 'skew' | 'skewX' | 'skewY' | 'translate' | 'translate3d' | 'translateX' | 'translateY' | 'translateZ'
|
||||
| 'opacity' | 'backgroundColor' | 'width' | 'height' | 'left' | 'right' | 'top' | 'bottom'
|
||||
|
||||
interface Styles {
|
||||
[key : string] : any
|
||||
}
|
||||
|
||||
interface StepConfig {
|
||||
duration?: number
|
||||
timingFunction?: string
|
||||
delay?: number
|
||||
needLayout?: boolean
|
||||
transformOrigin?: string
|
||||
}
|
||||
interface StepAnimate {
|
||||
styles?: Styles
|
||||
config?: StepConfig
|
||||
}
|
||||
interface StepAnimates {
|
||||
[key: number]: StepAnimate
|
||||
}
|
||||
// export interface CreateAnimationOptions extends UniApp.CreateAnimationOptions {
|
||||
// ref?: string
|
||||
// }
|
||||
|
||||
type Callback = (time: number) => void
|
||||
const animateTypes1 : AnimationTypes[] = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
|
||||
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
|
||||
'translateZ'
|
||||
]
|
||||
const animateTypes2 : AnimationTypes[] = ['opacity', 'backgroundColor']
|
||||
const animateTypes3 : AnimationTypes[] = ['width', 'height', 'left', 'right', 'top', 'bottom']
|
||||
|
||||
class LimeAnimation {
|
||||
ref : any
|
||||
context : any
|
||||
options : UniApp.CreateAnimationOptions
|
||||
// stack : any[] = []
|
||||
next : number = 0
|
||||
currentStepAnimates : StepAnimates = {}
|
||||
duration : number = 0
|
||||
constructor(options : CreateAnimationOptions) {
|
||||
const {ref} = options
|
||||
this.ref = ref
|
||||
this.options = options
|
||||
}
|
||||
addAnimate(type : AnimationTypes, args: (string | number)[]) {
|
||||
let aniObj = this.currentStepAnimates[this.next]
|
||||
let stepAnimate:StepAnimate = {}
|
||||
if (!aniObj) {
|
||||
stepAnimate = {styles: {}, config: {}}
|
||||
} else {
|
||||
stepAnimate = aniObj
|
||||
}
|
||||
|
||||
if (animateTypes1.includes(type)) {
|
||||
if (!stepAnimate.styles.transform) {
|
||||
stepAnimate.styles.transform = ''
|
||||
}
|
||||
let unit = ''
|
||||
if (type === 'rotate') {
|
||||
unit = 'deg'
|
||||
}
|
||||
stepAnimate.styles.transform += `${type}(${args.map((v: number) => v + unit).join(',')}) `
|
||||
} else {
|
||||
stepAnimate.styles[type] = `${args.join(',')}`
|
||||
}
|
||||
this.currentStepAnimates[this.next] = stepAnimate
|
||||
}
|
||||
animateRun(styles: Styles = {}, config:StepConfig = {}, ref: any) {
|
||||
const el = ref || this.ref
|
||||
if (!el) return
|
||||
return new Promise((resolve) => {
|
||||
const time = +new Date()
|
||||
nvueAnimation.transition(el, {
|
||||
styles,
|
||||
...config
|
||||
}, () => {
|
||||
resolve(+new Date() - time)
|
||||
})
|
||||
})
|
||||
}
|
||||
nextAnimate(animates: StepAnimates, step: number = 0, ref: any, cb: Callback) {
|
||||
let obj = animates[step]
|
||||
if (obj) {
|
||||
let { styles, config } = obj
|
||||
// this.duration += config.duration
|
||||
this.animateRun(styles, config, ref).then((time: number) => {
|
||||
step += 1
|
||||
this.duration += time
|
||||
this.nextAnimate(animates, step, ref, cb)
|
||||
})
|
||||
} else {
|
||||
this.currentStepAnimates = {}
|
||||
cb && cb(this.duration)
|
||||
}
|
||||
}
|
||||
step(config:StepConfig = {}) {
|
||||
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
|
||||
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
|
||||
this.next++
|
||||
return this
|
||||
}
|
||||
export(ref: any, cb?: Callback) {
|
||||
ref = ref || this.ref
|
||||
if(!ref) return
|
||||
this.duration = 0
|
||||
this.next = 0
|
||||
this.nextAnimate(this.currentStepAnimates, 0, ref, cb)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
|
||||
LimeAnimation.prototype[type] = function(...args: (string | number)[]) {
|
||||
this.addAnimate(type, args)
|
||||
return this
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
export function createAnimation(options : CreateAnimationOptions) {
|
||||
// #ifndef APP-NVUE
|
||||
return uni.createAnimation({ ...options })
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
return new LimeAnimation(options)
|
||||
// #endif
|
||||
}
|
||||
73
uni_modules/lime-shared/createCanvas/index.ts
Normal file
73
uni_modules/lime-shared/createCanvas/index.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue'
|
||||
import { getRect } from '@/uni_modules/lime-shared/getRect'
|
||||
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
|
||||
export const isCanvas2d = canIUseCanvas2d()
|
||||
// #endif
|
||||
|
||||
|
||||
export function createCanvas(canvasId : string, component : ComponentInternalInstance) {
|
||||
// #ifdef UNI-APP-X
|
||||
uni.createCanvasContextAsync({
|
||||
canvasId,
|
||||
component,
|
||||
success(context : CanvasContext) {
|
||||
|
||||
},
|
||||
fail(error : UniError) {
|
||||
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X
|
||||
const isCanvas2d = canIUseCanvas2d()
|
||||
getRect('#' + canvasId, context, isCanvas2d).then(res => {
|
||||
if (res.node) {
|
||||
res.node.width = res.width
|
||||
res.node.height = res.height
|
||||
return res.node
|
||||
} else {
|
||||
const ctx = uni.createCanvasContext(canvasId, context)
|
||||
if (!ctx._drawImage) {
|
||||
ctx._drawImage = ctx.drawImage
|
||||
ctx.drawImage = function (...args) {
|
||||
const { path } = args.shift()
|
||||
ctx._drawImage(path, ...args)
|
||||
}
|
||||
}
|
||||
if (!ctx.getImageData) {
|
||||
ctx.getImageData = function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.canvasGetImageData({
|
||||
canvasId,
|
||||
x: parseInt(arguments[0]),
|
||||
y: parseInt(arguments[1]),
|
||||
width: parseInt(arguments[2]),
|
||||
height: parseInt(arguments[3]),
|
||||
success(res) {
|
||||
resolve(res)
|
||||
},
|
||||
fail(err) {
|
||||
reject(err)
|
||||
}
|
||||
}, context)
|
||||
})
|
||||
|
||||
}
|
||||
return {
|
||||
getContext(type: string) {
|
||||
if(type == '2d') {
|
||||
return ctx
|
||||
}
|
||||
},
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
createImage
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
71
uni_modules/lime-shared/createImage/index.ts
Normal file
71
uni_modules/lime-shared/createImage/index.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
import {isBrowser} from '../isBrowser'
|
||||
class Image {
|
||||
currentSrc: string | null = null
|
||||
naturalHeight: number = 0
|
||||
naturalWidth: number = 0
|
||||
width: number = 0
|
||||
height: number = 0
|
||||
tagName: string = 'IMG'
|
||||
path: string = ''
|
||||
crossOrigin: string = ''
|
||||
referrerPolicy: string = ''
|
||||
onload: () => void = () => {}
|
||||
onerror: () => void = () => {}
|
||||
complete: boolean = false
|
||||
constructor() {}
|
||||
set src(src: string) {
|
||||
console.log('src', src)
|
||||
if(!src) {
|
||||
return this.onerror()
|
||||
}
|
||||
src = src.replace(/^@\//,'/')
|
||||
this.currentSrc = src
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
const localReg = /^\.|^\/(?=[^\/])/;
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
|
||||
res.path = localReg.test(src) ? `/${res.path}` : res.path;
|
||||
// #endif
|
||||
this.complete = true
|
||||
this.path = res.path
|
||||
this.naturalWidth = this.width = res.width
|
||||
this.naturalHeight = this.height = res.height
|
||||
this.onload()
|
||||
},
|
||||
fail: () => {
|
||||
this.onerror()
|
||||
}
|
||||
})
|
||||
}
|
||||
get src() {
|
||||
return this.currentSrc
|
||||
}
|
||||
}
|
||||
interface UniImage extends WechatMiniprogram.Image {
|
||||
complete?: boolean
|
||||
naturalHeight?: number
|
||||
naturalWidth?: number
|
||||
}
|
||||
/** 创建用于 canvas 的 img */
|
||||
export function createImage(canvas?: any): HTMLImageElement | UniImage {
|
||||
if(canvas && canvas.createImage) {
|
||||
return (canvas as WechatMiniprogram.Canvas).createImage()
|
||||
} else if(this && this['tagName'] == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){
|
||||
return new Image()
|
||||
} else if(isBrowser) {
|
||||
return new window.Image()
|
||||
}
|
||||
return new Image()
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export function createImage():Image{
|
||||
// console.error('当前环境不支持')
|
||||
return new Image()
|
||||
}
|
||||
// #endif
|
||||
50
uni_modules/lime-shared/cssToObj/index.ts
Normal file
50
uni_modules/lime-shared/cssToObj/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X
|
||||
type UTSJSONObject = Record<string, any>
|
||||
// #endif
|
||||
|
||||
|
||||
/**
|
||||
* 将 CSS 字符串转换为样式对象
|
||||
* @param css CSS 字符串,例如 "color: red; font-size: 16px;"
|
||||
* @returns CSSProperties 对象
|
||||
*/
|
||||
export function cssToObj(css : string | UTSJSONObject | null) : UTSJSONObject {
|
||||
// #ifdef APP-ANDROID
|
||||
if(css == null) return {}
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
if(!css) return {}
|
||||
// #endif
|
||||
if(typeof css == 'object') return css as UTSJSONObject
|
||||
|
||||
|
||||
const style : UTSJSONObject = {};
|
||||
|
||||
(css as string).split(';').forEach(decl => {
|
||||
// #ifdef APP-ANDROID
|
||||
const res = decl.split(':').map(s => s.trim());
|
||||
if(res.length > 1) {
|
||||
const [prop, val] = res;
|
||||
if (prop != '' && val != '') {
|
||||
const camelProp = prop!.replace(/-([a-z])/g, (_a: string|null, b: string, _c: number, _d: string):string => {
|
||||
return b.toUpperCase()
|
||||
});
|
||||
style[camelProp] = val!;
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-ANDROID
|
||||
const [prop, val] = decl.split(':').map(s => s.trim());
|
||||
if (prop && val) {
|
||||
const camelProp = prop.replace(/-([a-z])/g, (_, c) => {
|
||||
return c.toUpperCase()
|
||||
});
|
||||
style[camelProp] = val;
|
||||
}
|
||||
// #endif
|
||||
});
|
||||
|
||||
return style;
|
||||
}
|
||||
11
uni_modules/lime-shared/debounce/index.ts
Normal file
11
uni_modules/lime-shared/debounce/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
// export * from './uvue.uts'
|
||||
export { debounce } from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export * from './vue.ts'
|
||||
export { debounce } from './vue.ts'
|
||||
// #endif
|
||||
36
uni_modules/lime-shared/debounce/uvue.uts
Normal file
36
uni_modules/lime-shared/debounce/uvue.uts
Normal file
@@ -0,0 +1,36 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 防抖函数,通过延迟一定时间来限制函数的执行频率。
|
||||
* @param fn 要防抖的函数。
|
||||
* @param wait 触发防抖的等待时间,单位为毫秒。
|
||||
* @returns 防抖函数。
|
||||
*/
|
||||
export function debounce<A extends any>(fn : (args: A)=> void, wait = 300): (args: A)=> void {
|
||||
let timer = -1
|
||||
|
||||
return (args: A) => {
|
||||
if (timer >-1) {clearTimeout(timer)};
|
||||
|
||||
timer = setTimeout(()=>{
|
||||
fn(args)
|
||||
}, wait)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 示例
|
||||
// 定义一个函数
|
||||
// function saveData(data: string) {
|
||||
// // 模拟保存数据的操作
|
||||
// console.log(`Saving data: ${data}`);
|
||||
// }
|
||||
|
||||
// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数
|
||||
// const debouncedSaveData = debounce(saveData, 500);
|
||||
|
||||
// // 连续调用防抖函数
|
||||
// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数
|
||||
// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数
|
||||
|
||||
// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2"
|
||||
40
uni_modules/lime-shared/debounce/vue.ts
Normal file
40
uni_modules/lime-shared/debounce/vue.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
// @ts-nocheck
|
||||
type Timeout = ReturnType<typeof setTimeout> | null;
|
||||
/**
|
||||
* 防抖函数,通过延迟一定时间来限制函数的执行频率。
|
||||
* @param fn 要防抖的函数。
|
||||
* @param wait 触发防抖的等待时间,单位为毫秒。
|
||||
* @returns 防抖函数。
|
||||
*/
|
||||
export function debounce<A extends any, R>(
|
||||
fn : (...args : A) => R,
|
||||
wait : number = 300) : (...args : A) => void {
|
||||
let timer : Timeout = null;
|
||||
|
||||
return function (...args : A) {
|
||||
if (timer) clearTimeout(timer); // 如果上一个 setTimeout 存在,则清除它
|
||||
|
||||
// 设置一个新的 setTimeout,在指定的等待时间后调用防抖函数
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args); // 使用提供的参数调用原始函数
|
||||
}, wait);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 示例
|
||||
// 定义一个函数
|
||||
// function saveData(data: string) {
|
||||
// // 模拟保存数据的操作
|
||||
// console.log(`Saving data: ${data}`);
|
||||
// }
|
||||
|
||||
// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数
|
||||
// const debouncedSaveData = debounce(saveData, 500);
|
||||
|
||||
// // 连续调用防抖函数
|
||||
// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数
|
||||
// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数
|
||||
|
||||
// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2"
|
||||
18
uni_modules/lime-shared/dom/index.ts
Normal file
18
uni_modules/lime-shared/dom/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// @ts-nocheck
|
||||
export function findClosestElementWithStyle(startEl: UniElement | null, styleProperty: string): UniElement | null {
|
||||
let currentEl: UniElement | null = startEl;
|
||||
|
||||
while (currentEl != null) {
|
||||
// Check if the current element has the style property with a non-empty value
|
||||
const styleValue = currentEl?.style.getPropertyValue(styleProperty) ?? '';
|
||||
if (styleValue.trim() != '') {
|
||||
return currentEl;
|
||||
}
|
||||
|
||||
// Move to parent element
|
||||
currentEl = currentEl.parentElement;
|
||||
}
|
||||
|
||||
// Return null if no element with the specified style was found
|
||||
return null;
|
||||
};
|
||||
11
uni_modules/lime-shared/exif/index.ts
Normal file
11
uni_modules/lime-shared/exif/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export * from './vue.ts'
|
||||
export { exif } from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
// export * from './uvue.uts'
|
||||
export { exif } from './uvue.uts'
|
||||
// #endif
|
||||
7
uni_modules/lime-shared/exif/uvue.uts
Normal file
7
uni_modules/lime-shared/exif/uvue.uts
Normal file
@@ -0,0 +1,7 @@
|
||||
class EXIF {
|
||||
constructor(){
|
||||
console.error('当前环境不支持')
|
||||
}
|
||||
}
|
||||
|
||||
export const exif = new EXIF()
|
||||
1057
uni_modules/lime-shared/exif/vue.ts
Normal file
1057
uni_modules/lime-shared/exif/vue.ts
Normal file
File diff suppressed because it is too large
Load Diff
11
uni_modules/lime-shared/fillZero/index.ts
Normal file
11
uni_modules/lime-shared/fillZero/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 在数字前填充零,返回字符串形式的结果
|
||||
* @param number 要填充零的数字
|
||||
* @param length 填充零后的字符串长度,默认为2
|
||||
* @returns 填充零后的字符串
|
||||
*/
|
||||
export function fillZero(number: number, length: number = 2): string {
|
||||
// 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度
|
||||
return `${number}`.padStart(length, '0');
|
||||
}
|
||||
23
uni_modules/lime-shared/findLastIndex/index.ts
Normal file
23
uni_modules/lime-shared/findLastIndex/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// @/uni_modules/lime-shared/arrayUtils.ts
|
||||
|
||||
/**
|
||||
* 兼容版 findLastIndex(ES2023 polyfill)
|
||||
*/
|
||||
export function findLastIndex<T>(
|
||||
array : T[],
|
||||
predicate : (value : T, index : number, obj : T[]) => boolean) : number {
|
||||
|
||||
// #ifndef UNI-APP-X && APP-ANDROID
|
||||
if (typeof array.findLastIndex == 'function') {
|
||||
// 如果原生支持,直接用(未来兼容)
|
||||
return array.findLastIndex(predicate);
|
||||
}
|
||||
// #endif
|
||||
// 否则降级到 for 循环
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
if (predicate(array[i], i, array)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
36
uni_modules/lime-shared/floatAdd/index.ts
Normal file
36
uni_modules/lime-shared/floatAdd/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { isNumber } from '../isNumber'
|
||||
/**
|
||||
* 返回两个浮点数相加的结果
|
||||
* @param num1 第一个浮点数
|
||||
* @param num2 第二个浮点数
|
||||
* @returns 两个浮点数的相加结果
|
||||
*/
|
||||
export function floatAdd(num1 : number, num2 : number) : number {
|
||||
// 检查 num1 和 num2 是否为数字类型
|
||||
if (!(isNumber(num1) || isNumber(num2))) {
|
||||
console.warn('Please pass in the number type');
|
||||
return NaN;
|
||||
}
|
||||
|
||||
let r1 : number, r2 : number, m : number;
|
||||
|
||||
try {
|
||||
// 获取 num1 小数点后的位数
|
||||
r1 = num1.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r1 = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取 num2 小数点后的位数
|
||||
r2 = num2.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r2 = 0;
|
||||
}
|
||||
|
||||
// 计算需要扩大的倍数
|
||||
m = Math.pow(10, Math.max(r1, r2));
|
||||
|
||||
// 返回相加结果
|
||||
return (num1 * m + num2 * m) / m;
|
||||
}
|
||||
45
uni_modules/lime-shared/floatDiv/index.ts
Normal file
45
uni_modules/lime-shared/floatDiv/index.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { floatMul } from '../floatMul';
|
||||
import { isNumber } from '../isNumber';
|
||||
|
||||
/**
|
||||
* 除法函数,用于处理浮点数除法并保持精度。
|
||||
* @param {number} num1 - 被除数。
|
||||
* @param {number} num2 - 除数。
|
||||
* @returns {number} 除法运算的结果,保留正确的精度。
|
||||
*/
|
||||
export function floatDiv(num1:number, num2:number):number {
|
||||
// 如果传入的不是数字类型,则打印警告并返回NaN
|
||||
if (!isNumber(num1) || !isNumber(num2)) {
|
||||
console.warn('请传入数字类型');
|
||||
return NaN;
|
||||
}
|
||||
|
||||
let m1 = 0, // 被除数小数点后的位数
|
||||
m2 = 0, // 除数小数点后的位数
|
||||
s1 = num1.toString(), // 将被除数转换为字符串
|
||||
s2 = num2.toString(); // 将除数转换为字符串
|
||||
|
||||
// 计算被除数小数点后的位数
|
||||
try {
|
||||
m1 += s1.split('.')[1].length;
|
||||
} catch (error) {}
|
||||
|
||||
// 计算除数小数点后的位数
|
||||
try {
|
||||
m2 += s2.split('.')[1].length;
|
||||
} catch (error) {}
|
||||
|
||||
// 进行除法运算并处理小数点后的位数,使用之前定义的乘法函数保持精度
|
||||
// #ifdef APP-ANDROID
|
||||
return floatMul(
|
||||
parseFloat(s1.replace('.', '')) / parseFloat(s2.replace('.', '')),
|
||||
Math.pow(10, m2 - m1),
|
||||
);
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
return floatMul(
|
||||
Number(s1.replace('.', '')) / Number(s2.replace('.', '')),
|
||||
Math.pow(10, m2 - m1),
|
||||
);
|
||||
// #endif
|
||||
}
|
||||
44
uni_modules/lime-shared/floatMul/index.ts
Normal file
44
uni_modules/lime-shared/floatMul/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
// @ts-nocheck
|
||||
import {isNumber} from '../isNumber';
|
||||
// #ifdef APP-ANDROID
|
||||
import BigDecimal from 'java.math.BigDecimal'
|
||||
// import BigDecimal from 'java.math.BigDecimal'
|
||||
// import StringBuilder from 'java.lang.StringBuilder'
|
||||
// import java.math.BigDecimal;
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 乘法函数,用于处理浮点数乘法并保持精度。
|
||||
* @param {number} num1 - 第一个乘数。
|
||||
* @param {number} num2 - 第二个乘数。
|
||||
* @returns {number} 乘法运算的结果,保留正确的精度。
|
||||
*/
|
||||
export function floatMul(num1 : number, num2 : number) : number {
|
||||
if (!(isNumber(num1) || isNumber(num2))) {
|
||||
console.warn('Please pass in the number type');
|
||||
return NaN;
|
||||
}
|
||||
let m = 0;
|
||||
// #ifdef APP-ANDROID
|
||||
let s1 = BigDecimal.valueOf(num1.toDouble()).toPlainString(); //new UTSNumber(num1).toString() // //`${num1.toFloat()}`// num1.toString(),
|
||||
let s2 = BigDecimal.valueOf(num2.toDouble()).toPlainString(); //new UTSNumber(num2).toString() //`${num2.toFloat()}`//.toString();
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
let s1:string = `${num1}`// num1.toString(),
|
||||
let s2:string = `${num2}`//.toString();
|
||||
// #endif
|
||||
|
||||
try {
|
||||
m += s1.split('.')[1].length;
|
||||
} catch (error) { }
|
||||
try {
|
||||
m += s2.split('.')[1].length;
|
||||
} catch (error) { }
|
||||
|
||||
// #ifdef APP-ANDROID
|
||||
return parseFloat(s1.replace('.', '')) * parseFloat(s2.replace('.', '')) / Math.pow(10, m);
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m);
|
||||
// #endif
|
||||
}
|
||||
32
uni_modules/lime-shared/floatSub/index.ts
Normal file
32
uni_modules/lime-shared/floatSub/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { isNumber } from '../isNumber';
|
||||
/**
|
||||
* 减法函数,用于处理浮点数减法并保持精度。
|
||||
* @param {number} num1 - 被减数。
|
||||
* @param {number} num2 - 减数。
|
||||
* @returns {number} 减法运算的结果,保留正确的精度。
|
||||
*/
|
||||
export function floatSub(num1 : number, num2 : number) : number {
|
||||
if (!(isNumber(num1) || isNumber(num2))) {
|
||||
console.warn('Please pass in the number type');
|
||||
return NaN;
|
||||
}
|
||||
let r1:number, r2:number, m:number, n:number;
|
||||
try {
|
||||
r1 = num1.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r1 = 0;
|
||||
}
|
||||
try {
|
||||
r2 = num2.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r2 = 0;
|
||||
}
|
||||
m = Math.pow(10, Math.max(r1, r2));
|
||||
n = r1 >= r2 ? r1 : r2;
|
||||
// #ifndef APP-ANDROID
|
||||
return Number(((num1 * m - num2 * m) / m).toFixed(n));
|
||||
// #endif
|
||||
// #ifdef APP-ANDROID
|
||||
return parseFloat(((num1 * m - num2 * m) / m).toFixed(n));
|
||||
// #endif
|
||||
}
|
||||
53
uni_modules/lime-shared/getClassStr/index.ts
Normal file
53
uni_modules/lime-shared/getClassStr/index.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
import { isNumber } from '../isNumber'
|
||||
import { isString } from '../isString'
|
||||
import { isDef } from '../isDef'
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 获取对象的类名字符串
|
||||
* @param obj - 需要处理的对象
|
||||
* @returns 由对象属性作为类名组成的字符串
|
||||
*/
|
||||
export function getClassStr<T>(obj : T) : string {
|
||||
let classNames : string[] = [];
|
||||
// #ifdef APP-ANDROID
|
||||
if (obj instanceof UTSJSONObject) {
|
||||
(obj as UTSJSONObject).toMap().forEach((value, key) => {
|
||||
if (isDef(value)) {
|
||||
if (isNumber(value)) {
|
||||
classNames.push(key);
|
||||
}
|
||||
if (isString(value) && value !== '') {
|
||||
classNames.push(key);
|
||||
}
|
||||
if (typeof value == 'boolean' && (value as boolean)) {
|
||||
classNames.push(key);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
// 遍历对象的属性
|
||||
for (let key in obj) {
|
||||
// 检查属性确实属于对象自身且其值为true
|
||||
if ((obj as any).hasOwnProperty(key) && obj[key]) {
|
||||
// 将属性名添加到类名数组中
|
||||
classNames.push(key);
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// 将类名数组用空格连接成字符串并返回
|
||||
return classNames.join(' ');
|
||||
}
|
||||
|
||||
|
||||
// 示例
|
||||
// const obj = { foo: true, bar: false, baz: true };
|
||||
// const classNameStr = getClassStr(obj);
|
||||
// console.log(classNameStr); // 输出: "foo baz"
|
||||
11
uni_modules/lime-shared/getCurrentPage/index.ts
Normal file
11
uni_modules/lime-shared/getCurrentPage/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export * from './vue.ts'
|
||||
export { getCurrentPage } from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
// export * from './uvue.uts'
|
||||
export { getCurrentPage } from './uvue.uts'
|
||||
// #endif
|
||||
5
uni_modules/lime-shared/getCurrentPage/uvue.uts
Normal file
5
uni_modules/lime-shared/getCurrentPage/uvue.uts
Normal file
@@ -0,0 +1,5 @@
|
||||
// @ts-nocheck
|
||||
export const getCurrentPage = ():Page => {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1]
|
||||
};
|
||||
6
uni_modules/lime-shared/getCurrentPage/vue.ts
Normal file
6
uni_modules/lime-shared/getCurrentPage/vue.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// @ts-nocheck
|
||||
/** 获取当前页 */
|
||||
export const getCurrentPage = () => {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance;
|
||||
};
|
||||
62
uni_modules/lime-shared/getLocalFilePath/index.ts
Normal file
62
uni_modules/lime-shared/getLocalFilePath/index.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef APP-NVUE || APP-VUE
|
||||
export const getLocalFilePath = (path : string) => {
|
||||
if (typeof plus == 'undefined') return path
|
||||
if (/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path
|
||||
if (/^\//.test(path)) {
|
||||
const localFilePath = plus.io.convertAbsoluteFileSystem(path)
|
||||
if (localFilePath !== path) {
|
||||
return localFilePath
|
||||
} else {
|
||||
path = path.slice(1)
|
||||
}
|
||||
}
|
||||
return '_www/' + path
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export { getResourcePath as getLocalFilePath } from '@/uni_modules/lime-file-utils'
|
||||
// export const getLocalFilePath = (path : string) : string => {
|
||||
// let uri = path
|
||||
// if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) {
|
||||
// return uri
|
||||
// }
|
||||
// if (uri.startsWith("file://")) {
|
||||
// uri = uri.substring("file://".length)
|
||||
// } else if (uri.startsWith("unifile://")) {
|
||||
// uri = UTSAndroid.convert2AbsFullPath(uri)
|
||||
// } else {
|
||||
// uri = UTSAndroid.convert2AbsFullPath(uri)
|
||||
// if (uri.startsWith("/android_asset/")) {
|
||||
// uri = uri.replace("/android_asset/", "")
|
||||
// }
|
||||
// }
|
||||
// if (new File(uri).exists()) {
|
||||
// return uri
|
||||
// } else {
|
||||
// return null
|
||||
// }
|
||||
// // return UTSAndroid.convert2AbsFullPath(path)
|
||||
// }
|
||||
// #endif
|
||||
// #ifdef APP-IOS
|
||||
// export const getLocalFilePath = (path : string) : string => {
|
||||
// try {
|
||||
// let uri = path
|
||||
// if (uri.startsWith("http") || uri.startsWith("<svg") || uri.startsWith("data:image/svg+xml")) {
|
||||
// return uri
|
||||
// }
|
||||
// if (uri.startsWith("file://")) {
|
||||
// return uri.substring("file://".length)
|
||||
// } else if (path.startsWith("/var/")) {
|
||||
// return path
|
||||
// }
|
||||
// return UTSiOS.getResourcePath(path)
|
||||
// } catch (e) {
|
||||
// return null
|
||||
// }
|
||||
// // return UTSiOS.getResourcePath(path)
|
||||
// }
|
||||
// #endif
|
||||
138
uni_modules/lime-shared/getRect/index.ts
Normal file
138
uni_modules/lime-shared/getRect/index.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
// export * from './uvue.uts'
|
||||
export { getRect, getAllRect } from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export * from './vue.ts'
|
||||
export { getRect, getAllRect } from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取视口滚动条位置信息
|
||||
*/
|
||||
export function getViewportScrollInfo() : Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
uni.createSelectorQuery()
|
||||
.selectViewport()
|
||||
.scrollOffset((res) => {
|
||||
resolve(res);
|
||||
}).exec();
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
```
|
||||
page
|
||||
╱
|
||||
╭───────────────╮ viewport
|
||||
╭─│─ ─ ─ ─ ─ ─ ─ ─│─╮ ╱
|
||||
│ │ ╭───────────╮ │ │
|
||||
│ │ │ element │ │ │
|
||||
│ │ ╰───────────╯ │ │
|
||||
╰─│─ ─ ─ ─ ─ ─ ─ ─│─╯
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────╯
|
||||
```
|
||||
|
||||
# 参数
|
||||
- viewportHeight: viewport 高度
|
||||
- viewportScrollTop: viewport 垂直滚动值
|
||||
- elementHeight: element 高度
|
||||
- elementOffsetTop: element 距离页面顶部距离
|
||||
|
||||
# 选项
|
||||
- position: element 在视窗中的位置(start, center, end, nearest)
|
||||
- startOffset: element 距离视窗顶部的偏移量
|
||||
- endOffset: element 距离视窗底部的偏移量
|
||||
|
||||
# 结果值
|
||||
- viewportScrollTop: viewport 新的垂直滚动值
|
||||
|
||||
*/
|
||||
|
||||
export type ScrollIntoViewOptions = {
|
||||
/** 元素顶部需要保留的缓冲距离(默认 0) */
|
||||
startOffset ?: number;
|
||||
/** 元素底部需要保留的缓冲距离(默认 0) */
|
||||
endOffset ?: number;
|
||||
/** 滚动对齐方式:start/center/end/nearest(默认 nearest) */
|
||||
position ?: 'start' | 'center' | 'end' | 'nearest';
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算元素需要滚动到可视区域的目标滚动位置
|
||||
* @param viewportHeight 视口高度(像素)
|
||||
* @param viewportScrollTop 当前滚动位置(像素)
|
||||
* @param elementHeight 元素高度(像素)
|
||||
* @param elementOffsetTop 元素相对于父容器顶部的偏移量(像素)
|
||||
* @param options 配置选项
|
||||
* @returns 计算后的目标滚动位置(像素)
|
||||
*
|
||||
* @example
|
||||
* // 示例:将元素滚动到视口顶部对齐
|
||||
* const scrollTop = getScrollIntoViewValue(
|
||||
* 500, // 视口高度
|
||||
* 200, // 当前滚动位置
|
||||
* 100, // 元素高度
|
||||
* 300, // 元素偏移量
|
||||
* { position: 'start' }
|
||||
* );
|
||||
*/
|
||||
export function getScrollIntoViewValue(
|
||||
viewportHeight : number,
|
||||
viewportScrollTop : number,
|
||||
elementHeight : number,
|
||||
elementOffsetTop : number,
|
||||
options : ScrollIntoViewOptions = {}
|
||||
) : number {
|
||||
let { startOffset = 0, endOffset = 0, position = 'nearest'} = options;
|
||||
|
||||
// 计算元素相对于视口的上下偏移量
|
||||
const elementToViewportTopOffset = elementOffsetTop - viewportScrollTop - startOffset;
|
||||
const elementToViewportBottomOffset =
|
||||
elementOffsetTop +
|
||||
elementHeight -
|
||||
viewportScrollTop -
|
||||
viewportHeight +
|
||||
endOffset;
|
||||
|
||||
// 处理 nearest 模式,自动选择最近边缘
|
||||
if (position == 'nearest') {
|
||||
if (elementToViewportTopOffset >= 0 && elementToViewportBottomOffset <= 0) {
|
||||
return viewportScrollTop;
|
||||
}
|
||||
position =
|
||||
Math.abs(elementToViewportTopOffset) > Math.abs(elementToViewportBottomOffset)
|
||||
? 'end'
|
||||
: 'start';
|
||||
}
|
||||
|
||||
// 根据不同的对齐位置计算目标滚动位置
|
||||
let nextScrollTop = 0;
|
||||
switch (position) {
|
||||
case 'start':
|
||||
// 顶部对齐:元素顶部对齐视口顶部(考虑顶部缓冲)
|
||||
nextScrollTop = elementOffsetTop - startOffset;
|
||||
break;
|
||||
case 'center':
|
||||
// 居中对齐:元素中心对齐视口中心(考虑上下缓冲)
|
||||
nextScrollTop =
|
||||
elementOffsetTop -
|
||||
(viewportHeight - elementHeight - endOffset - startOffset) / 2 +
|
||||
startOffset;
|
||||
break;
|
||||
case 'end':
|
||||
// 底部对齐:元素底部对齐视口底部(考虑底部缓冲)
|
||||
nextScrollTop =
|
||||
elementOffsetTop + elementHeight - viewportHeight + endOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
return nextScrollTop;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user