【电销老师和招生老师】 新增# 总结模板
parent
702b348e83
commit
dd67c9edb6
19
src/App.vue
19
src/App.vue
|
|
@ -28,5 +28,24 @@ onShow(async () => {
|
|||
appStore.updateLocation()
|
||||
} else appStore.closeTimer()
|
||||
})
|
||||
|
||||
// function genDates() {
|
||||
// const weekdays = ['日', '一', '二', '三', '四', '五', '六']
|
||||
// const currentDate = new Date()
|
||||
// const currentDayOfWeek = currentDate.getDay()
|
||||
// let preDateOfWeek = currentDate.getDate() - 1
|
||||
// const daysInRange = []
|
||||
// for (let i = currentDayOfWeek; i <= weekdays.length - 1; i++) {
|
||||
// preDateOfWeek = preDateOfWeek + 1
|
||||
// daysInRange.push(preDateOfWeek)
|
||||
// }
|
||||
// let currentDateOfWeek = currentDate.getDate()
|
||||
// for (let i = 0; i <= weekdays.length - daysInRange.length; i++) {
|
||||
// currentDateOfWeek = currentDateOfWeek - 1
|
||||
// daysInRange.unshift(currentDateOfWeek)
|
||||
// }
|
||||
// console.log(daysInRange)
|
||||
// }
|
||||
// genDates()
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<view class="date-strip__item">
|
||||
<view
|
||||
class="date-strip__grid"
|
||||
v-for="(item, index) in dates"
|
||||
@click="onClick(item, index)"
|
||||
:key="item.key"
|
||||
>
|
||||
<text>
|
||||
{{ item.prefix }}
|
||||
</text>
|
||||
<view
|
||||
class="date-strip__grid-info"
|
||||
:style="[
|
||||
curIndex == index ? { background: '#0E66FB', 'border-radius': '99px' } : {}
|
||||
]"
|
||||
>
|
||||
<text
|
||||
class="date-strip__grid-day"
|
||||
:class="{
|
||||
'date-strip__grid--active-text': curIndex == index
|
||||
}"
|
||||
>
|
||||
{{ item.text }}{{ item.index }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import dateStripItemProps from './stripItemProps'
|
||||
|
||||
const props = defineProps({
|
||||
...dateStripItemProps
|
||||
})
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const date = new Date()
|
||||
const day = date.getDate()
|
||||
const curIndex = ref(props.dates.findIndex(item => item.day == day))
|
||||
const onClick = (day, index: number) => {
|
||||
emit('click', day)
|
||||
curIndex.value = index
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
.date-strip__item {
|
||||
display: flex;
|
||||
.date-strip__grid {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
&-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
&--active-text {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<scroll-view
|
||||
class="date-strip date-strip__scroll"
|
||||
:scroll-x="true"
|
||||
:show-scrollbar="false"
|
||||
direction="horizontal"
|
||||
:style="[styles]"
|
||||
>
|
||||
<date-strip-item
|
||||
:dates="displayWeeks[0].dates"
|
||||
:selectedDate="selectedDate"
|
||||
@click="onClick"
|
||||
/>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watchEffect } from 'vue'
|
||||
import dateStripProps from './props'
|
||||
import { getWeekRange, addDays, addWeeks, calcType, daysBetween } from './utils'
|
||||
import dateStripItem from './date-strip-item.vue'
|
||||
|
||||
const props = defineProps({
|
||||
...dateStripProps
|
||||
})
|
||||
const emit = defineEmits([
|
||||
'change',
|
||||
'select',
|
||||
'scroll',
|
||||
'panelChange',
|
||||
'update:modelValue',
|
||||
'input'
|
||||
])
|
||||
// 当前选中的周索引
|
||||
const currentWeekIndex = ref(0)
|
||||
const selectedDate = ref<Date | null>(null)
|
||||
// 计算一周的星期顺序
|
||||
const firstDayOfWeek = computed((): number => Math.min(Math.max(props.firstDayOfWeek, 0), 6))
|
||||
|
||||
// 计算最小和最大日期
|
||||
const today = new Date()
|
||||
const minDate = computed((): Date => (props.minDate != null ? new Date(props.minDate!) : today))
|
||||
const maxDate = computed(
|
||||
(): Date => (props.maxDate != null ? new Date(props.maxDate!) : addDays(today, 31))
|
||||
)
|
||||
|
||||
const days = computed((): number => {
|
||||
return daysBetween(maxDate.value, minDate.value)
|
||||
})
|
||||
|
||||
const styles = computed(() => {
|
||||
const style: Record<string, any> = {}
|
||||
|
||||
if (props.height) {
|
||||
style['height'] = props.height
|
||||
}
|
||||
if (props.bgColor) {
|
||||
style['background'] = props.bgColor
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
const cache = new Map()
|
||||
const createWeek = (currentStartDate: Date, length: number) => {
|
||||
const dates = []
|
||||
const time = currentStartDate.getTime()
|
||||
if (cache.has(time)) {
|
||||
return cache.get(time)!
|
||||
}
|
||||
for (let i = 0; i < length; i++) {
|
||||
const date = new Date(time)
|
||||
date.setDate(currentStartDate.getDate() + i)
|
||||
const week = date.getDay()
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth() + 1
|
||||
const day = date.getDate()
|
||||
const dateObj = {
|
||||
key: `${year}-${month}-${day}`,
|
||||
date,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
text: `${day}`.padStart(2, '0'),
|
||||
type: calcType(date, minDate.value, maxDate.value, selectedDate.value),
|
||||
prefix: props.weekdays[week]
|
||||
}
|
||||
dates.push(props.format != null ? props.format!(dateObj) : dateObj)
|
||||
}
|
||||
const obj = {
|
||||
start: new Date(dates[0].year, dates[0].month - 1, dates[0].day).getTime(),
|
||||
end: new Date(
|
||||
dates[length - 1].year,
|
||||
dates[length - 1].month - 1,
|
||||
dates[length - 1].day
|
||||
).getTime(),
|
||||
dates
|
||||
}
|
||||
|
||||
// cache.set(time, obj)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
const currentDate = ref<Date>(today)
|
||||
|
||||
// 计算要显示的三周数据
|
||||
const displayWeeks = computed(() => {
|
||||
const weeks = []
|
||||
if (props.switchMode == 'week') {
|
||||
const currentRange = getWeekRange(currentDate.value, firstDayOfWeek.value)
|
||||
const offsetMap = new Map<number, number[]>([
|
||||
[0, [0, 1, -1]],
|
||||
[1, [-1, 0, 1]],
|
||||
[2, [1, -1, 0]]
|
||||
])
|
||||
const indices = offsetMap.get(currentWeekIndex.value)!
|
||||
indices.forEach(i => {
|
||||
const weekDate = addWeeks(currentRange.start, i)
|
||||
weeks.push(createWeek(weekDate, 7))
|
||||
})
|
||||
} else {
|
||||
weeks.push(createWeek(minDate.value, days.value))
|
||||
}
|
||||
|
||||
return weeks
|
||||
})
|
||||
|
||||
const onClick = (day: Day) => {
|
||||
// if (day.type == 'disabled') return
|
||||
selectedDate.value = day.date
|
||||
const v = day.date.getTime()
|
||||
emit('change', day)
|
||||
emit('select', v)
|
||||
emit('update:modelValue', v)
|
||||
// #ifdef VUE2
|
||||
emit('input', v)
|
||||
// #endif
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
const value = props.value || props.modelValue
|
||||
if (!value) return
|
||||
selectedDate.value = new Date(value)
|
||||
})
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
export default {
|
||||
// 第一天从星期几开始,默认 1 = 周一
|
||||
firstDayOfWeek: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 日期格式,可选
|
||||
format: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 最大可选日期,不传则默认半年后
|
||||
maxDate: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 最小可选日期,不传则默认今天
|
||||
minDate: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 当前选择的日期
|
||||
value: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 默认值(如果使用非 v-model 绑定)
|
||||
defaultValue: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 模型值(如果使用 v-model 绑定)
|
||||
modelValue: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 日期行高
|
||||
height: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 网格宽度
|
||||
gridWidth: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 主题色,对底部按钮和选中日期生效
|
||||
color: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的背景色
|
||||
activeBgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的文字颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 圆角半径
|
||||
radius: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 切换模式,'week' 或 'none'
|
||||
switchMode: {
|
||||
type: String,
|
||||
default: 'week'
|
||||
},
|
||||
|
||||
// 形状,'square'、'circle' 或 'none'
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
|
||||
// 控制是否显示切换上一屏和下一屏的按钮,仅在按周切换时有效
|
||||
showNavigation: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 星期几的名称数组
|
||||
weekdays: {
|
||||
type: Array,
|
||||
default: () => ['日', '一', '二', '三', '四', '五', '六']
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// @ts-nocheck
|
||||
export default {
|
||||
dates: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 网格宽度
|
||||
gridWidth: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 主题色,对底部按钮和选中日期生效
|
||||
color: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的背景色
|
||||
activeBgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的文字颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 圆角半径
|
||||
radius: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 切换模式,'week' 或 'none'
|
||||
switchMode: {
|
||||
type: String,
|
||||
default: 'week'
|
||||
},
|
||||
|
||||
// 形状,'square'、'circle' 或 'none'
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
selectedDate: {
|
||||
type: [Date, String],
|
||||
default: null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// @ts-nocheck
|
||||
import { WeekRange, DateType } from './type'
|
||||
|
||||
/**
|
||||
* 获取指定日期所在周的日期范围。
|
||||
* @param {Date} date - 指定日期。
|
||||
* @param {number} firstDayOfWeek - 一周的第一天,0 表示周日,1 表示周一,以此类推。
|
||||
* @returns {WeekRange} 返回一个包含周起始和结束日期的对象。
|
||||
*/
|
||||
|
||||
export function getWeekRange(date: Date, firstDayOfWeek: number): WeekRange {
|
||||
const start = new Date(date.getTime())
|
||||
const dayOffset = (date.getDay() - firstDayOfWeek + 7) % 7
|
||||
start.setDate(start.getDate() - dayOffset)
|
||||
|
||||
const end = new Date(start.getTime())
|
||||
end.setDate(end.getDate() + 6)
|
||||
return { start, end } as WeekRange
|
||||
}
|
||||
|
||||
/**
|
||||
* 向指定日期添加天数。
|
||||
* @param {Date} date - 基础日期。
|
||||
* @param {number} days - 要添加的天数,可以是正数或负数。
|
||||
* @returns {Date} 返回一个新的日期对象,该对象是基础日期加上指定天数后的结果。
|
||||
*/
|
||||
export function addDays(date: Date, days: number): Date {
|
||||
const result = new Date(date.getTime())
|
||||
result.setDate(result.getDate() + days)
|
||||
return result
|
||||
}
|
||||
|
||||
export function addWeeks(date: Date, weeks: number): Date {
|
||||
const result = new Date(date.getTime())
|
||||
result.setDate(result.getDate() + weeks * 7)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个日期是否表示同一天(忽略时间部分)。
|
||||
*
|
||||
* @param date1 - 第一个日期。
|
||||
* @param date2 - 第二个日期。
|
||||
* @returns 如果两个日期是同一天,返回 true;否则返回 false。
|
||||
*/
|
||||
function isSameDate(date1: Date, date2: Date): boolean {
|
||||
return (
|
||||
date1.getFullYear() == date2.getFullYear() &&
|
||||
date1.getMonth() == date2.getMonth() &&
|
||||
date1.getDate() == date2.getDate()
|
||||
)
|
||||
}
|
||||
export function calcType(
|
||||
date: Date,
|
||||
minDate: Date,
|
||||
maxDate: Date,
|
||||
selectedDate: Date | null
|
||||
): DateType {
|
||||
// 检查日期是否早于 minDate 或晚于 maxDate
|
||||
if (date.getTime() < minDate.getTime() || date.getTime() > maxDate.getTime()) {
|
||||
return 'disabled'
|
||||
}
|
||||
// 如果 selectedDate 不为 null,检查日期是否等于 selectedDate
|
||||
if (selectedDate != null && isSameDate(date, selectedDate)) {
|
||||
return 'selected'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function daysBetween(date1: Date, date2: Date): number {
|
||||
// 将两个日期转换为毫秒
|
||||
const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime())
|
||||
return Math.floor(diffInMilliseconds / (1000 * 3600 * 24))
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<scroll-view
|
||||
class="l-date-strip l-date-strip__scroll"
|
||||
:scroll-x="true"
|
||||
:scroll-left="scrollLeft"
|
||||
:show-scrollbar="false"
|
||||
direction="horizontal"
|
||||
:style="[styles]"
|
||||
v-if="switchMode == 'none'"
|
||||
>
|
||||
<l-date-strip-item
|
||||
:dates="displayWeeks[0].dates"
|
||||
:color="color"
|
||||
:activeBgColor="activeBgColor"
|
||||
:activeColor="activeColor"
|
||||
:bgColor="bgColor"
|
||||
:radius="radius"
|
||||
:switchMode="switchMode"
|
||||
:shape="shape"
|
||||
@click="onClick"
|
||||
></l-date-strip-item>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<style scoped></style>
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<view>
|
||||
<view class="form">
|
||||
<view
|
||||
class="px-[32rpx] flex flex-col gap-[24rpx]"
|
||||
v-for="(item, index) in modelValue"
|
||||
:key="`unique_${index}`"
|
||||
>
|
||||
<view class="prefix">{{ item.formTitle }}</view>
|
||||
<view>
|
||||
<u-input
|
||||
v-if="item.formType == 1"
|
||||
v-model="item.value"
|
||||
placeholder="请输入"
|
||||
border="surround"
|
||||
></u-input>
|
||||
<u-textarea
|
||||
v-else-if="item.formType == 2"
|
||||
v-model="item.value"
|
||||
class="c-textarea"
|
||||
placeholder="请输入"
|
||||
></u-textarea>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="px-[60rpx] mt-[48rpx]">
|
||||
<u-button class="btn" color="#0E66FB" shape="circle" @click="handleConfirm">
|
||||
确认
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PropType } from 'vue'
|
||||
|
||||
export interface ISummary {
|
||||
formTitle: string
|
||||
formType: number
|
||||
value: string
|
||||
}
|
||||
defineProps({
|
||||
modelValue: {
|
||||
type: Array as PropType<ISummary[]>,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['handleConfirm'])
|
||||
const handleConfirm = () => {
|
||||
emit('handleConfirm')
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32rpx;
|
||||
.prefix {
|
||||
&::before {
|
||||
content: '* ';
|
||||
color: #f5222d;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.btn) {
|
||||
button {
|
||||
@apply h-[88rpx];
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,5 +1,17 @@
|
|||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/recruitsale/summary/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/telesale/summary/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/recruitsale/home/index",
|
||||
"style": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<view class="pb-[24rpx]">
|
||||
<date-strip v-model="value" @change="handleDateChange" height="160rpx" />
|
||||
<w-summary-form v-model="templateItems" @handle-confirm="handleConfirm" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import dateStrip from '@/components/date-strip/index.vue'
|
||||
|
||||
const templateItems = ref([
|
||||
{ formType: 1, formTitle: '今日加微信数量', value: '' },
|
||||
{ formType: 1, formTitle: '今日添加微信通过人数', value: '' },
|
||||
{ formType: 1, formTitle: '今日和多少家长微信语音', value: '' },
|
||||
{ formType: 1, formTitle: '今日有群发消息给家长', value: '' },
|
||||
{ formType: 1, formTitle: '今日有群发消息给家长', value: '' },
|
||||
{ formType: 1, formTitle: '学生已被统招录取多少', value: '' },
|
||||
{ formType: 1, formTitle: '不想在湛江读书多少', value: '' },
|
||||
{ formType: 1, formTitle: '中专类型有多少 ', value: '' },
|
||||
{ formType: 1, formTitle: '340分以下有多少', value: '' },
|
||||
{ formType: 1, formTitle: '340到400分有多少', value: '' },
|
||||
{ formType: 1, formTitle: '400以上有多少', value: '' },
|
||||
{ formType: 1, formTitle: '情况不清楚不回多少', value: '' },
|
||||
{ formType: 1, formTitle: '现在总添加微信量', value: '' },
|
||||
{ formType: 1, formTitle: '第二天有多少家长到校参观', value: '' },
|
||||
{ formType: 1, formTitle: '本周六日有多少家长到校参观', value: '' },
|
||||
{ formType: 1, formTitle: '目前加的数据有多少意向学生', value: '' },
|
||||
{ formType: 1, formTitle: '本周报名目标', value: '' },
|
||||
{ formType: 1, formTitle: '明天工作计划', value: '' },
|
||||
{ formType: 2, formTitle: '有遇到什么问题', value: '' }
|
||||
])
|
||||
const handleConfirm = () => {
|
||||
console.log(templateItems.value)
|
||||
}
|
||||
const value = ref(new Date().getTime())
|
||||
const handleDateChange = item => {
|
||||
console.log(item)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<view class="pb-[24rpx]">
|
||||
<date-strip v-model="value" @change="handleDateChange" height="160rpx" />
|
||||
<w-summary-form v-model="templateItems" @handle-confirm="handleConfirm" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import dateStrip from '@/components/date-strip/index.vue'
|
||||
|
||||
const templateItems = ref([
|
||||
{ formType: 1, formTitle: '今日打电话数量(包括不接、挂、空)', value: '' },
|
||||
{ formType: 1, formTitle: '今日打出多少条有效数据(可以加微信)', value: '' },
|
||||
{ formType: 1, formTitle: '家长主动加我们微信数据', value: '' },
|
||||
{ formType: 2, formTitle: '有遇到什么问题', value: '' }
|
||||
])
|
||||
const handleConfirm = () => {
|
||||
console.log(templateItems.value)
|
||||
}
|
||||
const value = ref(new Date().getTime())
|
||||
const handleDateChange = item => {
|
||||
console.log(item)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -126,6 +126,7 @@ page {
|
|||
padding-right: 0 !important;
|
||||
// background-color: $gray-1 !important;
|
||||
}
|
||||
|
||||
// 显示字段的表单,不需要输入
|
||||
.c-form-item-wrapper {
|
||||
@apply px-[32rpx];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
## 0.0.2(2025-02-10)
|
||||
- fix: 修复因导入`unitConvert`为小写名称导致微信小程序无法使用
|
||||
## 0.0.1(2025-01-28)
|
||||
- init
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<view
|
||||
class="l-date-strip__item"
|
||||
:class="'l-date-strip__item--' + switchMode"
|
||||
>
|
||||
<view
|
||||
class="l-date-strip__grid"
|
||||
:style="[
|
||||
styles,
|
||||
item.type == 'selected' && shape == 'square' && activeBgColor != null ? {background: activeBgColor}: {},
|
||||
shape == 'square' && radius != null ? {'border-radius': radius} : {}
|
||||
]"
|
||||
:class="[
|
||||
'l-date-strip__grid--' + shape,
|
||||
{
|
||||
'l-date-strip__grid--active-bg' : shape == 'square' && item.type == 'selected',
|
||||
'l-date-strip__grid--disabled': item.type == 'disabled',
|
||||
'l-date-strip__grid--selected': item.type == 'selected'
|
||||
}
|
||||
]"
|
||||
v-for="item in dates"
|
||||
@click="onClick(item)"
|
||||
:key="item.key">
|
||||
<text
|
||||
class="l-date-strip__grid-prefix"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-text' : item.type == 'selected' && shape == 'square',
|
||||
'l-date-strip__grid--active-text-none' : item.type == 'selected' && shape == 'none',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && activeColor != null ? { color: activeColor} : {},
|
||||
]">{{item.prefix}}</text>
|
||||
<view
|
||||
class="l-date-strip__grid-info"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-bg' : shape == 'circle' && item.type == 'selected',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && shape == 'circle' && activeBgColor != null ? {background: activeBgColor}: {},
|
||||
shape == 'square' && radius != null ? {'border-radius': radius}: {}
|
||||
]">
|
||||
<text
|
||||
class="l-date-strip__grid-day"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-text' : item.type == 'selected' && shape != 'none',
|
||||
'l-date-strip__grid--active-text-none' : item.type == 'selected' && shape == 'none',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && activeColor != null ? { color: activeColor} : {},
|
||||
]">{{item.text}}</text>
|
||||
<text
|
||||
class="l-date-strip__grid-suffix"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-text' : item.type == 'selected' && shape != 'none',
|
||||
'l-date-strip__grid--active-text-none' : item.type == 'selected' && shape == 'none',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && activeColor != null ? { color: activeColor} : {},
|
||||
]" v-if="item.suffix != null">{{item.suffix}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts" setup>
|
||||
import { DateStripItemProps } from './type';
|
||||
import { Day } from '../l-date-strip/type';
|
||||
const emit = defineEmits(['click'])
|
||||
const props = withDefaults(defineProps<DateStripItemProps>(), {
|
||||
dates: [] as Day[],
|
||||
shape: 'square',
|
||||
switchMode: 'week',
|
||||
})
|
||||
|
||||
const styles = computed(():Map<string, any>=>{
|
||||
const style = new Map<string, any>()
|
||||
|
||||
if(props.gridWidth != null && props.switchMode == 'none') {
|
||||
style.set('width', props.gridWidth!)
|
||||
}
|
||||
return style
|
||||
})
|
||||
const onClick = (day: Day) => {
|
||||
emit('click', day)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../l-date-strip';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<template>
|
||||
<view
|
||||
class="l-date-strip__item"
|
||||
:class="'l-date-strip__item--' + switchMode"
|
||||
>
|
||||
<view
|
||||
class="l-date-strip__grid"
|
||||
:style="[
|
||||
styles,
|
||||
item.type == 'selected' && shape == 'square' && activeBgColor != null ? {background: activeBgColor}: {},
|
||||
shape == 'square' && radius != null ? {'border-radius': radius} : {}
|
||||
]"
|
||||
:class="[
|
||||
'l-date-strip__grid--' + shape,
|
||||
{
|
||||
'l-date-strip__grid--active-bg' : shape == 'square' && item.type == 'selected',
|
||||
'l-date-strip__grid--disabled': item.type == 'disabled',
|
||||
'l-date-strip__grid--selected': item.type == 'selected'
|
||||
}
|
||||
]"
|
||||
v-for="item in dates"
|
||||
@click="onClick(item)"
|
||||
:key="item.key">
|
||||
<text
|
||||
class="l-date-strip__grid-prefix"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-text' : item.type == 'selected' && shape == 'square',
|
||||
'l-date-strip__grid--active-text-none' : item.type == 'selected' && shape == 'none',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && activeColor ? { color: activeColor} : {},
|
||||
]">{{item.prefix}}</text>
|
||||
<view
|
||||
class="l-date-strip__grid-info"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-bg' : shape == 'circle' && item.type == 'selected',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && shape == 'circle' && activeBgColor ? {background: activeBgColor}: {},
|
||||
shape == 'square' && radius ? {'border-radius': radius}: {}
|
||||
]">
|
||||
<text
|
||||
class="l-date-strip__grid-day"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-text' : item.type == 'selected' && shape != 'none',
|
||||
'l-date-strip__grid--active-text-none' : item.type == 'selected' && shape == 'none',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && activeColor ? { color: activeColor} : {},
|
||||
]">{{item.text}}</text>
|
||||
<text
|
||||
class="l-date-strip__grid-suffix"
|
||||
:class="{
|
||||
'l-date-strip__grid--active-text' : item.type == 'selected' && shape != 'none',
|
||||
'l-date-strip__grid--active-text-none' : item.type == 'selected' && shape == 'none',
|
||||
}"
|
||||
:style="[
|
||||
item.type == 'selected' && activeColor ? { color: activeColor} : {},
|
||||
]" v-if="item.suffix">{{item.suffix}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
// @ts-nocheck
|
||||
import { defineComponent, computed } from '@/uni_modules/lime-shared/vue';
|
||||
import dateStripItemProps from './props';
|
||||
import { Day } from '../l-date-strip/type';
|
||||
|
||||
export default defineComponent({
|
||||
props: dateStripItemProps,
|
||||
emits: ['click'],
|
||||
setup(props, {emit}) {
|
||||
const styles = computed(()=>{
|
||||
const style = {}
|
||||
|
||||
if(props.gridWidth && props.switchMode == 'none') {
|
||||
style['width'] = props.gridWidth
|
||||
}
|
||||
return style
|
||||
})
|
||||
const onClick = (day) => {
|
||||
emit('click', day)
|
||||
}
|
||||
return {
|
||||
onClick,
|
||||
styles
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../l-date-strip';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// @ts-nocheck
|
||||
export default {
|
||||
dates: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 网格宽度
|
||||
gridWidth: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 主题色,对底部按钮和选中日期生效
|
||||
color: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的背景色
|
||||
activeBgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的文字颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 圆角半径
|
||||
radius: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 切换模式,'week' 或 'none'
|
||||
switchMode: {
|
||||
type: String,
|
||||
default: 'week',
|
||||
},
|
||||
|
||||
// 形状,'square'、'circle' 或 'none'
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// @ts-nocheck
|
||||
import { Day } from '../l-date-strip/type';
|
||||
|
||||
export interface DateStripItemProps {
|
||||
dates: Day[];
|
||||
/**
|
||||
* 主题色,对底部按钮和选中日期生效
|
||||
*/
|
||||
color ?: string;
|
||||
activeBgColor ?: string;
|
||||
activeColor ?: string;
|
||||
bgColor ?: string;
|
||||
radius ?: string;
|
||||
gridWidth ?: string;
|
||||
switchMode: 'week' | 'none';
|
||||
shape: 'square' | 'circle' | 'text';
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
@import '~@/uni_modules/lime-style/index.scss';
|
||||
$prefix: l !default;
|
||||
$date-strip: #{$prefix}-date-strip;
|
||||
|
||||
$date-strip-bg-color: create-var(date-strip-bg-color, $bg-color-container);
|
||||
$date-strip-height: create-var(date-strip-height, 86px);
|
||||
$date-strip-padding: create-var(date-strip-padding, 8px 0);
|
||||
$date-strip-font-size: create-var(date-strip-font-size, 16px);
|
||||
$date-strip-color: create-var(date-strip-color, $text-color-1);
|
||||
$date-strip-prefix-color: create-var(date-strip-prefix-color, $text-color-3);
|
||||
$date-strip-prefix-font-size: create-var(date-strip-prefix-font-size, 14px);
|
||||
$date-strip-suffix-color: create-var(date-strip-color, $text-color-2);
|
||||
$date-strip-suffix-font-size: create-var(date-strip-font-size, 12px);
|
||||
$date-strip-active-color: create-var(date-strip-active-color, $primary-color);
|
||||
$date-strip-square-radius: create-var(date-strip-square-radius, 5px);
|
||||
|
||||
$date-strip-grid-width: create-var(date-strip-grid-width, 50px);
|
||||
// $date-strip-grid-prefix-padding: create-var(date-strip-grid-prefix-padding, 4px);
|
||||
$date-strip-grid-square-padding: create-var(date-strip-grid-square-padding, 6px 0);
|
||||
$date-strip-grid-circle-radius: create-var(date-strip-grid-circle-radius, 99px);
|
||||
// $date-strip-grid-suffix-translate: create-var(date-strip-grid-suffix-translate, 60%);
|
||||
|
||||
.#{$date-strip} {
|
||||
height: $date-strip-height;
|
||||
background-color: $date-strip-bg-color;
|
||||
&__scroll {
|
||||
flex-direction: row;
|
||||
}
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: $date-strip-padding;
|
||||
box-sizing: border-box;
|
||||
/* #ifndef APP-ANDROID || APP-IOS */
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
&--week {
|
||||
flex: 1;
|
||||
.#{$date-strip}__grid {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
&--none {
|
||||
.#{$date-strip}__grid {
|
||||
width: $date-strip-grid-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__grid {
|
||||
/* #ifndef APP-ANDROID || APP-IOS */
|
||||
flex-shrink: 0;
|
||||
/* #endif */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 4rpx;
|
||||
transition-duration: 300ms;
|
||||
transition-property: background-color, color;
|
||||
transition-timing-function: linear;
|
||||
|
||||
&-prefix,&-day,&-suffix {
|
||||
text-align: center;
|
||||
transition-duration: 200ms;
|
||||
transition-property: color;
|
||||
transition-timing-function: linear;
|
||||
}
|
||||
&-prefix {
|
||||
font-size: $date-strip-prefix-font-size;
|
||||
// font-weight: bold;
|
||||
color: $date-strip-prefix-color;
|
||||
}
|
||||
&--none {
|
||||
.l-date-strip__grid-prefix {
|
||||
padding-bottom: 4px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
}
|
||||
&--circle {
|
||||
.l-date-strip__grid-prefix {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.l-date-strip__grid-info {
|
||||
border-radius: $date-strip-grid-circle-radius;
|
||||
}
|
||||
}
|
||||
&--square {
|
||||
border-radius: $date-strip-square-radius;
|
||||
padding: $date-strip-grid-square-padding;
|
||||
}
|
||||
&-suffix {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(60%);
|
||||
font-size: $date-strip-suffix-font-size;
|
||||
color: $date-strip-suffix-color;
|
||||
}
|
||||
&-day {
|
||||
font-size: $date-strip-font-size;
|
||||
color: $date-strip-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
&-info {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
&--active-bg {
|
||||
background-color: $date-strip-active-color;
|
||||
}
|
||||
&--active-text {
|
||||
color: white;
|
||||
&-none {
|
||||
color: $date-strip-active-color;
|
||||
}
|
||||
}
|
||||
// &--selected {
|
||||
// &.l-date-strip__grid--circle {
|
||||
// .l-date-strip__grid {
|
||||
// &-info {
|
||||
// // border-radius: 99px;
|
||||
// background-color: $date-strip-active-color;
|
||||
// }
|
||||
// &-day,&-suffix {
|
||||
// color: white;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// &.l-date-strip__grid--square {
|
||||
// background-color: $date-strip-active-color;
|
||||
// border-radius: $date-strip-square-radius;
|
||||
|
||||
// .l-date-strip__grid {
|
||||
// &-prefix,&-day,&-suffix {
|
||||
// color: white;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// &.l-date-strip__grid--none {
|
||||
// .l-date-strip__grid {
|
||||
// &-prefix,&-day,&-suffix {
|
||||
// color: $date-strip-active-color;
|
||||
// // font-weight: bold;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
&--disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
<template>
|
||||
<scroll-view
|
||||
class="l-date-strip l-date-strip__scroll"
|
||||
:scroll-x="true"
|
||||
:scroll-left="scrollLeft"
|
||||
:show-scrollbar="false"
|
||||
direction="horizontal"
|
||||
:style="[styles]"
|
||||
v-if="switchMode == 'none'">
|
||||
<l-date-strip-item
|
||||
:dates="displayWeeks[0].dates"
|
||||
:color="color"
|
||||
:activeBgColor="activeBgColor"
|
||||
:activeColor="activeColor"
|
||||
:bgColor="bgColor"
|
||||
:radius="radius"
|
||||
:switchMode="switchMode"
|
||||
:shape="shape"
|
||||
@click="onClick">
|
||||
</l-date-strip-item>
|
||||
</scroll-view>
|
||||
<swiper
|
||||
v-else
|
||||
class="l-date-strip"
|
||||
:style="[styles]"
|
||||
:current="currentWeekIndex"
|
||||
:circular="swiperCircular"
|
||||
@animationfinish="swiperFinish"
|
||||
@change="swiperChange">
|
||||
<swiper-item v-for="(week, g) in displayWeeks" :key="g">
|
||||
<l-date-strip-item
|
||||
:dates="week.dates"
|
||||
:color="color"
|
||||
:activeBgColor="activeBgColor"
|
||||
:activeColor="activeColor"
|
||||
:bgColor="bgColor"
|
||||
:radius="radius"
|
||||
:switchMode="switchMode"
|
||||
:shape="shape"
|
||||
@click="onClick">
|
||||
</l-date-strip-item>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</template>
|
||||
<script lang="uts" setup>
|
||||
import { DateStripProps, WeekDateCollection, Day, DateType } from './type';
|
||||
import { getWeekRange, addDays, addWeeks, calcType, daysBetween } from './utils';
|
||||
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
const emit = defineEmits(['change', 'select', 'scroll', 'panelChange', 'update:modelValue'])
|
||||
const props = withDefaults(defineProps<DateStripProps>(), {
|
||||
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
firstDayOfWeek: 1,
|
||||
shape: 'square',
|
||||
switchMode: 'week'
|
||||
})
|
||||
// 当前选中的周索引
|
||||
const currentWeekIndex = ref(0)
|
||||
const scrollLeft = ref(0)
|
||||
// 是否循环滚动
|
||||
const swiperCircular = ref(true);
|
||||
const selectedDate = ref<Date|null>(null);
|
||||
// 计算一周的星期顺序
|
||||
const firstDayOfWeek = computed(() : number => Math.min(Math.max(props.firstDayOfWeek, 0), 6));
|
||||
|
||||
// 计算最小和最大日期
|
||||
const today = new Date();
|
||||
const minDate = computed(() : Date => (props.minDate != null ? new Date(props.minDate!) : today));
|
||||
const maxDate = computed(() : Date => (props.maxDate != null
|
||||
? new Date(props.maxDate!)
|
||||
: addDays(today, 31))
|
||||
);
|
||||
|
||||
const days = computed(():number => {
|
||||
return daysBetween(maxDate.value, minDate.value)
|
||||
})
|
||||
|
||||
const styles = computed(():Map<string, any>=>{
|
||||
const style = new Map<string, any>()
|
||||
|
||||
if(props.height != null) {
|
||||
style.set('height', props.height!)
|
||||
}
|
||||
if(props.bgColor != null) {
|
||||
style.set('background', props.bgColor!)
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
const cache = new Map<number,WeekDateCollection>()
|
||||
const createWeek = (currentStartDate: Date, length: number):WeekDateCollection => {
|
||||
const dates : Day[] = [];
|
||||
const time = currentStartDate.getTime()
|
||||
if(cache.has(time)) {
|
||||
return cache.get(time)!
|
||||
}
|
||||
for (let i = 0; i < length; i++) {
|
||||
const date = new Date(time);
|
||||
date.setDate(currentStartDate.getDate() + i);
|
||||
const week = date.getDay();
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate()
|
||||
const dateObj:Day = {
|
||||
key: `${year}-${month}-${day}`,
|
||||
date,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
text: `${day}`.padStart(2, '0'),
|
||||
type: calcType(date, minDate.value, maxDate.value, selectedDate.value),
|
||||
prefix: props.weekdays[week],
|
||||
}
|
||||
dates.push(props.format != null ? props.format!(dateObj) : dateObj);
|
||||
}
|
||||
const obj:WeekDateCollection = {
|
||||
start: new Date(dates[0].year, dates[0].month - 1, dates[0].day).getTime(),
|
||||
end: new Date(dates[length - 1].year, dates[length - 1].month - 1, dates[length - 1].day).getTime(),
|
||||
dates
|
||||
} as WeekDateCollection
|
||||
|
||||
// cache.set(time, obj)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
const currentDate = ref<Date>(today)
|
||||
|
||||
// 计算要显示的三周数据
|
||||
const displayWeeks = computed(() : WeekDateCollection[] => {
|
||||
const weeks: WeekDateCollection[] = [];
|
||||
if(props.switchMode == 'week') {
|
||||
const currentRange = getWeekRange(currentDate.value, firstDayOfWeek.value);
|
||||
const offsetMap = new Map<number, number[]>([
|
||||
[0, [0, 1, -1]],
|
||||
[1, [-1, 0, 1]],
|
||||
[2, [1, -1, 0]],
|
||||
])
|
||||
let indices = offsetMap.get(currentWeekIndex.value)!
|
||||
indices.forEach(i => {
|
||||
const weekDate = addWeeks(currentRange.start, i);
|
||||
weeks.push(createWeek(weekDate, 7))
|
||||
})
|
||||
} else {
|
||||
weeks.push(createWeek(minDate.value, days.value))
|
||||
}
|
||||
|
||||
return weeks
|
||||
})
|
||||
|
||||
const swiperChange = (event : UniSwiperChangeEvent) => {
|
||||
const delta = event.detail.current - currentWeekIndex.value;
|
||||
const newDate = addWeeks(currentDate.value, delta == 1 || delta == -2 ? 1 : -1);
|
||||
currentDate.value = newDate;
|
||||
currentWeekIndex.value = event.detail.current;
|
||||
}
|
||||
const swiperFinish = (_event : UniSwiperAnimationFinishEvent) => {
|
||||
|
||||
}
|
||||
|
||||
const onClick = (day: Day) => {
|
||||
if(day.type == 'disabled') return
|
||||
selectedDate.value = day.date
|
||||
const v = day.date.getTime()
|
||||
emit('change', v)
|
||||
emit('select', v)
|
||||
emit('update:modelValue', v)
|
||||
}
|
||||
|
||||
|
||||
const scrollToDate = (date: Date) => {
|
||||
currentDate.value = date
|
||||
if(props.switchMode != 'none') return
|
||||
scrollLeft.value = unitConvert(props.gridWidth ?? 50) * daysBetween(date, minDate.value)
|
||||
}
|
||||
|
||||
watchEffect(()=>{
|
||||
const value = props.value ?? props.modelValue
|
||||
if(value == null) return
|
||||
selectedDate.value = new Date(value)
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
nextTick(()=>{
|
||||
scrollToDate(currentDate.value)
|
||||
})
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
scrollToDate
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import './index';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
<template>
|
||||
<scroll-view
|
||||
class="l-date-strip l-date-strip__scroll"
|
||||
:scroll-x="true"
|
||||
:scroll-left="scrollLeft"
|
||||
:show-scrollbar="false"
|
||||
direction="horizontal"
|
||||
:style="[styles]"
|
||||
v-if="switchMode == 'none'">
|
||||
<l-date-strip-item
|
||||
:dates="displayWeeks[0].dates"
|
||||
:color="color"
|
||||
:activeBgColor="activeBgColor"
|
||||
:activeColor="activeColor"
|
||||
:bgColor="bgColor"
|
||||
:radius="radius"
|
||||
:switchMode="switchMode"
|
||||
:shape="shape"
|
||||
@click="onClick">
|
||||
</l-date-strip-item>
|
||||
</scroll-view>
|
||||
<swiper
|
||||
v-else
|
||||
class="l-date-strip"
|
||||
:style="[styles]"
|
||||
:current="currentWeekIndex"
|
||||
:circular="swiperCircular"
|
||||
@animationfinish="swiperFinish"
|
||||
@change="swiperChange">
|
||||
<swiper-item v-for="(week, g) in displayWeeks" :key="g">
|
||||
<l-date-strip-item
|
||||
:dates="week.dates"
|
||||
:color="color"
|
||||
:activeBgColor="activeBgColor"
|
||||
:activeColor="activeColor"
|
||||
:bgColor="bgColor"
|
||||
:radius="radius"
|
||||
:switchMode="switchMode"
|
||||
:shape="shape"
|
||||
@click="onClick">
|
||||
</l-date-strip-item>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
// @ts-nocheck
|
||||
import { defineComponent, ref, computed, watchEffect, onMounted, nextTick } from '@/uni_modules/lime-shared/vue';
|
||||
import dateStripProps from './props';
|
||||
import { WeekDateCollection, Day, DateType } from './type';
|
||||
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
import { getWeekRange, addDays, addWeeks, calcType, daysBetween } from './utils';
|
||||
|
||||
export default defineComponent({
|
||||
props: dateStripProps,
|
||||
emits: ['change', 'select', 'scroll', 'panelChange', 'update:modelValue', 'input'],
|
||||
setup(props, {emit, expose}) {
|
||||
// 当前选中的周索引
|
||||
const currentWeekIndex = ref(0)
|
||||
const scrollLeft = ref(0)
|
||||
// 是否循环滚动
|
||||
const swiperCircular = ref(true);
|
||||
const selectedDate = ref<Date|null>(null);
|
||||
// 计算一周的星期顺序
|
||||
const firstDayOfWeek = computed(() : number => Math.min(Math.max(props.firstDayOfWeek, 0), 6));
|
||||
|
||||
// 计算最小和最大日期
|
||||
const today = new Date();
|
||||
const minDate = computed(() : Date => (props.minDate != null ? new Date(props.minDate!) : today));
|
||||
const maxDate = computed(() : Date => (props.maxDate != null
|
||||
? new Date(props.maxDate!)
|
||||
: addDays(today, 31))
|
||||
);
|
||||
|
||||
const days = computed(():number => {
|
||||
return daysBetween(maxDate.value, minDate.value)
|
||||
})
|
||||
|
||||
const styles = computed(()=>{
|
||||
const style:Record<string, any> = {}
|
||||
|
||||
if(props.height) {
|
||||
style['height'] = props.height
|
||||
}
|
||||
if(props.bgColor) {
|
||||
style['background'] = props.bgColor
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
const cache = new Map<number, WeekDateCollection>();
|
||||
const createWeek = (currentStartDate: Date, length: number):WeekDateCollection => {
|
||||
const dates : Day[] = [];
|
||||
const time = currentStartDate.getTime()
|
||||
if(cache.has(time)) {
|
||||
return cache.get(time)!
|
||||
}
|
||||
for (let i = 0; i < length; i++) {
|
||||
const date = new Date(time);
|
||||
date.setDate(currentStartDate.getDate() + i);
|
||||
const week = date.getDay();
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate()
|
||||
const dateObj:Day = {
|
||||
key: `${year}-${month}-${day}`,
|
||||
date,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
text: `${day}`.padStart(2, '0'),
|
||||
type: calcType(date, minDate.value, maxDate.value, selectedDate.value),
|
||||
prefix: props.weekdays[week],
|
||||
}
|
||||
dates.push(props.format != null ? props.format!(dateObj) : dateObj);
|
||||
}
|
||||
const obj:WeekDateCollection = {
|
||||
start: new Date(dates[0].year, dates[0].month - 1, dates[0].day).getTime(),
|
||||
end: new Date(dates[length - 1].year, dates[length - 1].month - 1, dates[length - 1].day).getTime(),
|
||||
dates
|
||||
} as WeekDateCollection
|
||||
|
||||
// cache.set(time, obj)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
const currentDate = ref<Date>(today)
|
||||
|
||||
// 计算要显示的三周数据
|
||||
const displayWeeks = computed(() : WeekDateCollection[] => {
|
||||
const weeks: WeekDateCollection[] = [];
|
||||
if(props.switchMode == 'week') {
|
||||
const currentRange = getWeekRange(currentDate.value, firstDayOfWeek.value);
|
||||
const offsetMap = new Map<number, number[]>([
|
||||
[0, [0, 1, -1]],
|
||||
[1, [-1, 0, 1]],
|
||||
[2, [1, -1, 0]],
|
||||
])
|
||||
let indices = offsetMap.get(currentWeekIndex.value)!
|
||||
indices.forEach(i => {
|
||||
const weekDate = addWeeks(currentRange.start, i);
|
||||
weeks.push(createWeek(weekDate, 7))
|
||||
})
|
||||
} else {
|
||||
weeks.push(createWeek(minDate.value, days.value))
|
||||
}
|
||||
|
||||
return weeks
|
||||
})
|
||||
|
||||
const swiperChange = (event : UniSwiperChangeEvent) => {
|
||||
const delta = event.detail.current - currentWeekIndex.value;
|
||||
const newDate = addWeeks(currentDate.value, delta == 1 || delta == -2 ? 1 : -1);
|
||||
currentDate.value = newDate;
|
||||
currentWeekIndex.value = event.detail.current;
|
||||
}
|
||||
const swiperFinish = (_event : UniSwiperAnimationFinishEvent) => {
|
||||
|
||||
}
|
||||
|
||||
const onClick = (day: Day) => {
|
||||
if(day.type == 'disabled') return
|
||||
selectedDate.value = day.date
|
||||
const v = day.date.getTime()
|
||||
emit('change', v)
|
||||
emit('select', v)
|
||||
emit('update:modelValue', v)
|
||||
// #ifdef VUE2
|
||||
emit('input', v)
|
||||
// #endif
|
||||
}
|
||||
|
||||
|
||||
const scrollToDate = (date: Date) => {
|
||||
currentDate.value = date
|
||||
if(props.switchMode != 'none') return
|
||||
scrollLeft.value = unitConvert(props.gridWidth || 50) * daysBetween(date, minDate.value)
|
||||
}
|
||||
|
||||
watchEffect(()=>{
|
||||
const value = props.value || props.modelValue
|
||||
if(!value) return
|
||||
selectedDate.value = new Date(value)
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
nextTick(()=>{
|
||||
scrollToDate(currentDate.value)
|
||||
})
|
||||
})
|
||||
|
||||
// #ifdef VUE3
|
||||
expose({
|
||||
scrollToDate
|
||||
})
|
||||
// #endif
|
||||
return {
|
||||
styles,
|
||||
scrollLeft,
|
||||
currentWeekIndex,
|
||||
swiperCircular,
|
||||
swiperFinish,
|
||||
swiperChange,
|
||||
displayWeeks,
|
||||
onClick,
|
||||
// #ifdef VUE2
|
||||
scrollToDate
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import './index';
|
||||
</style>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
export default {
|
||||
// 第一天从星期几开始,默认 1 = 周一
|
||||
firstDayOfWeek: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
// 日期格式,可选
|
||||
format: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 最大可选日期,不传则默认半年后
|
||||
maxDate: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 最小可选日期,不传则默认今天
|
||||
minDate: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 当前选择的日期
|
||||
value: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 默认值(如果使用非 v-model 绑定)
|
||||
defaultValue: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 模型值(如果使用 v-model 绑定)
|
||||
modelValue: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 日期行高
|
||||
height: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 网格宽度
|
||||
gridWidth: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 主题色,对底部按钮和选中日期生效
|
||||
color: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的背景色
|
||||
activeBgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 选中日期的文字颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 背景色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 圆角半径
|
||||
radius: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
|
||||
// 切换模式,'week' 或 'none'
|
||||
switchMode: {
|
||||
type: String,
|
||||
default: 'week',
|
||||
},
|
||||
|
||||
// 形状,'square'、'circle' 或 'none'
|
||||
shape: {
|
||||
type: String,
|
||||
default: 'square'
|
||||
},
|
||||
|
||||
// 控制是否显示切换上一屏和下一屏的按钮,仅在按周切换时有效
|
||||
showNavigation: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 星期几的名称数组
|
||||
weekdays: {
|
||||
type: Array,
|
||||
default: () => ['日', '一', '二', '三', '四', '五', '六']
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// @ts-nocheck
|
||||
export type DateType = 'selected' | 'disabled' | '';
|
||||
|
||||
/**
|
||||
* 定义一个表示周范围的类
|
||||
*/
|
||||
export type WeekRange = {
|
||||
start: Date
|
||||
end: Date
|
||||
}
|
||||
|
||||
export type Day = {
|
||||
date : Date;
|
||||
key: string,
|
||||
day : number;
|
||||
year : number;
|
||||
month : number;
|
||||
text: string;
|
||||
type : DateType;
|
||||
prefix : string;
|
||||
prefixStyle ?: UTSJSONObject;
|
||||
textStyle?: UTSJSONObject;
|
||||
suffix ?: string;
|
||||
suffixStyle ?: UTSJSONObject;
|
||||
}
|
||||
|
||||
|
||||
export type WeekDateCollection = {
|
||||
start: number,
|
||||
end: number,
|
||||
dates : Day[];
|
||||
}
|
||||
|
||||
export type DateFormatType = (day : Day) => Day;
|
||||
export interface DateStripProps {
|
||||
/**
|
||||
* 第一天从星期几开始,默认 0 = 周日
|
||||
* @default 1
|
||||
*/
|
||||
firstDayOfWeek : number;
|
||||
format ?: DateFormatType;
|
||||
/**
|
||||
* 最大可选的日期,不传则默认半年后
|
||||
*/
|
||||
maxDate ?: number;
|
||||
/**
|
||||
* 最小可选的日期,不传则默认今天
|
||||
*/
|
||||
minDate ?: number;
|
||||
/**
|
||||
* 当前选择的日期
|
||||
*/
|
||||
value ?: number;
|
||||
defaultValue ?: number;
|
||||
modelValue ?: number;
|
||||
/**
|
||||
* 日期行高
|
||||
*/
|
||||
height ?: string;
|
||||
gridWidth ?: string;
|
||||
/**
|
||||
* 主题色,对底部按钮和选中日期生效
|
||||
*/
|
||||
color ?: string;
|
||||
activeBgColor ?: string;
|
||||
activeColor ?: string;
|
||||
bgColor ?: string;
|
||||
radius ?: string;
|
||||
switchMode: 'week' | 'none';
|
||||
shape: 'square' | 'circle' | 'none';
|
||||
/**
|
||||
* 控制是否显示切换上一屏和下一屏的按钮。
|
||||
* 该属性仅在按周切换时有效。
|
||||
*/
|
||||
showNavigation ?: boolean;
|
||||
weekdays : string[];
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// @ts-nocheck
|
||||
import { WeekRange, DateType } from './type';
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定日期所在周的日期范围。
|
||||
* @param {Date} date - 指定日期。
|
||||
* @param {number} firstDayOfWeek - 一周的第一天,0 表示周日,1 表示周一,以此类推。
|
||||
* @returns {WeekRange} 返回一个包含周起始和结束日期的对象。
|
||||
*/
|
||||
|
||||
export function getWeekRange(date : Date, firstDayOfWeek : number) : WeekRange {
|
||||
const start = new Date(date.getTime());
|
||||
const dayOffset = (date.getDay() - firstDayOfWeek + 7) % 7;
|
||||
start.setDate(start.getDate() - dayOffset);
|
||||
|
||||
const end = new Date(start.getTime());
|
||||
end.setDate(end.getDate() + 6);
|
||||
return { start, end } as WeekRange
|
||||
};
|
||||
|
||||
/**
|
||||
* 向指定日期添加天数。
|
||||
* @param {Date} date - 基础日期。
|
||||
* @param {number} days - 要添加的天数,可以是正数或负数。
|
||||
* @returns {Date} 返回一个新的日期对象,该对象是基础日期加上指定天数后的结果。
|
||||
*/
|
||||
export function addDays(date : Date, days : number) : Date {
|
||||
const result = new Date(date.getTime());
|
||||
result.setDate(result.getDate() + days);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
export function addWeeks(date : Date, weeks : number) : Date {
|
||||
const result = new Date(date.getTime());
|
||||
result.setDate(result.getDate() + weeks * 7);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断两个日期是否表示同一天(忽略时间部分)。
|
||||
*
|
||||
* @param date1 - 第一个日期。
|
||||
* @param date2 - 第二个日期。
|
||||
* @returns 如果两个日期是同一天,返回 true;否则返回 false。
|
||||
*/
|
||||
function isSameDate(date1 : Date, date2 : Date) : boolean {
|
||||
return (
|
||||
date1.getFullYear() == date2.getFullYear() &&
|
||||
date1.getMonth() == date2.getMonth() &&
|
||||
date1.getDate() == date2.getDate()
|
||||
);
|
||||
}
|
||||
export function calcType(date : Date, minDate : Date, maxDate : Date, selectedDate : Date | null) : DateType {
|
||||
// 检查日期是否早于 minDate 或晚于 maxDate
|
||||
if (date.getTime() < minDate.getTime() || date.getTime() > maxDate.getTime()) {
|
||||
return 'disabled';
|
||||
}
|
||||
// 如果 selectedDate 不为 null,检查日期是否等于 selectedDate
|
||||
if (selectedDate != null && isSameDate(date, selectedDate)) {
|
||||
return 'selected';
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
|
||||
export function daysBetween(date1: Date, date2: Date): number {
|
||||
// 将两个日期转换为毫秒
|
||||
const diffInMilliseconds = Math.abs(date2.getTime() - date1.getTime());
|
||||
return Math.floor(diffInMilliseconds / (1000 * 3600 * 24));
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text ultra">DateStrip 日期横条</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">
|
||||
<l-date-strip @change="onChange"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">切换方式</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip switchMode="none"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">选择样式</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip shape="circle"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">shape none</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip shape="none"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">自定义样式</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip bgColor="yellow" activeBgColor="#000" ></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">自定义日期范围</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip :minDate="minDate" :maxDate="maxDate"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">自定义日期文案</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip :format="customFormat"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Day } from '@/uni_modules/lime-date-strip';
|
||||
|
||||
const minDate = new Date(2022, 0, 10).getTime()
|
||||
const maxDate = new Date(2027, 10, 27).getTime()
|
||||
|
||||
const onChange= (e: number) => {
|
||||
console.log('n', e)
|
||||
}
|
||||
|
||||
|
||||
const customFormat = (day : Day) : Day => {
|
||||
const { date } = day;
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const curDate = date.getDate();
|
||||
|
||||
day.suffix = '¥60';
|
||||
|
||||
if (year == 2025) {
|
||||
if (month == 2) {
|
||||
const map = new Map<number, string>([
|
||||
[1, '初一'],
|
||||
[2, '初二'],
|
||||
[3, '初三'],
|
||||
[14, '情人节'],
|
||||
[15, '元宵节'],
|
||||
])
|
||||
if (map.has(curDate)) {
|
||||
day.prefix = map.get(curDate)!;
|
||||
day.suffix = '¥100';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return day;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.btn {
|
||||
margin-bottom: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
align-self: center;
|
||||
}
|
||||
.demo-block {
|
||||
margin: 32px 0 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;
|
||||
margin-left: 20px;
|
||||
&.large {
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 26px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
&.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;
|
||||
margin-left: 20px;
|
||||
}
|
||||
&__body {
|
||||
margin: 16px 0;
|
||||
overflow: visible;
|
||||
.demo-block {
|
||||
// margin-top: 0px;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<template>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text ultra">DateStrip 日期横条</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">
|
||||
<l-date-strip v-model="value" @change="onChange"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">切换方式</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip switchMode="none"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">选中样式</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip shape="circle"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">shape none</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip shape="none"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">自定义样式</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip bgColor="yellow" activeBgColor="#000" ></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">自定义日期范围</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip :minDate="minDate" :maxDate="maxDate"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text">自定义日期文案</text>
|
||||
<view class="demo-block__body">
|
||||
<l-date-strip :format="customFormat"></l-date-strip>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
value: new Date().getTime(),
|
||||
minDate: new Date(2022, 0, 10).getTime(),
|
||||
maxDate: new Date(2027, 10, 27).getTime()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(e) {
|
||||
console.log('n', e)
|
||||
},
|
||||
customFormat(day) {
|
||||
const { date } = day;
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const curDate = date.getDate();
|
||||
|
||||
day.suffix = '¥60';
|
||||
|
||||
if (year == 2025) {
|
||||
if (month == 2) {
|
||||
const map = new Map([
|
||||
[1, '初一'],
|
||||
[2, '初二'],
|
||||
[3, '初三'],
|
||||
[14, '情人节'],
|
||||
[15, '元宵节'],
|
||||
])
|
||||
if (map.has(curDate)) {
|
||||
day.prefix = map.get(curDate);
|
||||
day.suffix = '¥100';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return day
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.btn {
|
||||
margin-bottom: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
align-self: center;
|
||||
}
|
||||
.demo-block {
|
||||
margin: 32px 0 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;
|
||||
margin-left: 20px;
|
||||
&.large {
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 26px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
&.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;
|
||||
margin-left: 20px;
|
||||
}
|
||||
&__body {
|
||||
margin: 16px 0;
|
||||
overflow: visible;
|
||||
.demo-block {
|
||||
// margin-top: 0px;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// @ts-nocheck
|
||||
export * from './components/l-date-strip/type';
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"id": "lime-date-strip",
|
||||
"displayName": "lime-date-strip 日期横条",
|
||||
"version": "0.0.2",
|
||||
"description": "lime-date-strip 日期横条用于展示周日历或一组日历信息,兼容uniapp/uniappx",
|
||||
"keywords": [
|
||||
"lime-date-strip",
|
||||
"周日历",
|
||||
"时间周",
|
||||
"日期横条"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^4.45"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"lime-style",
|
||||
"lime-shared"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "u",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u",
|
||||
"app-uvue": "y",
|
||||
"app-harmony": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "u",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
# lime-date-strip 日期横条
|
||||
- 用于展示周日历或一组日历信息
|
||||
- 插件依赖`lime-style`,`lime-shared`不喜勿下
|
||||
|
||||
## 安装
|
||||
插件市场导入即可,首次可能需要重新编译
|
||||
|
||||
## 代码演示
|
||||
|
||||
|
||||
### 基础用法
|
||||
通过`v-model`可以绑定选择的日历
|
||||
```html
|
||||
<l-date-strip v-model="value" @change="onChange"></l-date-strip>
|
||||
```
|
||||
```js
|
||||
const value = new Date().getTime()
|
||||
|
||||
const onChange = (time: number) => {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 切换方式
|
||||
默认是以周为周期`(swiper)`的切换方式,通过`switchMode`设置为`none`可以换成`scroll-view`,这时是从`minDate`到`maxDate`的所有日期。
|
||||
|
||||
```html
|
||||
<l-date-strip switchMode="none" :minDate="minDate" :maxDate="maxDate"></l-date-strip>
|
||||
```
|
||||
```js
|
||||
const minDate: new Date(2022, 0, 10).getTime()
|
||||
const maxDate: new Date(2027, 10, 27).getTime()
|
||||
```
|
||||
|
||||
### 选中样式
|
||||
|
||||
通过 `shape` 可以设置为选中框的形状,可选值有:`circle`,`square`,`none`。
|
||||
|
||||
```html
|
||||
<l-date-strip shape="circle"></l-date-strip>
|
||||
```
|
||||
|
||||
|
||||
### 自定义样式
|
||||
|
||||
通过 `bgColor` 可以设置背景色,`activeBgColor`可设置选中的背景色。
|
||||
|
||||
```html
|
||||
<l-date-strip bgColor="yellow" activeBgColor="#000" ></l-date-strip>
|
||||
```
|
||||
|
||||
### 自定义日期文案
|
||||
通过传入 `format` 函数来对日历上每一格的内容进行格式化。
|
||||
|
||||
```html
|
||||
<l-date-strip :format="customFormat"></l-date-strip>
|
||||
```
|
||||
```js
|
||||
// uniapp 不需要设置类型
|
||||
import { Day } from '@/uni_modules/lime-date-strip';
|
||||
const customFormat = (day : Day) : Day => {
|
||||
const { date } = day;
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const curDate = date.getDate();
|
||||
|
||||
day.suffix = '¥60';
|
||||
|
||||
if (year == 2025) {
|
||||
if (month == 2) {
|
||||
const map = new Map<number, string>([
|
||||
[1, '初一'],
|
||||
[2, '初二'],
|
||||
[3, '初三'],
|
||||
[14, '情人节'],
|
||||
[15, '元宵节'],
|
||||
])
|
||||
if (map.has(curDate)) {
|
||||
day.prefix = map.get(curDate)!;
|
||||
day.suffix = '¥100';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return day;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 查看示例
|
||||
- 导入后直接使用这个标签查看演示效果
|
||||
|
||||
```html
|
||||
// 代码位于 uni_modules/lime-date-strip/compoents/lime-date-strip
|
||||
<lime-date-strip />
|
||||
```
|
||||
|
||||
### 插件标签
|
||||
- 默认 l-date-strip 为 component
|
||||
- 默认 lime-date-strip 为 demo
|
||||
|
||||
### 关于vue2的使用方式
|
||||
- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置
|
||||
- 关键代码是: 在main.js中 在vue2部分加上这一段即可.
|
||||
|
||||
```js
|
||||
// vue2
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
Vue.use(VueCompositionAPI)
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| ------ | ---------------------- | --------- | ------- |
|
||||
| v-modle | 选中的日期 | _number_ | `` |
|
||||
| defaultValue | 选中的日期 | _number_ | `` |
|
||||
| value | 选中的日期 | _number_ | `` |
|
||||
| switchMode |切换模式:<br>`none` 平铺展示所有日期,不展示切换按钮,<br>`week` 按周方式切换 | _string_ | `week` |
|
||||
| shape |选中框形状:<br>`square` 方块,包含星期和日期,<br>`circle` 圆形,只包含日期, <br>`none` 文本高亮 | _string_ | `square` |
|
||||
| minDate | 可选择的最小日期 | _number_ | |
|
||||
| maxDate | 可选择的最大日期 | _number_ | 当前日期的31天 |
|
||||
| height | 插件高度 | _string_ | |
|
||||
| gridWidth | 每格日期宽度,仅switchMode为`none`生效 | _string_ | |
|
||||
| activeBgColor | 选中框背景色 | _string_ | |
|
||||
| activeColor | 选中框文本色 | _string_ | |
|
||||
| bgColor | 横条背景色 | _string_ | |
|
||||
| radius | 选中框圆角 | _string_ | |
|
||||
| firstDayOfWeek | 第一天从星期几开始,默认 0 = 周日 | _0-6_ | `0` |
|
||||
|
||||
|
||||
|
||||
### Events
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
| ------ | ---------------- | ------------------- |
|
||||
| change | 点击时触发 | _event: number_ |
|
||||
| select | 点击时触发 | _event: number_ |
|
||||
|
||||
|
||||
|
||||
|
||||
## 主题定制
|
||||
|
||||
### 样式变量
|
||||
|
||||
组件提供了下列 CSS 变量,可用于自定义样式)。uvue app无效。
|
||||
|
||||
| 名称 | 默认值 | 描述 |
|
||||
| --- | --- | --- |
|
||||
| --l-date-strip-bg-color: | _$bg-color-container_ | - |
|
||||
| --l-date-strip-height: | _86px_ | - |
|
||||
| --l-date-strip-padding: | _8px 0_ | - |
|
||||
| --l-date-strip-font-size: | _16px_ | - |
|
||||
| --l-date-strip-color: | _$text-color-1_ | - |
|
||||
| --l-date-strip-prefix-color: | _$text-color-3_ | - |
|
||||
| --l-date-strip-prefix-font-size: | _14px_ | - |
|
||||
| --l-date-strip-suffix-color: | _$text-color-2_ | - |
|
||||
| --l-date-strip-suffix-font-size: | _12px_ | - |
|
||||
| --l-date-strip-active-color: | _$primary-color_ | - |
|
||||
| --l-date-strip-square-radius: | _5px_ | - |
|
||||
| --l-date-strip-grid-width: | _50px_ | - |
|
||||
| --l-date-strip-grid-square-padding: | _6px 0_ | - |
|
||||
| --l-date-strip-grid-circle-radius: | _99px_ | - |
|
||||
|
||||
|
||||
## 打赏
|
||||
|
||||
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
|
||||

|
||||

|
||||
|
|
@ -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
|
||||
function addUnit(value: number): string
|
||||
function addUnit(value: any|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)
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
// @ts-nocheck
|
||||
import type { ComponentPublicInstance } 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, type Ref } 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 })
|
||||
} else if(isRef(source) && typeof source.value == 'number') {
|
||||
watch(source as Ref<number>, watchFunc, { immediate })
|
||||
}
|
||||
// else if(source instanceof Ref<number>){
|
||||
// watch(source as Ref<number>, watchFunc, { immediate })
|
||||
// }
|
||||
|
||||
const stop = ()=>{
|
||||
|
||||
}
|
||||
return outputRef //as UseTransitionReturnType
|
||||
}
|
||||
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
import { raf, cancelRaf} from '../raf'
|
||||
// @ts-nocheck
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
||||
|
|
@ -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] 当前环境不支持')
|
||||
}
|
||||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
||||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
@ -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('');
|
||||
};
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// @ts-nocheck
|
||||
import { isString } from "../isString";
|
||||
import { isNumber } from "../isNumber";
|
||||
/**
|
||||
* 将金额转换为中文大写形式
|
||||
* @param {number | string} amount - 需要转换的金额,可以是数字或字符串
|
||||
* @returns {string} 转换后的中文大写金额
|
||||
*/
|
||||
function capitalizedAmount(amount : number) : string
|
||||
function capitalizedAmount(amount : string) : string
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
|
||||
## 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)
|
||||
- 无
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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(大于最大值,被限制为最大值)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// @ts-nocheck
|
||||
/**
|
||||
* 深度克隆一个对象或数组
|
||||
* @param obj 要克隆的对象或数组
|
||||
* @returns 克隆后的对象或数组
|
||||
*/
|
||||
export function cloneDeep<T>(obj: any): T {
|
||||
// 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回
|
||||
// if(['number', 'string'].includes(typeof obj) || Array.isArray(obj)){
|
||||
// return obj as T
|
||||
// }
|
||||
if(typeof obj == 'object'){
|
||||
return JSON.parse(JSON.stringify(obj as T)) as T;
|
||||
}
|
||||
return obj as T
|
||||
}
|
||||
|
||||
|
|
@ -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 (嵌套对象的副本也是独立的)
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<template>
|
||||
<view id="shared" style="height: 500px; width: 300px; background-color: aqua;">
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
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'
|
||||
|
||||
// #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)
|
||||
// }
|
||||
// })
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X
|
||||
export * from './type.ts'
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
// @ts-nocheck
|
||||
// export * from '@/uni_modules/lime-animateIt'
|
||||
export function createAnimation() {
|
||||
console.error('当前环境不支持,请使用:lime-animateIt')
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// @ts-nocheck
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
|
||||
// #endif
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
class EXIF {
|
||||
constructor(){
|
||||
console.error('当前环境不支持')
|
||||
}
|
||||
}
|
||||
|
||||
export const exif = new EXIF()
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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');
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 UNI-APP-X && APP
|
||||
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 UNI-APP-X && APP
|
||||
// 遍历对象的属性
|
||||
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"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
// @ts-nocheck
|
||||
export const getCurrentPage = ():Page => {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1]
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// @ts-nocheck
|
||||
/** 获取当前页 */
|
||||
export const getCurrentPage = () => {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.uts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// @ts-nocheck
|
||||
export function getRect(selector : string, context: ComponentPublicInstance):Promise<NodeInfo> {
|
||||
return new Promise((resolve)=>{
|
||||
uni.createSelectorQuery().in(context).select(selector).boundingClientRect(res =>{
|
||||
resolve(res as NodeInfo)
|
||||
}).exec();
|
||||
})
|
||||
}
|
||||
|
||||
export function getAllRect(selector : string, context: ComponentPublicInstance):Promise<NodeInfo[]> {
|
||||
return new Promise((resolve)=>{
|
||||
uni.createSelectorQuery().in(context).selectAll(selector).boundingClientRect(res =>{
|
||||
resolve(res as NodeInfo[])
|
||||
}).exec();
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
// @ts-nocheck
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
// 当编译环境是 APP-NVUE 时,引入 uni.requireNativePlugin('dom'),具体插件用途未知
|
||||
const dom = uni.requireNativePlugin('dom')
|
||||
// #endif
|
||||
|
||||
/**
|
||||
* 获取节点信息
|
||||
* @param selector 选择器字符串
|
||||
* @param context ComponentInternalInstance 对象
|
||||
* @param node 是否获取node
|
||||
* @returns 包含节点信息的 Promise 对象
|
||||
*/
|
||||
export function getRect(selector : string, context : ComponentInternalInstance|ComponentPublicInstance, node: boolean = false) {
|
||||
// 之前是个对象,现在改成实例,防止旧版会报错
|
||||
if(context== null) {
|
||||
return Promise.reject('context is null')
|
||||
}
|
||||
if(context.context){
|
||||
context = context.context
|
||||
}
|
||||
// #ifdef MP || VUE2
|
||||
if (context.proxy) context = context.proxy
|
||||
// #endif
|
||||
return new Promise<UniNamespace.NodeInfo>((resolve, reject) => {
|
||||
// #ifndef APP-NVUE
|
||||
const dom = uni.createSelectorQuery().in(context).select(selector);
|
||||
const result = (rect: UniNamespace.NodeInfo) => {
|
||||
if (rect) {
|
||||
resolve(rect)
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
dom.boundingClientRect(result).exec()
|
||||
} else {
|
||||
dom.fields({
|
||||
node: true,
|
||||
size: true,
|
||||
rect: true
|
||||
}, result).exec()
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
let { context } = options
|
||||
if (/#|\./.test(selector) && context.refs) {
|
||||
selector = selector.replace(/#|\./, '')
|
||||
if (context.refs[selector]) {
|
||||
selector = context.refs[selector]
|
||||
if(Array.isArray(selector)) {
|
||||
selector = selector[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
dom.getComponentRect(selector, (res) => {
|
||||
if (res.size) {
|
||||
resolve(res.size)
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export function getAllRect(selector : string, context: ComponentInternalInstance|ComponentPublicInstance, node:boolean = false) {
|
||||
if(context== null) {
|
||||
return Promise.reject('context is null')
|
||||
}
|
||||
// #ifdef MP || VUE2
|
||||
if (context.proxy) context = context.proxy
|
||||
// #endif
|
||||
return new Promise<UniNamespace.NodeInfo>((resolve, reject) => {
|
||||
// #ifndef APP-NVUE
|
||||
const dom = uni.createSelectorQuery().in(context).selectAll(selector);
|
||||
const result = (rect: UniNamespace.NodeInfo[]) => {
|
||||
if (rect) {
|
||||
resolve(rect)
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
dom.boundingClientRect(result).exec()
|
||||
} else {
|
||||
dom.fields({
|
||||
node: true,
|
||||
size: true,
|
||||
rect: true
|
||||
}, result).exec()
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
let { context } = options
|
||||
if (/#|\./.test(selector) && context.refs) {
|
||||
selector = selector.replace(/#|\./, '')
|
||||
if (context.refs[selector]) {
|
||||
selector = context.refs[selector]
|
||||
if(Array.isArray(selector)) {
|
||||
selector = selector[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
dom.getComponentRect(selector, (res) => {
|
||||
if (res.size) {
|
||||
resolve([res.size])
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X && APP
|
||||
interface CSSProperties {
|
||||
[key : string] : string | number | null
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
// #ifdef UNI-APP-X && APP
|
||||
type CSSProperties = UTSJSONObject
|
||||
// #endif
|
||||
// #endif
|
||||
/**
|
||||
* 将字符串转换为带有连字符分隔的小写形式
|
||||
* @param key - 要转换的字符串
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function toLowercaseSeparator(key : string):string {
|
||||
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取样式对象对应的样式字符串
|
||||
* @param style - CSS样式对象
|
||||
* @returns 由非空有效样式属性键值对组成的字符串
|
||||
*/
|
||||
export function getStyleStr(style : CSSProperties) : string {
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
let styleStr = '';
|
||||
style.toMap().forEach((value, key) => {
|
||||
if(value !== null && value != '') {
|
||||
styleStr += `${toLowercaseSeparator(key as string)}: ${value};`
|
||||
}
|
||||
})
|
||||
return styleStr
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X && APP
|
||||
return Object.keys(style)
|
||||
.filter(
|
||||
(key) =>
|
||||
style[key] !== undefined &&
|
||||
style[key] !== null &&
|
||||
style[key] !== '')
|
||||
.map((key : string) => `${toLowercaseSeparator(key)}: ${style[key]};`)
|
||||
.join(' ');
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 示例
|
||||
// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null };
|
||||
// const styleStr = getStyleStr(style);
|
||||
// console.log(styleStr);
|
||||
// 输出: "color: red; font-size: 16px;"
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X
|
||||
interface CSSProperties {
|
||||
[key : string] : string | number
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
type CSSProperties = UTSJSONObject
|
||||
// #endif
|
||||
/**
|
||||
* 将字符串转换为带有连字符分隔的小写形式
|
||||
* @param key - 要转换的字符串
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function toLowercaseSeparator(key : string) : string {
|
||||
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取样式对象对应的样式字符串
|
||||
* @param style - CSS样式对象
|
||||
* @returns 由非空有效样式属性键值对组成的字符串
|
||||
*/
|
||||
export function getStyleStr(style : CSSProperties) : string {
|
||||
let styleStr = '';
|
||||
style.toMap().forEach((value, key) => {
|
||||
if(value !== null && value != '') {
|
||||
styleStr += `${toLowercaseSeparator(key as string)}: ${value};`
|
||||
}
|
||||
})
|
||||
return styleStr
|
||||
}
|
||||
|
||||
// 示例
|
||||
// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null };
|
||||
// const styleStr = getStyleStr(style);
|
||||
// console.log(styleStr);
|
||||
// 输出: "color: red; font-size: 16px;"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-nocheck
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export * from './uvue.ts'
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export * from './vue.ts'
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// @ts-nocheck
|
||||
/**
|
||||
* 检查对象或数组是否具有指定的属性或键
|
||||
* @param obj 要检查的对象或数组
|
||||
* @param key 指定的属性或键
|
||||
* @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false
|
||||
*/
|
||||
function hasOwn(obj: UTSJSONObject, key: string): boolean
|
||||
function hasOwn(obj: Map<string, unknown>, key: string): boolean
|
||||
function hasOwn(obj: any, key: string): boolean {
|
||||
if(obj instanceof UTSJSONObject){
|
||||
return obj[key] != null
|
||||
}
|
||||
if(obj instanceof Map<string, unknown>){
|
||||
return (obj as Map<string, unknown>).has(key)
|
||||
}
|
||||
return false
|
||||
}
|
||||
export {
|
||||
hasOwn
|
||||
}
|
||||
// 示例
|
||||
// const obj = { name: 'John', age: 30 };
|
||||
|
||||
// if (hasOwn(obj, 'name')) {
|
||||
// console.log("对象具有 'name' 属性");
|
||||
// } else {
|
||||
// console.log("对象不具有 'name' 属性");
|
||||
// }
|
||||
// // 输出: 对象具有 'name' 属性
|
||||
|
||||
// const arr = [1, 2, 3];
|
||||
|
||||
// if (hasOwn(arr, 'length')) {
|
||||
// console.log("数组具有 'length' 属性");
|
||||
// } else {
|
||||
// console.log("数组不具有 'length' 属性");
|
||||
// }
|
||||
// 输出: 数组具有 'length' 属性
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// @ts-nocheck
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
/**
|
||||
* 检查对象或数组是否具有指定的属性或键
|
||||
* @param obj 要检查的对象或数组
|
||||
* @param key 指定的属性或键
|
||||
* @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false
|
||||
*/
|
||||
export function hasOwn(obj: Object | Array<any>, key: string): boolean {
|
||||
return hasOwnProperty.call(obj, key);
|
||||
}
|
||||
|
||||
// 示例
|
||||
// const obj = { name: 'John', age: 30 };
|
||||
|
||||
// if (hasOwn(obj, 'name')) {
|
||||
// console.log("对象具有 'name' 属性");
|
||||
// } else {
|
||||
// console.log("对象不具有 'name' 属性");
|
||||
// }
|
||||
// // 输出: 对象具有 'name' 属性
|
||||
|
||||
// const arr = [1, 2, 3];
|
||||
|
||||
// if (hasOwn(arr, 'length')) {
|
||||
// console.log("数组具有 'length' 属性");
|
||||
// } else {
|
||||
// console.log("数组不具有 'length' 属性");
|
||||
// }
|
||||
// 输出: 数组具有 'length' 属性
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// @ts-nocheck
|
||||
// validator
|
||||
// export * from './isString'
|
||||
// export * from './isNumber'
|
||||
// export * from './isNumeric'
|
||||
// export * from './isDef'
|
||||
// export * from './isFunction'
|
||||
// export * from './isObject'
|
||||
// export * from './isPromise'
|
||||
// export * from './isBase64'
|
||||
|
||||
// export * from './hasOwn'
|
||||
|
||||
// // 单位转换
|
||||
// export * from './addUnit'
|
||||
// export * from './unitConvert'
|
||||
// export * from './toNumber'
|
||||
|
||||
// export * from './random'
|
||||
// export * from './range'
|
||||
// export * from './fillZero'
|
||||
|
||||
// // image
|
||||
// export * from './base64ToPath'
|
||||
// export * from './pathToBase64'
|
||||
// export * from './exif'
|
||||
|
||||
// // canvas
|
||||
// export * from './canIUseCanvas2d'
|
||||
|
||||
// // page
|
||||
// export * from './getCurrentPage'
|
||||
|
||||
// // dom
|
||||
// export * from './getRect'
|
||||
// export * from './selectComponent'
|
||||
// export * from './createAnimation'
|
||||
|
||||
// // delay
|
||||
// export * from './sleep'
|
||||
// export * from './debounce'
|
||||
// export * from './throttle'
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 判断一个字符串是否为Base64编码。
|
||||
* Base64编码的字符串只包含A-Z、a-z、0-9、+、/ 和 = 这些字符。
|
||||
* @param {string} str - 要检查的字符串。
|
||||
* @returns {boolean} 如果字符串是Base64编码,返回true,否则返回false。
|
||||
*/
|
||||
export function isBase64(str: string): boolean {
|
||||
const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
||||
return base64Regex.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个字符串是否为Base64编码的data URI。
|
||||
* Base64编码的data URI通常以"data:"开头,后面跟着MIME类型和编码信息,然后是Base64编码的数据。
|
||||
* @param {string} str - 要检查的字符串。
|
||||
* @returns {boolean} 如果字符串是Base64编码的data URI,返回true,否则返回false。
|
||||
*/
|
||||
export function isBase64DataUri(str: string): boolean {
|
||||
const dataUriRegex = /^data:([a-zA-Z]+\/[a-zA-Z0-9-+.]+)(;base64)?,([a-zA-Z0-9+/]+={0,2})$/;
|
||||
return dataUriRegex.test(str);
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// @ts-nocheck
|
||||
// #ifdef WEB
|
||||
export const isBrowser = typeof window !== 'undefined';
|
||||
// #endif
|
||||
|
||||
// #ifndef WEB
|
||||
export const isBrowser = false;
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否已定义(不为 undefined)且不为 null
|
||||
* @param value 要检查的值
|
||||
* @returns 如果值已定义且不为 null,则返回 true;否则返回 false
|
||||
*/
|
||||
// #ifndef UNI-APP-X
|
||||
export function isDef(value: unknown): boolean {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
export function isDef(value : any|null) : boolean {
|
||||
// #ifdef UNI-APP-X && APP
|
||||
return value != null;
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X && APP
|
||||
return value != null && value != undefined;
|
||||
// #endif
|
||||
}
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// @ts-nocheck
|
||||
import {isDef} from '../isDef'
|
||||
import {isString} from '../isString'
|
||||
import {isNumber} from '../isNumber'
|
||||
/**
|
||||
* 判断一个值是否为空。
|
||||
*
|
||||
* 对于字符串,去除首尾空格后判断长度是否为0。
|
||||
* 对于数组,判断长度是否为0。
|
||||
* 对于对象,判断键的数量是否为0。
|
||||
* 对于null或undefined,直接返回true。
|
||||
* 其他类型(如数字、布尔值等)默认不为空。
|
||||
*
|
||||
* @param {any} value - 要检查的值。
|
||||
* @returns {boolean} 如果值为空,返回true,否则返回false。
|
||||
*/
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export function isEmpty(value : any | null) : boolean {
|
||||
// 为null
|
||||
if(!isDef(value)){
|
||||
return true
|
||||
}
|
||||
// 为空字符
|
||||
if(isString(value)){
|
||||
return value.toString().trim().length == 0
|
||||
}
|
||||
// 为数值
|
||||
if(isNumber(value)){
|
||||
return false
|
||||
}
|
||||
|
||||
if(typeof value == 'object'){
|
||||
// 数组
|
||||
if(Array.isArray(value)){
|
||||
return (value as Array<unknown>).length == 0
|
||||
}
|
||||
// Map
|
||||
if(value instanceof Map<unknown, unknown>) {
|
||||
return value.size == 0
|
||||
}
|
||||
// Set
|
||||
if(value instanceof Set<unknown>) {
|
||||
return value.size == 0
|
||||
}
|
||||
if(value instanceof UTSJSONObject) {
|
||||
return value.toMap().size == 0
|
||||
}
|
||||
return JSON.stringify(value) == '{}'
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export function isEmpty(value: any): boolean {
|
||||
// 检查是否为null或undefined
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查字符串是否为空
|
||||
if (typeof value === 'string') {
|
||||
return value.trim().length === 0;
|
||||
}
|
||||
|
||||
// 检查数组是否为空
|
||||
if (Array.isArray(value)) {
|
||||
return value.length === 0;
|
||||
}
|
||||
|
||||
// 检查对象是否为空
|
||||
if (typeof value === 'object') {
|
||||
return Object.keys(value).length === 0;
|
||||
}
|
||||
|
||||
// 其他类型(如数字、布尔值等)不为空
|
||||
return false;
|
||||
}
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为函数类型
|
||||
* @param val 要检查的值
|
||||
* @returns 如果值的类型是函数类型,则返回 true;否则返回 false
|
||||
*/
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export const isFunction = (val: any):boolean => typeof val == 'function';
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export const isFunction = (val: unknown): val is Function =>
|
||||
typeof val === 'function';
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为数字类型
|
||||
* @param value 要检查的值,可以是 number 类型或 string 类型的数字
|
||||
* @returns 如果值是数字类型且不是 NaN,则返回 true;否则返回 false
|
||||
*/
|
||||
|
||||
// #ifndef UNI-APP-X
|
||||
export function isNumber(value: number | string | null): boolean {
|
||||
return typeof value === 'number' && !isNaN(value);
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef UNI-APP-X
|
||||
export function isNumber(value: any|null): boolean {
|
||||
// #ifdef APP-ANDROID
|
||||
return ['Byte', 'UByte','Short','UShort','Int','UInt','Long','ULong','Float','Double','number'].includes(typeof value)
|
||||
// #endif
|
||||
// #ifdef APP-IOS
|
||||
return ['Int8', 'UInt8','Int16','UInt16','Int32','UInt32','Int64','UInt64','Int','UInt','Float','Float16','Float32','Float64','Double', 'number'].includes(typeof value)
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X && APP
|
||||
return typeof value === 'number' && !isNaN(value);
|
||||
// #endif
|
||||
}
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 检查一个值是否为数字类型或表示数字的字符串
|
||||
* @param value 要检查的值,可以是 string 类型或 number 类型
|
||||
* @returns 如果值是数字类型或表示数字的字符串,则返回 true;否则返回 false
|
||||
*/
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export function isNumeric(value: string | number | undefined | null): boolean {
|
||||
return /^(-)?\d+(\.\d+)?$/.test(value);
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
import {isNumber} from '../isNumber';
|
||||
import {isString} from '../isString';
|
||||
export function isNumeric(value : any|null) : boolean {
|
||||
if(value == null) {
|
||||
return false
|
||||
}
|
||||
if(isNumber(value)) {
|
||||
return true
|
||||
} else if(isString(value)) {
|
||||
// const regex = "-?\\d+(\\.\\d+)?".toRegex()
|
||||
const regex = new RegExp("^(-)?\\d+(\\.\\d+)?$")
|
||||
return regex.test(value as string) //regex.matches(value as string)
|
||||
}
|
||||
return false
|
||||
// return /^(-)?\d+(\.\d+)?$/.test(value);
|
||||
}
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为对象类型
|
||||
* @param val 要检查的值
|
||||
* @returns 如果值的类型是对象类型,则返回 true;否则返回 false
|
||||
*/
|
||||
|
||||
// #ifndef UNI-APP-X && APP
|
||||
export const isObject = (val : unknown) : val is Record<any, any> =>
|
||||
val !== null && typeof val === 'object';
|
||||
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export const isObject = (val : any | null) : boolean =>{
|
||||
return val !== null && typeof val === 'object';
|
||||
}
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// @ts-nocheck
|
||||
import {isFunction} from '../isFunction'
|
||||
import {isObject} from '../isObject'
|
||||
/**
|
||||
* 检查一个值是否为 Promise 类型
|
||||
* @param val 要检查的值
|
||||
* @returns 如果值的类型是 Promise 类型,则返回 true;否则返回 false
|
||||
*/
|
||||
// #ifndef APP-ANDROID
|
||||
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
|
||||
// 使用 isObject 函数判断值是否为对象类型
|
||||
// 使用 isFunction 函数判断值是否具有 then 方法和 catch 方法
|
||||
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
|
||||
};
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef APP-ANDROID
|
||||
export const isPromise = (val: any): boolean => {
|
||||
return val instanceof Promise<unknown>
|
||||
};
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为字符串类型
|
||||
* @param str 要检查的值
|
||||
* @returns 如果值的类型是字符串类型,则返回 true;否则返回 false
|
||||
*/
|
||||
// #ifndef UNI-APP-X && APP
|
||||
// export const isString = (str: unknown): str is string => typeof str === 'string';
|
||||
export function isString (str: unknown): str is string {
|
||||
return typeof str == 'string'
|
||||
}
|
||||
// #endif
|
||||
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
export function isString (str: any|null): boolean {
|
||||
return typeof str == 'string'
|
||||
}
|
||||
// #endif
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// @ts-nocheck
|
||||
// export function toLowercaseSeparator(key: string) {
|
||||
// return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 将字符串转换为指定连接符的命名约定
|
||||
* @param str 要转换的字符串
|
||||
* @param separator 指定的连接符,默认为 "-"
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function kebabCase(str : string, separator : string = "-") : string {
|
||||
return str
|
||||
|
||||
// #ifdef UNI-APP-X && APP
|
||||
.replace(/[A-Z]/g, (match : string, _ : number, _ : string) : string => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母
|
||||
// #endif
|
||||
// #ifndef UNI-APP-X && APP
|
||||
.replace(/[A-Z]/g, (match : string) : string => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母
|
||||
// #endif
|
||||
.replace(/[\s_-]+/g, separator) // 将空格、下划线和短横线替换为指定连接符
|
||||
.replace(new RegExp(`^${separator}|${separator}$`, "g"), "") // 删除开头和结尾的连接符
|
||||
.toLowerCase(); // 将结果转换为全小写
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// @ts-nocheck
|
||||
// #ifndef UNI-APP-X
|
||||
type UTSJSONObject = Record<string, any>
|
||||
// #endif
|
||||
export function obj2url(data: UTSJSONObject, isPrefix: boolean = false): string {
|
||||
const prefix = isPrefix ? '?' : '';
|
||||
const _result = [];
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"id": "lime-shared",
|
||||
"displayName": "lime-shared",
|
||||
"version": "0.2.9",
|
||||
"description": "本人插件的几个公共函数,获取当前页,图片的base64转临时路径,图片的exif信息等",
|
||||
"keywords": [
|
||||
"lime-shared",
|
||||
"exif"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "sdk-js",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-uvue": "y",
|
||||
"app-nvue": "y",
|
||||
"app-harmony": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y",
|
||||
"钉钉": "y",
|
||||
"快手": "y",
|
||||
"飞书": "y",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue