updates
This commit is contained in:
parent
e4f3b1dd21
commit
7f884a7367
|
|
@ -1,7 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { arrayIsNotEmpty, stringIsNotEmpty } from "@/common/util";
|
import { arrayIsNotEmpty, stringIsNotEmpty } from "@/common/util";
|
||||||
import vip from "../vip/index.vue";
|
|
||||||
|
|
||||||
|
|
||||||
const app = getApp()
|
const app = getApp()
|
||||||
|
|
||||||
|
|
@ -12,17 +10,11 @@ const MAX_CACHE_PAGE = 3;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "模拟填报",
|
name: "模拟填报",
|
||||||
computed: {
|
|
||||||
vip() {
|
|
||||||
return vip
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
///
|
|
||||||
systemName: '',
|
systemName: '',
|
||||||
num: {kcj: 0, jwt: 0, nlq: 0, kbd: 0,},
|
num: { kcj: 0, jwt: 0, nlq: 0, kbd: 0 },
|
||||||
circle: {kcj: 0, jwt: 0, nlq: 0, kbd: 0,},//百分比
|
circle: { kcj: 0, jwt: 0, nlq: 0, kbd: 0 }, // 百分比
|
||||||
modelVale: 0,
|
modelVale: 0,
|
||||||
topFlag: false, // 显示回到顶部
|
topFlag: false, // 显示回到顶部
|
||||||
otherMajor: {
|
otherMajor: {
|
||||||
|
|
@ -52,11 +44,11 @@ export default {
|
||||||
professionalCategory: null, // 报考方向
|
professionalCategory: null, // 报考方向
|
||||||
culturalScore: null, // 文化分
|
culturalScore: null, // 文化分
|
||||||
professionalScore: null, // 统考分
|
professionalScore: null, // 统考分
|
||||||
|
id: null, // 分数ID
|
||||||
|
batch: null, // 批次
|
||||||
},
|
},
|
||||||
/*筛选项 参数 start*/
|
|
||||||
defaultSelected: [],
|
defaultSelected: [],
|
||||||
menuList: app.globalData.StaticConstant.fillVolunteerMenuData,
|
menuList: app.globalData.StaticConstant.fillVolunteerMenuData,
|
||||||
/*筛选项 参数 end*/
|
|
||||||
tabIndex: 0,
|
tabIndex: 0,
|
||||||
scrollInto: "",
|
scrollInto: "",
|
||||||
newsList: [],
|
newsList: [],
|
||||||
|
|
@ -78,10 +70,7 @@ export default {
|
||||||
volunteerUndergraduateList: [], // 体育本科
|
volunteerUndergraduateList: [], // 体育本科
|
||||||
volunteerMap: new Map()
|
volunteerMap: new Map()
|
||||||
}, // 已填志愿信息
|
}, // 已填志愿信息
|
||||||
years: app.globalData.StaticConstant.years,
|
|
||||||
//
|
|
||||||
status: 'more',
|
status: 'more',
|
||||||
statusTypes: app.globalData.StaticConstant.loadStatusTypes,
|
|
||||||
contentText: app.globalData.StaticConstant.loadContentText,
|
contentText: app.globalData.StaticConstant.loadContentText,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -131,32 +120,22 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
onConfirm(e) {
|
onConfirm(e) {
|
||||||
console.log('onConfirm')
|
console.log('onConfirm')
|
||||||
if (e.type === 'filter') {
|
if (e.type === 'filter' && e.children) {
|
||||||
if (e.children) {
|
// 使用映射来简化代码
|
||||||
|
const fieldMap = {
|
||||||
|
'科研教学': 'kyjxList',
|
||||||
|
'录取方式': 'rulesEnrollProbabilityList',
|
||||||
|
'院校标签': 'tagsList',
|
||||||
|
'办学类型': 'schoolNatureList',
|
||||||
|
'地区': 'addressList'
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < e.children.length; i++) {
|
for (let i = 0; i < e.children.length; i++) {
|
||||||
if (e.children[i].title === '科研教学') {
|
const child = e.children[i]
|
||||||
this.selectForm.kyjxList = e.children[i].value
|
const field = fieldMap[child.title]
|
||||||
if (this.selectForm.kyjxList === null) {
|
|
||||||
this.selectForm.kyjxList = []
|
if (field) {
|
||||||
}
|
this.selectForm[field] = Array.isArray(child.value) ? child.value : []
|
||||||
} else if (e.children[i].title === '录取方式') {
|
|
||||||
this.selectForm.rulesEnrollProbabilityList = e.children[i].value
|
|
||||||
if (this.selectForm.rulesEnrollProbabilityList === null) {
|
|
||||||
this.selectForm.rulesEnrollProbabilityList = []
|
|
||||||
}
|
|
||||||
} else if (e.children[i].title === '院校标签') {
|
|
||||||
this.selectForm.tagsList = e.children[i].value
|
|
||||||
if (this.selectForm.tagsList === null) {
|
|
||||||
this.selectForm.tagsList = []
|
|
||||||
}
|
|
||||||
} else if (e.children[i].title === '办学类型') {
|
|
||||||
this.selectForm.schoolNatureList = e.children[i].value
|
|
||||||
if (this.selectForm.schoolNatureList === null) {
|
|
||||||
this.selectForm.schoolNatureList = []
|
|
||||||
}
|
|
||||||
} else if (e.children[i].title === '地区') {
|
|
||||||
this.selectForm.addressList = e.children[i].value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,10 +182,10 @@ export default {
|
||||||
confirmColor: 'red',
|
confirmColor: 'red',
|
||||||
//取消字体的颜色
|
//取消字体的颜色
|
||||||
cancelColor: '#dddddd',
|
cancelColor: '#dddddd',
|
||||||
success: function (res) {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
// 执行确认后的操作
|
// 执行确认后的操作
|
||||||
that.goto('/pages/zyb/vip/index')
|
this.goto('/pages/zyb/vip/index')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -275,17 +254,15 @@ export default {
|
||||||
},
|
},
|
||||||
/*填报志愿*/
|
/*填报志愿*/
|
||||||
saveVolunteer(item) {
|
saveVolunteer(item) {
|
||||||
let that = this
|
|
||||||
let u = uni
|
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: item.volunteerIndexs ? '移除志愿' : '确认志愿',
|
title: item.volunteerIndexs ? '移除志愿' : '确认志愿',
|
||||||
content: '[' + item.schoolCode + ']' + item.schoolName + '-' + item.batch + '-[' + item.enrollmentCode + ']' + item.majorName,
|
content: '[' + item.schoolCode + ']' + item.schoolName + '-' + item.batch + '-[' + item.enrollmentCode + ']' + item.majorName,
|
||||||
confirmText: '确定',
|
confirmText: '确定',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
success: function (res) {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
app.globalData.Request.post(app.globalData.ApiConstant.Volunteer.volunteerSave, {
|
app.globalData.Request.post(app.globalData.ApiConstant.Volunteer.volunteerSave, {
|
||||||
volunteerId: that.filledVolunteer.volunteerId,
|
volunteerId: this.filledVolunteer.volunteerId,
|
||||||
schoolCode: item.schoolCode,
|
schoolCode: item.schoolCode,
|
||||||
majorCode: item.majorCode,
|
majorCode: item.majorCode,
|
||||||
enrollmentCode: item.enrollmentCode,
|
enrollmentCode: item.enrollmentCode,
|
||||||
|
|
@ -294,17 +271,17 @@ export default {
|
||||||
}).then(r => {
|
}).then(r => {
|
||||||
if (r.success) {
|
if (r.success) {
|
||||||
if (r.result) {
|
if (r.result) {
|
||||||
that.filledVolunteer.volunteerId = r.result
|
this.filledVolunteer.volunteerId = r.result
|
||||||
}
|
}
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
uni.showToast({title: '添加成功', icon: "none"});
|
uni.showToast({title: '添加成功', icon: "none"});
|
||||||
}, 500)
|
}, 500)
|
||||||
that.artVolunteerDetail('load')
|
this.artVolunteerDetail('load')
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
that.getOtherMajorList()
|
this.getOtherMajorList()
|
||||||
}, 500)
|
}, 500)
|
||||||
} else {
|
} else {
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
uni.showToast({title: r.message, icon: "none"});
|
uni.showToast({title: r.message, icon: "none"});
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
@ -347,132 +324,24 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app.globalData.Request.get(app.globalData.ApiConstant.Volunteer.artVolunteerDetail, {id: this.filledVolunteer.volunteerId}).then(res => {
|
app.globalData.Request.get(app.globalData.ApiConstant.Volunteer.artVolunteerDetail, {id: this.filledVolunteer.volunteerId}).then(res => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
let dataResult = res.result
|
let dataResult = res.result
|
||||||
// 有数据情况下
|
// 有数据情况下
|
||||||
const volunteerMap = new Map();
|
const volunteerMap = new Map();
|
||||||
if (dataResult != null) {
|
if (dataResult) {
|
||||||
// 分数信息
|
|
||||||
///this.scoreInfo = res.result.userScoreInfo
|
|
||||||
// 处理 已填志愿信息
|
// 处理 已填志愿信息
|
||||||
this.filledVolunteer.volunteerId = dataResult.id
|
this.filledVolunteer.volunteerId = dataResult.id
|
||||||
this.filledVolunteer.volunteerName = dataResult.volunteerName
|
this.filledVolunteer.volunteerName = dataResult.volunteerName
|
||||||
if (true) {
|
|
||||||
let key = ''
|
// 处理各批次志愿数据
|
||||||
let volunteer = {indexStr: 0}
|
this.processVolunteerBatch(dataResult.volunteerRecordEarlyAdmissionList, 'volunteerEarlyAdmissionList', 2);
|
||||||
//==================================提前批 start
|
this.processVolunteerBatch(dataResult.volunteerRecordUndergraduateAList, 'volunteerUndergraduateAList', 12);
|
||||||
let volunteerRecordEarlyAdmissionList = dataResult.volunteerRecordEarlyAdmissionList;
|
this.processVolunteerBatch(dataResult.volunteerRecordUndergraduateBList, 'volunteerUndergraduateBList', 12);
|
||||||
for (let i = 0; i < volunteerRecordEarlyAdmissionList.length; i++) {
|
this.processVolunteerBatch(dataResult.volunteerRecordUndergraduateList, 'volunteerUndergraduateList', 12);
|
||||||
key = volunteerRecordEarlyAdmissionList[i].enrollmentCode + "_" + volunteerRecordEarlyAdmissionList[i].majorCode + "_" + volunteerRecordEarlyAdmissionList[i].schoolCode
|
this.processVolunteerBatch(dataResult.volunteerRecordJuniorCollegeList, 'volunteerJuniorCollegeList', 12);
|
||||||
volunteer = volunteerRecordEarlyAdmissionList[i]
|
|
||||||
volunteerMap.set(key, volunteerRecordEarlyAdmissionList[i])
|
|
||||||
}
|
|
||||||
let indexs = 1;
|
|
||||||
let volunteerRecordEarlyAdmissionList2 = [];
|
|
||||||
while (indexs <= 2) {
|
|
||||||
let record = {
|
|
||||||
actives: false,
|
|
||||||
indexs: indexs
|
|
||||||
}
|
|
||||||
for (let i = 0; i < volunteerRecordEarlyAdmissionList.length; i++) {
|
|
||||||
if (volunteerRecordEarlyAdmissionList[i].indexs === indexs) {
|
|
||||||
record = volunteerRecordEarlyAdmissionList[i]
|
|
||||||
record.actives = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
volunteerRecordEarlyAdmissionList2.push(record)
|
|
||||||
indexs++;
|
|
||||||
}
|
|
||||||
this.filledVolunteer.volunteerEarlyAdmissionList = volunteerRecordEarlyAdmissionList2
|
|
||||||
//==================================提前批 end
|
|
||||||
//==================================本科A段 start
|
|
||||||
let volunteerUndergraduateAList = dataResult.volunteerRecordUndergraduateAList;
|
|
||||||
for (let i = 0; i < volunteerUndergraduateAList.length; i++) {
|
|
||||||
key = volunteerUndergraduateAList[i].enrollmentCode + "_" + volunteerUndergraduateAList[i].majorCode + "_" + volunteerUndergraduateAList[i].schoolCode
|
|
||||||
volunteerMap.set(key, volunteerUndergraduateAList[i])
|
|
||||||
}
|
|
||||||
indexs = 1;
|
|
||||||
let volunteerRecordUndergraduateAList2 = [];
|
|
||||||
while (indexs <= 12) {
|
|
||||||
let record = {actives: false, indexs: indexs}
|
|
||||||
for (let i = 0; i < volunteerUndergraduateAList.length; i++) {
|
|
||||||
if (volunteerUndergraduateAList[i].indexs === indexs) {
|
|
||||||
record = volunteerUndergraduateAList[i]
|
|
||||||
record.actives = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
volunteerRecordUndergraduateAList2.push(record)
|
|
||||||
indexs++;
|
|
||||||
}
|
|
||||||
this.filledVolunteer.volunteerUndergraduateAList = volunteerRecordUndergraduateAList2
|
|
||||||
//==================================本科A段 end
|
|
||||||
//==================================本科B段 start
|
|
||||||
let volunteerUndergraduateBList = dataResult.volunteerRecordUndergraduateBList;
|
|
||||||
for (let i = 0; i < volunteerUndergraduateBList.length; i++) {
|
|
||||||
key = volunteerUndergraduateBList[i].enrollmentCode + "_" + volunteerUndergraduateBList[i].majorCode + "_" + volunteerUndergraduateBList[i].schoolCode
|
|
||||||
volunteerMap.set(key, volunteerUndergraduateBList[i])
|
|
||||||
}
|
|
||||||
indexs = 1;
|
|
||||||
let volunteerUndergraduateBList2 = [];
|
|
||||||
while (indexs <= 12) {
|
|
||||||
let record = {actives: false, indexs: indexs}
|
|
||||||
for (let i = 0; i < volunteerUndergraduateBList.length; i++) {
|
|
||||||
if (volunteerUndergraduateBList[i].indexs === indexs) {
|
|
||||||
record = volunteerUndergraduateBList[i]
|
|
||||||
record.actives = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
volunteerUndergraduateBList2.push(record)
|
|
||||||
indexs++;
|
|
||||||
}
|
|
||||||
this.filledVolunteer.volunteerUndergraduateBList = volunteerUndergraduateBList2
|
|
||||||
//==================================本科 start
|
|
||||||
let volunteerUndergraduateList = dataResult.volunteerRecordUndergraduateList;
|
|
||||||
for (let i = 0; i < volunteerUndergraduateList.length; i++) {
|
|
||||||
key = volunteerUndergraduateList[i].enrollmentCode + "_" + volunteerUndergraduateList[i].majorCode + "_" + volunteerUndergraduateList[i].schoolCode
|
|
||||||
volunteerMap.set(key, volunteerUndergraduateList[i])
|
|
||||||
}
|
|
||||||
indexs = 1;
|
|
||||||
let volunteerUndergraduateList2 = [];
|
|
||||||
while (indexs <= 12) {
|
|
||||||
let record = {actives: false, indexs: indexs}
|
|
||||||
for (let i = 0; i < volunteerUndergraduateList.length; i++) {
|
|
||||||
if (volunteerUndergraduateList[i].indexs === indexs) {
|
|
||||||
record = volunteerUndergraduateList[i]
|
|
||||||
record.actives = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
volunteerUndergraduateList2.push(record)
|
|
||||||
indexs++;
|
|
||||||
}
|
|
||||||
this.filledVolunteer.volunteerUndergraduateList = volunteerUndergraduateList2
|
|
||||||
//==================================本科 end
|
|
||||||
//==================================高职高专 start
|
|
||||||
let volunteerJuniorCollegeList = dataResult.volunteerRecordJuniorCollegeList;
|
|
||||||
for (let i = 0; i < volunteerJuniorCollegeList.length; i++) {
|
|
||||||
key = volunteerJuniorCollegeList[i].enrollmentCode + "_" + volunteerJuniorCollegeList[i].majorCode + "_" + volunteerJuniorCollegeList[i].schoolCode
|
|
||||||
volunteerMap.set(key, volunteerJuniorCollegeList[i])
|
|
||||||
}
|
|
||||||
indexs = 1;
|
|
||||||
let volunteerJuniorCollegeList2 = [];
|
|
||||||
while (indexs <= 12) {
|
|
||||||
let record = {
|
|
||||||
actives: false,
|
|
||||||
indexs: indexs
|
|
||||||
}
|
|
||||||
for (let i = 0; i < volunteerJuniorCollegeList.length; i++) {
|
|
||||||
if (volunteerJuniorCollegeList[i].indexs === indexs) {
|
|
||||||
record = volunteerJuniorCollegeList[i]
|
|
||||||
record.actives = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
volunteerJuniorCollegeList2.push(record)
|
|
||||||
indexs++;
|
|
||||||
}
|
|
||||||
this.filledVolunteer.volunteerJuniorCollegeList = volunteerJuniorCollegeList2
|
|
||||||
}
|
|
||||||
//==================================高职高专 end
|
|
||||||
} else {
|
} else {
|
||||||
this.filledVolunteer.volunteerEarlyAdmissionList = []
|
this.filledVolunteer.volunteerEarlyAdmissionList = []
|
||||||
this.filledVolunteer.volunteerUndergraduateAList = []
|
this.filledVolunteer.volunteerUndergraduateAList = []
|
||||||
|
|
@ -484,6 +353,7 @@ export default {
|
||||||
this.filledVolunteer.volunteerMap = volunteerMap
|
this.filledVolunteer.volunteerMap = volunteerMap
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
console.error('获取志愿详情失败:', err)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if (e && e === 'load') {
|
if (e && e === 'load') {
|
||||||
this.laodRecommendVolunterList()
|
this.laodRecommendVolunterList()
|
||||||
|
|
@ -492,54 +362,62 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 通用处理各批次志愿数据的方法
|
||||||
|
processVolunteerBatch(volunteerList, targetListName, maxCount) {
|
||||||
|
const volunteerMap = this.filledVolunteer.volunteerMap;
|
||||||
|
if (!volunteerList || !Array.isArray(volunteerList)) return;
|
||||||
|
|
||||||
|
// 添加到映射表
|
||||||
|
for (let i = 0; i < volunteerList.length; i++) {
|
||||||
|
const item = volunteerList[i];
|
||||||
|
const key = item.enrollmentCode + "_" + item.majorCode + "_" + item.schoolCode;
|
||||||
|
volunteerMap.set(key, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成带状态的列表(从1到maxCount)
|
||||||
|
let result = [];
|
||||||
|
for (let indexs = 1; indexs <= maxCount; indexs++) {
|
||||||
|
let record = {
|
||||||
|
actives: false,
|
||||||
|
indexs: indexs
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找对应索引的数据
|
||||||
|
for (let i = 0; i < volunteerList.length; i++) {
|
||||||
|
if (volunteerList[i].indexs === indexs) {
|
||||||
|
record = { ...volunteerList[i], actives: true };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filledVolunteer[targetListName] = result;
|
||||||
|
},
|
||||||
//刷新推荐的志愿列表的志愿填报状态
|
//刷新推荐的志愿列表的志愿填报状态
|
||||||
laodRecommendVolunterList() {
|
laodRecommendVolunterList() {
|
||||||
//判断专业是否已经填报过
|
//判断专业是否已经填报过
|
||||||
if (this.filledVolunteer.volunteerMap) {
|
if (this.filledVolunteer.volunteerMap && this.recommendVolunteerList) {
|
||||||
let key = ''
|
for (let i = 0; i < this.recommendVolunteerList.length; i++) {
|
||||||
let volunteer = {}
|
const item = this.recommendVolunteerList[i];
|
||||||
let recommendVolunteerList = this.recommendVolunteerList
|
const key = item.enrollmentCode + "_" + item.majorCode + "_" + item.schoolCode;
|
||||||
if (recommendVolunteerList) {
|
const volunteer = this.filledVolunteer.volunteerMap.get(key);
|
||||||
for (let i = 0; i < recommendVolunteerList.length; i++) {
|
item.volunteerIndexs = volunteer ? volunteer.indexs : '';
|
||||||
key = recommendVolunteerList[i].enrollmentCode + "_" + recommendVolunteerList[i].majorCode + "_" + recommendVolunteerList[i].schoolCode
|
|
||||||
volunteer = this.filledVolunteer.volunteerMap.get(key)
|
|
||||||
if (volunteer != null) {
|
|
||||||
//这个专业已填过
|
|
||||||
recommendVolunteerList[i].volunteerIndexs = volunteer.indexs
|
|
||||||
} else {
|
|
||||||
recommendVolunteerList[i].volunteerIndexs = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.recommendVolunteerList = recommendVolunteerList
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//获取推荐志愿列表
|
//获取推荐志愿列表
|
||||||
getRecommendVolunteerList() {
|
getRecommendVolunteerList() {
|
||||||
this.status = 'loading'
|
this.status = 'loading'
|
||||||
let kyjxStrs = ''
|
|
||||||
if (this.selectForm.kyjxList && this.selectForm.kyjxList.length > 0) {
|
|
||||||
kyjxStrs = this.selectForm.kyjxList.join(',')
|
|
||||||
}
|
|
||||||
let tagsStrs = ''
|
|
||||||
if (this.selectForm.tagsList && this.selectForm.tagsList.length > 0) {
|
|
||||||
tagsStrs = this.selectForm.tagsList.join(",")
|
|
||||||
}
|
|
||||||
|
|
||||||
let schoolNatureStrs = ''
|
// 简化参数处理
|
||||||
if (this.selectForm.schoolNatureList && this.selectForm.schoolNatureList.length > 0) {
|
const kyjxStrs = this.selectForm.kyjxList?.join(',') || ''
|
||||||
schoolNatureStrs = this.selectForm.schoolNatureList.join(",")
|
const tagsStrs = this.selectForm.tagsList?.join(',') || ''
|
||||||
}
|
const schoolNatureStrs = this.selectForm.schoolNatureList?.join(',') || ''
|
||||||
//地区
|
const province = arrayIsNotEmpty(this.selectForm.addressList) ? this.selectForm.addressList.join(',') : ''
|
||||||
let province = ''
|
const rulesEnrollProbability = arrayIsNotEmpty(this.selectForm.rulesEnrollProbabilityList) ? this.selectForm.rulesEnrollProbabilityList.join(',') : ''
|
||||||
if (arrayIsNotEmpty(this.selectForm.addressList)) {
|
|
||||||
province = this.selectForm.addressList.join(",")
|
|
||||||
}
|
|
||||||
//录取方式
|
|
||||||
let rulesEnrollProbability = ''
|
|
||||||
if (arrayIsNotEmpty(this.selectForm.rulesEnrollProbabilityList)) {
|
|
||||||
rulesEnrollProbability = this.selectForm.rulesEnrollProbabilityList.join(",")
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
schoolName: this.selectForm.schoolName, // 学校名称
|
schoolName: this.selectForm.schoolName, // 学校名称
|
||||||
|
|
@ -554,50 +432,45 @@ export default {
|
||||||
kyjxStrs: kyjxStrs,
|
kyjxStrs: kyjxStrs,
|
||||||
scoreId: this.scoreInfo.id,
|
scoreId: this.scoreInfo.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
app.globalData.Request.get(app.globalData.ApiConstant.Major.recommendMajorPage, params).then(res => {
|
app.globalData.Request.get(app.globalData.ApiConstant.Major.recommendMajorPage, params).then(res => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
let result = res.result
|
let result = res.result
|
||||||
let allNumber = result.allNumber
|
let allNumber = result.allNumber
|
||||||
|
|
||||||
|
// 更新统计数据
|
||||||
this.circle.kcj = this.toPercent(result.kcj, allNumber) // 可冲击
|
this.circle.kcj = this.toPercent(result.kcj, allNumber) // 可冲击
|
||||||
this.circle.jwt = this.toPercent(result.jwt, allNumber) // 较稳妥
|
this.circle.jwt = this.toPercent(result.jwt, allNumber) // 较稳妥
|
||||||
this.circle.nlq = this.toPercent(result.nan, allNumber) // 难录取
|
this.circle.nlq = this.toPercent(result.nan, allNumber) // 难录取
|
||||||
this.circle.kbd = this.toPercent(result.kbd, allNumber) // 可保底
|
this.circle.kbd = this.toPercent(result.kbd, allNumber) // 可保底
|
||||||
|
|
||||||
this.num.kcj = result.kcj // 可冲击
|
this.num.kcj = result.kcj // 可冲击
|
||||||
this.num.jwt = result.jwt // 较稳妥
|
this.num.jwt = result.jwt // 较稳妥
|
||||||
this.num.nlq = result.nan // 难录取
|
this.num.nlq = result.nan // 难录取
|
||||||
this.num.kbd = result.kbd // 可保底
|
this.num.kbd = result.kbd // 可保底
|
||||||
|
|
||||||
let records = result.pageList.records
|
let records = result.pageList.records
|
||||||
|
|
||||||
// 判断专业是否已经填报过
|
// 判断专业是否已经填报过
|
||||||
if (this.filledVolunteer.volunteerMap) {
|
if (this.filledVolunteer.volunteerMap && result && records) {
|
||||||
let key = ''
|
|
||||||
let volunteer = {}
|
|
||||||
if (result && records) {
|
|
||||||
for (let i = 0; i < records.length; i++) {
|
for (let i = 0; i < records.length; i++) {
|
||||||
key = records[i].enrollmentCode + "_" + records[i].majorCode + "_" + records[i].schoolCode
|
const key = records[i].enrollmentCode + "_" + records[i].majorCode + "_" + records[i].schoolCode
|
||||||
volunteer = this.filledVolunteer.volunteerMap.get(key)
|
const volunteer = this.filledVolunteer.volunteerMap.get(key)
|
||||||
if (volunteer != null) {
|
records[i].volunteerIndexs = volunteer ? volunteer.indexs : ''
|
||||||
//这个专业已填过
|
|
||||||
records[i].volunteerIndexs = volunteer.indexs
|
|
||||||
} else {
|
|
||||||
records[i].volunteerIndexs = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.recommendVolunteerList = [...this.recommendVolunteerList, ...records]
|
this.recommendVolunteerList = [...this.recommendVolunteerList, ...records]
|
||||||
if (result.pageList.current >= result.pageList.pages) {
|
|
||||||
this.status = 'noMore'
|
// 更新加载状态
|
||||||
} else {
|
this.status = result.pageList.current >= result.pageList.pages ? 'noMore' : 'more'
|
||||||
this.status = 'more'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改分页参数
|
// 修改分页参数
|
||||||
this.page.total = result.pageList.total
|
this.page.total = result.pageList.total
|
||||||
this.page.current = result.pageList.current
|
this.page.current = result.pageList.current
|
||||||
|
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
console.error('获取推荐志愿列表失败:', err)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -653,19 +526,12 @@ export default {
|
||||||
getOtherMajorList() {
|
getOtherMajorList() {
|
||||||
this.otherMajor.list = []
|
this.otherMajor.list = []
|
||||||
let e = this.otherMajor.nowMajor
|
let e = this.otherMajor.nowMajor
|
||||||
|
|
||||||
// 判断当前第一个专业是否已选中
|
// 判断当前第一个专业是否已选中
|
||||||
if (this.filledVolunteer.volunteerMap) {
|
if (this.filledVolunteer.volunteerMap && e) {
|
||||||
let key = ''
|
const key = e.enrollmentCode + "_" + e.majorCode + "_" + e.schoolCode
|
||||||
let volunteer = {}
|
const volunteer = this.filledVolunteer.volunteerMap.get(key)
|
||||||
if (e) {
|
this.otherMajor.nowMajor.volunteerIndexs = volunteer ? volunteer.indexs : ''
|
||||||
key = e.enrollmentCode + "_" + e.majorCode + "_" + e.schoolCode
|
|
||||||
volunteer = this.filledVolunteer.volunteerMap.get(key)
|
|
||||||
if (volunteer) {
|
|
||||||
this.otherMajor.nowMajor.volunteerIndexs = volunteer.indexs
|
|
||||||
} else {
|
|
||||||
this.otherMajor.nowMajor.volunteerIndexs = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组装查询参数
|
// 组装查询参数
|
||||||
|
|
@ -676,39 +542,32 @@ export default {
|
||||||
professionalCategory: this.scoreInfo.professionalCategory,
|
professionalCategory: this.scoreInfo.professionalCategory,
|
||||||
cognitioPolyclinic: this.scoreInfo.cognitioPolyclinic
|
cognitioPolyclinic: this.scoreInfo.cognitioPolyclinic
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前选中的专业 key
|
// 当前选中的专业 key
|
||||||
let eKey = e.enrollmentCode + "_" + e.majorCode + "_" + e.schoolCode
|
const eKey = e.enrollmentCode + "_" + e.majorCode + "_" + e.schoolCode
|
||||||
this.otherMajor.status = 'loading'
|
this.otherMajor.status = 'loading'
|
||||||
|
|
||||||
app.globalData.Request.get(app.globalData.ApiConstant.Major.recommendSchoolOtherMajor, params, {showLoad: false}).then(res => {
|
app.globalData.Request.get(app.globalData.ApiConstant.Major.recommendSchoolOtherMajor, params, {showLoad: false}).then(res => {
|
||||||
console.log('返回值')
|
console.log('返回值', res)
|
||||||
console.log(res)
|
|
||||||
if (res.success && res.result) {
|
if (res.success && res.result) {
|
||||||
let records = res.result.list
|
let records = res.result.list
|
||||||
|
|
||||||
// 判断专业是否已经填报过
|
// 判断专业是否已经填报过
|
||||||
if (this.filledVolunteer.volunteerMap) {
|
if (this.filledVolunteer.volunteerMap && records) {
|
||||||
let key = ''
|
const list = []
|
||||||
let volunteer = {}
|
|
||||||
let list = []
|
|
||||||
if (records) {
|
|
||||||
for (let i = 0; i < records.length; i++) {
|
for (let i = 0; i < records.length; i++) {
|
||||||
key = records[i].enrollmentCode + "_" + records[i].majorCode + "_" + records[i].schoolCode
|
const key = records[i].enrollmentCode + "_" + records[i].majorCode + "_" + records[i].schoolCode
|
||||||
if (eKey === key) {
|
if (eKey === key) continue
|
||||||
continue
|
|
||||||
}
|
const volunteer = this.filledVolunteer.volunteerMap.get(key)
|
||||||
volunteer = this.filledVolunteer.volunteerMap.get(key)
|
records[i].volunteerIndexs = volunteer ? volunteer.indexs : ''
|
||||||
if (volunteer != null) {
|
|
||||||
//这个专业已填过
|
|
||||||
records[i].volunteerIndexs = volunteer.indexs
|
|
||||||
} else {
|
|
||||||
records[i].volunteerIndexs = ''
|
|
||||||
}
|
|
||||||
list.push(records[i])
|
list.push(records[i])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
this.otherMajor.list = [...this.otherMajor.list, ...list]
|
this.otherMajor.list = [...this.otherMajor.list, ...list]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
console.error('获取其他专业失败:', err)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.otherMajor.status = 'noMore'
|
this.otherMajor.status = 'noMore'
|
||||||
});
|
});
|
||||||
|
|
@ -737,10 +596,10 @@ export default {
|
||||||
confirmColor: 'red',
|
confirmColor: 'red',
|
||||||
//取消字体的颜色
|
//取消字体的颜色
|
||||||
cancelColor: '#dddddd',
|
cancelColor: '#dddddd',
|
||||||
success: function (res) {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
// 执行确认后的操作
|
// 执行确认后的操作
|
||||||
that.goto('/pages/zyb/vip/index')
|
this.goto('/pages/zyb/vip/index')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -757,6 +616,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<view class="container">
|
||||||
<view class="divider"/>
|
<view class="divider"/>
|
||||||
<view class="backcolorwhite">
|
<view class="backcolorwhite">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
|
|
@ -771,7 +631,7 @@ export default {
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<le-dropdown
|
<le-dropdown
|
||||||
v-model:menuList="menuList"
|
:menuList="menuList"
|
||||||
themeColor="#3d76fd"
|
themeColor="#3d76fd"
|
||||||
:duration="200"
|
:duration="200"
|
||||||
:isCeiling="true"
|
:isCeiling="true"
|
||||||
|
|
@ -832,7 +692,7 @@ export default {
|
||||||
<view class="uni-flex uni-row flex border-top" style="padding: 20rpx 0 10rpx 0">
|
<view class="uni-flex uni-row flex border-top" style="padding: 20rpx 0 10rpx 0">
|
||||||
<view class="flex-item-05"></view>
|
<view class="flex-item-05"></view>
|
||||||
<view class="flex-item-2" v-if="num.nlq!==0" @click="switchPane('难录取')">
|
<view class="flex-item-2" v-if="num.nlq!==0" @click="switchPane('难录取')">
|
||||||
<l-circle v-model:current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2)?circle.nlq:0" size="130rpx"
|
<l-circle :current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2)?circle.nlq:0" size="130rpx"
|
||||||
strokeColor="#c83428">
|
strokeColor="#c83428">
|
||||||
<view class="centerTxt">
|
<view class="centerTxt">
|
||||||
<view class="num">
|
<view class="num">
|
||||||
|
|
@ -844,7 +704,7 @@ export default {
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-item-05" v-if="num.kcj!==0"></view>
|
<view class="flex-item-05" v-if="num.kcj!==0"></view>
|
||||||
<view class="flex-item-2" v-if="num.kcj!==0" @click="switchPane('可冲击')">
|
<view class="flex-item-2" v-if="num.kcj!==0" @click="switchPane('可冲击')">
|
||||||
<l-circle v-model:current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2)?circle.kcj:0" size="130rpx"
|
<l-circle :current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2)?circle.kcj:0" size="130rpx"
|
||||||
strokeColor="#F8880E">
|
strokeColor="#F8880E">
|
||||||
<view class="centerTxt">
|
<view class="centerTxt">
|
||||||
<view class="num">
|
<view class="num">
|
||||||
|
|
@ -856,7 +716,7 @@ export default {
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-item-05" v-if="num.jwt!==0"></view>
|
<view class="flex-item-05" v-if="num.jwt!==0"></view>
|
||||||
<view class="flex-item-2" v-if="num.jwt!==0" @click="switchPane('较稳妥')">
|
<view class="flex-item-2" v-if="num.jwt!==0" @click="switchPane('较稳妥')">
|
||||||
<l-circle v-model:current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2) ?circle.jwt:0" size="130rpx"
|
<l-circle :current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2) ?circle.jwt:0" size="130rpx"
|
||||||
strokeColor="#4975fd">
|
strokeColor="#4975fd">
|
||||||
<view class="centerTxt">
|
<view class="centerTxt">
|
||||||
<view class="num">
|
<view class="num">
|
||||||
|
|
@ -868,7 +728,7 @@ export default {
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-item-05" v-if="num.kbd!==0"></view>
|
<view class="flex-item-05" v-if="num.kbd!==0"></view>
|
||||||
<view class="flex-item-2" v-if="num.kbd!==0" @click="switchPane('可保底')">
|
<view class="flex-item-2" v-if="num.kbd!==0" @click="switchPane('可保底')">
|
||||||
<l-circle v-model:current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2) ?circle.kbd:0" size="130rpx"
|
<l-circle :current="modelVale" :percent="(vipInfo && vipInfo.vipLevel>=2) ?circle.kbd:0" size="130rpx"
|
||||||
strokeColor="#3e8e43">
|
strokeColor="#3e8e43">
|
||||||
<view class="centerTxt">
|
<view class="centerTxt">
|
||||||
<view class="num">
|
<view class="num">
|
||||||
|
|
@ -1149,6 +1009,7 @@ export default {
|
||||||
<view class="top" :style="{'display':(topFlag===true? 'block':'none')}">
|
<view class="top" :style="{'display':(topFlag===true? 'block':'none')}">
|
||||||
<uni-icons class="topc" type="arrowthinup" size="40" @click="clickTop"></uni-icons>
|
<uni-icons class="topc" type="arrowthinup" size="40" @click="clickTop"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
|
||||||
|
|
@ -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,12 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// #ifdef UNI-APP-X && APP
|
||||||
|
// export * from './uvue.uts'
|
||||||
|
export { Timeline, Animation } from './uvue.uts'
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// #ifndef UNI-APP-X && APP
|
||||||
|
// export * from './vue.ts'
|
||||||
|
export { Timeline, Animation } from './vue.ts'
|
||||||
|
// #endif
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import type { ComponentPublicInstance, Ref } from 'vue'
|
||||||
|
import { ease, linear } from './ease';
|
||||||
|
import { Timeline, Animation } from './';
|
||||||
|
export type UseTransitionOptions = {
|
||||||
|
duration ?: number
|
||||||
|
immediate ?: boolean
|
||||||
|
context ?: ComponentPublicInstance
|
||||||
|
}
|
||||||
|
// #ifndef UNI-APP-X && APP
|
||||||
|
import { ref, watch } from '@/uni_modules/lime-shared/vue'
|
||||||
|
|
||||||
|
export function useTransition(percent : Ref<number>|(() => number), options : UseTransitionOptions) : Ref<number> {
|
||||||
|
const current = ref(0)
|
||||||
|
const { immediate, duration = 300 } = options
|
||||||
|
let tl:Timeline|null = null;
|
||||||
|
let timer = -1
|
||||||
|
const isFunction = typeof percent === 'function'
|
||||||
|
watch(isFunction ? percent : () => percent.value, (v) => {
|
||||||
|
if(tl == null){
|
||||||
|
tl = new Timeline()
|
||||||
|
}
|
||||||
|
tl.start();
|
||||||
|
tl.add(
|
||||||
|
new Animation(
|
||||||
|
current.value,
|
||||||
|
v,
|
||||||
|
duration,
|
||||||
|
0,
|
||||||
|
ease,
|
||||||
|
nowValue => {
|
||||||
|
current.value = nowValue
|
||||||
|
clearTimeout(timer)
|
||||||
|
if(current.value == v){
|
||||||
|
timer = setTimeout(()=>{
|
||||||
|
tl?.pause();
|
||||||
|
tl = null
|
||||||
|
}, duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, { immediate })
|
||||||
|
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef UNI-APP-X && APP
|
||||||
|
type UseTransitionReturnType = Ref<number>
|
||||||
|
export function useTransition(source : any, options : UseTransitionOptions) : UseTransitionReturnType {
|
||||||
|
const outputRef : Ref<number> = ref(0)
|
||||||
|
const immediate = options.immediate ?? false
|
||||||
|
const duration = options.duration ?? 300
|
||||||
|
const context = options.context //as ComponentPublicInstance | null
|
||||||
|
let tl:Timeline|null = null;
|
||||||
|
let timer = -1
|
||||||
|
const watchFunc = (v : number) => {
|
||||||
|
if(tl == null){
|
||||||
|
tl = new Timeline()
|
||||||
|
}
|
||||||
|
tl!.start();
|
||||||
|
tl!.add(
|
||||||
|
new Animation(
|
||||||
|
outputRef.value,
|
||||||
|
v,
|
||||||
|
duration,
|
||||||
|
0,
|
||||||
|
ease,
|
||||||
|
nowValue => {
|
||||||
|
outputRef.value = nowValue
|
||||||
|
clearTimeout(timer)
|
||||||
|
if(outputRef.value == v){
|
||||||
|
timer = setTimeout(()=>{
|
||||||
|
tl?.pause();
|
||||||
|
tl = null
|
||||||
|
}, duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context != null && typeof source == 'string') {
|
||||||
|
context.$watch(source, watchFunc, { immediate } as WatchOptions)
|
||||||
|
} else if(typeof source == 'function'){
|
||||||
|
watch(source, watchFunc, { immediate })
|
||||||
|
}
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
else if(isRef(source) && source instanceof Ref<number>) {
|
||||||
|
watch(source as Ref<number>, watchFunc, { immediate })
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
else if(isRef(source) && typeof source.value == 'number') {
|
||||||
|
watch(source, watchFunc, { immediate })
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
const stop = ()=>{
|
||||||
|
|
||||||
|
}
|
||||||
|
return outputRef //as UseTransitionReturnType
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { raf, cancelRaf} from '../raf'
|
||||||
|
export class Timeline {
|
||||||
|
state : string
|
||||||
|
animations : Set<Animation> = new Set<Animation>()
|
||||||
|
delAnimations : Animation[] = []
|
||||||
|
startTimes : Map<Animation, number> = new Map<Animation, number>()
|
||||||
|
pauseTime : number = 0
|
||||||
|
pauseStart : number = Date.now()
|
||||||
|
tickHandler : number = 0
|
||||||
|
tickHandlers : number[] = []
|
||||||
|
tick : (() => void) | null = null
|
||||||
|
constructor() {
|
||||||
|
this.state = 'Initiated';
|
||||||
|
}
|
||||||
|
start() {
|
||||||
|
if (!(this.state == 'Initiated')) return;
|
||||||
|
this.state = 'Started';
|
||||||
|
|
||||||
|
let startTime = Date.now();
|
||||||
|
this.pauseTime = 0;
|
||||||
|
this.tick = () => {
|
||||||
|
let now = Date.now();
|
||||||
|
this.animations.forEach((animation : Animation) => {
|
||||||
|
let t:number;
|
||||||
|
const ani = this.startTimes.get(animation)
|
||||||
|
if (ani == null) return
|
||||||
|
if (ani < startTime) {
|
||||||
|
t = now - startTime - animation.delay - this.pauseTime;
|
||||||
|
} else {
|
||||||
|
t = now - ani - animation.delay - this.pauseTime;
|
||||||
|
}
|
||||||
|
if (t > animation.duration) {
|
||||||
|
this.delAnimations.push(animation)
|
||||||
|
// 不能在 foreach 里面 对 集合进行删除操作
|
||||||
|
// this.animations.delete(animation);
|
||||||
|
t = animation.duration;
|
||||||
|
}
|
||||||
|
if (t > 0) animation.run(t);
|
||||||
|
})
|
||||||
|
// 不能在 foreach 里面 对 集合进行删除操作
|
||||||
|
while (this.delAnimations.length > 0) {
|
||||||
|
const animation = this.delAnimations.pop();
|
||||||
|
if (animation == null) return
|
||||||
|
this.animations.delete(animation);
|
||||||
|
}
|
||||||
|
// cancelAnimationFrame(this.tickHandler);
|
||||||
|
if (this.state != 'Started') return
|
||||||
|
|
||||||
|
this.tickHandler = raf(()=>{
|
||||||
|
this.tick!()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.tickHandlers.push(this.tickHandler)
|
||||||
|
}
|
||||||
|
if(this.tick != null) {
|
||||||
|
this.tick!()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
pause() {
|
||||||
|
if (!(this.state === 'Started')) return;
|
||||||
|
this.state = 'Paused';
|
||||||
|
this.pauseStart = Date.now();
|
||||||
|
cancelRaf(this.tickHandler);
|
||||||
|
// cancelRaf(this.tickHandler);
|
||||||
|
}
|
||||||
|
resume() {
|
||||||
|
if (!(this.state === 'Paused')) return;
|
||||||
|
this.state = 'Started';
|
||||||
|
this.pauseTime += Date.now() - this.pauseStart;
|
||||||
|
this.tick!();
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.pause();
|
||||||
|
this.state = 'Initiated';
|
||||||
|
this.pauseTime = 0;
|
||||||
|
this.pauseStart = 0;
|
||||||
|
this.animations.clear()
|
||||||
|
this.delAnimations.clear()
|
||||||
|
this.startTimes.clear()
|
||||||
|
this.tickHandler = 0;
|
||||||
|
}
|
||||||
|
add(animation : Animation, startTime ?: number | null) {
|
||||||
|
if (startTime == null) startTime = Date.now();
|
||||||
|
this.animations.add(animation);
|
||||||
|
this.startTimes.set(animation, startTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Animation {
|
||||||
|
startValue : number
|
||||||
|
endValue : number
|
||||||
|
duration : number
|
||||||
|
timingFunction : (t : number) => number
|
||||||
|
delay : number
|
||||||
|
template : (t : number) => void
|
||||||
|
constructor(
|
||||||
|
startValue : number,
|
||||||
|
endValue : number,
|
||||||
|
duration : number,
|
||||||
|
delay : number,
|
||||||
|
timingFunction : (t : number) => number,
|
||||||
|
template : (v : number) => void) {
|
||||||
|
this.startValue = startValue;
|
||||||
|
this.endValue = endValue;
|
||||||
|
this.duration = duration;
|
||||||
|
this.timingFunction = timingFunction;
|
||||||
|
this.delay = delay;
|
||||||
|
this.template = template;
|
||||||
|
}
|
||||||
|
|
||||||
|
run(time : number) {
|
||||||
|
let range = this.endValue - this.startValue;
|
||||||
|
let progress = time / this.duration
|
||||||
|
if(progress != 1) progress = this.timingFunction(progress)
|
||||||
|
this.template(this.startValue + range * progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,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,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,111 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { isString } from "../isString";
|
||||||
|
import { isNumber } from "../isNumber";
|
||||||
|
/**
|
||||||
|
* 将金额转换为中文大写形式
|
||||||
|
* @param {number | string} amount - 需要转换的金额,可以是数字或字符串
|
||||||
|
* @returns {string} 转换后的中文大写金额
|
||||||
|
*/
|
||||||
|
export function capitalizedAmount(amount : number) : string
|
||||||
|
export function capitalizedAmount(amount : string) : string
|
||||||
|
export function capitalizedAmount(amount : any | null) : string {
|
||||||
|
try {
|
||||||
|
let _amountStr :string;
|
||||||
|
let _amountNum :number = 0;
|
||||||
|
// 如果输入是字符串,先将其转换为数字,并去除逗号
|
||||||
|
if (typeof amount == 'string') {
|
||||||
|
_amountNum = parseFloat((amount as string).replace(/,/g, ''));
|
||||||
|
}
|
||||||
|
if(isNumber(amount)) {
|
||||||
|
_amountNum = amount as number
|
||||||
|
}
|
||||||
|
// 判断输入是否为有效的金额 || isNaN(amount)
|
||||||
|
if (amount == null) throw new Error('不是有效的金额!');
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
// 处理负数情况
|
||||||
|
if (_amountNum < 0) {
|
||||||
|
result = '欠';
|
||||||
|
_amountNum = Math.abs(_amountNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 金额不能超过千亿以上
|
||||||
|
if (_amountNum >= 10e11) throw new Error('计算金额过大!');
|
||||||
|
|
||||||
|
// 保留两位小数并转换为字符串
|
||||||
|
_amountStr = _amountNum.toFixed(2);
|
||||||
|
|
||||||
|
// 定义数字、单位和小数单位的映射
|
||||||
|
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||||
|
const units = ['', '拾', '佰', '仟'];
|
||||||
|
const bigUnits = ['', '万', '亿'];
|
||||||
|
const decimalUnits = ['角', '分'];
|
||||||
|
|
||||||
|
// 分离整数部分和小数部分
|
||||||
|
const amountArray = _amountStr.split('.');
|
||||||
|
let integerPart = amountArray[0]; // string| number[]
|
||||||
|
const decimalPart = amountArray[1];
|
||||||
|
|
||||||
|
// 处理整数部分
|
||||||
|
if (integerPart != '0') {
|
||||||
|
let _integerPart = integerPart.split('').map((item):number => parseInt(item));
|
||||||
|
|
||||||
|
// 将整数部分按四位一级进行分组
|
||||||
|
const levels = _integerPart.reverse().reduce((prev:string[][], item, index):string[][] => {
|
||||||
|
// const level = prev?.[0]?.length < 4 ? prev[0] : [];
|
||||||
|
const level = prev.length > 0 && prev[0].length < 4 ? prev[0]: []
|
||||||
|
|
||||||
|
const value = item == 0 ? digits[item] : digits[item] + units[index % 4];
|
||||||
|
|
||||||
|
level.unshift(value);
|
||||||
|
|
||||||
|
if (level.length == 1) {
|
||||||
|
prev.unshift(level);
|
||||||
|
} else {
|
||||||
|
prev[0] = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev;
|
||||||
|
}, [] as string[][]);
|
||||||
|
// 将分组后的整数部分转换为中文大写形式
|
||||||
|
result += levels.reduce((prev, item, index):string => {
|
||||||
|
let _level = bigUnits[levels.length - index - 1];
|
||||||
|
let _item = item.join('').replace(/(零)\1+/g, '$1');
|
||||||
|
|
||||||
|
if (_item == '零') {
|
||||||
|
_level = '';
|
||||||
|
_item = '';
|
||||||
|
} else if (_item.endsWith('零')) {
|
||||||
|
_item = _item.slice(0, _item.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev + _item + _level;
|
||||||
|
}, '');
|
||||||
|
} else {
|
||||||
|
result += '零';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加元
|
||||||
|
result += '元';
|
||||||
|
|
||||||
|
// 处理小数部分
|
||||||
|
if (decimalPart != '00') {
|
||||||
|
if (result == '零元') result = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < decimalPart.length; i++) {
|
||||||
|
const digit = parseInt(decimalPart.charAt(i));
|
||||||
|
|
||||||
|
if (digit != 0) {
|
||||||
|
result += digits[digit] + decimalUnits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result += '整';
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error : Error) {
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -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,53 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
// #ifdef UNI-APP-X && APP
|
||||||
|
import { isNumber } from '../isNumber'
|
||||||
|
import { isString } from '../isString'
|
||||||
|
import { isDef } from '../isDef'
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对象的类名字符串
|
||||||
|
* @param obj - 需要处理的对象
|
||||||
|
* @returns 由对象属性作为类名组成的字符串
|
||||||
|
*/
|
||||||
|
export function classNames<T>(obj : T) : string {
|
||||||
|
let classNames : string[] = [];
|
||||||
|
// #ifdef APP-ANDROID || APP-HARMONY
|
||||||
|
if (obj instanceof UTSJSONObject) {
|
||||||
|
(obj as UTSJSONObject).toMap().forEach((value, key) => {
|
||||||
|
if (isDef(value)) {
|
||||||
|
if (isNumber(value)) {
|
||||||
|
classNames.push(key);
|
||||||
|
}
|
||||||
|
if (isString(value) && value !== '') {
|
||||||
|
classNames.push(key);
|
||||||
|
}
|
||||||
|
if (typeof value == 'boolean' && (value as boolean)) {
|
||||||
|
classNames.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-ANDROID || APP-HARMONY
|
||||||
|
// 遍历对象的属性
|
||||||
|
for (let key in obj) {
|
||||||
|
// 检查属性确实属于对象自身且其值为true
|
||||||
|
if ((obj as any).hasOwnProperty(key) && obj[key]) {
|
||||||
|
// 将属性名添加到类名数组中
|
||||||
|
classNames.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
// 将类名数组用空格连接成字符串并返回
|
||||||
|
return classNames.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 示例
|
||||||
|
// const obj = { foo: true, bar: false, baz: true };
|
||||||
|
// const classNameStr = stringify(obj);
|
||||||
|
// console.log(classNameStr); // 输出: "foo baz"
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
/**
|
||||||
|
* 深度克隆一个对象或数组
|
||||||
|
* @param obj 要克隆的对象或数组
|
||||||
|
* @returns 克隆后的对象或数组
|
||||||
|
*/
|
||||||
|
export function cloneDeep<T>(obj : any) : T {
|
||||||
|
if (obj instanceof Set) {
|
||||||
|
const set = new Set<any>();
|
||||||
|
obj.forEach((item : any) => {
|
||||||
|
set.add(item)
|
||||||
|
})
|
||||||
|
return set as T;
|
||||||
|
}
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
const map = new Map<any, any>();
|
||||||
|
obj.forEach((value : any, key : any) => {
|
||||||
|
map.set(key, value)
|
||||||
|
})
|
||||||
|
return map as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof RegExp) {
|
||||||
|
return new RegExp(obj) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return (obj as any[]).map((item : any):any => item) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof Date) {
|
||||||
|
return new Date(obj.getTime()) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj == 'object') {
|
||||||
|
return UTSJSONObject.assign<T>({}, toRaw(obj))!
|
||||||
|
}
|
||||||
|
return obj as T
|
||||||
|
}
|
||||||
|
|
@ -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,407 @@
|
||||||
|
<template>
|
||||||
|
<view id="shared" style="height: 500px; width: 300px; background-color: aqua;">
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
// #ifdef WEB
|
||||||
|
import validator from 'validator'
|
||||||
|
// #endif
|
||||||
|
import { getRect, getAllRect } from '@/uni_modules/lime-shared/getRect'
|
||||||
|
|
||||||
|
import { camelCase } from '@/uni_modules/lime-shared/camelCase'
|
||||||
|
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
|
||||||
|
import { clamp } from '@/uni_modules/lime-shared/clamp'
|
||||||
|
import { cloneDeep } from '@/uni_modules/lime-shared/cloneDeep'
|
||||||
|
import { closest } from '@/uni_modules/lime-shared/closest'
|
||||||
|
import { debounce } from '@/uni_modules/lime-shared/debounce'
|
||||||
|
import { fillZero } from '@/uni_modules/lime-shared/fillZero'
|
||||||
|
import { floatAdd } from '@/uni_modules/lime-shared/floatAdd'
|
||||||
|
import { floatMul } from '@/uni_modules/lime-shared/floatMul'
|
||||||
|
import { floatDiv } from '@/uni_modules/lime-shared/floatDiv'
|
||||||
|
import { floatSub } from '@/uni_modules/lime-shared/floatSub'
|
||||||
|
import { getClassStr } from '@/uni_modules/lime-shared/getClassStr'
|
||||||
|
import { getCurrentPage } from '@/uni_modules/lime-shared/getCurrentPage'
|
||||||
|
import { getStyleStr } from '@/uni_modules/lime-shared/getStyleStr'
|
||||||
|
import { hasOwn } from '@/uni_modules/lime-shared/hasOwn'
|
||||||
|
import { isBase64 } from '@/uni_modules/lime-shared/isBase64'
|
||||||
|
import { isBrowser } from '@/uni_modules/lime-shared/isBrowser'
|
||||||
|
import { isDef } from '@/uni_modules/lime-shared/isDef'
|
||||||
|
import { isEmpty } from '@/uni_modules/lime-shared/isEmpty'
|
||||||
|
import { isFunction } from '@/uni_modules/lime-shared/isFunction'
|
||||||
|
import { isNumber } from '@/uni_modules/lime-shared/isNumber'
|
||||||
|
import { isNumeric } from '@/uni_modules/lime-shared/isNumeric'
|
||||||
|
import { isObject } from '@/uni_modules/lime-shared/isObject'
|
||||||
|
import { isPromise } from '@/uni_modules/lime-shared/isPromise'
|
||||||
|
import { isString } from '@/uni_modules/lime-shared/isString'
|
||||||
|
import { kebabCase } from '@/uni_modules/lime-shared/kebabCase'
|
||||||
|
import { raf, doubleRaf } from '@/uni_modules/lime-shared/raf'
|
||||||
|
import { random } from '@/uni_modules/lime-shared/random'
|
||||||
|
import { range } from '@/uni_modules/lime-shared/range'
|
||||||
|
import { sleep } from '@/uni_modules/lime-shared/sleep'
|
||||||
|
import { throttle } from '@/uni_modules/lime-shared/throttle'
|
||||||
|
import { toArray } from '@/uni_modules/lime-shared/toArray'
|
||||||
|
import { toBoolean } from '@/uni_modules/lime-shared/toBoolean'
|
||||||
|
import { toNumber } from '@/uni_modules/lime-shared/toNumber'
|
||||||
|
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||||
|
import { getCurrentInstance } from '@/uni_modules/lime-shared/vue'
|
||||||
|
import { capitalizedAmount } from '@/uni_modules/lime-shared/capitalizedAmount'
|
||||||
|
|
||||||
|
import { obj2url } from '@/uni_modules/lime-shared/obj2url'
|
||||||
|
import { isURL, type IsURLOptions } from '@/uni_modules/lime-shared/isURL'
|
||||||
|
import { isIP } from '@/uni_modules/lime-shared/isIP'
|
||||||
|
import { isDate, type IsDateOptions } from '@/uni_modules/lime-shared/isDate'
|
||||||
|
import { isEmail } from '@/uni_modules/lime-shared/isEmail'
|
||||||
|
import { isRegExp } from '@/uni_modules/lime-shared/isRegExp'
|
||||||
|
import { isValidDomain, type IsValidDomainOptions } from '@/uni_modules/lime-shared/isValidDomain'
|
||||||
|
import { merge } from '@/uni_modules/lime-shared/merge'
|
||||||
|
import { isByteLength, type IsByteLengthOptions } from '@/uni_modules/lime-shared/isByteLength'
|
||||||
|
|
||||||
|
// #ifdef VUE2
|
||||||
|
type UTSJSONObject = any
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
const context = getCurrentInstance()
|
||||||
|
// getRect('#shared', context!).then(res =>{
|
||||||
|
// console.log('res', res.bottom)
|
||||||
|
// })
|
||||||
|
// getAllRect('#shared', context).then(res =>{
|
||||||
|
// console.log('res', res)
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
// console.log('camelCase::', camelCase("hello world"));
|
||||||
|
// console.log('camelCase::', camelCase("my_name_is_john", true));
|
||||||
|
// console.log('canIUseCanvas2d::', canIUseCanvas2d());
|
||||||
|
// console.log('clamp::', clamp(5 ,0, 10));
|
||||||
|
// console.log('cloneDeep::', cloneDeep<UTSJSONObject>({a:5}));
|
||||||
|
// console.log('closest::', closest([1, 3, 5, 7, 9], 6));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// const saveData = (data: any) => {
|
||||||
|
// // 模拟保存数据的操作
|
||||||
|
// console.log(`Saving data: ${data}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const debouncedSaveData = debounce(saveData, 500);
|
||||||
|
// debouncedSaveData('Data 1');
|
||||||
|
// debouncedSaveData('Data 2');
|
||||||
|
|
||||||
|
// console.log('fillZero', fillZero(1))
|
||||||
|
// console.log('floatAdd', floatAdd(0.1, 0.2), floatAdd(1.05, 0.05), floatAdd(0.1, 0.7), floatAdd(0.0001, 0.0002), floatAdd(123.456 , 789.012))
|
||||||
|
// console.log('floatMul', floatMul(0.1, 0.02), floatMul(1.0255, 100))
|
||||||
|
// console.log('floatDiv', floatDiv(10.44, 100), floatDiv(1.0255, 100), floatDiv(5.419909340994699, 0.2))
|
||||||
|
// console.log('floatSub', floatSub(0.4, 0.1), floatSub(1.0255, 100))
|
||||||
|
// const now = () : number => System.nanoTime() / 1_000_000.0
|
||||||
|
// console.log('capitalizedAmount', capitalizedAmount(0.4))
|
||||||
|
// console.log('capitalizedAmount', capitalizedAmount(100))
|
||||||
|
// console.log('capitalizedAmount', capitalizedAmount(100000000))
|
||||||
|
// console.log('capitalizedAmount', capitalizedAmount('2023.04'))
|
||||||
|
// console.log('capitalizedAmount', capitalizedAmount(-1024))
|
||||||
|
// console.log('now', now(), Date.now())
|
||||||
|
// console.log('getClassStr', getClassStr({hover: true}))
|
||||||
|
// console.log('getStyleStr', getStyleStr({ color: 'red', fontSize: '16px', backgroundColor: '', border: null }))
|
||||||
|
// console.log('hasOwn', hasOwn({a: true}, 'key'))
|
||||||
|
// console.log('isBase64::', isBase64("SGVsbG8sIFdvcmxkIQ=="));
|
||||||
|
// console.log('isBrowser::', isBrowser);
|
||||||
|
// console.log('isDef::', isDef('6'));
|
||||||
|
// console.log('isEmpty::', isEmpty({a: true}));
|
||||||
|
|
||||||
|
// const b = () =>{}
|
||||||
|
// console.log('isFunction::', isFunction(b));
|
||||||
|
// console.log('isNumber::', isNumber('6'));
|
||||||
|
// console.log('isNumeric::', isNumeric('6'));
|
||||||
|
// console.log('isObject::', isObject({}));
|
||||||
|
|
||||||
|
// const promise = ():Promise<boolean> => {
|
||||||
|
// return new Promise((resolve) => {
|
||||||
|
// resolve(true)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// const a = promise()
|
||||||
|
// console.log('isPromise::', isPromise(a));
|
||||||
|
// console.log('isString::', isString('null'));
|
||||||
|
// console.log('kebabCase::', kebabCase('my love'));
|
||||||
|
// console.log('raf::', raf(()=>{
|
||||||
|
// console.log('raf:::1')
|
||||||
|
// }));
|
||||||
|
// console.log('doubleRaf::', doubleRaf(()=>{
|
||||||
|
// console.log('doubleRaf:::1')
|
||||||
|
// }));
|
||||||
|
// console.log('random', random(0, 10))
|
||||||
|
// console.log('random', random(0, 1, 2))
|
||||||
|
// console.log('range', range(0, 10, 2))
|
||||||
|
// console.log('sleep', sleep(300).then(res => {
|
||||||
|
// console.log('log')
|
||||||
|
// }))
|
||||||
|
|
||||||
|
// const handleScroll = (a: string) => {
|
||||||
|
// console.log("Scroll event handled!", a);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒
|
||||||
|
// const throttledScroll = throttle(handleScroll, 500);
|
||||||
|
// throttledScroll('5');
|
||||||
|
// const page = getCurrentPage()
|
||||||
|
// console.log('getCurrentPage::', page)
|
||||||
|
|
||||||
|
// console.log('toArray', toArray<number>(5))
|
||||||
|
// console.log('toBoolean', toBoolean(5))
|
||||||
|
// console.log('toNumber', toNumber('5'))
|
||||||
|
// console.log('unitConvert', unitConvert('5'))
|
||||||
|
|
||||||
|
// uni.getImageInfo({
|
||||||
|
// src: '/static/logo.png',
|
||||||
|
// success(res) {
|
||||||
|
// console.log('res', res)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
// --------------------------
|
||||||
|
// IPv4 验证示例
|
||||||
|
// --------------------------
|
||||||
|
|
||||||
|
// 标准IPv4格式
|
||||||
|
// console.log(isIP('192.168.1.1', 4)); // true
|
||||||
|
// console.log(isIP('255.255.255.255', { version: 4 })); // true
|
||||||
|
|
||||||
|
// // 边界值验证
|
||||||
|
// console.log(isIP('0.0.0.0', 4)); // true
|
||||||
|
// console.log(isIP('223.255.255.255', '4')); // true
|
||||||
|
|
||||||
|
// // 非法IPv4案例
|
||||||
|
// console.log(isIP('256.400.999.1', 4)); // false(数值超限)
|
||||||
|
// console.log(isIP('192.168.01', 4)); // false(段数不足)
|
||||||
|
|
||||||
|
// // --------------------------
|
||||||
|
// // IPv6 验证示例
|
||||||
|
// // --------------------------
|
||||||
|
|
||||||
|
// // 标准IPv6格式
|
||||||
|
// console.log(isIP('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 6)); // true
|
||||||
|
// console.log(isIP('fe80::1%eth0', { version: 6 })); // true(带区域标识)
|
||||||
|
|
||||||
|
// // 压缩格式验证
|
||||||
|
// console.log(isIP('2001:db8::1', '6')); // true(双冒号压缩)
|
||||||
|
// console.log(isIP('::1', 6)); // true(本地环回简写)
|
||||||
|
|
||||||
|
// // IPv4混合格式
|
||||||
|
// console.log(isIP('::ffff:192.168.1.1', 6)); // true(IPv4映射地址)
|
||||||
|
|
||||||
|
// // 非法IPv6案例
|
||||||
|
// console.log(isIP('2001::gggg::1', 6)); // false(非法字符)
|
||||||
|
// console.log(isIP('fe80::1%', 6)); // false(空区域标识)
|
||||||
|
|
||||||
|
// // --------------------------
|
||||||
|
// // 自动版本检测
|
||||||
|
// // --------------------------
|
||||||
|
|
||||||
|
// // 有效地址检测
|
||||||
|
// console.log(isIP('172.16.254.1')); // true(自动识别IPv4)
|
||||||
|
// console.log(isIP('2001:db8:3333:4444:5555:6666:7777:8888')); // true(自动识别IPv6)
|
||||||
|
|
||||||
|
// // 无效地址检测
|
||||||
|
// console.log(isIP('192.168.1.256')); // false(无效IPv4)
|
||||||
|
// console.log(isIP('2001::gggg::1')); // false(无效IPv6)
|
||||||
|
|
||||||
|
// // --------------------------
|
||||||
|
// // 特殊场景
|
||||||
|
// // --------------------------
|
||||||
|
|
||||||
|
// // 带前后空格处理
|
||||||
|
// console.log(isIP(' 203.0.113.50 ', 4)); // true(自动trim)
|
||||||
|
// console.log(isIP(' fe80::1%1 ', 6)); // true(自动trim)
|
||||||
|
|
||||||
|
// // 非法版本指定
|
||||||
|
// console.log(isIP('192.168.1.1', 5)); // false(不存在IPv5)
|
||||||
|
// console.log(isIP('2001:db8::1', 'ipv6')); // false(版本参数格式错误)
|
||||||
|
|
||||||
|
// // --------------------------
|
||||||
|
// // 边界案例
|
||||||
|
// // --------------------------
|
||||||
|
|
||||||
|
// // 最小/最大有效值
|
||||||
|
// console.log(isIP('0.0.0.0', 4)); // true
|
||||||
|
// console.log(isIP('255.255.255.255', 4)); // true
|
||||||
|
// console.log(isIP('0000:0000:0000:0000:0000:0000:0000:0000', 6)); // true
|
||||||
|
|
||||||
|
// // 超长地址验证
|
||||||
|
// console.log(isIP('192.168.1.1.1', 4)); // false(IPv4段数过多)
|
||||||
|
// console.log(isIP('2001:db8:1:2:3:4:5:6:7', 6)); // false(IPv6段数过多)
|
||||||
|
|
||||||
|
// const original = { color: 'red' };
|
||||||
|
// const merged = merge({ ...original }, { color: 'blue', size: 'M' });
|
||||||
|
|
||||||
|
// console.log('original', original); // 输出: { color: 'red' } (保持不变)
|
||||||
|
// console.log('merged', merged); // 输出: { color: 'red', size: 'M' }
|
||||||
|
|
||||||
|
|
||||||
|
type ColorType = {
|
||||||
|
color?: string,
|
||||||
|
size?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const merged2 = merge({ color: 'red' }, { size: 'M' } as ColorType);
|
||||||
|
console.log('merged2:::', merged2)
|
||||||
|
|
||||||
|
// // 使用配置对象参数
|
||||||
|
// console.log(isByteLength('hello', { min: 3, max: 7 } as ByteLengthOptions)); // true (5字节)
|
||||||
|
// console.log(isByteLength('hello', { min: 6 } as ByteLengthOptions)); // false (5 < 6)
|
||||||
|
// console.log(isByteLength('hello', { max: 4 } as ByteLengthOptions)); // false (5 > 4)
|
||||||
|
|
||||||
|
// // 使用独立参数(旧式调用)
|
||||||
|
// console.log(isByteLength('hello', 3, 7)); // true
|
||||||
|
// console.log(isByteLength('hello', 6)); // false
|
||||||
|
// console.log(isByteLength('hello', null, 4)); // false
|
||||||
|
|
||||||
|
// =====================
|
||||||
|
// 多字节字符处理示例
|
||||||
|
// =====================
|
||||||
|
|
||||||
|
// 中文字符(UTF-8 每个汉字3字节)
|
||||||
|
// console.log(isByteLength('中国', { min: 6 })); // true (2字 × 3字节 = 6)
|
||||||
|
// console.log(isByteLength('中国', { max: 5 })); // false (6 > 5)
|
||||||
|
|
||||||
|
// // 表情符号(多数占用4字节)
|
||||||
|
// console.log(isByteLength('🌟', { min: 4, max: 4 })); // true
|
||||||
|
// console.log(isByteLength('👨👩👧👦', { max: 15 })); // false (家庭表情占25字节)
|
||||||
|
|
||||||
|
// // 混合字符集
|
||||||
|
// console.log(isByteLength('aé🌟', { min: 7 })); // true
|
||||||
|
|
||||||
|
// // URL编码字符
|
||||||
|
// console.log(isByteLength('%20', { min: 3 })); // true(实际字节长度3)
|
||||||
|
// console.log(isByteLength('%E2%82%AC', { max: 3 })); // false(欧元符号编码为3字节)
|
||||||
|
|
||||||
|
// // 构造函数创建的正则表达式
|
||||||
|
// console.log(isRegExp(new RegExp('hello'))); // true
|
||||||
|
// console.log(isRegExp(new RegExp('\\d+', 'gi'))); // true
|
||||||
|
|
||||||
|
// // 字面量正则表达式
|
||||||
|
// console.log(isRegExp(/abc/)); // true
|
||||||
|
// console.log(isRegExp(/^[0-9]+$/gi)); // true
|
||||||
|
|
||||||
|
// // 字符串(含正则格式字符串)
|
||||||
|
// console.log(isRegExp('/abc/')); // false
|
||||||
|
// console.log(isRegExp('new RegExp("abc")')); // false
|
||||||
|
|
||||||
|
// console.log(isEmail('"John"@example.com')) // false(实际有效)
|
||||||
|
// console.log(isEmail('中国@例子.中国')) // false(实际有效)
|
||||||
|
|
||||||
|
// // 简单键值对
|
||||||
|
// console.log(obj2url({ name: '张三', age: 25 }));
|
||||||
|
// // "name=%E5%BC%A0%E4%B8%89&age=25"
|
||||||
|
// // 包含布尔值
|
||||||
|
// console.log(obj2url({ active: true, admin: false }));
|
||||||
|
// // "active=true&admin=false"
|
||||||
|
|
||||||
|
// // 数字处理
|
||||||
|
// console.log(obj2url({ page: 1, limit: 10 }));
|
||||||
|
// // "page=1&limit=10"
|
||||||
|
|
||||||
|
// 基础验证
|
||||||
|
// console.log("example.com =>", isValidDomain("example.com")); // true
|
||||||
|
// console.log("sub.example.co.uk =>", isValidDomain("sub.example.co.uk")); // true
|
||||||
|
|
||||||
|
// // 缺少TLD的情况
|
||||||
|
// console.log("localhost =>", isValidDomain("localhost")); // false
|
||||||
|
// console.log("localhost (不要求TLD) =>", isValidDomain("localhost", { requireTld: false } as DomainOptions)); // true
|
||||||
|
|
||||||
|
// // 带结尾点号的情况
|
||||||
|
// console.log("example.com. =>", isValidDomain("example.com.")); // false
|
||||||
|
// console.log("example.com. (允许结尾点号) =>", isValidDomain("example.com.", { allowTrailingDot: true } as DomainOptions)); // true
|
||||||
|
|
||||||
|
// // 带下划线的情况
|
||||||
|
// console.log("my_site.com =>", isValidDomain("my_site.com")); // false
|
||||||
|
// console.log("my_site.com (允许下划线) =>", isValidDomain("my_site.com", { allowUnderscore: true } as DomainOptions)); // true
|
||||||
|
|
||||||
|
// // 非法字符测试
|
||||||
|
// console.log("含有空格的域名 =>", isValidDomain("exa mple.com")); // false
|
||||||
|
// console.log("含有!的域名 =>", isValidDomain("exa!mple.com")); // false
|
||||||
|
|
||||||
|
// // 长度测试
|
||||||
|
// const longPart = "a".repeat(64);
|
||||||
|
// console.log(`超长部分 (${longPart.length}字符) =>`, isValidDomain(`${longPart}.com`)); // false
|
||||||
|
|
||||||
|
// // 连字符测试
|
||||||
|
// console.log("以连字符开头 =>", isValidDomain("-example.com")); // false
|
||||||
|
// console.log("以连字符结尾 =>", isValidDomain("example-.com")); // false
|
||||||
|
|
||||||
|
// // 国际化域名测试
|
||||||
|
// console.log("中文域名 =>", isValidDomain("中国.中国")); // true
|
||||||
|
// console.log("日文域名 =>", isValidDomain("ドメイン.テスト")); // true
|
||||||
|
|
||||||
|
// // 基础格式验证
|
||||||
|
// console.log("1. 标准日期格式验证:");
|
||||||
|
// console.log("2023/12/31 =>", isDate("2023/12/31")); // true
|
||||||
|
// console.log("1999-01-01 =>", isDate("1999-01-01")); // true
|
||||||
|
// console.log("02.28.2023 =>", isDate("02.28.2023", { delimiters: ['.'], format: 'MM.DD.YYYY' } as DateOptions)); // true (自定义分隔符)
|
||||||
|
|
||||||
|
// // 严格模式验证
|
||||||
|
// console.log("2. 严格模式验证:");
|
||||||
|
// console.log("严格匹配格式:", isDate("2023/02/28", { strictMode: true, format: "YYYY/MM/DD" }as DateOptions)); // true
|
||||||
|
// console.log("长度不符:", isDate("2023/2/28", { strictMode: true, format: "YYYY/MM/DD" }as DateOptions)); // false
|
||||||
|
// console.log("错误分隔符:", isDate("2023-02-28", { strictMode: true, format: "YYYY/MM/DD" }as DateOptions)); // false
|
||||||
|
|
||||||
|
// // 两位年份处理
|
||||||
|
// console.log("3. 两位年份验证:");
|
||||||
|
// console.log("23 -> 2023:", isDate("23/12/31", { format: "YY/MM/DD" } as DateOptions)); // true → 2023-12-31
|
||||||
|
// console.log("87 -> 1987:", isDate("87-01-01", { format: "YY-MM-DD" } as DateOptions)); // true → 1987-01-01
|
||||||
|
// console.log("负数年份:", isDate("-100/12/31", { format: "YYYY/MM/DD" } as DateOptions)); // false
|
||||||
|
|
||||||
|
// // 日期有效性验证
|
||||||
|
// console.log("4. 无效日期检测:");
|
||||||
|
// console.log("闰年2020-02-29:", isDate("2020/02/29")); // true
|
||||||
|
// console.log("非闰年2023-02-29:", isDate("2023/02/29")); // false
|
||||||
|
// console.log("月份溢出:", isDate("2023/13/01")); // false
|
||||||
|
// console.log("日期溢出:", isDate("2023/12/32")); // false
|
||||||
|
|
||||||
|
// // Date对象验证
|
||||||
|
// console.log("5. Date对象验证:");
|
||||||
|
// console.log("有效Date对象:", isDate(new Date())); // true
|
||||||
|
// console.log("无效Date对象:", isDate(new Date("invalid")), new Date("invalid")); // false
|
||||||
|
// console.log("严格模式Date对象:", isDate(new Date(), { strictMode: true } as DateOptions)); // false
|
||||||
|
|
||||||
|
// // 自定义格式验证
|
||||||
|
// console.log("6. 自定义格式测试:");
|
||||||
|
// console.log("MM-DD-YYYY:", isDate("12-31-2023", { format: "MM-DD-YYYY" } as DateOptions)); // true
|
||||||
|
// console.log("DD.MM.YY:", isDate("31.12.23", { format: "DD.MM.YY", delimiters: ['.'] } as DateOptions)); // true
|
||||||
|
// console.log("中文分隔符:", isDate("2023年12月31日", {
|
||||||
|
// format: "YYYY年MM月DD日",
|
||||||
|
// strictMode: true,
|
||||||
|
// delimiters: ['年', '月', '日']
|
||||||
|
// } as DateOptions )); // true
|
||||||
|
|
||||||
|
|
||||||
|
// 示例测试
|
||||||
|
// console.log("示例1 标准HTTP URL:", isURL("http://example.com")); // true
|
||||||
|
// console.log("示例2 需要端口时缺少端口:", isURL("https://example.com", { requirePort: true } as URLOptions)); // false
|
||||||
|
// console.log("示例3 协议相对URL:", isURL("//example.com", { allowProtocolRelativeUrls: true })); // true
|
||||||
|
console.log("示例4 IPv6地址:", isURL("http://[2001:db8::1]:8080", {})); // true
|
||||||
|
// console.log("示例5 带认证信息被禁用:", isURL("user:pass@example.com", { disallowAuth: true })); // false
|
||||||
|
console.log("示例6 查询参数被禁用:", isURL("http://example.com?q=test", { allowQueryComponents: true })); // false
|
||||||
|
console.log("示例7 非字符串输入:", isURL(null, {})); // false
|
||||||
|
console.log("示例8 邮件协议被排除:", isURL("mailto:test@example.com", {})); // false
|
||||||
|
console.log("示例9 自定义协议:", isURL("ftp://files.example.com", { protocols: ["ftp"] })); // true
|
||||||
|
console.log("示例10 白名单检查:", isURL("http://trusted.com", { hostWhitelist: ["trusted.com"] })); // true
|
||||||
|
|
||||||
|
|
||||||
|
// #ifdef WEB
|
||||||
|
// console.log('validator', validator.isURL())
|
||||||
|
console.log("示例4 IPv6地址:", validator.isURL("http://[2001:db8::1]:8080", {})); // true
|
||||||
|
console.log("示例6 查询参数被禁用:", validator.isURL("http://example.com?q=test", { allow_query_components: true })); // false
|
||||||
|
console.log("示例8 邮件协议被排除:", validator.isURL("mailto:test@example.com", {})); // false
|
||||||
|
console.log("示例9 自定义协议:", isURL("ftp://files.example.com", { protocols: ["ftp"] })); // true
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -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,45 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// #ifndef UNI-APP-X
|
||||||
|
type UTSJSONObject = Record<string, any>
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 CSS 字符串转换为样式对象
|
||||||
|
* @param css CSS 字符串,例如 "color: red; font-size: 16px;"
|
||||||
|
* @returns CSSProperties 对象
|
||||||
|
*/
|
||||||
|
export function cssToObj(css : string | UTSJSONObject | null) : UTSJSONObject {
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
if(css == null) return {}
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
if(!css) return {}
|
||||||
|
// #endif
|
||||||
|
if(typeof css == 'object') return css as UTSJSONObject
|
||||||
|
|
||||||
|
|
||||||
|
const style : UTSJSONObject = {};
|
||||||
|
|
||||||
|
(css as string).split(';').forEach(decl => {
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
const res = decl.split(':').map(s => s.trim());
|
||||||
|
if(res.length > 1) {
|
||||||
|
const [prop, val] = res;
|
||||||
|
if (prop != '' && val != '') {
|
||||||
|
const camelProp = prop!.replace(/-([a-z])/g, (_: string, _offset: number,c: string):string => c.toUpperCase());
|
||||||
|
style[camelProp] = val!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
const [prop, val] = decl.split(':').map(s => s.trim());
|
||||||
|
if (prop && val) {
|
||||||
|
const camelProp = prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
||||||
|
style[camelProp] = val;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
});
|
||||||
|
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
@ -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,18 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
export function findClosestElementWithStyle(startEl: UniElement | null, styleProperty: string): UniElement | null {
|
||||||
|
let currentEl: UniElement | null = startEl;
|
||||||
|
|
||||||
|
while (currentEl != null) {
|
||||||
|
// Check if the current element has the style property with a non-empty value
|
||||||
|
const styleValue = currentEl?.style.getPropertyValue(styleProperty) ?? '';
|
||||||
|
if (styleValue.trim() != '') {
|
||||||
|
return currentEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to parent element
|
||||||
|
currentEl = currentEl.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return null if no element with the specified style was found
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
@ -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,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,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,17 @@
|
||||||
|
// @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
|
||||||
|
const refs = context.refs || context.$refs
|
||||||
|
if (/#|\./.test(selector) && refs) {
|
||||||
|
selector = selector.replace(/#|\./, '')
|
||||||
|
if (refs[selector]) {
|
||||||
|
selector = 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,28 @@
|
||||||
|
/**
|
||||||
|
* 生成指定长度的伪随机字符串,通常用作唯一标识符(非标准GUID)
|
||||||
|
*
|
||||||
|
* 此函数使用Math.random()生成基于36进制(数字+小写字母)的随机字符串。当长度超过11位时,
|
||||||
|
* 会通过递归拼接多个随机段实现。注意:该方法生成的并非标准GUID/UUID,不适合高安全性场景。
|
||||||
|
*
|
||||||
|
* @param {number} [len=32] - 要生成的字符串长度,默认32位
|
||||||
|
* @returns {string} 生成的伪随机字符串,包含0-9和a-z字符
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* guid(); // 返回32位字符串,例如"3zyf6a5f3kb4ayy9jq9v1a70z0qdm0bk"
|
||||||
|
* guid(5); // 返回5位字符串,例如"kf3a9"
|
||||||
|
* guid(20); // 返回20位字符串,由两段随机字符串拼接而成
|
||||||
|
*
|
||||||
|
* @note
|
||||||
|
* 1. 由于使用Math.random(),随机性存在安全缺陷,不适用于密码学用途
|
||||||
|
* 2. 当长度>11时采用递归拼接,可能略微影响性能(在极端大长度情况下)
|
||||||
|
* 3. 字符串补全时使用'0'填充,可能略微降低末尾字符的随机性
|
||||||
|
*/
|
||||||
|
export function guid(len:number = 32):string {
|
||||||
|
// crypto.randomUUID();
|
||||||
|
return len <= 11
|
||||||
|
? Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.substring(2, 2 + len)
|
||||||
|
.padEnd(len, '0')
|
||||||
|
: guid(11) + guid(len - 11);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// @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)
|
||||||
|
}
|
||||||
|
if(typeof obj == 'object') {
|
||||||
|
const obj2 = {...toRaw(obj)}
|
||||||
|
return obj2[key] != null
|
||||||
|
}
|
||||||
|
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,24 @@
|
||||||
|
/**
|
||||||
|
* 检查一个值是否为严格的布尔值(仅限 `true` 或 `false`)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* isBoolean(true); // true
|
||||||
|
* isBoolean(false); // true
|
||||||
|
* isBoolean(0); // false
|
||||||
|
* isBoolean(null); // false
|
||||||
|
*
|
||||||
|
* @param {unknown} value - 要检查的值
|
||||||
|
* @returns {value is boolean} 如果值是 `true` 或 `false` 则返回 `true`,否则返回 `false`
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 此函数使用严格相等(`===`)检查,避免隐式类型转换。
|
||||||
|
* 注意:不适用于 `Boolean` 包装对象(如 `new Boolean(true)`)。
|
||||||
|
*/
|
||||||
|
export function isBoolean(value: any|null): boolean {
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
return value == true || value == false
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
return value === true || value === false
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// import assertString from './util/assertString';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字节长度验证配置选项
|
||||||
|
*/
|
||||||
|
export type IsByteLengthOptions = {
|
||||||
|
/** 允许的最小字节长度 */
|
||||||
|
min ?: number;
|
||||||
|
/** 允许的最大字节长度 */
|
||||||
|
max ?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字符串字节长度是否在指定范围内
|
||||||
|
* @function
|
||||||
|
* @overload 使用配置对象
|
||||||
|
* @param str - 要检查的字符串
|
||||||
|
* @param options - 配置选项对象
|
||||||
|
* @returns 是否满足字节长度要求
|
||||||
|
*
|
||||||
|
* @overload 使用独立参数
|
||||||
|
* @param str - 要检查的字符串
|
||||||
|
* @param min - 最小字节长度
|
||||||
|
* @param max - 最大字节长度(可选)
|
||||||
|
* @returns 是否满足字节长度要求
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 使用配置对象
|
||||||
|
* isByteLength('🇨🇳', { min: 4, max: 8 }); // true(unicode 国旗符号占 8 字节)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 旧式参数调用
|
||||||
|
* isByteLength('hello', 3, 7); // true(实际字节长度 5)
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 1. 使用 URL 编码计算字节长度(更准确处理多字节字符)
|
||||||
|
* 2. 同时支持两种参数格式:
|
||||||
|
* - 配置对象格式 { min, max }
|
||||||
|
* - 独立参数格式 (min, max)
|
||||||
|
* 3. 不传 max 参数时只验证最小长度
|
||||||
|
* 4. 严格空值处理,允许设置 0 值
|
||||||
|
*/
|
||||||
|
export function isByteLength(str : string, optionsOrMin ?: IsByteLengthOptions) : boolean;
|
||||||
|
export function isByteLength(str : string, optionsOrMin ?: number) : boolean;
|
||||||
|
export function isByteLength(str : string, optionsOrMin : number, maxParam : number | null) : boolean;
|
||||||
|
export function isByteLength(
|
||||||
|
str : string,
|
||||||
|
optionsOrMin ?: IsByteLengthOptions | number,
|
||||||
|
maxParam : number | null = null
|
||||||
|
) : boolean {
|
||||||
|
// assertString(str);
|
||||||
|
|
||||||
|
/** 最终计算的最小长度 */
|
||||||
|
let min: number;
|
||||||
|
|
||||||
|
/** 最终计算的最大长度 */
|
||||||
|
let max : number | null;
|
||||||
|
|
||||||
|
// 参数逻辑处理
|
||||||
|
if (optionsOrMin != null && typeof optionsOrMin == 'object') {
|
||||||
|
// 使用对象配置的情况
|
||||||
|
const options = optionsOrMin as IsByteLengthOptions;
|
||||||
|
min = Math.max(options.min ?? 0, 0); // 确保最小值为正整数
|
||||||
|
max = options.max;
|
||||||
|
} else {
|
||||||
|
// 使用独立参数的情况
|
||||||
|
min = Math.max(
|
||||||
|
typeof optionsOrMin == 'number' ? optionsOrMin : 0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
max = maxParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL 编码后的字节长度计算
|
||||||
|
const encoded = encodeURI(str);
|
||||||
|
const len = (encoded?.split(/%..|./).length ?? 0) - 1;
|
||||||
|
|
||||||
|
// 执行验证逻辑
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
return len >= min && (typeof max == 'undefined' || len <= (max ?? 0));
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
return len >= min && (max == null || len <= max);
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期验证配置选项
|
||||||
|
*/
|
||||||
|
export type IsDateOptions = {
|
||||||
|
/** 日期格式字符串,默认 'YYYY/MM/DD' */
|
||||||
|
format ?: string;
|
||||||
|
/** 允许的分隔符数组,默认 ['/', '-'] */
|
||||||
|
delimiters ?: string[];
|
||||||
|
/** 是否严格匹配格式,默认 false */
|
||||||
|
strictMode ?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证日期格式字符串是否合法
|
||||||
|
* @param format - 需要验证的格式字符串
|
||||||
|
* @returns 是否合法格式
|
||||||
|
*/
|
||||||
|
function isValidFormat(format : string) : boolean {
|
||||||
|
return /(^(y{4}|y{2})[年./-](m{1,2})[月./-](d{1,2}(日)?)$)|(^(m{1,2})[./-](d{1,2})[./-]((y{4}|y{2})$))|(^(d{1,2})[./-](m{1,2})[./-]((y{4}|y{2})$))/i.test(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将日期部分和格式部分组合成键值对数组
|
||||||
|
* @param date - 分割后的日期部分数组
|
||||||
|
* @param format - 分割后的格式部分数组
|
||||||
|
* @returns 组合后的二维数组
|
||||||
|
*/
|
||||||
|
function zip(date : string[], format : string[]) : string[][] {
|
||||||
|
const zippedArr : string[][] = [];
|
||||||
|
const len = Math.max(date.length, format.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const key = i < date.length ? date[i] : ''
|
||||||
|
const value = i < format.length ? format[i] : ''
|
||||||
|
|
||||||
|
zippedArr.push([key, value])
|
||||||
|
}
|
||||||
|
|
||||||
|
return zippedArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 验证日期对象 */
|
||||||
|
function validateDateObject(date : Date, strictMode : boolean) : boolean {
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
return !strictMode && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
return !strictMode && !isNaN(date.getTime())
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function escapeRegExp(str: string): string {
|
||||||
|
return str//.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
function enhancedSplit(
|
||||||
|
str: string,
|
||||||
|
delimiters: string[]
|
||||||
|
): string[] {
|
||||||
|
// 构建动态分隔符正则表达式
|
||||||
|
const escapedDelimiters = delimiters.map(d => escapeRegExp(d));
|
||||||
|
const pattern = new RegExp(
|
||||||
|
`[${escapedDelimiters.join('')}]+`, // 匹配任意允许的分隔符
|
||||||
|
'g'
|
||||||
|
);
|
||||||
|
|
||||||
|
return str.split(pattern).filter(p => p != '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证输入是否为有效日期
|
||||||
|
* @param input - 输入值,可以是字符串或 Date 对象
|
||||||
|
* @param options - 配置选项,可以是字符串(简写格式)或配置对象
|
||||||
|
* @returns 是否为有效日期
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* isDate('2023/12/31'); // true
|
||||||
|
* isDate(new Date()); // true
|
||||||
|
* isDate('02-29-2023', { strictMode: true }); // false(2023年不是闰年)
|
||||||
|
*/
|
||||||
|
export function isDate(input : Date, options ?: IsDateOptions) : boolean;
|
||||||
|
export function isDate(input : string, options ?: string | IsDateOptions) : boolean;
|
||||||
|
export function isDate(input : string | Date, options : string | IsDateOptions | null = null) : boolean {
|
||||||
|
// 处理参数重载:允许第二个参数直接传格式字符串
|
||||||
|
// Date对象验证
|
||||||
|
|
||||||
|
|
||||||
|
let format = 'YYYY/MM/DD'
|
||||||
|
let delimiters = ['/', '-']
|
||||||
|
let strictMode = false
|
||||||
|
|
||||||
|
|
||||||
|
if (options != null) {
|
||||||
|
if (typeof options == 'string') {
|
||||||
|
format = options as string
|
||||||
|
} else {
|
||||||
|
format = (options as IsDateOptions).format ?? format
|
||||||
|
delimiters = (options as IsDateOptions).delimiters ?? delimiters
|
||||||
|
strictMode = (options as IsDateOptions).strictMode ?? strictMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (input instanceof Date) {
|
||||||
|
return validateDateObject(input, strictMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 字符串类型验证
|
||||||
|
if (!isValidFormat(format)) return false;
|
||||||
|
// 严格模式长度检查
|
||||||
|
if (strictMode && input.length != format.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 获取格式中的分隔符
|
||||||
|
const formatDelimiter = delimiters.find((d) : boolean => format.indexOf(d) != -1);
|
||||||
|
// 获取实际使用的分隔符
|
||||||
|
const dateDelimiter = strictMode
|
||||||
|
? formatDelimiter
|
||||||
|
: delimiters.find((d) : boolean => input.indexOf(d) != -1);
|
||||||
|
// 分割日期和格式
|
||||||
|
const dateParts = strictMode ? enhancedSplit(input, delimiters) : input.split(dateDelimiter ?? '');
|
||||||
|
const formatParts = strictMode ? enhancedSplit(format.toLowerCase(), delimiters) : format.toLowerCase().split(formatDelimiter ?? '');
|
||||||
|
// 组合成键值对
|
||||||
|
const dateAndFormat = zip(dateParts, formatParts);
|
||||||
|
const dateObj = new Map<string, string>();
|
||||||
|
|
||||||
|
|
||||||
|
// 解析日期组成部分
|
||||||
|
for (const [dateWord, formatWord] of dateAndFormat) {
|
||||||
|
if (dateWord == '' || formatWord == '' || dateWord.length != formatWord.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dateObj.set(formatWord.charAt(0), dateWord)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 年份处理
|
||||||
|
let fullYear = dateObj.get('y');
|
||||||
|
|
||||||
|
if (fullYear == null) return false;
|
||||||
|
|
||||||
|
// 检查年份前导负号
|
||||||
|
if (fullYear.startsWith('-')) return false;
|
||||||
|
|
||||||
|
// 两位年份转四位
|
||||||
|
if (fullYear.length == 2) {
|
||||||
|
const parsedYear = parseInt(fullYear, 10);
|
||||||
|
|
||||||
|
if (isNaN(parsedYear)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
const century = currentYear - (currentYear % 100);
|
||||||
|
fullYear = (parseInt(fullYear, 10) < (currentYear % 100))
|
||||||
|
? `${century + 100 + parseInt(fullYear, 10)}`
|
||||||
|
: `${century + parseInt(fullYear, 10)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 月份补零
|
||||||
|
const month = dateObj.get('m')?.padStart(2, '0') ?? '';
|
||||||
|
// 日期补零
|
||||||
|
const day = dateObj.get('d')?.padStart(2, '0') ?? '';
|
||||||
|
|
||||||
|
const isoDate = `${fullYear}-${month}-${day}T00:00:00.000Z`;
|
||||||
|
|
||||||
|
// return new Date(time).getDate() == parseInt(day);
|
||||||
|
|
||||||
|
// 构造 ISO 日期字符串验证
|
||||||
|
try {
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
const date = new Date(isoDate);
|
||||||
|
return date.getUTCDate() === parseInt(day, 10) &&
|
||||||
|
(date.getUTCMonth() + 1) === parseInt(month, 10) &&
|
||||||
|
date.getUTCFullYear() === parseInt(fullYear, 10);
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
const date = new Date(isoDate);
|
||||||
|
return date.getDate() == parseInt(day, 10) &&
|
||||||
|
(date.getMonth() + 1) == parseInt(month, 10) &&
|
||||||
|
date.getFullYear() == parseInt(fullYear, 10);
|
||||||
|
// #endif
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证电子邮件地址格式
|
||||||
|
* @param email - 要验证的字符串
|
||||||
|
* @returns 是否通过验证
|
||||||
|
*/
|
||||||
|
export function isEmail(email : string) : boolean {
|
||||||
|
const emailRegex = /\S+@\S+\.\S+/;
|
||||||
|
return emailRegex.test(email);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
// @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 false
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
// #ifndef UNI-APP-X && APP
|
||||||
|
export function isEmpty(value: any): boolean {
|
||||||
|
// 检查是否为null或undefined
|
||||||
|
if (value == null || value == undefined || value == '') {
|
||||||
|
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,64 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
// #ifndef APP-ANDROID || APP-HARMONY || APP-IOS
|
||||||
|
type UTSJSONObject = {
|
||||||
|
version : string | number | null;
|
||||||
|
};
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-ANDROID || APP-HARMONY || APP-IOS
|
||||||
|
// type Options = UTSJSONObject
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
|
||||||
|
const IPv4AddressFormat = `(${IPv4SegmentFormat}\\.){3}${IPv4SegmentFormat}`;
|
||||||
|
const IPv4AddressRegExp = new RegExp(`^${IPv4AddressFormat}$`);
|
||||||
|
|
||||||
|
const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';
|
||||||
|
const IPv6AddressRegExp = new RegExp(
|
||||||
|
'^(' +
|
||||||
|
`(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +
|
||||||
|
`(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +
|
||||||
|
`(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +
|
||||||
|
`(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +
|
||||||
|
`(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +
|
||||||
|
`(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +
|
||||||
|
`(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +
|
||||||
|
`(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +
|
||||||
|
')(%[0-9a-zA-Z.]{1,})?$'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证IP地址格式
|
||||||
|
* @param {string} ipAddress - 要验证的IP地址
|
||||||
|
* @param {Options|string|number} options - 配置选项或版本号
|
||||||
|
* @returns {boolean} 是否匹配有效的IP地址格式
|
||||||
|
*/
|
||||||
|
export function isIP(ipAddress : string | null, options : UTSJSONObject | string | number | null = null) : boolean {
|
||||||
|
// assertString(ipAddress);
|
||||||
|
if(ipAddress == null) return false
|
||||||
|
let version : string | number | null;
|
||||||
|
|
||||||
|
if (typeof options == 'object') {
|
||||||
|
version = (options as UTSJSONObject|null)?.['version'];
|
||||||
|
} else {
|
||||||
|
version = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionStr = version != null ? `${version}` : '';
|
||||||
|
|
||||||
|
if (versionStr == '') {
|
||||||
|
return isIP(ipAddress, 4) || isIP(ipAddress, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionStr == '4') {
|
||||||
|
return IPv4AddressRegExp.test(ipAddress.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionStr == '6') {
|
||||||
|
return IPv6AddressRegExp.test(ipAddress.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
/**
|
||||||
|
* 检测输入值是否为正则表达式对象
|
||||||
|
* @param obj - 需要检测的任意类型值
|
||||||
|
* @returns 如果检测值是正则表达式返回 true,否则返回 false
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 基础检测
|
||||||
|
* isRegExp(/abc/); // true
|
||||||
|
* isRegExp(new RegExp('abc')); // true
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 非正则表达式检测
|
||||||
|
* isRegExp('hello'); // false
|
||||||
|
* isRegExp({}); // false
|
||||||
|
* isRegExp(null); // false
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 1. 通过 Object.prototype.toString 的可靠类型检测
|
||||||
|
* 2. 支持跨执行环境的可靠检测:
|
||||||
|
* - 浏览器多 iframe 环境
|
||||||
|
* - Node.js 的 vm 模块
|
||||||
|
* 3. 比 instanceof 检测更可靠
|
||||||
|
* 4. 支持 ES3+ 全环境兼容
|
||||||
|
*/
|
||||||
|
export function isRegExp(obj : any) : boolean {
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
return Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
return obj instanceof RegExp//Object.prototype.toString.call(obj) === '[object RegExp]';
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { isValidDomain, type IsValidDomainOptions } from '../isValidDomain';
|
||||||
|
import { isIP } from '../isIP';
|
||||||
|
import { isRegExp } from '../isRegExp';
|
||||||
|
// import {merge} from '../merge';
|
||||||
|
|
||||||
|
/** URL 验证配置选项 */
|
||||||
|
export type IsURLOptions = {
|
||||||
|
/** 允许的协议列表(默认 ['http', 'https', 'ftp']) */
|
||||||
|
protocols ?: string[];
|
||||||
|
/** 需要顶级域名(默认 true) */
|
||||||
|
requireTld ?: boolean;
|
||||||
|
/** 需要协议头(默认 false) */
|
||||||
|
requireProtocol ?: boolean;
|
||||||
|
/** 需要主机地址(默认 true) */
|
||||||
|
requireHost ?: boolean;
|
||||||
|
/** 需要端口号(默认 false) */
|
||||||
|
requirePort ?: boolean;
|
||||||
|
/** 需要有效协议(默认 true) */
|
||||||
|
requireValidProtocol ?: boolean;
|
||||||
|
/** 允许下划线(默认 false) */
|
||||||
|
allowUnderscores ?: boolean;
|
||||||
|
/** 允许结尾点号(默认 false) */
|
||||||
|
allowTrailingDot ?: boolean;
|
||||||
|
/** 允许协议相对地址(默认 false) */
|
||||||
|
allowProtocolRelativeUrls ?: boolean;
|
||||||
|
/** 允许片段标识(默认 true) */
|
||||||
|
allowFragments ?: boolean;
|
||||||
|
/** 允许查询参数(默认 true) */
|
||||||
|
allowQueryComponents ?: boolean;
|
||||||
|
/** 禁用认证信息(默认 false) */
|
||||||
|
disallowAuth ?: boolean;
|
||||||
|
/** 验证长度(默认 true) */
|
||||||
|
validateLength ?: boolean;
|
||||||
|
/** 最大允许长度(默认 2084) */
|
||||||
|
maxAllowedLength ?: number;
|
||||||
|
/** 白名单主机列表 */
|
||||||
|
hostWhitelist ?: Array<string | RegExp>;
|
||||||
|
/** 黑名单主机列表 */
|
||||||
|
hostBlacklist ?: Array<string | RegExp>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkHost(host : string, matches : any[]) : boolean {
|
||||||
|
for (let i = 0; i < matches.length; i++) {
|
||||||
|
let match = matches[i];
|
||||||
|
if (host == match || (isRegExp(match) && (match as RegExp).test(host))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数
|
||||||
|
function isValidPort(port : number | null) : boolean {
|
||||||
|
return port != null && !isNaN(port) && port > 0 && port <= 65535;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateHost(host : string, options : IsURLOptions | null, isIPv6 : boolean) : boolean {
|
||||||
|
if (isIPv6) return isIP(host, 6);
|
||||||
|
return isIP(host) || isValidDomain(host, {
|
||||||
|
requireTld: options?.requireTld ?? true,
|
||||||
|
allowUnderscore: options?.allowUnderscores ?? true,
|
||||||
|
allowTrailingDot: options?.allowTrailingDot ?? false
|
||||||
|
} as IsValidDomainOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** 匹配 IPv6 地址的正则表达式 */
|
||||||
|
const WRAPPED_IPV6_REGEX = /^\[([^\]]+)\](?::([0-9]+))?$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证字符串是否为有效的 URL
|
||||||
|
* @param url - 需要验证的字符串
|
||||||
|
* @param options - 配置选项
|
||||||
|
* @returns 是否为有效 URL
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* isURL('https://example.com'); // true
|
||||||
|
* isURL('user:pass@example.com', { disallowAuth: true }); // false
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function isURL(url : string | null, options : IsURLOptions | null = null) : boolean {
|
||||||
|
// assertString(url);
|
||||||
|
|
||||||
|
// 1. 基础格式校验
|
||||||
|
if (url == null || url == '' || url.length == 0 || /[\s<>]/.test(url) || url.startsWith('mailto:')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 合并配置选项
|
||||||
|
let protocols = options?.protocols ?? ['http', 'https', 'ftp']
|
||||||
|
// let requireTld = options?.requireTld ?? true
|
||||||
|
let requireProtocol = options?.requireProtocol ?? false
|
||||||
|
let requireHost = options?.requireHost ?? true
|
||||||
|
let requirePort = options?.requirePort ?? false
|
||||||
|
let requireValidProtocol = options?.requireValidProtocol ?? true
|
||||||
|
// let allowUnderscores = options?.allowUnderscores ?? false
|
||||||
|
// let allowTrailingDot = options?.allowTrailingDot ?? false
|
||||||
|
let allowProtocolRelativeUrls = options?.allowProtocolRelativeUrls ?? false
|
||||||
|
let allowFragments = options?.allowFragments ?? true
|
||||||
|
let allowQueryComponents = options?.allowQueryComponents ?? true
|
||||||
|
let validateLength = options?.validateLength ?? true
|
||||||
|
let maxAllowedLength = options?.maxAllowedLength ?? 2084
|
||||||
|
let hostWhitelist = options?.hostWhitelist
|
||||||
|
let hostBlacklist = options?.hostBlacklist
|
||||||
|
let disallowAuth = options?.disallowAuth ?? false
|
||||||
|
|
||||||
|
|
||||||
|
// 2. 长度校验
|
||||||
|
if (validateLength && url!.length > maxAllowedLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 3. 片段和查询参数校验
|
||||||
|
if (!allowFragments && url.includes('#')) return false;
|
||||||
|
if (!allowQueryComponents && (url.includes('?') || url.includes('&'))) return false;
|
||||||
|
|
||||||
|
// 处理 URL 组成部分
|
||||||
|
const [urlWithoutFragment] = url.split('#');
|
||||||
|
const [baseUrl] = urlWithoutFragment.split('?');
|
||||||
|
// 4. 协议处理
|
||||||
|
const protocolParts = baseUrl.split('://');
|
||||||
|
let protocol:string;
|
||||||
|
let remainingUrl = baseUrl;
|
||||||
|
|
||||||
|
if (protocolParts.length > 1) {
|
||||||
|
protocol = protocolParts.shift()!.toLowerCase();
|
||||||
|
if (requireValidProtocol && !protocols!.includes(protocol)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
remainingUrl = protocolParts.join('://');
|
||||||
|
} else if (requireProtocol) {
|
||||||
|
return false;
|
||||||
|
} else if (baseUrl.startsWith('//')) {
|
||||||
|
if (!allowProtocolRelativeUrls) return false;
|
||||||
|
remainingUrl = baseUrl.slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingUrl == '') return false;
|
||||||
|
|
||||||
|
// 5. 处理主机部分
|
||||||
|
const [hostPart] = remainingUrl.split('/', 1);
|
||||||
|
const authParts = hostPart.split('@');
|
||||||
|
|
||||||
|
// 认证信息校验
|
||||||
|
if (authParts.length > 1) {
|
||||||
|
if (disallowAuth || authParts[0] == '') return false;
|
||||||
|
const auth = authParts.shift()!;
|
||||||
|
if (auth.split(':').length > 2) return false;
|
||||||
|
const [user, password] = auth.split(':');
|
||||||
|
if (user == '' && password == '') return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostname = authParts.join('@');
|
||||||
|
|
||||||
|
// 6. 解析主机和端口
|
||||||
|
type HostInfo = {
|
||||||
|
host ?: string;
|
||||||
|
ipv6 ?: string;
|
||||||
|
port ?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hostInfo : HostInfo = {};
|
||||||
|
const ipv6Match = hostname.match(WRAPPED_IPV6_REGEX);
|
||||||
|
if (ipv6Match != null) {
|
||||||
|
hostInfo.ipv6 = ipv6Match.length > 1 ? ipv6Match[1] : null;
|
||||||
|
const portStr = ipv6Match.length > 2 ? ipv6Match[2] : null;
|
||||||
|
if (portStr != null) {
|
||||||
|
hostInfo.port = parseInt(portStr);
|
||||||
|
if (!isValidPort(hostInfo.port)) return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const [host, ...portParts] = hostname.split(':');
|
||||||
|
hostInfo.host = host;
|
||||||
|
if (portParts.length > 0) {
|
||||||
|
const portStr = portParts.join(':');
|
||||||
|
hostInfo.port = parseInt(portStr);
|
||||||
|
if (!isValidPort(hostInfo.port)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 端口校验
|
||||||
|
if (requirePort && hostInfo.port == null) return false;
|
||||||
|
// 8. 主机验证逻辑
|
||||||
|
const finalHost = hostInfo.host ?? hostInfo.ipv6;
|
||||||
|
if (finalHost == null) return requireHost ? false : true;
|
||||||
|
// 白名单/黑名单检查
|
||||||
|
if (hostWhitelist != null && !checkHost(finalHost!, hostWhitelist!)) return false;
|
||||||
|
if (hostBlacklist != null && checkHost(finalHost!, hostBlacklist!)) return false;
|
||||||
|
|
||||||
|
// 9. 综合校验
|
||||||
|
return validateHost(
|
||||||
|
finalHost,
|
||||||
|
options,
|
||||||
|
!(hostInfo.ipv6 == null || hostInfo.ipv6 == '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
/**
|
||||||
|
* 域名验证配置选项
|
||||||
|
*/
|
||||||
|
export type IsValidDomainOptions = {
|
||||||
|
/**
|
||||||
|
* 是否要求必须包含顶级域名(TLD)
|
||||||
|
* @default true
|
||||||
|
* @example
|
||||||
|
* true -> "example.com" 有效
|
||||||
|
* false -> "localhost" 有效
|
||||||
|
*/
|
||||||
|
requireTld : boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许域名部分包含下划线(_)
|
||||||
|
* @default false
|
||||||
|
* @example
|
||||||
|
* true -> "my_site.com" 有效
|
||||||
|
* false -> "my_site.com" 无效
|
||||||
|
*/
|
||||||
|
allowUnderscore : boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许域名以点号(.)结尾
|
||||||
|
* @default false
|
||||||
|
* @example
|
||||||
|
* true -> "example.com." 有效
|
||||||
|
* false -> "example.com." 无效
|
||||||
|
*/
|
||||||
|
allowTrailingDot : boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证字符串是否为合法域名
|
||||||
|
* @param str 要验证的字符串
|
||||||
|
* @param options 配置选项,默认 {
|
||||||
|
* requireTld: true, // 需要顶级域名
|
||||||
|
* allowUnderscore: false,// 允许下划线
|
||||||
|
* allowTrailingDot: false// 允许结尾点号
|
||||||
|
* }
|
||||||
|
* @returns 验证结果
|
||||||
|
*
|
||||||
|
* 示例:
|
||||||
|
* isValidDomain("example.com") // true
|
||||||
|
* isValidDomain("my_site.com", { allowUnderscore: true }) // true
|
||||||
|
*/
|
||||||
|
export function isValidDomain(
|
||||||
|
str : string,
|
||||||
|
options : IsValidDomainOptions = {
|
||||||
|
requireTld: true,
|
||||||
|
allowUnderscore: false,
|
||||||
|
allowTrailingDot: false
|
||||||
|
}
|
||||||
|
) : boolean {
|
||||||
|
// 预处理字符串
|
||||||
|
let domain = str;
|
||||||
|
|
||||||
|
// 处理结尾点号
|
||||||
|
if (options.allowTrailingDot && domain.endsWith('.')) {
|
||||||
|
domain = domain.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分割域名部分
|
||||||
|
const parts = domain.split('.');
|
||||||
|
if (parts.length == 1 && options.requireTld) return false;
|
||||||
|
|
||||||
|
// 验证顶级域名
|
||||||
|
const tld = parts[parts.length - 1];
|
||||||
|
if (options.requireTld) {
|
||||||
|
if (!/^[a-z\u00A1-\uFFFF]{2,}$/i.test(tld) || /\s/.test(tld)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个部分
|
||||||
|
const domainRegex = options.allowUnderscore
|
||||||
|
? /^[a-z0-9\u00A1-\uFFFF](?:[a-z0-9-\u00A1-\uFFFF_]*[a-z0-9\u00A1-\uFFFF])?$/i
|
||||||
|
: /^[a-z0-9\u00A1-\uFFFF](?:[a-z0-9-\u00A1-\uFFFF]*[a-z0-9\u00A1-\uFFFF])?$/i;
|
||||||
|
|
||||||
|
return parts.every(part => {
|
||||||
|
// 长度校验
|
||||||
|
if (part.length > 63) return false;
|
||||||
|
// 格式校验
|
||||||
|
if (!domainRegex.test(part)) return false;
|
||||||
|
// 禁止开头/结尾连字符
|
||||||
|
return !(part.startsWith('-') || part.endsWith('-'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
/**
|
||||||
|
* 深度合并两个对象,用默认值填充目标对象中未定义的属性
|
||||||
|
*
|
||||||
|
* @template T - 合并对象的泛型类型
|
||||||
|
* @param {T} obj - 目标对象(将被修改)
|
||||||
|
* @param {T} defaults - 包含默认值的对象
|
||||||
|
* @returns {T} 合并后的对象(即修改后的obj参数)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function merge<T>(obj : T, defaults : T) : T {
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
try {
|
||||||
|
if(obj instanceof UTSJSONObject && defaults instanceof UTSJSONObject) {
|
||||||
|
return UTSJSONObject.assign<T>(obj, defaults)!// as T
|
||||||
|
}
|
||||||
|
const obj1 = { ...toRaw(obj) }
|
||||||
|
const obj2 = { ...toRaw(defaults) }
|
||||||
|
return UTSJSONObject.assign<T>(obj1, obj2)!
|
||||||
|
} catch (error) {
|
||||||
|
return defaults
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
for (const key in defaults) {
|
||||||
|
if (obj[key] === undefined || obj[key] === null) {
|
||||||
|
obj[key] = defaults[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// #ifndef UNI-APP-X
|
||||||
|
type UTSJSONObject = Record<string, any>
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象转换为URL查询字符串
|
||||||
|
* @param data - 需要转换的键值对对象
|
||||||
|
* @param isPrefix - 是否添加问号前缀,默认为false
|
||||||
|
* @returns 格式化后的URL查询参数字符串
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 基础用法
|
||||||
|
* obj2url({ name: '张三', age: 25 });
|
||||||
|
* // => "name=%E5%BC%A0%E4%B8%89&age=25"
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 数组参数处理
|
||||||
|
* obj2url({ tags: ['js', 'ts'] });
|
||||||
|
* // => "tags[]=js&tags[]=ts"
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // 包含空值的过滤
|
||||||
|
* obj2url({ name: '', age: null, city: undefined });
|
||||||
|
* // => ""
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 1. 自动过滤空值(空字符串、null、undefined)
|
||||||
|
* 2. 支持数组参数转换(自动添加[]后缀)
|
||||||
|
* 3. 自动进行URI编码
|
||||||
|
* 4. 支持自定义是否添加问号前缀
|
||||||
|
*/
|
||||||
|
export function obj2url(data : UTSJSONObject, isPrefix : boolean = false) : string {
|
||||||
|
const prefix = isPrefix ? '?' : '';
|
||||||
|
|
||||||
|
const _result:string[] = [];
|
||||||
|
const empty:(any|null)[] = ['', null]
|
||||||
|
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
empty.push(undefined)
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
for (const key in data) {
|
||||||
|
const value = data[key];
|
||||||
|
|
||||||
|
// 去掉为空的参数
|
||||||
|
if (empty.includes(value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
(value as any[]).forEach((_value) => {
|
||||||
|
_result.push(
|
||||||
|
encodeURIComponent(key) + '[]=' + encodeURIComponent(`${_value}`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_result.push(encodeURIComponent(key) + '=' + encodeURIComponent(`${value}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _result.length > 0 ? prefix + _result.join('&') : '';
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// #ifndef UNI-APP-X
|
||||||
|
interface UTSJSONObject {
|
||||||
|
[key : string] : string | number | null
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串转换为带有连字符分隔的小写形式
|
||||||
|
* @param key - 要转换的字符串
|
||||||
|
* @returns 转换后的字符串
|
||||||
|
*/
|
||||||
|
export function toLowercaseSeparator(key : string):string {
|
||||||
|
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取样式对象对应的样式字符串
|
||||||
|
* @param style - CSS样式对象
|
||||||
|
* @returns 由非空有效样式属性键值对组成的字符串
|
||||||
|
*/
|
||||||
|
export function objToCss(style : UTSJSONObject) : string {
|
||||||
|
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
let styleStr = '';
|
||||||
|
style.toMap().forEach((value, key) => {
|
||||||
|
if(value != null && value != '') {
|
||||||
|
styleStr += `${toLowercaseSeparator(key as string)}: ${value};`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return styleStr
|
||||||
|
// #endif
|
||||||
|
// #ifndef APP-ANDROID
|
||||||
|
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 = objToCss(style);
|
||||||
|
// console.log(styleStr);
|
||||||
|
// 输出: "color: red; font-size: 16px;"
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// import { processFile, ProcessFileOptions } from '@/uni_modules/lime-file-utils'
|
||||||
|
export function pathToBase64(path : string) : Promise<string> {
|
||||||
|
console.error('pathToBase64: 当前环境不支持,请使用 【lime-file-utils】')
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// processFile({
|
||||||
|
// type: 'toDataURL',
|
||||||
|
// path,
|
||||||
|
// success(res : string) {
|
||||||
|
// resolve(res)
|
||||||
|
// },
|
||||||
|
// fail(err: any){
|
||||||
|
// reject(err)
|
||||||
|
// }
|
||||||
|
// } as ProcessFileOptions)
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
import { getLocalFilePath } from '../getLocalFilePath'
|
||||||
|
// #endif
|
||||||
|
function isImage(extension : string) {
|
||||||
|
const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "svg"];
|
||||||
|
return imageExtensions.includes(extension.toLowerCase());
|
||||||
|
}
|
||||||
|
// #ifdef H5
|
||||||
|
function getSVGFromURL(url: string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', url, true);
|
||||||
|
xhr.responseType = 'text';
|
||||||
|
|
||||||
|
xhr.onload = function () {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const svg = xhr.responseText;
|
||||||
|
resolve(svg);
|
||||||
|
} else {
|
||||||
|
reject(new Error(xhr.statusText));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = function () {
|
||||||
|
reject(new Error('Network error'));
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
/**
|
||||||
|
* 路径转base64
|
||||||
|
* @param {Object} string
|
||||||
|
*/
|
||||||
|
export function pathToBase64(path : string) : Promise<string> {
|
||||||
|
if (/^data:/.test(path)) return path
|
||||||
|
let extension = path.substring(path.lastIndexOf('.') + 1);
|
||||||
|
const isImageFile = isImage(extension)
|
||||||
|
let prefix = ''
|
||||||
|
if (isImageFile) {
|
||||||
|
prefix = 'image/';
|
||||||
|
if(extension == 'svg') {
|
||||||
|
extension += '+xml'
|
||||||
|
}
|
||||||
|
} else if (extension === 'pdf') {
|
||||||
|
prefix = 'application/pdf';
|
||||||
|
} else if (extension === 'txt') {
|
||||||
|
prefix = 'text/plain';
|
||||||
|
} else {
|
||||||
|
// 添加更多文件类型的判断
|
||||||
|
// 如果不是图片、PDF、文本等类型,可以设定默认的前缀或采取其他处理
|
||||||
|
prefix = 'application/octet-stream';
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef H5
|
||||||
|
if (isImageFile) {
|
||||||
|
if(extension == 'svg') {
|
||||||
|
getSVGFromURL(path).then(svg => {
|
||||||
|
const base64 = btoa(svg);
|
||||||
|
resolve(`data:image/svg+xml;base64,${base64}`);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let image = new Image();
|
||||||
|
image.setAttribute("crossOrigin", 'Anonymous');
|
||||||
|
image.onload = function () {
|
||||||
|
let canvas = document.createElement('canvas');
|
||||||
|
canvas.width = this.naturalWidth;
|
||||||
|
canvas.height = this.naturalHeight;
|
||||||
|
canvas.getContext('2d').drawImage(image, 0, 0);
|
||||||
|
let result = canvas.toDataURL(`${prefix}${extension}`)
|
||||||
|
resolve(result);
|
||||||
|
canvas.height = canvas.width = 0
|
||||||
|
}
|
||||||
|
image.src = path + '?v=' + Math.random()
|
||||||
|
image.onerror = (error) => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
reject('not image');
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP
|
||||||
|
if (uni.canIUse('getFileSystemManager')) {
|
||||||
|
uni.getFileSystemManager().readFile({
|
||||||
|
filePath: path,
|
||||||
|
encoding: 'base64',
|
||||||
|
success: (res) => {
|
||||||
|
resolve(`data:${prefix}${extension};base64,${res.data}`)
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error({ error, path })
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => {
|
||||||
|
entry.file((file : any) => {
|
||||||
|
const fileReader = new plus.io.FileReader()
|
||||||
|
fileReader.onload = (data) => {
|
||||||
|
resolve(data.target.result)
|
||||||
|
}
|
||||||
|
fileReader.onerror = (error) => {
|
||||||
|
console.error({ error, path })
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
fileReader.readAsDataURL(file)
|
||||||
|
}, reject)
|
||||||
|
}, reject)
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// 是否支持被动事件监听
|
||||||
|
export const supportsPassive = true;
|
||||||
|
|
||||||
|
// #ifdef uniVersion < 4.25
|
||||||
|
// 请求动画帧
|
||||||
|
export function raf(fn: TimerCallback): number {
|
||||||
|
return setTimeout(fn, 1000 / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消动画帧
|
||||||
|
export function cancelRaf(id: number) {
|
||||||
|
clearTimeout(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 双倍动画帧
|
||||||
|
export function doubleRaf(fn: TimerCallback): void {
|
||||||
|
raf(():number => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
// #ifdef uniVersion >= 4.25
|
||||||
|
// 请求动画帧
|
||||||
|
export function raf(fn: UniAnimationFrameCallback): number
|
||||||
|
export function raf(fn: UniAnimationFrameCallbackWithNoArgument): number
|
||||||
|
export function raf(fn: any): number {
|
||||||
|
if(typeof fn == 'UniAnimationFrameCallback') {
|
||||||
|
return requestAnimationFrame(fn as UniAnimationFrameCallback);
|
||||||
|
} else {
|
||||||
|
return requestAnimationFrame(fn as UniAnimationFrameCallbackWithNoArgument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消动画帧
|
||||||
|
export function cancelRaf(id: number) {
|
||||||
|
cancelAnimationFrame(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 双倍动画帧
|
||||||
|
export function doubleRaf(fn: UniAnimationFrameCallback): void
|
||||||
|
export function doubleRaf(fn: UniAnimationFrameCallbackWithNoArgument): void
|
||||||
|
export function doubleRaf(fn: any): void {
|
||||||
|
raf(():number => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
type Callback = () => void//Function
|
||||||
|
// 是否支持被动事件监听
|
||||||
|
export const supportsPassive = true;
|
||||||
|
|
||||||
|
// 请求动画帧
|
||||||
|
export function raf(fn : Callback) : number {
|
||||||
|
// #ifndef WEB
|
||||||
|
return setTimeout(fn, 1000 / 60); // 请求动画帧
|
||||||
|
// #endif
|
||||||
|
// #ifdef WEB
|
||||||
|
return requestAnimationFrame(fn); // 请求动画帧
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消动画帧
|
||||||
|
export function cancelRaf(id : number) {
|
||||||
|
// 如果是在浏览器环境下,使用 cancelAnimationFrame 方法
|
||||||
|
// #ifdef WEB
|
||||||
|
cancelAnimationFrame(id); // 取消动画帧
|
||||||
|
// #endif
|
||||||
|
// #ifndef WEB
|
||||||
|
clearTimeout(id); // 取消动画帧
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// 双倍动画帧
|
||||||
|
export function doubleRaf(fn : Callback) : void {
|
||||||
|
raf(() => {
|
||||||
|
raf(fn)
|
||||||
|
}); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// #ifdef UNI-APP-X
|
||||||
|
// export * from './uvue.uts'
|
||||||
|
export { selectAllComponent } from './uvue.uts'
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef UNI-APP-X
|
||||||
|
// export * from './vue.ts'
|
||||||
|
export { selectAllComponent } from './vue.ts'
|
||||||
|
// #endif
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { type ComponentPublicInstance } from 'vue';
|
||||||
|
|
||||||
|
type SelectOptions = {
|
||||||
|
context : ComponentPublicInstance,
|
||||||
|
needAll : boolean | null,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectAllComponent(selector : string, options : UTSJSONObject) : ComponentPublicInstance[]|null {
|
||||||
|
const context = options.get('context')! as ComponentPublicInstance;
|
||||||
|
let needAll = options.get('needAll') as boolean;
|
||||||
|
let result:ComponentPublicInstance[] = []
|
||||||
|
|
||||||
|
if(needAll == null) { needAll = true };
|
||||||
|
|
||||||
|
if(context.$children.length > 0) {
|
||||||
|
const queue:ComponentPublicInstance[] = [...context.$children];
|
||||||
|
while(queue.length > 0) {
|
||||||
|
const child = queue.shift();
|
||||||
|
const name = child?.$options?.name;
|
||||||
|
if(name == selector) {
|
||||||
|
result.push(child as ComponentPublicInstance)
|
||||||
|
} else {
|
||||||
|
const children = child?.$children
|
||||||
|
if(children !== null) {
|
||||||
|
queue.push(...children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(result.length > 0 && !needAll) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(result.length > 0) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
interface SelectOptions {
|
||||||
|
context?: any
|
||||||
|
needAll?: boolean
|
||||||
|
node?: boolean
|
||||||
|
}
|
||||||
|
// #ifdef MP
|
||||||
|
function selectMPComponent(key: string, name: string, context: any, needAll: boolean) {
|
||||||
|
const {proxy, $vm} = context
|
||||||
|
context = $vm || proxy
|
||||||
|
if(!['ref','component'].includes(key)) {
|
||||||
|
const queue = [context]
|
||||||
|
let result = null
|
||||||
|
const selector = (key == 'id' ? '#': '.') + name;
|
||||||
|
while(queue.length > 0) {
|
||||||
|
const child = queue.shift();
|
||||||
|
const flag = child?.selectComponent(selector)
|
||||||
|
if(flag) {
|
||||||
|
if(!needAll) {return result = flag.$vm}
|
||||||
|
return result = child.selectAllComponents(selector).map(item => item.$vm)
|
||||||
|
} else {
|
||||||
|
child.$children && (queue.push(...child.$children));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
const {$templateRefs} = context.$
|
||||||
|
const nameMap = {}
|
||||||
|
for (var i = 0; i < $templateRefs.length; i++) {
|
||||||
|
const item = $templateRefs[i]
|
||||||
|
nameMap[item.i] = item.r
|
||||||
|
}
|
||||||
|
let result = []
|
||||||
|
if(context.$children.length) {
|
||||||
|
const queue = [...context.$children]
|
||||||
|
while(queue.length > 0) {
|
||||||
|
const child = queue.shift();
|
||||||
|
if(key == 'component' && (child.type?.name === name || child.$?.type?.name === name)) {
|
||||||
|
result.push(child)
|
||||||
|
} else if(child.$refs && child.$refs[name]) {
|
||||||
|
result = child.$refs[name]
|
||||||
|
} else if(nameMap[child.id] === name){
|
||||||
|
result.push(child)
|
||||||
|
} else {
|
||||||
|
child.$children && (queue.push(...child.$children));
|
||||||
|
}
|
||||||
|
if(result.length && !needAll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needAll ? result : result[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
function selectH5Component(key: string, name: string, context: any, needAll: boolean) {
|
||||||
|
const {_, component } = context
|
||||||
|
const child = {component: _ || component || context, children: null , subTree: null, props: null}
|
||||||
|
let result = []
|
||||||
|
let queue = [child]
|
||||||
|
while(queue.length > 0 ) {
|
||||||
|
const child = queue.shift()
|
||||||
|
const {component, children , props, subTree} = child
|
||||||
|
if(key === 'component' && component?.type?.name == name) {
|
||||||
|
result.push(component)
|
||||||
|
} else if(key === 'ref' && component && (props?.ref == name || component[key][name])) {
|
||||||
|
if(props?.ref == name) {
|
||||||
|
//exposed
|
||||||
|
result.push(component)
|
||||||
|
} else if(component[key][name]) {
|
||||||
|
result.push(component[key][name])
|
||||||
|
}
|
||||||
|
} else if(key !== 'ref' && component?.exposed && new RegExp(`\\b${name}\\b`).test(component.attrs[key])) {
|
||||||
|
// exposed
|
||||||
|
result.push(component)
|
||||||
|
} else if(children && Array.isArray(children)) {
|
||||||
|
queue.push(...children)
|
||||||
|
} else if(!component && subTree) {
|
||||||
|
queue.push(subTree)
|
||||||
|
} else if(component?.subTree) {
|
||||||
|
queue.push(component.subTree)
|
||||||
|
}
|
||||||
|
if(result.length && !needAll) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needAll ? result : result[0]
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP
|
||||||
|
function selectAPPComponent(key: string, name: string, context: any, needAll: boolean, node: boolean) {
|
||||||
|
let result = []
|
||||||
|
// const {_, component} = context
|
||||||
|
// const child = {component: _ || component || context, children: null, props: null, subTree: null}
|
||||||
|
const queue = [context]
|
||||||
|
while(queue.length > 0) {
|
||||||
|
const child = queue.shift()
|
||||||
|
const {component, children, props, subTree} = child
|
||||||
|
const isComp = component && props && component.exposed && !node
|
||||||
|
if(key == 'component' && child.type && child.type.name === name) {
|
||||||
|
result.push(component)
|
||||||
|
} else if(props?.[key] === name && node) {
|
||||||
|
result.push(child)
|
||||||
|
} else if(key === 'ref' && isComp && (props.ref === name || props.ref_key === name)) {
|
||||||
|
// exposed
|
||||||
|
result.push(component)
|
||||||
|
} else if(key !== 'ref' && isComp && new RegExp(`\\b${name}\\b`).test(props[key])) {
|
||||||
|
// exposed
|
||||||
|
result.push(component)
|
||||||
|
}
|
||||||
|
// else if(component && component.subTree && Array.isArray(component.subTree.children)){
|
||||||
|
// queue.push(...component.subTree.children)
|
||||||
|
// }
|
||||||
|
else if(subTree) {
|
||||||
|
queue.push(subTree)
|
||||||
|
} else if(component && component.subTree){
|
||||||
|
queue.push(component.subTree)
|
||||||
|
}
|
||||||
|
else if(children && Array.isArray(children)) {
|
||||||
|
queue.push(...children)
|
||||||
|
}
|
||||||
|
if(result.length && !needAll) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needAll ? result : result[0]
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
export function selectAllComponent(selector: string, options: SelectOptions = {}) {
|
||||||
|
// . class
|
||||||
|
// # id
|
||||||
|
// $ ref
|
||||||
|
// @ component name
|
||||||
|
const reg = /^(\.|#|@|\$)([a-zA-Z_0-9\-]+)$/;
|
||||||
|
if(!reg.test(selector)) return null
|
||||||
|
let { context, needAll = true, node} = options
|
||||||
|
const [,prefix, name] = selector.match(reg)
|
||||||
|
const symbolMappings = {'.': 'class', '#': 'id', '$':'ref', '@':'component'}
|
||||||
|
|
||||||
|
const key = symbolMappings [prefix] //prefix === '.' ? 'class' : prefix === '#' ? 'id' : 'ref';
|
||||||
|
// #ifdef MP
|
||||||
|
return selectMPComponent(key, name, context, needAll)
|
||||||
|
// #endif
|
||||||
|
// #ifdef H5
|
||||||
|
return selectH5Component(key, name, context, needAll)
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP
|
||||||
|
return selectAPPComponent(key, name, context, needAll, node)
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { type ComponentPublicInstance } from 'vue';
|
||||||
|
// #ifdef APP
|
||||||
|
function findChildren(selector: string, context: ComponentPublicInstance, needAll: boolean): ComponentPublicInstance [] | null{
|
||||||
|
let result:ComponentPublicInstance[] = []
|
||||||
|
|
||||||
|
if(context !== null && context.$children.length > 0) {
|
||||||
|
const queue:ComponentPublicInstance[] = [...context.$children];
|
||||||
|
while(queue.length > 0) {
|
||||||
|
const child = queue.shift();
|
||||||
|
const name = child?.$options?.name;
|
||||||
|
if(name == selector) {
|
||||||
|
result.push(child as ComponentPublicInstance)
|
||||||
|
} else {
|
||||||
|
const children = child?.$children
|
||||||
|
if(children !== null) {
|
||||||
|
queue.push(...children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(result.length > 0 && !needAll) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(result.length > 0) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
class Query {
|
||||||
|
context : ComponentPublicInstance | null = null
|
||||||
|
selector : string = ''
|
||||||
|
// components : ComponentPublicInstance[] = []
|
||||||
|
constructor(selector : string, context : ComponentPublicInstance | null) {
|
||||||
|
this.selector = selector
|
||||||
|
this.context = context
|
||||||
|
}
|
||||||
|
in(context : ComponentPublicInstance) : Query {
|
||||||
|
return new Query(this.selector, context)
|
||||||
|
}
|
||||||
|
find(): ComponentPublicInstance | null {
|
||||||
|
const selector = this.selector
|
||||||
|
if(selector == '') return null
|
||||||
|
const component = findChildren(selector, this.context!, false)
|
||||||
|
return component != null ? component[0]: null
|
||||||
|
}
|
||||||
|
findAll():ComponentPublicInstance[] | null {
|
||||||
|
const selector = this.selector
|
||||||
|
if(selector == '') return null
|
||||||
|
return findChildren(selector, this.context!, true)
|
||||||
|
}
|
||||||
|
closest(): ComponentPublicInstance | null {
|
||||||
|
const selector = this.selector
|
||||||
|
if(selector == '') return null
|
||||||
|
let parent = this.context!.$parent
|
||||||
|
let name = parent?.$options?.name;
|
||||||
|
while (parent != null && (name == null || selector != name)) {
|
||||||
|
parent = parent.$parent
|
||||||
|
if (parent != null) {
|
||||||
|
name = parent.$options.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectComponent(selector: string): Query{
|
||||||
|
return new Query(selector, null)
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// selectComponent('selector').in(this).find()
|
||||||
|
// selectComponent('selector').in(this).findAll()
|
||||||
|
// selectComponent('selector').in(this).closest()
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// #ifdef MP
|
||||||
|
function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean) {
|
||||||
|
const { proxy, $vm } = context
|
||||||
|
context = $vm || proxy
|
||||||
|
if ((selector.startsWith('.') || selector.startsWith('#'))) {
|
||||||
|
const queue = [context]
|
||||||
|
let result = null
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const child = queue.shift();
|
||||||
|
const flag = child?.selectComponent(selector)
|
||||||
|
if (flag) {
|
||||||
|
if (!needAll) { return result = flag.$vm }
|
||||||
|
return result = child.selectAllComponents(selector).map(item => item.$vm)
|
||||||
|
} else {
|
||||||
|
child.$children && (queue.push(...child.$children));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
const { $templateRefs } = context.$
|
||||||
|
const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector
|
||||||
|
const nameMap = {}
|
||||||
|
for (var i = 0; i < $templateRefs.length; i++) {
|
||||||
|
const item = $templateRefs[i]
|
||||||
|
nameMap[item.i] = item.r
|
||||||
|
}
|
||||||
|
let result = []
|
||||||
|
if (context.$children.length) {
|
||||||
|
const queue = [...context.$children]
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const child = queue.shift();
|
||||||
|
if (child.type?.name === selectorValue || child.$?.type?.name === selectorValue) {
|
||||||
|
result.push(child)
|
||||||
|
} else if (child.$refs && child.$refs[selectorValue]) {
|
||||||
|
result = child.$refs[selectorValue]
|
||||||
|
} else if (nameMap[child.id] === selectorValue) {
|
||||||
|
result.push(child)
|
||||||
|
} else {
|
||||||
|
child.$children && (queue.push(...child.$children));
|
||||||
|
}
|
||||||
|
if (result.length && !needAll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needAll ? result : result[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean){
|
||||||
|
const {_, component } = context
|
||||||
|
const child = {component: _ || component || context, children: null , subTree: null, props: null}
|
||||||
|
let result = []
|
||||||
|
let queue = [child]
|
||||||
|
const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector
|
||||||
|
while(queue.length > 0 ) {
|
||||||
|
const child = queue.shift()
|
||||||
|
const {component, children , props, subTree} = child
|
||||||
|
if(component?.type?.name == selectorValue) {
|
||||||
|
result.push(component)
|
||||||
|
} else if(selector.startsWith('$') && component && (props?.ref == selectorValue || component[key][selectorValue])) {
|
||||||
|
if(props?.ref == selectorValue) {
|
||||||
|
//exposed
|
||||||
|
result.push(component)
|
||||||
|
} else if(component[key][selectorValue]) {
|
||||||
|
result.push(component[key][selectorValue])
|
||||||
|
}
|
||||||
|
} else if(!selector.startsWith('$') && component?.exposed && new RegExp(`\\b${selectorValue}\\b`).test(component.attrs[key])) {
|
||||||
|
// exposed
|
||||||
|
result.push(component)
|
||||||
|
} else if(children && Array.isArray(children)) {
|
||||||
|
queue.push(...children)
|
||||||
|
} else if(!component && subTree) {
|
||||||
|
queue.push(subTree)
|
||||||
|
} else if(component?.subTree) {
|
||||||
|
queue.push(component.subTree)
|
||||||
|
}
|
||||||
|
if(result.length && !needAll) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needAll ? result : result[0]
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP
|
||||||
|
function findChildren(selector : string, context : ComponentPublicInstance, needAll : boolean){
|
||||||
|
let result = []
|
||||||
|
const selectorValue = /#|\.|@|$/.test(selector) ? selector.substring(1) : selector
|
||||||
|
const queue = [context]
|
||||||
|
while(queue.length > 0) {
|
||||||
|
const child = queue.shift()
|
||||||
|
const {component, children, props, subTree} = child
|
||||||
|
const isComp = component && props && component.exposed && !node
|
||||||
|
if(child.type && child.type.name === selectorValue) {
|
||||||
|
result.push(component)
|
||||||
|
} else if(props?.[key] === selectorValue && node) {
|
||||||
|
result.push(child)
|
||||||
|
} else if(selector.startsWith('$') && isComp && (props.ref === selectorValue || props.ref_key === selectorValue)) {
|
||||||
|
// exposed
|
||||||
|
result.push(component)
|
||||||
|
} else if(!selector.startsWith('$') && isComp && new RegExp(`\\b${selectorValue}\\b`).test(props[key])) {
|
||||||
|
// exposed
|
||||||
|
result.push(component)
|
||||||
|
}
|
||||||
|
else if(subTree) {
|
||||||
|
queue.push(subTree)
|
||||||
|
} else if(component && component.subTree){
|
||||||
|
queue.push(component.subTree)
|
||||||
|
}
|
||||||
|
else if(children && Array.isArray(children)) {
|
||||||
|
queue.push(...children)
|
||||||
|
}
|
||||||
|
if(result.length && !needAll) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needAll ? result : result[0]
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
class Query {
|
||||||
|
context : ComponentPublicInstance | null = null
|
||||||
|
selector : string = ''
|
||||||
|
// components : ComponentPublicInstance[] = []
|
||||||
|
constructor(selector : string, context : ComponentPublicInstance | null) {
|
||||||
|
this.selector = selector
|
||||||
|
this.context = context
|
||||||
|
}
|
||||||
|
in(context : ComponentPublicInstance) : Query {
|
||||||
|
return new Query(this.selector, context)
|
||||||
|
}
|
||||||
|
find() : ComponentPublicInstance | null {
|
||||||
|
return findChildren(this.selector, this.context, false)
|
||||||
|
}
|
||||||
|
findAll() : ComponentPublicInstance[] | null {
|
||||||
|
return findChildren(this.selector, this.context, true)
|
||||||
|
}
|
||||||
|
closest() : ComponentPublicInstance | null {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectComponent(selector: string) {
|
||||||
|
return new Query(selector)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import {isDef} from '../isDef'
|
||||||
|
// #ifdef UNI-APP-X
|
||||||
|
import {type ComponentPublicInstance} from '../vue'
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
type HasSelectorFunc = (selector : string, element : UniElement) => boolean
|
||||||
|
|
||||||
|
const hasSelectorClassName : HasSelectorFunc = (selector : string, element : UniElement) : boolean => {
|
||||||
|
return element.classList.includes(selector)
|
||||||
|
}
|
||||||
|
const hasSelectorId : HasSelectorFunc = (selector : string, element : UniElement) : boolean => {
|
||||||
|
return element.getAttribute("id") == selector
|
||||||
|
}
|
||||||
|
const hasSelectorTagName : HasSelectorFunc = (selector : string, element : UniElement) : boolean => {
|
||||||
|
return element.tagName!.toLowerCase() == selector.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProcessSelectorResult = {
|
||||||
|
selectorValue : string
|
||||||
|
hasSelector : HasSelectorFunc
|
||||||
|
}
|
||||||
|
const processSelector = (selector : string) : ProcessSelectorResult => {
|
||||||
|
|
||||||
|
const selectorValue = /#|\./.test(selector) ? selector.substring(1) : selector
|
||||||
|
let hasSelector : HasSelectorFunc
|
||||||
|
|
||||||
|
if (selector.startsWith('.')) {
|
||||||
|
hasSelector = hasSelectorClassName
|
||||||
|
} else if (selector.startsWith('#')) {
|
||||||
|
hasSelector = hasSelectorId
|
||||||
|
} else {
|
||||||
|
hasSelector = hasSelectorTagName
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectorValue,
|
||||||
|
hasSelector
|
||||||
|
} as ProcessSelectorResult
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isNotEmptyString(str:string): boolean {
|
||||||
|
return str.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isElement(element:UniElement|null):boolean {
|
||||||
|
return isDef(element) && element?.tagName != 'COMMENT';
|
||||||
|
}
|
||||||
|
|
||||||
|
type ElementArray = Array<UniElement|null>
|
||||||
|
class Query {
|
||||||
|
context : ComponentPublicInstance | null = null
|
||||||
|
selector : string = ''
|
||||||
|
elements : ElementArray = []
|
||||||
|
constructor(selector : string | null, context : ComponentPublicInstance | null) {
|
||||||
|
this.context = context
|
||||||
|
if(selector != null){
|
||||||
|
this.selector = selector
|
||||||
|
}
|
||||||
|
this.find(this.selector)
|
||||||
|
}
|
||||||
|
in(context : ComponentPublicInstance) : Query {
|
||||||
|
return new Query(this.selector, context)
|
||||||
|
}
|
||||||
|
findAll(selector : string): Query {
|
||||||
|
if (isDef(this.context)) {
|
||||||
|
const root = this.context?.$el //as Element | null;
|
||||||
|
if (isDef(root)) {
|
||||||
|
this.elements = [root!] //as ElementArray
|
||||||
|
}
|
||||||
|
const { selectorValue, hasSelector } = processSelector(selector)
|
||||||
|
const foundElements : ElementArray = [];
|
||||||
|
|
||||||
|
function findChildren(element : UniElement) {
|
||||||
|
element.children.forEach((child : UniElement) => {
|
||||||
|
if (hasSelector(selectorValue, child)) {
|
||||||
|
foundElements.push(child)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.elements.forEach(el => {
|
||||||
|
findChildren(el!);
|
||||||
|
});
|
||||||
|
this.elements = foundElements
|
||||||
|
} else if (selector.startsWith('#')) {
|
||||||
|
const element = uni.getElementById(selector)
|
||||||
|
if (isElement(element!)) {
|
||||||
|
this.elements = [element]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 在当前元素集合中查找匹配的元素
|
||||||
|
*/
|
||||||
|
find(selector : string) : Query {
|
||||||
|
if (isDef(this.context)) {
|
||||||
|
const root = this.context?.$el //as Element | null;
|
||||||
|
if (isElement(root)) {
|
||||||
|
this.elements = [root] //as ElementArray
|
||||||
|
}
|
||||||
|
if(isNotEmptyString(selector) && this.elements.length > 0){
|
||||||
|
const { selectorValue, hasSelector } = processSelector(selector)
|
||||||
|
const foundElements : ElementArray = [];
|
||||||
|
function findChildren(element : UniElement) {
|
||||||
|
element.children.forEach((child : UniElement) => {
|
||||||
|
if (hasSelector(selectorValue, child) && foundElements.length < 1) {
|
||||||
|
foundElements.push(child)
|
||||||
|
}
|
||||||
|
if (foundElements.length < 1) {
|
||||||
|
findChildren(child);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.elements.forEach(el => {
|
||||||
|
findChildren(el!);
|
||||||
|
});
|
||||||
|
this.elements = foundElements
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (selector.startsWith('#')) {
|
||||||
|
const element = uni.getElementById(selector)
|
||||||
|
if (isElement(element!)) {
|
||||||
|
this.elements = [element]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取当前元素集合的直接子元素
|
||||||
|
*/
|
||||||
|
children() : Query {
|
||||||
|
// if (this.elements.length > 0) {
|
||||||
|
// const children = this.elements.reduce((acc, el) => [...acc, ...Array.from(el.children)], []);
|
||||||
|
// this.elements = children;
|
||||||
|
// }
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取当前元素集合的父元素
|
||||||
|
*/
|
||||||
|
parent() : Query {
|
||||||
|
// if (this.elements.length > 0) {
|
||||||
|
// const parents = this.elements.map(el => el.parentElement).filter(parent => parent !== null) as ElementArray;
|
||||||
|
// this.elements = parents
|
||||||
|
// // this.elements = Array.from(new Set(parents));
|
||||||
|
// }
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取当前元素集合的兄弟元素
|
||||||
|
*/
|
||||||
|
siblings() : Query {
|
||||||
|
// if (this.elements.length > 0) {
|
||||||
|
// const siblings = this.elements.reduce((acc, el) => [...acc, ...Array.from(el.parentElement?.children || [])], []);
|
||||||
|
// this.elements = siblings.filter(sibling => sibling !== null && !this.elements?.includes(sibling));
|
||||||
|
// }
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取当前元素集合的下一个兄弟元素
|
||||||
|
*/
|
||||||
|
next() : Query {
|
||||||
|
// if (this.elements.length > 0) {
|
||||||
|
// const nextElements = this.elements.map(el => el.nextElementSibling).filter(next => next !== null) as ElementArray;
|
||||||
|
// this.elements = nextElements;
|
||||||
|
// }
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取当前元素集合的上一个兄弟元素
|
||||||
|
*/
|
||||||
|
prev() : Query {
|
||||||
|
// if (this.elements.length > 0) {
|
||||||
|
// const prevElements = this.elements.map(el => el.previousElementSibling).filter(prev => prev !== null) as ElementArray;
|
||||||
|
// this.elements = prevElements;
|
||||||
|
// }
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 从当前元素开始向上查找匹配的元素
|
||||||
|
*/
|
||||||
|
closest(selector : string) : Query {
|
||||||
|
if (isDef(this.context)) {
|
||||||
|
// && this.context.$parent != null && this.context.$parent.$el !== null
|
||||||
|
if(this.elements.length == 0 && isDef(this.context?.$parent) && isElement(this.context!.$parent?.$el)){
|
||||||
|
this.elements = [this.context!.$parent?.$el!]
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectorsArray = selector.split(',')
|
||||||
|
// const { selectorValue, hasSelector } = processSelector(selector)
|
||||||
|
const processedSelectors = selectorsArray.map((selector: string):ProcessSelectorResult => processSelector(selector))
|
||||||
|
const closestElements = this.elements.map((el) : UniElement | null => {
|
||||||
|
let closestElement : UniElement | null = el
|
||||||
|
while (closestElement !== null) {
|
||||||
|
// if (hasSelector(selectorValue, closestElement)) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
const isMatchingSelector = processedSelectors.some(({selectorValue, hasSelector}):boolean => {
|
||||||
|
return hasSelector(selectorValue, closestElement!)
|
||||||
|
})
|
||||||
|
if(isMatchingSelector){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closestElement = closestElement.parentElement;
|
||||||
|
}
|
||||||
|
return closestElement
|
||||||
|
})
|
||||||
|
this.elements = closestElements.filter((closest : UniElement | null) : boolean => isDef(closest))// as ElementArray
|
||||||
|
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从当前元素集合中过滤出匹配的元素
|
||||||
|
*/
|
||||||
|
filter() : Query {
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 从当前元素集合中排除匹配的元素
|
||||||
|
*/
|
||||||
|
not() { }
|
||||||
|
/**
|
||||||
|
* 从当前元素集合中查找包含匹配元素的元素
|
||||||
|
*/
|
||||||
|
has() { }
|
||||||
|
/**
|
||||||
|
* 获取当前元素集合的第一个
|
||||||
|
*/
|
||||||
|
first() : Query {
|
||||||
|
if (this.elements.length > 0) {
|
||||||
|
// this.elements = [this.elements[0]];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 最后一个元素
|
||||||
|
*/
|
||||||
|
last() : Query {
|
||||||
|
if (this.elements.length > 0) {
|
||||||
|
// this.elements = [this.elements[this.elements.length - 1]];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取当前元素在其兄弟元素中的索引
|
||||||
|
*/
|
||||||
|
index() : number | null {
|
||||||
|
// if (this.elements.length > 0 && this.elements.length > 0 && this.elements[0].parentElement !== null) {
|
||||||
|
// return Array.from(this.elements[0].parentElement.children).indexOf(this.elements[0]);
|
||||||
|
// }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
get(index : number) : UniElement | null {
|
||||||
|
if (this.elements.length > index) {
|
||||||
|
return this.elements[index] //as Element
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectElement(selector : string | null = null) : Query {
|
||||||
|
// if(typeof selector == 'string' || selector == null){
|
||||||
|
// return new Query(selector as string | null, null)
|
||||||
|
// }
|
||||||
|
// else if(selector instanceof ComponentPublicInstance){
|
||||||
|
// return new Query(null, selector)
|
||||||
|
// }
|
||||||
|
return new Query(selector, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// $('xxx').in(this).find('xxx')
|
||||||
|
// $('xxx').in(this).get()
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
/**
|
||||||
|
* 随机化数组中元素的顺序,使用 Fisher-Yates 算法
|
||||||
|
* @description 函数接受一个数组作为参数,返回一个新的数组,其中包含原数组随机化顺序后的元素。
|
||||||
|
* @param arr 要随机化的数组
|
||||||
|
* @returns 一个新的数组,其中包含原数组随机化顺序后的元素。
|
||||||
|
*/
|
||||||
|
export function shuffle<T>(arr : T[]) : T[] {
|
||||||
|
for (let i = arr.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1))
|
||||||
|
const temp = arr[i]
|
||||||
|
arr[i] = arr[j]
|
||||||
|
arr[j] = temp
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
/**
|
||||||
|
* 节流函数,用于限制函数的调用频率
|
||||||
|
* @param fn 要进行节流的函数
|
||||||
|
* @param delay 两次调用之间的最小间隔时间
|
||||||
|
* @returns 节流后的函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
// #ifndef UNI-APP-X && APP
|
||||||
|
// export function throttle(fn: (...args: any[]) => void, delay: number, options) {
|
||||||
|
// let flag = true; // 标记是否可以执行函数
|
||||||
|
|
||||||
|
// return (...args: any[]) => {
|
||||||
|
// if (flag) {
|
||||||
|
// flag = false; // 设置为不可执行状态
|
||||||
|
// fn(...args); // 执行传入的函数
|
||||||
|
|
||||||
|
// setTimeout(() => {
|
||||||
|
// flag = true; // 经过指定时间后,设置为可执行状态
|
||||||
|
// }, delay);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function throttle<T extends (...args: any[]) => void>(
|
||||||
|
fn: T,
|
||||||
|
delay: number,
|
||||||
|
{ leading = true, trailing = true }: { leading?: boolean; trailing?: boolean } = {}
|
||||||
|
): (...args: Parameters<T>) => void {
|
||||||
|
let isCoolingDown = false;
|
||||||
|
let lastArgs: Parameters<T> | null = null;
|
||||||
|
let timerId: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
const executeTrailing = () => {
|
||||||
|
if (trailing && lastArgs) {
|
||||||
|
fn(...lastArgs);
|
||||||
|
lastArgs = null;
|
||||||
|
}
|
||||||
|
isCoolingDown = false;
|
||||||
|
timerId = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return function (...args: Parameters<T>) {
|
||||||
|
// 1. 如果不在冷却期,且 leading=true,立即执行
|
||||||
|
if (!isCoolingDown && leading) {
|
||||||
|
fn(...args);
|
||||||
|
isCoolingDown = true;
|
||||||
|
timerId = setTimeout(executeTrailing, delay);
|
||||||
|
}
|
||||||
|
// 2. 如果在冷却期,记录最后一次调用的参数(用于 trailing)
|
||||||
|
else if (trailing) {
|
||||||
|
lastArgs = args;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
// #ifdef UNI-APP-X && APP
|
||||||
|
// type Rfun = (...args: any[]) => void
|
||||||
|
// type Rfun = (...args: any[]) => void
|
||||||
|
|
||||||
|
export function throttle<T extends any|null>(
|
||||||
|
fn: (args : T) => void,
|
||||||
|
delay: number):(args : T) => void {
|
||||||
|
let flag = true; // 标记是否可以执行函数
|
||||||
|
|
||||||
|
return (args : T) =>{
|
||||||
|
if(flag){
|
||||||
|
flag = false;
|
||||||
|
fn(args);
|
||||||
|
|
||||||
|
setTimeout(()=>{
|
||||||
|
flag = true;
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return (...args: any[]) => {
|
||||||
|
// // if (flag) {
|
||||||
|
// // flag = false; // 设置为不可执行状态
|
||||||
|
// // fn(...args); // 执行传入的函数
|
||||||
|
|
||||||
|
// // setTimeout(() => {
|
||||||
|
// // flag = true; // 经过指定时间后,设置为可执行状态
|
||||||
|
// // }, delay);
|
||||||
|
// // }
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// // 示例
|
||||||
|
// // 定义一个被节流的函数
|
||||||
|
// function handleScroll() {
|
||||||
|
// console.log("Scroll event handled!");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒
|
||||||
|
// const throttledScroll = throttle(handleScroll, 500);
|
||||||
|
|
||||||
|
// // 模拟多次调用 handleScroll
|
||||||
|
// throttledScroll(); // 输出 "Scroll event handled!"
|
||||||
|
// throttledScroll(); // 不会输出
|
||||||
|
// throttledScroll(); // 不会输出
|
||||||
|
|
||||||
|
// // 经过 500 毫秒后,再次调用 handleScroll
|
||||||
|
// setTimeout(() => {
|
||||||
|
// throttledScroll(); // 输出 "Scroll event handled!"
|
||||||
|
// }, 500);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { isNumber } from '../isNumber'
|
||||||
|
import { isString } from '../isString'
|
||||||
|
// 函数重载,定义多个函数签名
|
||||||
|
// function toBoolean(value : any) : boolean;
|
||||||
|
// function toBoolean(value : string) : boolean;
|
||||||
|
// function toBoolean(value : number) : boolean;
|
||||||
|
// function toBoolean(value : boolean) : boolean;
|
||||||
|
|
||||||
|
// #ifdef UNI-APP-X && APP
|
||||||
|
function toBoolean(value : any | null) : boolean {
|
||||||
|
// 根据输入值的类型,返回相应的布尔值
|
||||||
|
// if (isNumber(value)) {
|
||||||
|
// return (value as number) != 0;
|
||||||
|
// }
|
||||||
|
// if (isString(value)) {
|
||||||
|
// return `${value}`.length > 0;
|
||||||
|
// }
|
||||||
|
// if (typeof value == 'boolean') {
|
||||||
|
// return value as boolean;
|
||||||
|
// }
|
||||||
|
// #ifdef APP-IOS || APP-HARMONY
|
||||||
|
return value != null && value != undefined
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
return value != null
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
// #ifndef UNI-APP-X && APP
|
||||||
|
function toBoolean(value : any | null) : value is NonNullable<typeof value> {
|
||||||
|
return !!value//value !== null && value !== undefined;
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
export {
|
||||||
|
toBoolean
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
// 来自kux大佬的神之一手
|
||||||
|
// 原地址 https://ext.dcloud.net.cn/plugin?id=23291
|
||||||
|
export type ControlCommand = 'continue' | 'break' | null;
|
||||||
|
|
||||||
|
export type ControllableWhileReturn = {
|
||||||
|
start: () => void;
|
||||||
|
abort: () => void;
|
||||||
|
execContinue: () => 'continue';
|
||||||
|
execBreak: () => 'break';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Controller = {
|
||||||
|
abort: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function controllableWhile(
|
||||||
|
condition : () => boolean,
|
||||||
|
body: (controller: Controller) => ControlCommand
|
||||||
|
): ControllableWhileReturn {
|
||||||
|
let isActive = true;
|
||||||
|
|
||||||
|
const controller: Controller = {
|
||||||
|
abort: () => {
|
||||||
|
isActive = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const execContinue = () => 'continue';
|
||||||
|
const execBreak = () => 'break';
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: () => {
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
UTSAndroid.getDispatcher('io').async((_) => {
|
||||||
|
// #endif
|
||||||
|
while (isActive && condition()) {
|
||||||
|
const result = body(controller);
|
||||||
|
if (result == 'break') {
|
||||||
|
controller.abort();
|
||||||
|
break;
|
||||||
|
} else if (result == 'continue') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #ifdef APP-ANDROID
|
||||||
|
}, null);
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
abort: controller.abort,
|
||||||
|
execContinue: execContinue,
|
||||||
|
execBreak: execBreak
|
||||||
|
} as ControllableWhileReturn;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue