Compare commits
28 Commits
110e9d4adc
...
dev-qsh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a684d255d2 | ||
|
|
70fe0f0c42 | ||
|
|
62896b197d | ||
|
|
9b677ade15 | ||
|
|
4bd08bde70 | ||
|
|
e889c92d35 | ||
|
|
7442289a87 | ||
|
|
d70a0141cc | ||
|
|
459baca3f8 | ||
| f5c0df688c | |||
|
|
38cc93cba4 | ||
| ccef999bbe | |||
| 589eec1d80 | |||
| 242744c0f9 | |||
| e67e1edf12 | |||
| 8f50f42f8c | |||
| 65149c38cd | |||
| c26e306b3a | |||
| 6141270437 | |||
|
|
074b53f307 | ||
|
|
ab05529015 | ||
| 9e24edad28 | |||
| 67b3e63de9 | |||
|
|
056ca965b3 | ||
| 4fd6a21f35 | |||
| 7b1db47383 | |||
| 79236493e6 | |||
| 59c49bfdf2 |
@@ -6,5 +6,8 @@ VITE_APP_ENV = 'development'
|
||||
|
||||
# 金武联驾校/开发环境
|
||||
VITE_APP_BASE_API = 'https://jwl.ahduima.com/'
|
||||
# VITE_APP_BASE_API = 'http://tk-api.ahfkbg.com/'
|
||||
#
|
||||
VITE_WEB_BASE_URL = 'https://jwl.ahduima.com'
|
||||
VITE_WEB_BASE_URL = 'https://jwl.ahduima.com'
|
||||
# 资源地址
|
||||
VITE_RESOURSE_URL = 'http://huodong.ahduima.com'
|
||||
@@ -9,3 +9,6 @@ VITE_APP_BASE_API = 'https://jwl.ahduima.com/'
|
||||
|
||||
#
|
||||
VITE_WEB_BASE_URL = 'https://jwl.ahduima.com'
|
||||
|
||||
# 资源地址
|
||||
VITE_RESOURSE_URL = 'http://huodong.ahduima.com'
|
||||
@@ -4,8 +4,10 @@
|
||||
"scripts": {
|
||||
"dev:h5": "uni",
|
||||
"dev": "uni -p mp-weixin",
|
||||
"dev:dy": "uni -p mp-toutiao",
|
||||
"build:h5": "uni build",
|
||||
"build": "uni build -p mp-weixin",
|
||||
"build:dy": "uni build -p mp-toutiao",
|
||||
"build-test:mp-weixin": "uni --mode test -p mp-weixin"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -14,6 +16,7 @@
|
||||
"@dcloudio/uni-components": "3.0.0-alpha-3060420220922001",
|
||||
"@dcloudio/uni-h5": "3.0.0-alpha-3060420220922001",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3060420220922001",
|
||||
"@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3060420220922001",
|
||||
"jsencrypt-plus": "^0.1.0",
|
||||
"pinia": "2.0.36",
|
||||
"pinia-plugin-persist-uni": "^1.2.0",
|
||||
|
||||
28
project.config.json
Normal file
28
project.config.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"appid": "wx24c1b58020a5ce66",
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "3.3.3",
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"setting": {
|
||||
"coverView": true,
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"enhance": true,
|
||||
"showShadowRootInWxmlPanel": true,
|
||||
"packNpmRelationList": [],
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
}
|
||||
},
|
||||
"condition": {},
|
||||
"editorSetting": {
|
||||
"tabIndent": "insertSpaces",
|
||||
"tabSize": 2
|
||||
}
|
||||
}
|
||||
7
project.private.config.json
Normal file
7
project.private.config.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
|
||||
"projectname": "jwl-applet",
|
||||
"setting": {
|
||||
"compileHotReLoad": true
|
||||
}
|
||||
}
|
||||
1404
src/components/yan-qr/qrcode.js
Normal file
1404
src/components/yan-qr/qrcode.js
Normal file
File diff suppressed because it is too large
Load Diff
95
src/components/yan-qr/yan-qr.md
Normal file
95
src/components/yan-qr/yan-qr.md
Normal file
@@ -0,0 +1,95 @@
|
||||
## 使用方式
|
||||
``` javascript
|
||||
<yan-qr />
|
||||
```
|
||||
|
||||
## 属性说明
|
||||
|属性名 |类型 | 默认值 |说明 |
|
||||
|-- |-- |-----------|-- |
|
||||
|canvasId |String | 'qrcode' |canvas-id |
|
||||
|text |String | 'hello' |二维码内容 |
|
||||
|size |Number | 340 |单位是px |
|
||||
|margin |Number | 0 |边距 |
|
||||
|level |String | 'L' |二维码解析度L/M/Q/H |
|
||||
|fColor |String | '#000000' |二维码颜色 |
|
||||
|bColor |String | '#ffffff' |二维码背景颜色 |
|
||||
|fileType |String | 'png' |二维码图片类型 |
|
||||
|
||||
## 示例代码
|
||||
``` javascript
|
||||
<template>
|
||||
<view>
|
||||
<view>
|
||||
<view>
|
||||
<view>需要转换的文本:</view>
|
||||
<textarea v-model="content" @blur="inputText" placeholder="请在这里输入" />
|
||||
</view>
|
||||
</view>
|
||||
二维码
|
||||
<view>
|
||||
<yan-qr :filePath.sync="filePath" :text="text" :margin="20" />
|
||||
</view>
|
||||
二维码图片地址
|
||||
<view>
|
||||
<textarea v-model="filePath" disabled />
|
||||
</view>
|
||||
<button @click='btn'>生成二维码</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
qrShow: false,
|
||||
content: '',
|
||||
filePath: '',
|
||||
text: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filePath() {
|
||||
console.log(this.filePath);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
//*获取文本框内容*//
|
||||
inputText: function(e) {
|
||||
this.content = e.detail.value
|
||||
},
|
||||
|
||||
//*按钮*//
|
||||
btn: function() {
|
||||
if (this.content == '') {
|
||||
uni.showToast({ //显示对话框
|
||||
title: "请输入文本",
|
||||
icon: 'none',
|
||||
duration: 1000,
|
||||
})
|
||||
} else {
|
||||
this.text = this.content
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
border: 1px solid #000000;
|
||||
width: 98%;
|
||||
}
|
||||
button {
|
||||
width: 80%;
|
||||
margin-top: 180rpx;
|
||||
border-radius: 25px;
|
||||
color: aliceblue;
|
||||
background-color: #55aaff;
|
||||
}
|
||||
|
||||
</style>
|
||||
```
|
||||
115
src/components/yan-qr/yan-qr.vue
Normal file
115
src/components/yan-qr/yan-qr.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<view>
|
||||
<canvas :canvas-id="canvasId" v-show="qrShow" :style="qrShowStyle" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uQRCode from './qrcode.js'
|
||||
export default {
|
||||
props: {
|
||||
canvasId: {
|
||||
type: String,
|
||||
default: 'qrcode' //canvas-id
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: 'hello' //二维码内容
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 150 //二维码大小
|
||||
},
|
||||
margin: {
|
||||
type: Number,
|
||||
default: 0 //二维码边距
|
||||
},
|
||||
level: {
|
||||
type: String,
|
||||
default: 'L' //二维码质量L/M/Q/H
|
||||
},
|
||||
bColor: {
|
||||
type: String,
|
||||
default: '#ffffff' //二维码背景颜色
|
||||
},
|
||||
fColor: {
|
||||
type: String,
|
||||
default: '#000000' //二维码颜色
|
||||
},
|
||||
fileType: {
|
||||
type: String,
|
||||
default: 'png' //二维码图片类型
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
qrShow: false,
|
||||
qrShowStyle: {
|
||||
"width": "150px",
|
||||
"height": "150px",
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
text(newVal, oldVal) {
|
||||
this.onQrFun()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.onQrFun()
|
||||
},
|
||||
methods: {
|
||||
onQrFun() {
|
||||
this.qrShow = this.text != ''
|
||||
if(this.qrShow){
|
||||
this.qrShowStyle.width = this.size+"px"
|
||||
this.qrShowStyle.height = this.size+"px"
|
||||
let level;
|
||||
switch (this.level) {
|
||||
case "M":
|
||||
case "m":
|
||||
level = uQRCode.errorCorrectLevel.M
|
||||
break;
|
||||
case "Q":
|
||||
case "q":
|
||||
level = uQRCode.errorCorrectLevel.Q
|
||||
break;
|
||||
case "H":
|
||||
case "h":
|
||||
level = uQRCode.errorCorrectLevel.H
|
||||
break;
|
||||
default:
|
||||
level = uQRCode.errorCorrectLevel.L
|
||||
break;
|
||||
}
|
||||
uQRCode.make({
|
||||
canvasId: this.canvasId,
|
||||
componentInstance: this,
|
||||
text: this.text,
|
||||
size: this.size,
|
||||
margin: this.margin,
|
||||
backgroundColor: this.bColor,
|
||||
foregroundColor: this.fColor,
|
||||
fileType: this.fileType,
|
||||
errorCorrectLevel: level,
|
||||
success: res => {}
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: this.canvasId,
|
||||
success: (res) => {
|
||||
// 在H5平台下,tempFilePath 为 base64
|
||||
this.$emit("update:filePath", res.tempFilePath);
|
||||
}
|
||||
}, this);
|
||||
}, 600)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
118
src/jtools/api/activity.js
Normal file
118
src/jtools/api/activity.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import request from '../request/index.js';
|
||||
|
||||
//查询活动列表
|
||||
export function queryActivityList(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/list',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
//查询活动详情
|
||||
export function queryActivityDetail(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/detail',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
//查询抽奖次数
|
||||
export function queryLuckyNum(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/lucky/num',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
//查询中奖结果
|
||||
export function queryLuckyResult(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/lucky/result',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
//录入中奖结果
|
||||
export function saveWinner(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/winner/save',
|
||||
method: 'POST',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
//核销
|
||||
export function receiveWinner(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/winner/receive',
|
||||
method: 'POST',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
//查询中奖记录
|
||||
export function getLuckyRecord(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/lucky/record',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
|
||||
//查询中奖记录
|
||||
export function canRecieveGift(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/receive/user',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
|
||||
//查询中奖信息
|
||||
export function queryWinnerInfo(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/winner/info',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
|
||||
//查询助力信息
|
||||
export function queryHelpInfo(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/help/info',
|
||||
method: 'get',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
|
||||
//保存助力信息
|
||||
export function saveHelpInfo(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/help/save',
|
||||
method: 'post',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//微信登录获取手机号
|
||||
export function wxLogin(data) {
|
||||
return request({
|
||||
url: 'activity/applet/activity/wx/login',
|
||||
method: 'post',
|
||||
data,
|
||||
noToken: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ function service(options = {}) {
|
||||
useUserStore().logoutWithoutToken()
|
||||
//请求成功
|
||||
resolved(res.data);
|
||||
} else if(res.data.code != '0000'&&res.data.code !='4001') {
|
||||
} else if(res.data.code != '0000'&&res.data.code !='4001'&&res.data.code != 200) {
|
||||
uni.showToast({
|
||||
title: res?.data?.message || '访问出错',
|
||||
icon: 'none'
|
||||
|
||||
@@ -1,238 +1,157 @@
|
||||
import {
|
||||
defineStore
|
||||
} from 'pinia';
|
||||
import { defineStore } from 'pinia';
|
||||
import http from '@/jtools/request/index';
|
||||
import {
|
||||
queryQuestion,
|
||||
getVersion,
|
||||
querySysConfig
|
||||
} from '@/jtools/api/question';
|
||||
import { queryQuestion, getVersion, querySysConfig } from '@/jtools/api/question';
|
||||
import storage from '@/jtools/storage';
|
||||
let JSON_SPLIT_LENGTH = 10;
|
||||
|
||||
const question = defineStore({
|
||||
id: 'question',
|
||||
state: () => ({
|
||||
currentCartype: storage.get('carType') || '1001',
|
||||
currentCarName: storage.get('carName') || '小车C1/C2/C3',
|
||||
orderQuestion_subject1: storage.get('question_subject1') || [], //科目一顺序做题
|
||||
orderQuestion_subject4: storage.get('question_subject4') || [], //科目四顺序做题
|
||||
currentIndex_subject1: 0, //科目一索引 顺序做题
|
||||
currentIndex_subject4: 0, //科目四索引 顺序做题
|
||||
curSubject: storage.get('curSubject') || '1',
|
||||
loading_subject1: false,
|
||||
loading_subject4: false,
|
||||
version: storage.get('version') || ''
|
||||
}),
|
||||
id: 'question',
|
||||
state: () => ({
|
||||
currentCartype: storage.get('carType') || '1001',
|
||||
currentCarName: storage.get('carName') || '小车C1/C2/C3',
|
||||
orderQuestion_subject1: [], //科目一顺序做题
|
||||
orderQuestion_subject4: [], //科目四顺序做题
|
||||
currentIndex_subject1: 0, //科目一索引 顺序做题
|
||||
currentIndex_subject4: 0, //科目四索引 顺序做题
|
||||
curSubject: storage.get('curSubject') || '1',
|
||||
loading_subject1: false,
|
||||
loading_subject4: false,
|
||||
version: storage.get('version') || ''
|
||||
}),
|
||||
|
||||
actions: {
|
||||
resetStorage(){
|
||||
this.currentIndex_subject1=0
|
||||
this.currentIndex_subject4=0
|
||||
this.curSubject=0
|
||||
storage.remove('curSubject')
|
||||
storage.remove('wrongList_subject1')
|
||||
storage.remove('wrongList_subject4')
|
||||
storage.remove('rightList_subject1')
|
||||
storage.remove('rightList_subject4')
|
||||
this.getAllQuestion()
|
||||
},
|
||||
getAllQuestion() {
|
||||
this.currentCartype = storage.get('carType') || '1001'
|
||||
getVersion(this.currentCartype).then(resp => {
|
||||
if (resp.code === '0000') {
|
||||
querySysConfig(this.currentCartype, 'QuestionBank').then(res => {
|
||||
const urlList = JSON.parse(res.data.configJson)
|
||||
const urlOne = urlList.find(item => item.subject == '1').url
|
||||
const urlFour = urlList.find(item => item.subject == '4').url
|
||||
if (this.version != resp.data) {
|
||||
this.version = resp.data
|
||||
storage.set('version', resp.data)
|
||||
this.getOrderQuestion_sub1(true, urlOne)
|
||||
this.getOrderQuestion_sub4(true, urlFour)
|
||||
} else {
|
||||
this.getOrderQuestion_sub1(false, urlOne)
|
||||
this.getOrderQuestion_sub4(false, urlOne)
|
||||
}
|
||||
})
|
||||
actions: {
|
||||
resetStorage() {
|
||||
this.currentIndex_subject1 = 0;
|
||||
this.currentIndex_subject4 = 0;
|
||||
this.curSubject = 0;
|
||||
storage.remove('curSubject');
|
||||
storage.remove('wrongList_subject1');
|
||||
storage.remove('wrongList_subject4');
|
||||
storage.remove('rightList_subject1');
|
||||
storage.remove('rightList_subject4');
|
||||
// this.getAllQuestion()
|
||||
},
|
||||
getAllQuestion() {
|
||||
this.currentCartype = storage.get('carType') || '1001';
|
||||
getVersion(this.currentCartype).then(resp => {
|
||||
if (resp.code === '0000') {
|
||||
querySysConfig(this.currentCartype, 'QuestionBank').then(res => {
|
||||
const urlList = JSON.parse(res.data.configJson);
|
||||
const urlOne = urlList.find(item => item.subject == '1').url;
|
||||
const urlFour = urlList.find(item => item.subject == '4').url;
|
||||
if (this.version != resp.data) {
|
||||
this.version = resp.data;
|
||||
storage.set('version', resp.data);
|
||||
this.getOrderQuestion_sub1(true, urlOne);
|
||||
this.getOrderQuestion_sub4(true, urlFour);
|
||||
} else {
|
||||
this.getOrderQuestion_sub1(false, urlOne);
|
||||
this.getOrderQuestion_sub4(false, urlFour);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
divideArray(array, numChunks) {
|
||||
var chunkSize = Math.ceil(array.length / numChunks);
|
||||
var dividedArray = [];
|
||||
for (var i = 0; i < array.length; i += chunkSize) {
|
||||
dividedArray.push(array.slice(i, i + chunkSize));
|
||||
}
|
||||
return dividedArray;
|
||||
},
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
divideArray(array, numChunks) {
|
||||
var chunkSize = Math.ceil(array.length / numChunks);
|
||||
var dividedArray = [];
|
||||
for (var i = 0; i < array.length; i += chunkSize) {
|
||||
dividedArray.push(array.slice(i, i + chunkSize));
|
||||
}
|
||||
return dividedArray;
|
||||
},
|
||||
|
||||
//改变当前科目
|
||||
changeSubject(val) {
|
||||
this.curSubject = val
|
||||
storage.set('curSubject', val)
|
||||
},
|
||||
// 获取顺序做题科目1
|
||||
getOrderQuestion_sub1(isUpdate, url) {
|
||||
if (isUpdate) {
|
||||
this.loading_subject1 = true
|
||||
const that = this
|
||||
uni.request({
|
||||
url: url,
|
||||
success(resp) {
|
||||
if (resp.data) {
|
||||
that.orderQuestion_subject1 = resp.data.data
|
||||
const diveList = that.divideArray(that.orderQuestion_subject1, 5)
|
||||
that.loading_subject1 = false
|
||||
uni.setStorageSync('questionOneSub1', diveList[0])
|
||||
uni.setStorageSync('questionOneSub2', diveList[1])
|
||||
uni.setStorageSync('questionOneSub3', diveList[2])
|
||||
uni.setStorageSync('questionOneSub4', diveList[3])
|
||||
uni.setStorageSync('questionOneSub5', diveList[4])
|
||||
const falseList = storage.get('wrongList_subject1') || []
|
||||
const trueList = storage.get('rightList_subject1') || []
|
||||
const falseArr = []
|
||||
const rightArr = []
|
||||
that.orderQuestion_subject1.forEach(item => {
|
||||
if (falseList.includes(item.questionId)) {
|
||||
falseArr.push(item.questionId)
|
||||
}
|
||||
if (trueList.includes(item.questionId)) {
|
||||
rightArr.push(item.questionId)
|
||||
}
|
||||
})
|
||||
storage.set('wrongList_subject1', falseArr)
|
||||
storage.set('rightList_subject1', rightArr)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const list1 = uni.getStorageSync('questionOneSub1') || []
|
||||
const list2 = uni.getStorageSync('questionOneSub2') || []
|
||||
const list3 = uni.getStorageSync('questionOneSub3') || []
|
||||
const list4 = uni.getStorageSync('questionOneSub4') || []
|
||||
const list5 = uni.getStorageSync('questionOneSub5') || []
|
||||
this.orderQuestion_subject1 = [...list1, ...list2, ...list3, ...list4, ...list5]
|
||||
if (this.orderQuestion_subject1 && this.orderQuestion_subject1.length) {
|
||||
|
||||
} else {
|
||||
this.loading_subject1 = true
|
||||
const that = this
|
||||
uni.request({
|
||||
url: url,
|
||||
success(resp) {
|
||||
if (resp.data) {
|
||||
that.orderQuestion_subject1 = resp.data.data
|
||||
const diveList = that.divideArray(that.orderQuestion_subject1, 5)
|
||||
that.loading_subject1 = false
|
||||
uni.setStorageSync('questionOneSub1', diveList[0])
|
||||
uni.setStorageSync('questionOneSub2', diveList[1])
|
||||
uni.setStorageSync('questionOneSub3', diveList[2])
|
||||
uni.setStorageSync('questionOneSub4', diveList[3])
|
||||
uni.setStorageSync('questionOneSub5', diveList[4])
|
||||
const falseList = storage.get('wrongList_subject1') || []
|
||||
const trueList = storage.get('rightList_subject1') || []
|
||||
const falseArr = []
|
||||
const rightArr = []
|
||||
that.orderQuestion_subject1.forEach(item => {
|
||||
if (falseList.includes(item.questionId)) {
|
||||
falseArr.push(item.questionId)
|
||||
}
|
||||
if (trueList.includes(item.questionId)) {
|
||||
rightArr.push(item.questionId)
|
||||
}
|
||||
})
|
||||
storage.set('wrongList_subject1', falseArr)
|
||||
storage.set('rightList_subject1', rightArr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// 获取顺序做题科目4
|
||||
getOrderQuestion_sub4(isUpdate, url) {
|
||||
if (isUpdate) {
|
||||
this.loading_subject4 = true
|
||||
const that = this
|
||||
uni.request({
|
||||
url: url,
|
||||
success(resp) {
|
||||
if (resp.data) {
|
||||
that.orderQuestion_subject4 = resp.data.data
|
||||
const diveList = that.divideArray(that.orderQuestion_subject4, 5)
|
||||
that.loading_subject4 = false
|
||||
uni.setStorageSync('questionFourSub1', diveList[0])
|
||||
uni.setStorageSync('questionFourSub2', diveList[1])
|
||||
uni.setStorageSync('questionFourSub3', diveList[2])
|
||||
uni.setStorageSync('questionFourSub4', diveList[3])
|
||||
uni.setStorageSync('questionFourSub5', diveList[4])
|
||||
const falseList = storage.get('wrongList_subject4') || []
|
||||
const trueList = storage.get('rightList_subject4') || []
|
||||
const falseArr = []
|
||||
const rightArr = []
|
||||
that.orderQuestion_subject4.forEach(item => {
|
||||
if (falseList.includes(item.questionId)) {
|
||||
falseArr.push(item.questionId)
|
||||
}
|
||||
if (trueList.includes(item.questionId)) {
|
||||
rightArr.push(item.questionId)
|
||||
}
|
||||
})
|
||||
storage.set('wrongList_subject4', falseArr)
|
||||
storage.set('rightList_subject4', rightArr)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const list1 = uni.getStorageSync('questionFourSub1') || []
|
||||
const list2 = uni.getStorageSync('questionFourSub2') || []
|
||||
const list3 = uni.getStorageSync('questionFourSub3') || []
|
||||
const list4 = uni.getStorageSync('questionFourSub4') || []
|
||||
const list5 = uni.getStorageSync('questionFourSub5') || []
|
||||
this.orderQuestion_subject4 = [...list1, ...list2, ...list3, ...list4, ...list5]
|
||||
if (this.orderQuestion_subject4 && this.orderQuestion_subject4.length) {
|
||||
|
||||
} else {
|
||||
this.loading_subject4 = true
|
||||
const that = this
|
||||
uni.request({
|
||||
url: url,
|
||||
success(resp) {
|
||||
if (resp.data) {
|
||||
that.orderQuestion_subject4 = resp.data.data
|
||||
const diveList = that.divideArray(that.orderQuestion_subject4, 5)
|
||||
that.loading_subject4 = false
|
||||
uni.setStorageSync('questionFourSub1', diveList[0])
|
||||
uni.setStorageSync('questionFourSub2', diveList[1])
|
||||
uni.setStorageSync('questionFourSub3', diveList[2])
|
||||
uni.setStorageSync('questionFourSub4', diveList[3])
|
||||
uni.setStorageSync('questionFourSub5', diveList[4])
|
||||
const falseList = storage.get('wrongList_subject4') || []
|
||||
const trueList = storage.get('rightList_subject4') || []
|
||||
const falseArr = []
|
||||
const rightArr = []
|
||||
that.orderQuestion_subject4.forEach(item => {
|
||||
if (falseList.includes(item.questionId)) {
|
||||
falseArr.push(item.questionId)
|
||||
}
|
||||
if (trueList.includes(item.questionId)) {
|
||||
rightArr.push(item.questionId)
|
||||
}
|
||||
})
|
||||
storage.set('wrongList_subject4', falseArr)
|
||||
storage.set('rightList_subject4', rightArr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
//获取索引
|
||||
getCurrentIndex(index, val) {
|
||||
this[`currentIndex_subject${val}`] = index
|
||||
}
|
||||
}
|
||||
//改变当前科目
|
||||
changeSubject(val) {
|
||||
this.curSubject = val;
|
||||
storage.set('curSubject', val);
|
||||
},
|
||||
// 获取顺序做题科目1
|
||||
getOrderQuestion_sub1(isUpdate, url) {
|
||||
if (isUpdate) {
|
||||
this.loading_subject1 = true;
|
||||
this.loadAllQuestion(url, 1);
|
||||
} else {
|
||||
this.orderQuestion_subject1 = [];
|
||||
for (let i = 0; i < JSON_SPLIT_LENGTH; i++) {
|
||||
const arr = uni.getStorageSync(`question1Sub${i + 1}`) || [];
|
||||
this.orderQuestion_subject1 = [...this.orderQuestion_subject1, ...arr];
|
||||
}
|
||||
if (this.orderQuestion_subject1.length == 0) {
|
||||
this.loading_subject1 = true;
|
||||
this.loadAllQuestion(url, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 获取顺序做题科目4
|
||||
getOrderQuestion_sub4(isUpdate, url) {
|
||||
if (isUpdate) {
|
||||
this.loading_subject4 = true;
|
||||
this.loadAllQuestion(url, 4);
|
||||
} else {
|
||||
this.orderQuestion_subject4 = [];
|
||||
for (let i = 0; i < JSON_SPLIT_LENGTH; i++) {
|
||||
const arr = uni.getStorageSync(`question4Sub${i + 1}`) || [];
|
||||
this.orderQuestion_subject4 = [...this.orderQuestion_subject4, ...arr];
|
||||
}
|
||||
if (this.orderQuestion_subject4.length == 0) {
|
||||
this.loading_subject4 = true;
|
||||
this.loadAllQuestion(url, 4);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 加载线上题目
|
||||
loadAllQuestion(url, course) {
|
||||
const that = this;
|
||||
uni.request({
|
||||
url: url,
|
||||
success(resp) {
|
||||
if (resp?.data) {
|
||||
try {
|
||||
for (let i = 0; i < JSON_SPLIT_LENGTH; i++) {
|
||||
storage.remove(`question${course}Sub${i + 1}`);
|
||||
}
|
||||
that[`orderQuestion_subject${course}`] = resp.data?.data || [];
|
||||
const diveList = that.divideArray(that[`orderQuestion_subject${course}`], JSON_SPLIT_LENGTH);
|
||||
that[`loading_subject${course}`] = false;
|
||||
for (let i = 0; i < JSON_SPLIT_LENGTH; i++) {
|
||||
uni.setStorageSync(`question${course}Sub${i + 1}`, diveList[i]);
|
||||
}
|
||||
const falseList = storage.get(`wrongList_subject${course}`) || [];
|
||||
const trueList = storage.get(`rightList_subject${course}`) || [];
|
||||
const falseArr = [];
|
||||
const rightArr = [];
|
||||
that[`orderQuestion_subject${course}`].forEach(item => {
|
||||
if (falseList.includes(item.questionId)) {
|
||||
falseArr.push(item.questionId);
|
||||
}
|
||||
if (trueList.includes(item.questionId)) {
|
||||
rightArr.push(item.questionId);
|
||||
}
|
||||
});
|
||||
storage.set(`wrongList_subject${course}`, falseArr);
|
||||
storage.set(`rightList_subject${course}`, rightArr);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
uni.showToast({
|
||||
title: '题库下载异常,请稍后再试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '加载题目失败,请稍后再试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
//获取索引
|
||||
getCurrentIndex(index, val) {
|
||||
this[`currentIndex_subject${val}`] = index;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default question;
|
||||
export default question;
|
||||
|
||||
@@ -66,7 +66,13 @@
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
"usingComponents" : true,
|
||||
"appid" : "ttbbd1cd6c24e1c00801",
|
||||
"setting" : {
|
||||
"es6" : true,
|
||||
"postcss" : true,
|
||||
"minified" : true
|
||||
}
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
|
||||
18
src/package.json
Normal file
18
src/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"id": "yan-qr",
|
||||
"name": "动态生成二维码",
|
||||
"displayName": "动态生成二维码",
|
||||
"version": "1.0.0",
|
||||
"description": "动态生成二维码",
|
||||
"keywords": [
|
||||
"二维码",
|
||||
"生成二维码",
|
||||
"动态二维码"
|
||||
],
|
||||
"dcloudext": {
|
||||
"category": [
|
||||
"前端组件",
|
||||
"通用组件"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "金武联驾考",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
@@ -84,13 +84,6 @@
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/tijian",
|
||||
"style": {
|
||||
"navigationBarTitleText": "上传证件照",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/school",
|
||||
"style": {
|
||||
@@ -105,13 +98,13 @@
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/testTip",
|
||||
"style": {
|
||||
"navigationBarTitleText": "模拟考试",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/testTip",
|
||||
"style": {
|
||||
"navigationBarTitleText": "模拟考试",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"style": {
|
||||
@@ -146,34 +139,56 @@
|
||||
"navigationBarTitleText": "支付结果",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
"path": "pages/index/trueTest",
|
||||
"style": {
|
||||
"navigationBarTitleText": "真实考场模拟",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/uploadPic",
|
||||
"style": {
|
||||
"navigationBarTitleText": "上传证件照",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/secretPapers",
|
||||
"style": {
|
||||
"navigationBarTitleText": "考前密卷",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/changeCarType",
|
||||
"style": {
|
||||
"navigationBarTitleText": "切换车型",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}
|
||||
{
|
||||
"path": "pages/index/secretPapers",
|
||||
"style": {
|
||||
"navigationBarTitleText": "考前密卷",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/changeCarType",
|
||||
"style": {
|
||||
"navigationBarTitleText": "切换车型",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/activity",
|
||||
"style": {
|
||||
"navigationBarTitleText": "活动",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/myGift",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的奖品",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/qrCode",
|
||||
"style": {
|
||||
"navigationBarTitleText": "核销二维码",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/me/help",
|
||||
"style": {
|
||||
"navigationBarTitleText": "好友助力",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"globalStyle": {
|
||||
@@ -187,7 +202,8 @@
|
||||
"selectedColor": "#05C341",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"color": "#999999",
|
||||
"list": [{
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"iconPath": "static/image/tabbar/tab-home.png",
|
||||
"selectedIconPath": "static/image/tabbar/tab-home-selected.png",
|
||||
@@ -202,4 +218,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
src/pages/index/activity.vue
Normal file
26
src/pages/index/activity.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<view>
|
||||
<GGL></GGL>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GGL from './components/ggl/index'
|
||||
export default {
|
||||
components: {
|
||||
GGL
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -134,6 +134,9 @@
|
||||
mounted() {
|
||||
this.getTitle()
|
||||
},
|
||||
onShow() {
|
||||
this.getTitle()
|
||||
},
|
||||
computed: {
|
||||
...mapState(useUserStore, ["vipOnList", "token"]),
|
||||
...mapState(useQuestionStore, ["orderQuestion_subject1", "orderQuestion_subject4", "version"]), //映射函数,取出tagslist
|
||||
|
||||
229
src/pages/index/components/ggl/index.vue
Normal file
229
src/pages/index/components/ggl/index.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<view style="padding-bottom: 50px;background-color: #9e0f00;">
|
||||
<image class="wp100" mode="widthFix" src="https://jwl-jiakao-bq.oss-cn-hangzhou.aliyuncs.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/ggl_header.png"></image>
|
||||
<view class="tip">您今日还剩{{actiNum}}次刮奖机会,共有{{total}}人参加活动</view>
|
||||
<view class="scraping">
|
||||
<scraping-card style="z-index: 20;" :result="result" watermark="刮一刮" title="刮一刮赢取大奖" ref="reset" @complete="handleComplete" >
|
||||
<cover-view v-if="showBtn" class="gj">
|
||||
<cover-view class="btn" @tap="handleScrap">
|
||||
{{btnText}}
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</scraping-card>
|
||||
</view>
|
||||
<view class="relative mt30 m20lr">
|
||||
<image class="wp100" mode="widthFix" src="/static/image/index/tip.png"></image>
|
||||
<view class="title">中奖名单</view>
|
||||
<view class="card">
|
||||
<view v-for="(item, index) in winningList" :key="index" class="card-item">
|
||||
<text class="item-text">{{ hidePhoneNumber(item.phone) }}</text>
|
||||
<text class="item-text">{{ item.awards }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="relative mt30 m20lr">
|
||||
<image class="wp100" mode="widthFix" src="/static/image/index/tip.png"></image>
|
||||
<view class="title">活动说明</view>
|
||||
<view class="card">
|
||||
<u-parse :content="activityRule"></u-parse>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import useUserStore from '@/jtools/store/user'
|
||||
import ScrapingCard from './scraping.vue'
|
||||
import { queryActivityDetail,queryLuckyNum,queryLuckyResult,saveWinner,getLuckyRecord } from '@/jtools/api/activity'
|
||||
export default {
|
||||
components: {
|
||||
ScrapingCard
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
detailId: undefined,
|
||||
result: '',
|
||||
btnText: '点我刮奖',
|
||||
showBtn: true,
|
||||
actiNum: 0,
|
||||
total: 0,
|
||||
winningList: [],
|
||||
activityRule: undefined
|
||||
}
|
||||
},
|
||||
onLoad(op) {
|
||||
this.detailId = op.detailId
|
||||
this.getActivityDetail()
|
||||
this.getActivityNum()
|
||||
this.searchWinningList()
|
||||
},
|
||||
methods: {
|
||||
handleScrap() {
|
||||
if(!this.actiNum) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '暂无抽奖机会'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
queryLuckyResult({
|
||||
detailId: this.detailId
|
||||
}).then(resp => {
|
||||
if(resp.code == 200) {
|
||||
this.result = resp.msg
|
||||
this.showBtn = !this.showBtn
|
||||
this.$refs.reset.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
getActivityDetail() {
|
||||
queryActivityDetail({detailId: this.detailId}).then(resp => {
|
||||
if(resp.code == 200) {
|
||||
this.activityRule = resp.data.activity.activityRule
|
||||
}
|
||||
})
|
||||
},
|
||||
hidePhoneNumber(phoneNumber) {
|
||||
// 验证电话号码格式
|
||||
if (!/^\d{11}$/.test(phoneNumber)) {
|
||||
return "无效的电话号码";
|
||||
}
|
||||
|
||||
// 替换中间四位数字为星号
|
||||
return phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
},
|
||||
getActivityNum() {
|
||||
queryLuckyNum({
|
||||
detailId: this.detailId,
|
||||
phone: useUserStore().userInfo?.phone || ''
|
||||
}).then(resp => {
|
||||
if(resp.code == 200) {
|
||||
this.actiNum = resp.data.drawNum
|
||||
this.total = resp.data.totalPeople
|
||||
}
|
||||
})
|
||||
},
|
||||
searchWinningList() {
|
||||
getLuckyRecord({
|
||||
detailId: this.detailId,
|
||||
pageSize: 8
|
||||
}).then(resp => {
|
||||
if(resp.code == 200) {
|
||||
this.winningList = resp.rows
|
||||
}
|
||||
})
|
||||
},
|
||||
handleComplete() {
|
||||
saveWinner({
|
||||
phone: useUserStore().userInfo?.phone || '',
|
||||
detailId: this.detailId,
|
||||
awards: this.result
|
||||
}).then(resp => {
|
||||
this.btnText = '再刮一次!'
|
||||
if(resp.code == 200) {
|
||||
this.getActivityNum()
|
||||
let help = ",完成助力即可领取奖品!"
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: `恭喜获得${this.result}` + help
|
||||
})
|
||||
this.showBtn = true
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
|
||||
.tip {
|
||||
position: relative;
|
||||
margin-top: -80px;
|
||||
margin-left: 40px;
|
||||
margin-right: 40px;
|
||||
height: 52rpx;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-radius: 26rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
line-height: 52rpx;
|
||||
}
|
||||
.scraping {
|
||||
position: relative;
|
||||
margin-top: 20rpx;
|
||||
margin-left: 40px;
|
||||
margin-right: 40px;
|
||||
background-color: #fff;
|
||||
border: 3px solid #9e0f00; /* 实线边框 */
|
||||
border-top: 3px dashed #9e0f00; /* 虚线边框 */
|
||||
border-right: 3px dashed #9e0f00; /* 虚线边框 */
|
||||
border-bottom: 3px dashed #9e0f00; /* 虚线边框 */
|
||||
border-left: 3px dashed #9e0f00; /* 虚线边框 */
|
||||
border-radius: 5px; /* 圆角 */
|
||||
padding: 10px; /* 内容与边框之间的空间 */
|
||||
}
|
||||
.gj {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 21;
|
||||
.btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: calc(50% - 100rpx);
|
||||
width: 200rpx;
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
background-color: #BE1200;
|
||||
border-radius: 10rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
position: absolute;
|
||||
left: calc(50% - 160rpx);
|
||||
top: -14px;
|
||||
width: 320rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
text-align: center;
|
||||
background: linear-gradient(0deg, #ECB181, #FFEDD3);
|
||||
border: 6rpx solid #E32D1A;
|
||||
border-radius: 40rpx 40rpx 0 0;
|
||||
font-weight: 600;
|
||||
color: #BE1200;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
.card {
|
||||
position: relative;
|
||||
margin-top: -40rpx;
|
||||
padding: 26rpx;
|
||||
min-height: 280rpx;
|
||||
background: #FFEDD3;
|
||||
border: 6rpx solid #E32D1A;
|
||||
border-radius: 24rpx;
|
||||
.card-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
border-bottom: 1px solid #ecd3ae;
|
||||
}
|
||||
.card-item:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
.item-text {
|
||||
font-size: 28rpx;
|
||||
color: #BE1200;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
280
src/pages/index/components/ggl/scraping.vue
Normal file
280
src/pages/index/components/ggl/scraping.vue
Normal file
@@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<view class="scraping-happy" id="container">
|
||||
<canvas canvas-id="scraping-happy" class="scraping__canvas" :disable-scroll="true" @touchstart="touchstart"
|
||||
@touchmove="touchmove" @touchend="touchend" />
|
||||
<cover-view class="scraping__view">
|
||||
{{ showText }}
|
||||
</cover-view>
|
||||
<slot></slot>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/** @name 水印配置默认值 **/
|
||||
const WATERMARK = {
|
||||
text: '刮一刮',
|
||||
fontSize: 14,
|
||||
color: '#C5C5C5',
|
||||
}
|
||||
/** @name 标题配置默认值 **/
|
||||
const TITLE = {
|
||||
text: '刮一刮',
|
||||
fontSize: 16,
|
||||
color: '#333',
|
||||
}
|
||||
/**
|
||||
* @name 涂层配置默认值
|
||||
* @property { string } color 涂层颜色
|
||||
* @property { number } drawSize 清除涂层的画笔大小
|
||||
*/
|
||||
const MASK = {
|
||||
color: '#DDDDDD',
|
||||
drawSize: 20,
|
||||
}
|
||||
/** @name 容错值,解决部分机型涂层没有覆盖满的情况,主要原因是由于像素尺寸不同导致的,应尽可能让width与height保持整数 **/
|
||||
const TOLERANT = 3;
|
||||
|
||||
let ctx = null;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/** @name 涂层设置 **/
|
||||
mask: {
|
||||
type: [String, Object],
|
||||
},
|
||||
/** @name 水印设置 **/
|
||||
watermark: {
|
||||
type: [String, Object],
|
||||
},
|
||||
/** @name 提示文字 **/
|
||||
title: {
|
||||
type: [String, Object],
|
||||
},
|
||||
/** @name 刮开百分之多少直接消除图层,为0的时候不消除 **/
|
||||
percentage: {
|
||||
type: Number,
|
||||
default: 40,
|
||||
},
|
||||
result: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
touchX: 0,
|
||||
touchY: 0,
|
||||
showText: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
maskSetting() {
|
||||
return {
|
||||
...MASK,
|
||||
...(typeof this.mask === 'object' ? this.mask : {
|
||||
text: this.mask
|
||||
}),
|
||||
}
|
||||
},
|
||||
watermarkSetting() {
|
||||
return {
|
||||
...WATERMARK,
|
||||
...(typeof this.watermark === 'object' ? this.watermark : {
|
||||
text: this.watermark
|
||||
}),
|
||||
};
|
||||
},
|
||||
titleSetting() {
|
||||
return {
|
||||
...TITLE,
|
||||
...(typeof this.title === 'object' ? this.title : {
|
||||
text: this.title
|
||||
}),
|
||||
};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 获取画布实例
|
||||
ctx = uni.createCanvasContext('scraping-happy', this);
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
/** @name 初始化 **/
|
||||
init() {
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query
|
||||
.select('#container')
|
||||
.boundingClientRect(({
|
||||
width,
|
||||
height
|
||||
}) => {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
setTimeout(() => {
|
||||
this.initCanvas();
|
||||
this.showText = this.result
|
||||
}, 20)
|
||||
})
|
||||
.exec();
|
||||
},
|
||||
/** @name 初始化canvas状态 **/
|
||||
initCanvas() {
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = this;
|
||||
// 清空矩形内容
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
// 设置画笔颜色
|
||||
ctx.setFillStyle(this.maskSetting.color);
|
||||
// 绘制矩形
|
||||
ctx.fillRect(0, 0, width, height + TOLERANT);
|
||||
// 绘制水印
|
||||
this.drawWatermark();
|
||||
// 绘制提示文字
|
||||
this.drawTitle();
|
||||
// 绘制到canvas身上
|
||||
ctx.draw();
|
||||
},
|
||||
/** @name 绘制水印 **/
|
||||
drawWatermark() {
|
||||
if (!this.watermarkSetting.text) return;
|
||||
// 保存当前的绘图上下文
|
||||
ctx.save();
|
||||
// 旋转
|
||||
ctx.rotate((-10 * Math.PI) / 180);
|
||||
// 水印具体绘制过程
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = this;
|
||||
const watermarkWidth = this.watermarkSetting.text.length * this.watermarkSetting.fontSize;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let i = 0;
|
||||
while ((x <= width * 5 || y <= height * 5) && i < 300) {
|
||||
ctx.setFillStyle(this.watermarkSetting.color);
|
||||
ctx.setFontSize(this.watermarkSetting.fontSize);
|
||||
ctx.fillText(this.watermarkSetting.text, x, y);
|
||||
x += watermarkWidth + watermarkWidth * 1.6;
|
||||
if (x > width && y <= height) {
|
||||
x = -Math.random() * 100;
|
||||
y += this.watermarkSetting.fontSize * 3;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
ctx.restore();
|
||||
},
|
||||
/** @name 绘制提示文字 **/
|
||||
drawTitle() {
|
||||
if (!this.titleSetting.text) return;
|
||||
ctx.setTextAlign('center');
|
||||
ctx.setTextBaseline('middle');
|
||||
ctx.setFillStyle(this.titleSetting.color);
|
||||
ctx.setFontSize(this.titleSetting.fontSize);
|
||||
ctx.fillText(this.titleSetting.text, this.width / 2, this.height / 3);
|
||||
},
|
||||
/** @name 触摸事件 **/
|
||||
touchstart(e) {
|
||||
this.touchX = e.touches[0].x;
|
||||
this.touchY = e.touches[0].y;
|
||||
},
|
||||
async touchmove(e) {
|
||||
// 把画笔到画布中的指定点
|
||||
ctx.moveTo(this.touchX, this.touchY);
|
||||
// 清除涂层
|
||||
ctx.clearRect(this.touchX, this.touchY, this.maskSetting.drawSize, this.maskSetting.drawSize);
|
||||
ctx.draw(true);
|
||||
// 记录移动点位
|
||||
this.touchX = e.touches[0].x;
|
||||
this.touchY = e.touches[0].y;
|
||||
|
||||
// if (this.percentage > 0) {
|
||||
// const clearPercent = await this.getClearMaskPercent();
|
||||
// }
|
||||
},
|
||||
async touchend() {
|
||||
if (this.percentage > 0) {
|
||||
const clearPercent = await this.getClearMaskPercent();
|
||||
if (clearPercent >= this.percentage) {
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.clearRect(0, 0, this.width, this.height);
|
||||
ctx.stroke();
|
||||
ctx.draw(true);
|
||||
this.$emit('complete')
|
||||
}
|
||||
}
|
||||
},
|
||||
/** @name 计算被清除的涂层百分比 **/
|
||||
getClearMaskPercent() {
|
||||
return new Promise(resolve => {
|
||||
uni.canvasGetImageData({
|
||||
canvasId: 'scraping-happy',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
success: res => {
|
||||
// 区域内所有点的像素信息,它是一个数组,数组中每 "4" 项表示一个点的 rgba 值
|
||||
const allPointPixels = res.data;
|
||||
// 储存被清除的点-点的透明度
|
||||
const clearPoint = [];
|
||||
// 取透明度来判断,如果透明度值小于一半,则判断为该点已经被清除
|
||||
for (let i = 0; i < allPointPixels.length; i += 4) {
|
||||
if (allPointPixels[i + 3] < 128) {
|
||||
clearPoint.push(allPointPixels[i + 3]);
|
||||
}
|
||||
}
|
||||
// 已被清除的百分比 = 清除的点 / 全部的点
|
||||
const percent = (
|
||||
(clearPoint.length / (allPointPixels.length / 4)) *
|
||||
100
|
||||
).toFixed(2);
|
||||
resolve(percent);
|
||||
},
|
||||
fail: e => {
|
||||
console.log('canvasGetImageData', e);
|
||||
},
|
||||
}, this);
|
||||
});
|
||||
},
|
||||
/** @name 重置 **/
|
||||
reset() {
|
||||
this.initCanvas();
|
||||
this.touchX = 0;
|
||||
this.touchY = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.scraping-happy {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scraping__canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
/* background-color: red; */
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.scraping__view {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
color: #f29100;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,127 +1,153 @@
|
||||
<template>
|
||||
<view>
|
||||
<view v-if="getLoading" class="wp100 relative" style="height: 100vh;">
|
||||
<image class="wp100" mode="widthFix" src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/%E8%80%83%E8%AF%95%E6%8F%90%E9%86%92_20230906135037.png"></image>
|
||||
<view class="wp100 flex ai-c jc-c" style="position: absolute;bottom: 0;left: 0;padding-bottom: 124rpx;">
|
||||
<image style="width: 452rpx;" src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/%E9%87%91%E6%AD%A6%E8%81%94_20230831123333.png" mode="widthFix"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!getLoading">
|
||||
<j-navbar :isBack="false">金武联驾考</j-navbar>
|
||||
<u-sticky bgColor="#fff">
|
||||
<u-tabs :list="categoryList" :current="curTab" :scrollable="false" @change="changeCategory"></u-tabs>
|
||||
</u-sticky>
|
||||
<view style="background-color: rgb(245, 245, 245);">
|
||||
<template v-if="subject=='1' || subject=='4'">
|
||||
<Subject1 :subject="subject" :rightList="rightList" :wrongList="wrongList" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<Subject2 :subject="subject" ref="subjectRef" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<view v-if="getLoading" class="wp100 relative" style="height: 100vh;">
|
||||
<image class="wp100" mode="widthFix"
|
||||
src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/%E8%80%83%E8%AF%95%E6%8F%90%E9%86%92_20230906135037.png">
|
||||
</image>
|
||||
<view class="wp100 flex ai-c jc-c" style="position: absolute;bottom: 0;left: 0;padding-bottom: 124rpx;">
|
||||
<image style="width: 452rpx;"
|
||||
src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/%E9%87%91%E6%AD%A6%E8%81%94_20230831123333.png"
|
||||
mode="widthFix"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!getLoading">
|
||||
<!-- <j-navbar :isBack="false">金武联驾考</j-navbar> -->
|
||||
<u-sticky bgColor="#fff">
|
||||
<u-tabs :list="categoryList" :current="curTab" :scrollable="false" @change="changeCategory"></u-tabs>
|
||||
</u-sticky>
|
||||
<view class="m10tb" v-if="activityList&&activityList.length">
|
||||
<u-swiper class="acticity" keyName="image" :list="activityList" @click="handleToActivity"></u-swiper>
|
||||
</view>
|
||||
<view style="background-color: rgb(245, 245, 245);">
|
||||
<template v-if="subject=='1' || subject=='4'">
|
||||
<Subject1 :subject="subject" :rightList="rightList" :wrongList="wrongList" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<Subject2 :subject="subject" ref="subjectRef" />
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
mapState,
|
||||
mapActions
|
||||
} from 'pinia' //引入映射函数
|
||||
import useQuestionStore from '@/jtools/store/question' //引入store
|
||||
import storage from '@/jtools/storage';
|
||||
import {
|
||||
querySysConfigList,
|
||||
} from '@/jtools/api/question';
|
||||
import Subject1 from "./components/Subject1";
|
||||
import Subject2 from "./components/Subject2";
|
||||
export default {
|
||||
components: {
|
||||
Subject1,
|
||||
Subject2
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show:false,
|
||||
subject: storage.get('curSubject') || '1',
|
||||
curTab: 0,
|
||||
searchValue: '',
|
||||
cityName: '',
|
||||
categoryList: [],
|
||||
rightList: storage.get(`rightList_subject${this.subject}`) || [],
|
||||
wrongList: storage.get(`wrongList_subject${this.subject}`) || [],
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.show=true
|
||||
this.getSubjectConfig()
|
||||
if (this.subject == '1' || this.subject == '4') {
|
||||
this.rightList = storage.get(`rightList_subject${this.subject}`) || []
|
||||
this.wrongList = storage.get(`wrongList_subject${this.subject}`) || []
|
||||
}
|
||||
if(this.subject=='2'||this.subject=='3'){
|
||||
this.$refs.subjectRef.getDiverType()
|
||||
}
|
||||
},
|
||||
onHide(){
|
||||
this.show=false
|
||||
},
|
||||
computed: {
|
||||
...mapState(useQuestionStore, ["loading_subject4", "loading_subject1","curSubject","orderQuestion_subject1","orderQuestion_subject4"]), //映射函数,取出tagslist
|
||||
getLoading() {
|
||||
return this.loading_subject4 && this.loading_subject1
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
getLoading(newVal){
|
||||
if(this.show){
|
||||
if(newVal){
|
||||
if(this.loading_subject4 && this.loading_subject1){
|
||||
uni.hideTabBar();
|
||||
}
|
||||
}else{
|
||||
uni.showTabBar()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useQuestionStore, ['getOrderQuestion_sub4', 'getOrderQuestion_sub1', 'changeSubject']),
|
||||
//获取科目配置
|
||||
getSubjectConfig() {
|
||||
const carTypeId = storage.get('carType') || '1001'
|
||||
querySysConfigList(carTypeId, 'Subject').then(resp => {
|
||||
if (resp.code === '0000') {
|
||||
this.categoryList = resp.data.map(item => {
|
||||
return {
|
||||
...item,
|
||||
name: item.configItemName
|
||||
}
|
||||
})
|
||||
this.subject=storage.get('curSubject') || '1',
|
||||
this.curTab=this.categoryList.findIndex(item=>item.configItemCode==this.subject)
|
||||
}
|
||||
})
|
||||
},
|
||||
//切换科目
|
||||
async changeCategory(val) {
|
||||
this.subject = val.configItemCode
|
||||
this.changeSubject(this.subject)
|
||||
if (this.subject == '1' || this.subject == '4') {
|
||||
this.rightList = storage.get(`rightList_subject${this.subject}`) || []
|
||||
this.wrongList = storage.get(`wrongList_subject${this.subject}`) || []
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.$refs.subjectRef.getDiverType()
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
import {
|
||||
mapState,
|
||||
mapActions
|
||||
} from 'pinia' //引入映射函数
|
||||
import useQuestionStore from '@/jtools/store/question' //引入store
|
||||
import useUserStore from '@/jtools/store/user'
|
||||
import storage from '@/jtools/storage';
|
||||
import {
|
||||
querySysConfigList,
|
||||
} from '@/jtools/api/question';
|
||||
import Subject1 from "./components/Subject1";
|
||||
import Subject2 from "./components/Subject2";
|
||||
// import {
|
||||
// queryActivityList
|
||||
// } from '@/jtools/api/activity';
|
||||
export default {
|
||||
components: {
|
||||
Subject1,
|
||||
Subject2
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
getLoading: true,
|
||||
show: false,
|
||||
subject: storage.get('curSubject') || '1',
|
||||
curTab: 0,
|
||||
searchValue: '',
|
||||
cityName: '',
|
||||
categoryList: [],
|
||||
rightList: storage.get(`rightList_subject${this.subject}`) || [],
|
||||
wrongList: storage.get(`wrongList_subject${this.subject}`) || [],
|
||||
activityList: []
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
setTimeout(() => {
|
||||
this.getLoading = false
|
||||
this.show = true
|
||||
}, 100);
|
||||
this.getSubjectConfig()
|
||||
if (this.subject == '1' || this.subject == '4') {
|
||||
this.rightList = storage.get(`rightList_subject${this.subject}`) || []
|
||||
this.wrongList = storage.get(`wrongList_subject${this.subject}`) || []
|
||||
}
|
||||
if (this.subject == '2' || this.subject == '3') {
|
||||
this.$refs.subjectRef.getDiverType()
|
||||
}
|
||||
// this.queryActivityList()
|
||||
},
|
||||
onHide() {
|
||||
this.show = false
|
||||
},
|
||||
computed: {
|
||||
...mapState(useQuestionStore, ["loading_subject4", "loading_subject1", "curSubject", "orderQuestion_subject1",
|
||||
"orderQuestion_subject4"
|
||||
]), //映射函数,取出tagslist
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useQuestionStore, ['getOrderQuestion_sub4', 'getOrderQuestion_sub1', 'changeSubject']),
|
||||
//获取科目配置
|
||||
getSubjectConfig() {
|
||||
const carTypeId = storage.get('carType') || '1001'
|
||||
querySysConfigList(carTypeId, 'Subject').then(resp => {
|
||||
if (resp.code === '0000') {
|
||||
this.categoryList = resp.data.map(item => {
|
||||
return {
|
||||
...item,
|
||||
name: item.configItemName
|
||||
}
|
||||
})
|
||||
this.subject = storage.get('curSubject') || '1',
|
||||
this.curTab = this.categoryList.findIndex(item => item.configItemCode == this.subject) || 0
|
||||
}
|
||||
})
|
||||
},
|
||||
//切换科目
|
||||
async changeCategory(val) {
|
||||
this.subject = val.configItemCode
|
||||
this.changeSubject(this.subject)
|
||||
if (this.subject == '1' || this.subject == '4') {
|
||||
this.rightList = storage.get(`rightList_subject${this.subject}`) || []
|
||||
this.wrongList = storage.get(`wrongList_subject${this.subject}`) || []
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.$refs.subjectRef.getDiverType()
|
||||
}, 100)
|
||||
}
|
||||
},
|
||||
//查询活动列表
|
||||
queryActivityList() {
|
||||
queryActivityList({
|
||||
schoolId: useUserStore().userInfo?.schoolId || ''
|
||||
}).then(resp => {
|
||||
this.activityList = resp.data.map(item => ({
|
||||
...item,
|
||||
image: 'https://jwl.ahduima.com' + item.image
|
||||
}));
|
||||
})
|
||||
},
|
||||
// 去活动
|
||||
handleToActivity(index) {
|
||||
let detailId;
|
||||
this.activityList.find((item, index1) => {
|
||||
if (index === index1) {
|
||||
detailId = item.detailId;
|
||||
}
|
||||
})
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/activity?detailId=' + detailId,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .u-tabs__wrapper__nav__line {
|
||||
background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%) !important;
|
||||
bottom: 14rpx !important;
|
||||
}
|
||||
::v-deep .u-tabs__wrapper__nav__line {
|
||||
background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%) !important;
|
||||
bottom: 14rpx !important;
|
||||
}
|
||||
</style>
|
||||
@@ -216,14 +216,12 @@
|
||||
},
|
||||
toSubmit() {
|
||||
const restTime = this.time.hours * 60 * 60 + this.time.minutes * 60 + this.time.seconds
|
||||
const score = this.list.reduce((pre, cur) => {
|
||||
const isTrue = cur.trueAnswer == cur.yourAnswer.replace(/,-g/, '')
|
||||
let s = 0
|
||||
if (isTrue) {
|
||||
s = this.subject == 1 ? 1 : 2
|
||||
}
|
||||
return pre + s
|
||||
const trueCount = this.list.reduce((pre, cur) => {
|
||||
const ya = cur.trueAnswer.replace('A', '1').replace('B', '2').replace('C', '3').replace('D', '4').replace('E', '5').replace('F', '6').replace('G', '7')
|
||||
const isTrue = ya == cur.yourAnswer.replace(/,/g, '')
|
||||
return pre + 1 * (isTrue ? 1 : 0)
|
||||
}, 0)
|
||||
const score = (trueCount / this.list.length * 100).toFixed(0)
|
||||
submitTest({
|
||||
"carTypeId": storage.get('carType') || '1001',
|
||||
"score": score,
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
|
||||
<view class="list">
|
||||
<view class="list-call">
|
||||
<u-input class="sl-input" border="none" v-model="login.phone" type="number" maxlength="11"
|
||||
<u-input class="sl-input" border="none" v-model="loginForm.phone" type="number" maxlength="11"
|
||||
placeholder="输入手机号" />
|
||||
</view>
|
||||
<view class="list-call">
|
||||
<u-input class="sl-input" v-model="login.code" type="text" maxlength="6" border="none" placeholder="输入验证码">
|
||||
<!-- <view class="list-call">
|
||||
<u-input class="sl-input" v-model="loginForm.code" type="text" maxlength="6" border="none" placeholder="输入验证码">
|
||||
<template #suffix>
|
||||
<text class="fs14 mr10" style="color: #05C341;" @tap="getCode">{{ countDown == 0 ? '获取验证码' : countDown }}</text>
|
||||
</template>
|
||||
</u-input>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<view class="button-login" @tap="bindLogin()">
|
||||
@@ -39,9 +39,9 @@ import storage from '@/jtools/storage';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
login: {
|
||||
loginForm: {
|
||||
phone: '',
|
||||
code: ''
|
||||
code: '000000'
|
||||
},
|
||||
countDown: 0,
|
||||
js: undefined
|
||||
@@ -54,9 +54,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getCode() {
|
||||
if (isPhone(this.login.phone) && this.countDown == 0) {
|
||||
if (isPhone(this.loginForm.phone) && this.countDown == 0) {
|
||||
getCode({
|
||||
phone: this.login.phone
|
||||
phone: this.loginForm.phone
|
||||
}).then(resp => {
|
||||
// if (resp.code == '0000') {
|
||||
uni.showToast({
|
||||
@@ -80,8 +80,8 @@ export default {
|
||||
this.countDown = 0
|
||||
},
|
||||
bindLogin() {
|
||||
if (isPhone(this.login.phone) && this.login.code) {
|
||||
let params = { ...this.login }
|
||||
if (isPhone(this.loginForm.phone) && this.loginForm.code) {
|
||||
let params = { ...this.loginForm }
|
||||
if (storage.get('companyId')) {
|
||||
params.id = storage.get('companyId')
|
||||
}
|
||||
|
||||
254
src/pages/me/help.vue
Normal file
254
src/pages/me/help.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<view style="padding-bottom: 50px;background-color: #C5121B;">
|
||||
<image class="wp100 img" mode="widthFix"
|
||||
src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/%E5%8A%A9%E5%8A%9B%E4%B8%BB%E5%9B%BE.jpg">
|
||||
</image>
|
||||
<view class="relative tip">邀请好友帮助你进行助力即可有机会领取奖品</view>
|
||||
<view class="relative help_div">
|
||||
<!-- -->
|
||||
<view v-if="type == 1" class="relative help_card">
|
||||
<view class="relative help_tip">
|
||||
还差{{diffNum}}位好友助力即可领取奖品:
|
||||
</view>
|
||||
<view v-if="winnerInfo.awards != undefined" class="relative help_tip" style="font-size: 40rpx;">
|
||||
{{winnerInfo.awards}}
|
||||
</view>
|
||||
<view class="p20">
|
||||
<u-grid :border="false" col="3">
|
||||
<u-grid-item v-for="(item, index) in helpUserList" :key="index">
|
||||
<u-avatar v-if="item.id" class="p10tb border" size="120rpx" :src="item.avatarUrl"></u-avatar>
|
||||
<view v-else class="p10tb">
|
||||
<view class="avatar-text">
|
||||
<u-avatar bg-color="#fff" color="#ccc" size="120rpx" text="邀"></u-avatar>
|
||||
</view>
|
||||
</view>
|
||||
</u-grid-item>
|
||||
</u-grid>
|
||||
</view>
|
||||
<view class="help_btn">
|
||||
<button open-type="share" class="help_btn_font"> 邀请好友助力</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="type == 2" class="relative help_card">
|
||||
<view class="relative help_list" style="top: 200rpx;">
|
||||
<view v-if="winnerInfo?.activityName" class="help_tip"
|
||||
style="font-size: 40rpx; margin: 25rpx 0rpx;">
|
||||
{{winnerInfo?.activityName}}
|
||||
</view>
|
||||
<view v-if="winnerInfo.schoolName != undefined" class="help_tip">
|
||||
举办单位:{{winnerInfo.schoolName}}
|
||||
</view>
|
||||
<view v-if="winnerInfo.awards != undefined" class="help_tip" style="font-size: 45rpx;margin: 25rpx 0rpx;">
|
||||
奖品:{{winnerInfo.awards}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="help_btn">
|
||||
<button class="help_btn_font" open-type="chooseAvatar" @chooseavatar="handleHelp"
|
||||
:disabled="disBtn">帮好友助力</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import useUserStore from '@/jtools/store/user'
|
||||
import {
|
||||
queryActivityDetail,
|
||||
queryHelpInfo,
|
||||
saveHelpInfo,
|
||||
queryWinnerInfo
|
||||
} from '@/jtools/api/activity'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
winnerId: undefined,
|
||||
helpUserList: [],
|
||||
drawNum: 0,
|
||||
phone: undefined,
|
||||
type: 2,
|
||||
winnerInfo: undefined,
|
||||
disBtn: false
|
||||
}
|
||||
},
|
||||
onLoad(op) {
|
||||
this.winnerId = op.id
|
||||
// this.type = op.type ? Number(op.type) : 1
|
||||
this.phone = useUserStore().userInfo?.phone || undefined
|
||||
console.log(this.phone)
|
||||
this.getWinnerInfo()
|
||||
this.getHelpInfo()
|
||||
this.disBtn = false
|
||||
|
||||
},
|
||||
onShareAppMessage(res) {
|
||||
if (res.from === 'button') { // 来自页面内分享按钮
|
||||
console.log(res.target)
|
||||
}
|
||||
return {
|
||||
title: '我正在参与领奖活动柜,请帮我助力',
|
||||
path: '/pages/me/help?id=' + this.winnerId + '&type=2'
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
//查询中奖信息
|
||||
getWinnerInfo() {
|
||||
queryWinnerInfo({
|
||||
winnerId: this.winnerId
|
||||
}).then(resp => {
|
||||
if (resp.code == 200) {
|
||||
console.log(resp)
|
||||
this.winnerInfo = resp.data;
|
||||
if (this.phone && this.winnerInfo.phone == this.phone) {
|
||||
this.type = 1
|
||||
} else {
|
||||
this.type = 2
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
//查询助力信息
|
||||
getHelpInfo() {
|
||||
queryHelpInfo({
|
||||
winnerId: this.winnerId
|
||||
}).then(resp => {
|
||||
if (resp.code == 200) {
|
||||
this.drawNum = resp.data.helpNum;
|
||||
const list = resp.data.helpUsers.map(item => ({
|
||||
...item,
|
||||
avatarUrl: 'https://jwl.ahduima.com' + item.avatarUrl
|
||||
}));
|
||||
this.diffNum = (this.drawNum - list.length) < 0 ? 0 : (this.drawNum - list.length);
|
||||
const arr = new Array(this.diffNum).fill({})
|
||||
this.helpUserList = [...list,...arr]
|
||||
}
|
||||
})
|
||||
},
|
||||
//点击助力
|
||||
handleHelp(e) {
|
||||
this.disBtn = false
|
||||
uni.login({
|
||||
provider: 'weixin', //使用微信登录
|
||||
success: (loginRes) => {
|
||||
console.log(loginRes);
|
||||
uni.uploadFile({
|
||||
url: 'https://jwl.ahduima.com/activity/applet/activity/help/save',
|
||||
// url: 'http://192.168.1.6:8089/applet/activity/help/save',
|
||||
filePath: e.detail.avatarUrl,
|
||||
name: 'file',
|
||||
formData: {
|
||||
'code': loginRes.code,
|
||||
'winnerId': this.winnerId,
|
||||
},
|
||||
success: (uploadFileRes) => {
|
||||
console.log(uploadFileRes.data);
|
||||
let resp = JSON.parse(uploadFileRes.data);
|
||||
console.log(resp);
|
||||
|
||||
if (resp.code == 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: `助力成功`
|
||||
})
|
||||
this.disBtn = true
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'error',
|
||||
title: resp.msg
|
||||
})
|
||||
this.disBtn = true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.img {
|
||||
top: -130rpx !important;
|
||||
}
|
||||
|
||||
.tip {
|
||||
height: 75px;
|
||||
font-family: PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #FEFEFE;
|
||||
line-height: 48px;
|
||||
top: -520rpx;
|
||||
text-align: center;
|
||||
font-size: 35rpx;
|
||||
}
|
||||
|
||||
.avatar-text {
|
||||
border-radius: 50%;
|
||||
border: 1px dashed #ccc;
|
||||
}
|
||||
|
||||
|
||||
.help_div {
|
||||
width: 710rpx;
|
||||
height: 996rpx;
|
||||
top: -560rpx;
|
||||
margin-left: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
background: linear-gradient(0deg, #F33D2F 100%, rgba(197, 18, 27, 0.01) 0%);
|
||||
border-radius: 40rpx;
|
||||
|
||||
|
||||
.help_card {
|
||||
width: 630rpx;
|
||||
height: 886rpx;
|
||||
margin: 0rpx 39rpx;
|
||||
top: 55rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
|
||||
.help_tip {
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #010101;
|
||||
line-height: 55rpx;
|
||||
top: 15rpx;
|
||||
}
|
||||
|
||||
.help_list {
|
||||
margin: 68rpx 25rpx 25rpx 20rpx;
|
||||
height: 580rpx;
|
||||
|
||||
}
|
||||
|
||||
.help_btn {
|
||||
text-align: center;
|
||||
margin: 0 75rpx;
|
||||
margin-bottom: 30rpx;
|
||||
width: 500rpx;
|
||||
height: 86rpx;
|
||||
background: linear-gradient(0deg, #DE4224 0%, #B81706 100%);
|
||||
border-radius: 43rpx;
|
||||
|
||||
.help_btn_font {
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
line-height: 86rpx;
|
||||
background: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -12,6 +12,9 @@
|
||||
</view>
|
||||
<view class="mt5 fs14 cor-666">陪您学车 第{{ user.days }}天</view>
|
||||
</view>
|
||||
<view v-if="canRecieve" class="ml20 p20" @tap="handleScan">
|
||||
<u-icon name="scan" size="40"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info flex ai-c" v-else @tap="toLogin">
|
||||
<u-avatar class="br-p50 overflow-h" :size="64" src="@/static/image/mine/default_avatar.png" shape="circle"></u-avatar>
|
||||
@@ -39,7 +42,7 @@
|
||||
<view class="absolute flex ai-c jc-c" style="left: 0;top: 40px;right: 0;bottom: 0;">
|
||||
<view v-if="!isLogin || !vipOn.length" class="text-center">
|
||||
<view class="fs18 fwb" style="color: #7E4012FF;">开通VIP尊享以下权益</view>
|
||||
<view class="fs15" style="color: #7E4012FF;">精选500题 / 真是模考 / 考前密卷</view>
|
||||
<view class="fs15" style="color: #7E4012FF;">精选500题 / 真实模考 / 考前密卷</view>
|
||||
<view class="study fs16 text-center" style="margin: 25px auto 0;color: #F6E99FFF;">{{ isIOS ? 'iOS暂不可用' : '立即开通' }}</view>
|
||||
</view>
|
||||
<view v-else-if="vipOn.length" class="text-center">
|
||||
@@ -76,16 +79,21 @@
|
||||
<img src="/static/image/mine/wdzl.png" style="width: 24px;height: 24px;">
|
||||
</template>
|
||||
</u-cell>
|
||||
<u-cell size="large" title="我的体检" isLink @tap="handleTJ">
|
||||
<u-cell size="large" title="我的奖品" isLink @tap="handleGift">
|
||||
<template #icon>
|
||||
<img src="/static/image/mine/wdtj.png" style="width: 24px;height: 24px;">
|
||||
<u-icon size="24" name="gift"></u-icon>
|
||||
</template>
|
||||
</u-cell>
|
||||
<u-cell size="large" title="我的题库" :value="carName" isLink @tap="toChangeCarType">
|
||||
<u-cell size="large" title="我的题库" :value="carName" @tap="toChangeCarType">
|
||||
<template #icon>
|
||||
<img src="/static/image/mine/wdtk.png" style="width: 24px;height: 24px;">
|
||||
</template>
|
||||
</u-cell>
|
||||
<u-cell size="large" title="联系我们" @tap="callPhoneNumber">
|
||||
<template #icon>
|
||||
<img src="/static/image/mine/callme.png" style="width: 24px;height: 24px;">
|
||||
</template>
|
||||
</u-cell>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
<view v-if="isLogin" class="flex ai-c jc-c mt12 br8 bc-fff" style="height: 50px;" @tap="handleLogout">
|
||||
@@ -97,8 +105,8 @@
|
||||
<script>
|
||||
import useUserStore from '@/jtools/store/user'
|
||||
import storage from '@/jtools/storage';
|
||||
import { receiveWinner,canRecieveGift } from '@/jtools/api/activity'
|
||||
export default {
|
||||
components: {},
|
||||
computed: {
|
||||
isLogin() {
|
||||
return useUserStore().isLogin
|
||||
@@ -138,14 +146,31 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
carName: storage.get('carName') || '小车C1/C2/C3',
|
||||
isIOS: true
|
||||
isIOS: true,
|
||||
canRecieve: false
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.isIOS = this.$platform.device().includes('ios')
|
||||
this.carName = storage.get('carName') || '小车C1/C2/C3'
|
||||
this.searchActivityAuth()
|
||||
},
|
||||
methods: {
|
||||
searchActivityAuth() {
|
||||
canRecieveGift({
|
||||
phone: useUserStore().userInfo?.phone || ''
|
||||
}).then(resp => {
|
||||
if(resp.code == 200) {
|
||||
this.canRecieve = resp.data
|
||||
}
|
||||
})
|
||||
},
|
||||
// 拨打电话
|
||||
callPhoneNumber() {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: '15105693067'
|
||||
});
|
||||
},
|
||||
toChangeCarType() {
|
||||
uni.navigateTo({
|
||||
url: "/pages/me/changeCarType"
|
||||
@@ -192,19 +217,6 @@ export default {
|
||||
this.toLogin()
|
||||
}
|
||||
},
|
||||
handleTJ() {
|
||||
if (this.isLogin) {
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/me/tijian'
|
||||
// })
|
||||
uni.showToast({
|
||||
title: '敬请期待',
|
||||
icon: 'none'
|
||||
})
|
||||
} else {
|
||||
this.toLogin()
|
||||
}
|
||||
},
|
||||
toLogin() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/login'
|
||||
@@ -213,6 +225,30 @@ export default {
|
||||
handleLogout() {
|
||||
useUserStore().logout()
|
||||
},
|
||||
handleGift() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/me/myGift'
|
||||
})
|
||||
},
|
||||
handleScan() {
|
||||
uni.scanCode({
|
||||
scanType: ['qrCode'],
|
||||
success(res) {
|
||||
console.log('条码内容:' + res.result);
|
||||
receiveWinner({
|
||||
id: res.result,
|
||||
receiveUser: useUserStore().userInfo?.phone || []
|
||||
}).then(resp => {
|
||||
if(resp.code == 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '核销成功!'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -15,15 +15,6 @@
|
||||
<u-icon name="arrow-right" color="#999" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="flex jc-sb ai-c bb1" style="height: 110rpx;">
|
||||
<view class="title">身份证号</view>
|
||||
<view class="m30lr fs14 cor-333">{{sfzNum}}</view>
|
||||
</view>
|
||||
<view class="flex ai-c" style="height: 110rpx;" @tap="toUploadPic">
|
||||
<view class="title">证件照</view>
|
||||
<view class="m30lr fs14 cor-333 fl1"></view>
|
||||
<u-icon name="arrow-right" color="#999" />
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -40,11 +31,6 @@
|
||||
this.sfzNum=storage.get('sfzNum') || ''
|
||||
},
|
||||
methods: {
|
||||
toUploadPic(){
|
||||
uni.navigateTo({
|
||||
url: '/pages/me/uploadPic'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
96
src/pages/me/myGift.vue
Normal file
96
src/pages/me/myGift.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<view class="p10lr p20tb">
|
||||
<u-list @scrolltolower="loadmore">
|
||||
<u-list-item v-for="(item, index) in list" :key="index" class="item">
|
||||
<view class="relative">
|
||||
<img src="/static/image/mine/giftitem.png" style="width: 100%;" mode="widthFix" alt="" />
|
||||
<view class="ab_full df ai-c jcc">{{item.awards}}</view>
|
||||
</view>
|
||||
<view class="df ai-c jcsb p20tb p10lr">
|
||||
<view class="item-label">
|
||||
<view>活动名称:{{ item.activityName }}</view>
|
||||
<view>参与时间:{{ item.createTime }}</view>
|
||||
<view>有效期至:{{ item.endTime }}</view>
|
||||
</view>
|
||||
<view class="ml20" style="width: 120rpx;">
|
||||
<view v-if="item.status == 0" class="btn" @tap="handleHelp(item)">助力</view>
|
||||
<view v-else-if="item.status == 1" class="btn" @tap="handleWriteoff(item)">核销</view>
|
||||
<img v-else-if="item.status == 2" src="/static/image/mine/writeoff.png" style="width: 120rpx;height: 120rpx;" />
|
||||
<img v-else src="/static/image/mine/outtime.png" style="width: 120rpx;height: 120rpx;" />
|
||||
</view>
|
||||
</view>
|
||||
</u-list-item>
|
||||
</u-list>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import useUserStore from '@/jtools/store/user'
|
||||
import { getLuckyRecord } from '@/jtools/api/activity'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
pageNum: 1,
|
||||
list: []
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.pageNum = 1
|
||||
this.list = []
|
||||
this.handleSearch()
|
||||
},
|
||||
methods: {
|
||||
handleSearch() {
|
||||
getLuckyRecord({
|
||||
phone: useUserStore().userInfo.phone,
|
||||
pageSize: 10,
|
||||
pageNum: this.pageNum
|
||||
}).then(resp => {
|
||||
if(resp.code == 200) {
|
||||
if(resp.rows && resp.rows.length) {
|
||||
this.list = [...this.list, ...resp.rows]
|
||||
} else {
|
||||
this.pageNum = this.pageNum > 1 ? this.pageNum-1 : 1
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadmore() {
|
||||
this.handleSearch(this.pageNum++)
|
||||
},
|
||||
handleWriteoff(item) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/me/qrCode?id=${item.id}`
|
||||
})
|
||||
},
|
||||
handleHelp(item){
|
||||
uni.navigateTo({
|
||||
url: `/pages/me/help?id=${item.id}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.item {
|
||||
margin-bottom: 30rpx;
|
||||
border-radius: 36rpx;
|
||||
background-color: #fff;
|
||||
.item-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
.btn {
|
||||
width: 120rpx;
|
||||
height: 68rpx;
|
||||
text-align: center;
|
||||
line-height: 68rpx;
|
||||
border-radius: 34rpx;
|
||||
background-color: #BE1200;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
26
src/pages/me/qrCode.vue
Normal file
26
src/pages/me/qrCode.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<view class="df jcc" style="padding-top: 100px;">
|
||||
<qrcode v-if="value" :value="value" :size="200"/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import qrcode from '/src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue'
|
||||
export default {
|
||||
components: {
|
||||
qrcode
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
onLoad(option) {
|
||||
this.value = option.id
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,214 +0,0 @@
|
||||
<template>
|
||||
<view class="">
|
||||
<view class="wp100 bc-fff p14">
|
||||
<view class="title fontColor">第一步 上传学员图像</view>
|
||||
<view class="mt15">
|
||||
<!-- <u-upload width="165" height="165" :file-list=" fileList1" multiple :max-count="1" @afterRead="afterRead" @delete="deletePic" /> -->
|
||||
<!-- <u-upload ref="uUpload" class="mt25" :size-type="['compressed']" :file-list="fileList1" deletable :multiple="false" :max-count="1" width="165rpx" height="165rpx" @afterRead="afterRead" @delete="deletePic" /> -->
|
||||
<view style="width: 320rpx;height:300rpx;background-color: rgb(247, 255, 255);border-radius: 20rpx;">
|
||||
<view style="width: 320rpx;height:240rpx;" class="flex jc-c ai-c">
|
||||
<image v-if="fileList&&fileList.length" style="width: 240rpx;height: 240rpx;" :src="fileList[0].url">
|
||||
</image>
|
||||
<u-avatar v-else class="br-p50 overflow-h" :size="64" mp-avatar shape="circle"></u-avatar>
|
||||
</view>
|
||||
<u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" multiple :maxCount="1" width="150"
|
||||
height="150">
|
||||
<view
|
||||
style="width: 320rpx;height:60rpx;line-height:60rpx;background-color: #05C341;border-radius: 0 0 20rpx 20rpx;"
|
||||
class="text-center cor-fff">
|
||||
点击
|
||||
</view>
|
||||
</u-upload>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt20">
|
||||
<view class="title fontColor">第二步 核实后再提交</view>
|
||||
<u--form labelPosition="left" labelWidth="80" :model="form" :rules="rules" ref="form1">
|
||||
<u-form-item label="姓名" :required="true" prop="idCardName" borderBottom ref="item1">
|
||||
<u--input v-model="form.idCardName" border="none"></u--input>
|
||||
</u-form-item>
|
||||
<u-form-item label="身份证号" :required="true" prop="sfzmhm" borderBottom ref="item2">
|
||||
<u--input v-model="form.sfzmhm" border="none"></u--input>
|
||||
</u-form-item>
|
||||
</u--form>
|
||||
</view>
|
||||
<view style="margin-top: 20px;">
|
||||
<u-button type="primary" :style="{width: '100%',borderRadius:'40rpx',backgroundColor:'#05C341'}" :disabled="saving" text="提交"
|
||||
@click="submit" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import storage from '@/jtools/storage';
|
||||
import {
|
||||
addInfo,
|
||||
} from '@/jtools/api/index';
|
||||
export default {
|
||||
data() {
|
||||
const shenfenzhen = (rule, value, callback) => {
|
||||
/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value) ? callback() : callback(
|
||||
new Error('请输入正确的身份证号'))
|
||||
}
|
||||
return {
|
||||
form: {
|
||||
idCardName: '',
|
||||
sfzmhm: ''
|
||||
},
|
||||
driverLicenseImg:'',
|
||||
fileList:[],
|
||||
fileList1: [],
|
||||
uploadList: [],
|
||||
saving: false,
|
||||
rules: {
|
||||
idCardName: [{
|
||||
required: true,
|
||||
message: '请输入姓名',
|
||||
trigger: ['blur', 'change']
|
||||
}],
|
||||
sfzmhm: [{
|
||||
required: true,
|
||||
message: '请输入身份证号',
|
||||
trigger: ['blur', 'change']
|
||||
},{
|
||||
// 自定义验证函数,见上说明
|
||||
validator: shenfenzhen,
|
||||
message: '身份证号码不正确',
|
||||
// 触发器可以同时用blur和change
|
||||
trigger: ['change', 'blur'],
|
||||
}]
|
||||
}
|
||||
};
|
||||
},
|
||||
onReady() {
|
||||
this.$refs.form1.setRules(this.rules);
|
||||
},
|
||||
methods: {
|
||||
// 删除图片
|
||||
deletePic(event) {
|
||||
this.fileList1.splice(event.index, 1);
|
||||
this.uploadList.splice(event.index, 1);
|
||||
},
|
||||
// 新增图片
|
||||
async afterRead(event) {
|
||||
let imageInfoObj = await uni.getImageInfo({src: event.file[0].url})
|
||||
console.log(imageInfoObj);
|
||||
if(imageInfoObj.width!=720&&imageInfoObj.height!=720){
|
||||
uni.showToast({
|
||||
icon:'error',
|
||||
title:'证件照尺寸有误'
|
||||
})
|
||||
this.fileList=[]
|
||||
return false
|
||||
}
|
||||
this.driverLicenseImg = this.urlTobase64(event.file[0].url)
|
||||
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
|
||||
const lists = [].concat(event.file);
|
||||
this.fileList=lists
|
||||
this.fileList1 = [];
|
||||
uni.showToast({
|
||||
title:'上传成功!'
|
||||
})
|
||||
},
|
||||
uploadFilePromise(url) {
|
||||
this.saving = true;
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: process.env.VUE_APP_BASE_API + 'mongodb/uploadFile', // 仅为示例,非真实的接口地址
|
||||
filePath: url,
|
||||
name: 'file',
|
||||
success: (res) => {
|
||||
// setTimeout(() => {
|
||||
resolve(JSON.parse(res.data).data);
|
||||
// }, 100);
|
||||
this.saving = false;
|
||||
},
|
||||
fail: () => {
|
||||
resolve(null);
|
||||
this.saving = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
// 提交
|
||||
submit() {
|
||||
this.$refs.form1.validate().then((valid) => {
|
||||
if (valid) {
|
||||
const param={
|
||||
driverLicenseImg:this.driverLicenseImg,
|
||||
...this.form,
|
||||
schoolOrgCode:'340103000700',
|
||||
operater:'1d08daf852cf4ee28f67cb583f538cbf'
|
||||
}
|
||||
addInfo(param).then(resp=>{
|
||||
uni.showToast({
|
||||
title:"提交成功!",
|
||||
duration:2000,
|
||||
})
|
||||
setTimeout(()=>{
|
||||
uni.navigateBack()
|
||||
},1000)
|
||||
})
|
||||
} else {
|
||||
console.log('验证失败');
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
urlTobase64(url){
|
||||
let base64=''
|
||||
console.log(url);
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.getFileSystemManager().readFile({
|
||||
filePath: url, //选择图片返回的相对路径
|
||||
encoding: 'base64', //编码格式
|
||||
success: res => { //成功的回调
|
||||
console.log(res);
|
||||
base64 = 'data:image/jpeg;base64,' + res.data //不加上这串字符,在页面无法显示的哦
|
||||
resolve(base64);
|
||||
},fail: (e) => {
|
||||
console.log("图片转换失败");
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.upload-img {
|
||||
width: 165rpx;
|
||||
height: 165rpx;
|
||||
border: 1px dashed #c4c4c4;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.fontColor {
|
||||
color: #383838;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
::v-deep .u-textarea__count {
|
||||
background-color: #f9faf9 !important;
|
||||
}
|
||||
|
||||
::v-deep .u-upload__button>.u-icon>.u-icon__icon {
|
||||
font-size: 90rpx !important;
|
||||
}
|
||||
|
||||
::v-deep .u-button--square {
|
||||
border-radius: 40rpx !important;
|
||||
}
|
||||
::v-deep .u-button--primary{
|
||||
background-color: #05C341 !important;
|
||||
border-color: #05C341 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -7,18 +7,19 @@
|
||||
:class="tCurrent==item.value?'checked':'unchecked'" @tap="sectionChange(item.value)">{{item.label}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<swiper class="swiper mt20" :current="swiperIndex" :duration="duration" :autoplay="false"
|
||||
@change="onChange" @animationfinish="onAnimationfinish"
|
||||
@touchend="touchEnd">
|
||||
<swiper-item v-for="(quesItem,quesIndex) in swiperList" :key="quesIndex.questionId">
|
||||
<swiper class="swiper mt20" :current="swiperIndex" :duration="duration" :autoplay="false" @change="onChange"
|
||||
@animationfinish="onAnimationfinish" @touchend="touchEnd">
|
||||
<swiper-item v-for="(quesItem,quesIndex) in swiperList" :key="quesItem.questionId">
|
||||
<scroll-view scroll-y="true" class="swiper-scroll">
|
||||
<view>
|
||||
<view class="m14lr">
|
||||
<text class="tag_box">{{getQuestType(quesItem.type)}}</text>
|
||||
<text class="fs18" style="line-height: 42rpx;vertical-align: middle;">{{quesItem.question}}</text>
|
||||
</view>
|
||||
<view class="p14" v-if="quesItem.imageUrl">
|
||||
<image v-show="quesItem.imageUrl" style="width: 100%;height: auto;" mode="widthFix" :lazy-load="true" @load="onoff='1'" :src="quesItem.imageUrl"></image>
|
||||
<view class="p14 flex jc-c ai-c" v-if="quesItem.imageUrl">
|
||||
<image v-show="quesItem.imageUrl" style="width: auto;max-height:40vh;" mode="heightFix"
|
||||
:lazy-load="true" @load="onoff='1'" :src="quesItem.imageUrl" @click="preview(quesItem.imageUrl)">
|
||||
</image>
|
||||
</view>
|
||||
<template v-if="quesItem.type!='3'">
|
||||
<view class="flex m14lr ai-c mt20" v-for="(item,index) in quesItem.optionList" :key="item.op"
|
||||
@@ -41,7 +42,8 @@
|
||||
v-if="quesItem.clickAnswer&&!quesItem.trueAnswer.includes(quesItem.clickAnswer) || showBestAnswer">
|
||||
<view class="answer_box">
|
||||
<text class="fs18 fw600 cor-000">答案:{{getRightOp(quesItem.trueAnswer)}}</text>
|
||||
<view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000 mt5">答题技巧:{{quesItem.skillInfo}}</view>
|
||||
<view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000 mt5">
|
||||
答题技巧:{{quesItem.skillInfo}}</view>
|
||||
</view>
|
||||
<view class="flex ai-c jc-c mt10">
|
||||
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
|
||||
@@ -50,7 +52,10 @@
|
||||
</view>
|
||||
<view class="mt10">
|
||||
<view class="fw600 cor-000 mb10 flex ai-c">
|
||||
<view style="background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);height: 36rpx;width: 8rpx;" class="mr5"></view>题目解析</view>
|
||||
<view
|
||||
style="background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);height: 36rpx;width: 8rpx;"
|
||||
class="mr5"></view>题目解析
|
||||
</view>
|
||||
<view style="text-indent:2em;">{{quesItem.bestAnswer}}</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -92,24 +97,29 @@
|
||||
<view class="m14lr mt30" v-if="isShowAnswer">
|
||||
<view class="answer_box">
|
||||
<text class="fs18 fw600 cor-000">答案:{{getRightOp(quesItem.trueAnswer)}}</text>
|
||||
<view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000"> 答题技巧:{{quesItem.skillInfo}}</view>
|
||||
</view>
|
||||
<view class="flex ai-c jc-c mt10">
|
||||
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
|
||||
<view class="fs18 fw600 cor-000 p15lr">试题详解</view>
|
||||
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
|
||||
</view>
|
||||
<view class="mt10">
|
||||
<view class="fw600 cor-000 mb10 flex ai-c">
|
||||
<view style="background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);height: 36rpx;width: 8rpx;" class="mr5"></view>题目解析</view>
|
||||
<view style="text-indent:2em;">{{quesItem.bestAnswer}}</view>
|
||||
<view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000">
|
||||
答题技巧:{{quesItem.skillInfo}}</view>
|
||||
</view>
|
||||
<view class="flex ai-c jc-c mt10">
|
||||
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
|
||||
<view class="fs18 fw600 cor-000 p15lr">试题详解</view>
|
||||
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
|
||||
</view>
|
||||
<view class="mt10">
|
||||
<view class="fw600 cor-000 mb10 flex ai-c">
|
||||
<view
|
||||
style="background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);height: 36rpx;width: 8rpx;"
|
||||
class="mr5"></view>题目解析
|
||||
</view>
|
||||
<view style="text-indent:2em;">{{quesItem.bestAnswer}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<q-previewImage ref="previewRef" :urls="imgs" @open="open"></q-previewImage>
|
||||
<view class="wp100 flex jc-sb ai-c p14 bc-fff" v-if="isShowAll" style="position: fixed;bottom: 0;left: 0;">
|
||||
<view style="width: 220rpx;">
|
||||
<view v-if="type==='practice'" style="width: 220rpx;height: 80rpx;"></view>
|
||||
@@ -232,8 +242,8 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex ai-c jc-fs p14" style="flex-wrap: wrap;max-height: 400px;overflow-y: scroll;">
|
||||
<view v-for="(item,index) of questionList" :key="item.questionId" style="width:20%;position: relative;" class="flex ai-c jc-c"
|
||||
@tap="chooseQueston(index)">
|
||||
<view v-for="(item,index) of questionList" :key="item.questionId" style="width:20%;position: relative;"
|
||||
class="flex ai-c jc-c" @tap="chooseQueston(index)">
|
||||
<view class="tCircle mb10" :class="{
|
||||
'active':index == topicIndex,
|
||||
'success':type=='exam'?rightList.includes(item.questionId):storageRightList.includes(item.questionId),
|
||||
@@ -241,8 +251,8 @@
|
||||
}">
|
||||
{{index+1}}
|
||||
</view>
|
||||
<u-icon name="star-fill" v-if="collectList.includes(item.questionId)" style="position: absolute;right: 5px;top:-3px"
|
||||
color="rgb(249,236,141)" size="24"></u-icon>
|
||||
<u-icon name="star-fill" v-if="collectList.includes(item.questionId)"
|
||||
style="position: absolute;right: 5px;top:-3px" color="rgb(249,236,141)" size="24"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -317,10 +327,11 @@
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
subject:'1',
|
||||
showSkillInfo:'hidden',
|
||||
currentType:storage.get('carType') || '1001',
|
||||
onoff:'0',
|
||||
imgs: [],
|
||||
subject: '1',
|
||||
showSkillInfo: 'hidden',
|
||||
currentType: storage.get('carType') || '1001',
|
||||
onoff: '0',
|
||||
navTitle: '',
|
||||
originArray: '',
|
||||
showBestAnswer: false,
|
||||
@@ -333,7 +344,7 @@
|
||||
tCurrent: 0,
|
||||
index: 0,
|
||||
qIndex: 0,
|
||||
storageRightList:storage.get(`rightList_subject${this.subject}`) || [],
|
||||
storageRightList: storage.get(`rightList_subject${this.subject}`) || [],
|
||||
storageWrongList: storage.get(`wrongList_subject${this.subject}`) || [],
|
||||
rightList: [],
|
||||
wrongList: [],
|
||||
@@ -348,9 +359,9 @@
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const carType=storage.get('carType') || '1001'
|
||||
querySysConfig(carType, 'NeedSkillInfo').then(resp=>{
|
||||
if(resp.code === '0000'){
|
||||
const carType = storage.get('carType') || '1001'
|
||||
querySysConfig(carType, 'NeedSkillInfo').then(resp => {
|
||||
if (resp.code === '0000') {
|
||||
this.showSkillInfo = resp.data.configValue
|
||||
}
|
||||
})
|
||||
@@ -377,12 +388,27 @@
|
||||
|
||||
},
|
||||
timeCount() {
|
||||
const time = 45 * 60 * 1000
|
||||
const time = 45 * 60 * 1000
|
||||
return time
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useQuestionStore, ['getCurrentIndex']),
|
||||
open() {
|
||||
|
||||
},
|
||||
preview(url) {
|
||||
this.imgs = [url] //设置图片数组
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$nextTick(() => {
|
||||
this.imgs = [url]
|
||||
setTimeout(()=>{
|
||||
this.$refs.previewRef.open(url);
|
||||
},500)
|
||||
})
|
||||
// #endif
|
||||
|
||||
},
|
||||
getOriginArr(val) {
|
||||
const arr = JSON.parse(val)
|
||||
let arr1 = []
|
||||
@@ -536,11 +562,11 @@
|
||||
toSubmit() {
|
||||
const restTime = this.time.hours * 60 * 60 + this.time.minutes * 60 + this.time.seconds
|
||||
const score = (this.rightList.length / this.questionList.length * 100).toFixed(0)
|
||||
if(this.rightList.length+this.wrongList.length==0){
|
||||
if (this.rightList.length + this.wrongList.length == 0) {
|
||||
uni.navigateBack({
|
||||
delta:1
|
||||
delta: 1
|
||||
})
|
||||
}else{
|
||||
} else {
|
||||
submitTest({
|
||||
"carTypeId": storage.get('carType') || '1001',
|
||||
"score": score,
|
||||
@@ -769,12 +795,12 @@
|
||||
if (title) {
|
||||
this.navTitle = title
|
||||
}
|
||||
if(subject){
|
||||
if (subject) {
|
||||
this.subject = subject
|
||||
console.log(this.subject);
|
||||
this.storageRightList=storage.get(`rightList_subject${subject}`) || []
|
||||
this.storageWrongList=storage.get(`wrongList_subject${subject}`) || []
|
||||
this.collectList=storage.get(`collectList_subject${subject}`) || []
|
||||
this.storageRightList = storage.get(`rightList_subject${subject}`) || []
|
||||
this.storageWrongList = storage.get(`wrongList_subject${subject}`) || []
|
||||
this.collectList = storage.get(`collectList_subject${subject}`) || []
|
||||
}
|
||||
if (val && val.length) {
|
||||
this.questionList = JSON.parse(val)
|
||||
@@ -783,12 +809,12 @@
|
||||
}
|
||||
console.log(this.questionList);
|
||||
if (this.navTitle === '顺序答题') {
|
||||
if(subject){
|
||||
if (subject) {
|
||||
this.pickerTopic(this[`currentIndex_subject${subject}`])
|
||||
}else{
|
||||
} else {
|
||||
this.pickerTopic(this[`currentIndex_subject${this.subject}`])
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
this.pickerTopic(this.topicIndex)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
import GradesChart from "./components/GradesChart.vue"
|
||||
import storage from '@/jtools/storage';
|
||||
import {
|
||||
testTotal
|
||||
testTotal,getTestQuestionId
|
||||
} from '@/jtools/api/question';
|
||||
export default {
|
||||
components: {
|
||||
@@ -182,15 +182,28 @@
|
||||
}else{
|
||||
const list =JSON.stringify(this.wrongList)
|
||||
uni.navigateTo({
|
||||
url:"/pages/questionBank/questionBank?navTitle=错题&subject="+this.subject+"&questionList="+list
|
||||
url:"/pages/questionBank/questionBank?navTitle=错题&subject="+this.subject+"&questionIdList="+list
|
||||
})
|
||||
}
|
||||
},
|
||||
//重新考试
|
||||
toExams(){
|
||||
uni.navigateTo({
|
||||
url:"/pages/questionBank/practiceExams?subject="+this.subject
|
||||
getTestQuestionId({
|
||||
versionId: this.version,
|
||||
carTypeId: storage.get('carType') || '1001',
|
||||
subject: this.subject,
|
||||
}).then(async (resp) => {
|
||||
if (resp.code === '0000') {
|
||||
const arr = resp.data
|
||||
const listJson = JSON.stringify(arr)
|
||||
uni.navigateTo({
|
||||
url: "/pages/questionBank/practiceExams?title=模拟考试&subject=" + this.subject + "&questionIdList=" + listJson
|
||||
})
|
||||
}
|
||||
})
|
||||
// uni.navigateTo({
|
||||
// url:"/pages/questionBank/practiceExams?subject="+this.subject
|
||||
// })
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
BIN
src/static/image/index/tip.png
Normal file
BIN
src/static/image/index/tip.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
src/static/image/mine/callme.png
Normal file
BIN
src/static/image/mine/callme.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
src/static/image/mine/giftitem.png
Normal file
BIN
src/static/image/mine/giftitem.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/static/image/mine/outtime.png
Normal file
BIN
src/static/image/mine/outtime.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
src/static/image/mine/writeoff.png
Normal file
BIN
src/static/image/mine/writeoff.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
11
src/uni_modules/g-preview-img/changelog.md
Normal file
11
src/uni_modules/g-preview-img/changelog.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 1.0.4(2022-12-07)
|
||||
修改:判断在APP端不监听document的滚动
|
||||
## 1.0.3(2022-12-05)
|
||||
新增:支持视频预览,视频图片混用
|
||||
新增:支持预览单张,handlePreviewImg方法直接传入图片或视频地址即可单张预览
|
||||
## 1.0.2(2022-12-05)
|
||||
|
||||
## 1.0.1(2022-12-05)
|
||||
文档错误修改
|
||||
## 1.0.0(2022-11-29)
|
||||
初始化插件
|
||||
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<view class="pos">
|
||||
<uni-transition :mode-class="modeClass" :show="show">
|
||||
<!-- 多张图片预览 -->
|
||||
<view class="content" @tap="closedPreview">
|
||||
<swiper
|
||||
class="swiper"
|
||||
circular
|
||||
:current="curDot"
|
||||
@change="swiperChange"
|
||||
:indicator-dots="false"
|
||||
>
|
||||
<swiper-item v-for="(item, idx) in selfImgList" :key="idx">
|
||||
<movable-area scale-area>
|
||||
<movable-view
|
||||
:scale="!disabledScale"
|
||||
direction="all"
|
||||
scale="true"
|
||||
scale-min="0.5"
|
||||
scale-max="5"
|
||||
:scale-value="1"
|
||||
damping="150"
|
||||
friction="15"
|
||||
>
|
||||
<image v-if="isImg(item)" :src="item" mode="widthFix"></image>
|
||||
<view class="video-preview" v-else>
|
||||
<video
|
||||
:autoplay="true"
|
||||
:src="item"
|
||||
:enable-progress-gesture="false"
|
||||
:show-fullscreen-btn="false"
|
||||
></video>
|
||||
</view>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<!-- 指示器 -->
|
||||
<slot name="indicator" v-if="imgList.length > 1 && !indicatorDotsType">
|
||||
<view class="current-dot">
|
||||
<view class="change-buttom" @tap.stop="previousImg">
|
||||
<uni-icons class="font-white" type="back" size="30"></uni-icons>
|
||||
</view>
|
||||
<view class="font-white cur">
|
||||
{{ curDot + 1 }}/{{ imgList.length }}
|
||||
</view>
|
||||
<view class="change-buttom" @tap.stop="nextImg">
|
||||
<uni-icons class="font-white" type="forward" size="30"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
</uni-transition>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
// 过渡效果
|
||||
modeClass: {
|
||||
type: Array,
|
||||
default: ['fade', 'zoom-out'],
|
||||
},
|
||||
// 指示器类型 true 圆点 false 数字
|
||||
indicatorDotsType: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 图片列表
|
||||
imgList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 是否禁止放大缩小 禁止后swiper可以滑动切换
|
||||
disabledScale: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
curDot: 0,
|
||||
selfImgList: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
isImg() {
|
||||
return (src) => {
|
||||
return src.indexOf('.mp4') === -1 ? true : false;
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
//监听打开时阻止下面元素的滚动事件
|
||||
/* #ifdef APP-PLUS*/
|
||||
show(val) {
|
||||
if (val) {
|
||||
document
|
||||
.getElementsByClassName('pos')[0]
|
||||
.addEventListener('touchmove', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
},
|
||||
/* #endif */
|
||||
imgList: {
|
||||
handler(val) {
|
||||
if (val.length) {
|
||||
this.selfImgList = val.concat();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handlePreviewImg(param) {
|
||||
this.show = !this.show;
|
||||
if (typeof param === 'string') {
|
||||
this.selfImgList = [param];
|
||||
} else {
|
||||
if (param) {
|
||||
this.curDot = param;
|
||||
}
|
||||
}
|
||||
this.$emit('preview', this.show);
|
||||
},
|
||||
closedPreview() {
|
||||
this.show = !this.show;
|
||||
this.curDot = 0;
|
||||
this.$emit('preview', this.show);
|
||||
},
|
||||
swiperChange(e) {
|
||||
this.curDot = e.detail.current;
|
||||
this.$emit('changeImg', e.detail.current);
|
||||
},
|
||||
previousImg() {
|
||||
let num = this.imgList.length - 1;
|
||||
if (this.curDot <= 0) {
|
||||
this.curDot = num;
|
||||
} else {
|
||||
this.curDot--;
|
||||
}
|
||||
},
|
||||
nextImg() {
|
||||
let num = this.imgList.length - 1;
|
||||
if (this.curDot >= num) {
|
||||
this.curDot = 0;
|
||||
} else {
|
||||
this.curDot++;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
movable-view {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
movable-area {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
movable-view image {
|
||||
width: 100%;
|
||||
}
|
||||
.content {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #00000076;
|
||||
}
|
||||
.pos {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
}
|
||||
.swiper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.current-dot {
|
||||
position: absolute;
|
||||
bottom: 10%;
|
||||
left: 25%;
|
||||
width: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
.change-buttom {
|
||||
padding: 10rpx;
|
||||
border-radius: 50%;
|
||||
background: #3f3f3f;
|
||||
}
|
||||
.cur {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.font-white {
|
||||
color: #fff !important;
|
||||
}
|
||||
::v-deep {
|
||||
.uni-swiper-dots-horizontal {
|
||||
bottom: 12%;
|
||||
}
|
||||
.uni-swiper-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
}
|
||||
|
||||
uni-video {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
.video-preview {
|
||||
position: relative;
|
||||
.video-close {
|
||||
position: absolute;
|
||||
right: 50rpx;
|
||||
top: 50rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
84
src/uni_modules/g-preview-img/package.json
Normal file
84
src/uni_modules/g-preview-img/package.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"id": "g-preview-img",
|
||||
"displayName": "g-preview-img一款兼容vue2,vue3的图片预览插件,视频预览,支持单张多张,左右滑动,放大缩小",
|
||||
"version": "1.0.4",
|
||||
"description": "g-preview-img一款兼容vue2,vue3的图片预览插件,视频预览,支持单张多张,左右滑动,放大缩小",
|
||||
"keywords": [
|
||||
"vue2",
|
||||
"vue3",
|
||||
"图片预览",
|
||||
"视频预览"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "y",
|
||||
"Edge": "y",
|
||||
"Firefox": "y",
|
||||
"Safari": "y"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "u",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/uni_modules/g-preview-img/readme.md
Normal file
64
src/uni_modules/g-preview-img/readme.md
Normal file
@@ -0,0 +1,64 @@
|
||||

|
||||
### 一款兼容vue2,vue3的图片预览插件,视频预览,支持图片视频混用,支持单张多张,左右滑动,放大缩小
|
||||
|
||||
### 基础使用方法
|
||||
|
||||
```javascript
|
||||
<template>
|
||||
|
||||
<image v-for="(item,idx) in imgList" :src="item" :key="idx" @tap="handleClick(idx)"></image>
|
||||
<g-preview-img :imgList="imgList" ref="preview"><g-preview-img>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
const preview = ref(null)
|
||||
const imgList = ['图片路径1','图片路径2']
|
||||
const handleClick = (idx)=>{
|
||||
// idx为要打开的图片的索引,也可以不传,默认打开第一张
|
||||
// handlePreviewImg的参数支持传入单张图片地址或单个视频地址
|
||||
preview.value.handlePreviewImg(idx)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
| 属性名/事件 | 类型 | 默认值 | 说明 |
|
||||
| ----------------- | ------------ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| modeClass | Array/String | \['fade', 'zoom-out'] | uni-transition组件过渡效果,可选值见 <https://uniapp.dcloud.net.cn/component/uniui/uni-transition.html#mode-class-%E5%86%85%E7%BD%AE%E8%BF%87%E6%B8%A1%E5%8A%A8%E7%94%BB%E7%B1%BB%E5%9E%8B%E8%AF%B4%E6%98%8E> |
|
||||
| indicatorDotsType | Boolean | false | 多张图片的指示器,ture为圆点,false数字,当图片列表只有一张图片时,默认不展示指示器 |
|
||||
| imgList | Array | | 图片列表 |
|
||||
| disabledScale | Boolean | false | 是否禁止双指放大缩小 |
|
||||
| @preview | 打开关闭事件 | | 接受一个参数,ture为开启,false为关闭 |
|
||||
| @changeImg | 图片切换的事件 | | 参数为当前的图片索引 |
|
||||
|
||||
#### 插槽,自定义翻页按钮
|
||||
|
||||
```js
|
||||
|
||||
<g-preview-img :imgList="imgList" ref="preview">
|
||||
<template>
|
||||
<!--你的翻页按钮-->
|
||||
<view @tap.stop="previousImg">上一页</view>
|
||||
<view @tap.stop="nextImg">下一页</view>
|
||||
</template>
|
||||
</g-preview-img>
|
||||
|
||||
<script>
|
||||
const preview = ref(null)
|
||||
//上一页的方法
|
||||
const previousImg = ()=>{
|
||||
preview.value.previousImg()
|
||||
}
|
||||
//下一页的方法
|
||||
const previousImg = ()=>{
|
||||
preview.value.nextImg()
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 支持uniapp原生swiper的属性
|
||||
|
||||
插件内部swiper标签上绑定了$attrs,所以在使用时可以传入一些swiper的属性
|
||||
注意:不要传disable-touch这个属性,会有意想不到的错误
|
||||
|
||||
**插件bug会及时修复!!**
|
||||
25
src/uni_modules/lime-qrcode/changelog.md
Normal file
25
src/uni_modules/lime-qrcode/changelog.md
Normal file
@@ -0,0 +1,25 @@
|
||||
## 0.1.2(2023-12-14)
|
||||
- fix: uvue 引入 API 自定义包出错
|
||||
## 0.1.1(2023-12-11)
|
||||
- chore: uvue的二维码API独立,需要单独下载
|
||||
## 0.1.0(2023-12-07)
|
||||
- fix: 修复因utssdk目录导致无法运行
|
||||
## 0.0.9(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.8(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.7(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.6(2023-12-06)
|
||||
- feat: 支持uvue
|
||||
## 0.0.5(2023-07-30)
|
||||
- fix: 修复再次生成前没有清空,导致图形叠加
|
||||
## 0.0.4(2023-07-27)
|
||||
- fix: 修复相同尺寸无法再次生成
|
||||
## 0.0.3(2023-06-09)
|
||||
- feat: 支持通过`@vue/composition-api`在`vue2`上使用
|
||||
- chore: 更新文档
|
||||
## 0.0.2(2023-06-08)
|
||||
- chore: 更新文档
|
||||
## 0.0.1(2023-06-08)
|
||||
- 首次
|
||||
179
src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue
Normal file
179
src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<view class="l-qrcode" ref="l-qrcode" :style="[styles]">
|
||||
<image class="l-qrcode__icon" v-if="icon !=''" :src="icon" :style="[iconStyle]"></image>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { type PropType } from 'vue'
|
||||
import { QRCodeCanvas, QRCodePropsTypes , ImageSettings } from '@/uni_modules/lime-qrcodegen'
|
||||
// import { addUnit } from '@/uni_modules/lime-shared/addUnit'
|
||||
// import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
import { addUnit, unitConvert } from './utils'
|
||||
import { TakeSnapshotFailCallback, TakeSnapshotCompleteCallback, TakeSnapshotSuccessCallback} from './type'
|
||||
|
||||
const name = 'l-qrcode'
|
||||
export default {
|
||||
name,
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
size: {
|
||||
type: Object,
|
||||
default: 160
|
||||
},
|
||||
iconSize: {
|
||||
type: Object,
|
||||
default: 40
|
||||
},
|
||||
marginSize: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#000'
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
errorLevel: {
|
||||
type: String as PropType<'L' | 'M' | 'Q' | 'H'>,
|
||||
default: 'M' // 'L' | 'M' | 'Q' | 'H'
|
||||
},
|
||||
useCanvasToTempFilePath: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
// status: {
|
||||
// type: String as PropType<'active'|'expired'|'loading'>,
|
||||
// default: 'active' // active | expired | loading
|
||||
// }
|
||||
},
|
||||
emits: ['success'],
|
||||
data() {
|
||||
return {
|
||||
qrcode: null as QRCodeCanvas | null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styles() : Map<string, any> {
|
||||
const style = new Map<string, any>()
|
||||
const size = addUnit(this.size);
|
||||
style.set('width', size)
|
||||
style.set('height', size)
|
||||
style.set('background', this.bgColor)
|
||||
return style
|
||||
},
|
||||
iconStyle() : Map<string, any> {
|
||||
const style = new Map<string, any>()
|
||||
const size = addUnit(this.iconSize);
|
||||
style.set('width', size)
|
||||
style.set('height', size)
|
||||
return style
|
||||
},
|
||||
qrCodeProps() : QRCodePropsTypes {
|
||||
const param = {
|
||||
value: this.value,
|
||||
size: unitConvert(this.size),
|
||||
fgColor: this.color,
|
||||
level: ['L', 'M', 'Q', 'H'].includes(this.errorLevel) ? this.errorLevel : 'M',
|
||||
marginSize: this.marginSize,
|
||||
imageSettings: null,
|
||||
includeMargin: this.bordered,
|
||||
} as QRCodePropsTypes
|
||||
|
||||
if(this.iconSize != '' && this.icon != ''){
|
||||
const size = unitConvert(this.iconSize)
|
||||
param.imageSettings = {
|
||||
width: size,
|
||||
height: size,
|
||||
excavate: true
|
||||
} as ImageSettings
|
||||
}
|
||||
return param
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
canvasToTempFilePath(options :UTSJSONObject) : void {
|
||||
const el = (this.$refs['l-qrcode'] as Element);
|
||||
const format = options.getString('format') ?? 'png';
|
||||
const fail = options.get('fail') as TakeSnapshotFailCallback | null;
|
||||
const complete = options.get('complete') as TakeSnapshotCompleteCallback | null;
|
||||
const success = options.get('success') as TakeSnapshotSuccessCallback | null;
|
||||
const newOptions = {
|
||||
format,
|
||||
fail,
|
||||
complete,
|
||||
success,
|
||||
} as TakeSnapshotOptions
|
||||
el.takeSnapshot(newOptions)
|
||||
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const el = (this.$refs['l-qrcode'] as Element)
|
||||
const ctx = el.getDrawableContext();
|
||||
if (ctx == null) return
|
||||
this.qrcode = new QRCodeCanvas(ctx)
|
||||
const render = (v : QRCodePropsTypes) => {
|
||||
this.qrcode!.render(v);
|
||||
if (this.useCanvasToTempFilePath) {
|
||||
setTimeout(() => {
|
||||
this.canvasToTempFilePath({
|
||||
success: (res: TakeSnapshotSuccess) => {
|
||||
this.$emit('success', res.tempFilePath)
|
||||
},
|
||||
fail: (_: TakeSnapshotFail) => {
|
||||
uni.showToast({
|
||||
icon: 'error',
|
||||
title: '截图失败'
|
||||
})
|
||||
}
|
||||
})
|
||||
// TakeSnapshotOptions
|
||||
},200)
|
||||
}
|
||||
}
|
||||
this.$watch('qrCodeProps', (v: QRCodePropsTypes) => {
|
||||
render(v)
|
||||
}, {immediate: true})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.l-qrcode {
|
||||
position: relative;
|
||||
background-color: aqua;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&-mask {
|
||||
position: absolute;
|
||||
// inset: 0;
|
||||
// inset-block-start: 0;
|
||||
// inset-inline-start: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
line-height: 1.5714285714285714;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
223
src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue
Normal file
223
src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<view class="l-qrcode" :style="[styles]">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<canvas :style="styles" type="2d" :canvas-id="canvasId" :id="canvasId"></canvas>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<web-view
|
||||
ref="qrcodeRef"
|
||||
@pagefinish="onFinished"
|
||||
@error="onError"
|
||||
@onPostMessage="onMessage"
|
||||
:style="styles" src="/uni_modules/lime-qrcode/hybrid/html/index.html?v=1"></web-view>
|
||||
<!-- #endif -->
|
||||
<!-- <view class="l-qrcode-mask" v-if="['loading', 'expired'].includes(props.status)">
|
||||
<l-loading v-if="props.status == 'loading'"></l-loading>
|
||||
<view class="l-qrcode-expired" v-if="props.status == 'expired'">
|
||||
<slot></slot>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
// @ts-nocheck
|
||||
import { computed, defineComponent, getCurrentInstance, watch, onUnmounted, onMounted } from '@/uni_modules/lime-shared/vue';
|
||||
import QRCodeProps from './props'
|
||||
// #ifndef APP-NVUE
|
||||
import { getCanvas, isCanvas2d } from './useCanvas'
|
||||
import { QRCodeCanvas } from './qrcode.js';
|
||||
// #endif
|
||||
import { addUnit } from '@/uni_modules/lime-shared/addUnit'
|
||||
import { createImage } from '@/uni_modules/lime-shared/createImage'
|
||||
import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
|
||||
import { isBase64 } from '@/uni_modules/lime-shared/isBase64'
|
||||
import { pathToBase64 } from '@/uni_modules/lime-shared/pathToBase64'
|
||||
import { debounce } from '@/uni_modules/lime-shared/debounce'
|
||||
const name = 'l-qrcode'
|
||||
export default defineComponent({
|
||||
name,
|
||||
props: QRCodeProps,
|
||||
emits: ['success'],
|
||||
setup(props, {emit}) {
|
||||
const context = getCurrentInstance();
|
||||
const canvasId = `l-qrcode${context.uid}`
|
||||
const styles = computed(() => `width: ${addUnit(props.size)}; height: ${addUnit(props.size)};`)
|
||||
let qrcode = null
|
||||
let canvas = null
|
||||
const qrCodeProps = computed(() => {
|
||||
const { value, icon, size, color, bgColor, bordered, iconSize, errorLevel, marginSize } = props
|
||||
const imageSettings = {
|
||||
src: icon,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
height: unitConvert(iconSize),
|
||||
width: unitConvert(iconSize),
|
||||
excavate: true,
|
||||
}
|
||||
return {
|
||||
value,
|
||||
size: unitConvert(size),
|
||||
level: errorLevel,
|
||||
bgColor,
|
||||
fgColor: color,
|
||||
imageSettings: icon ? imageSettings : undefined,
|
||||
includeMargin: bordered,
|
||||
marginSize: marginSize ?? 0
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
const stacks = new Map()
|
||||
// #endif
|
||||
const canvasToTempFilePath = debounce((args: UniNamespace.CanvasToTempFilePathRes) => {
|
||||
if(!canvas) return
|
||||
// #ifndef APP-NVUE
|
||||
const copyArgs = Object.assign({
|
||||
canvasId,
|
||||
canvas: null
|
||||
}, args)
|
||||
|
||||
if (isCanvas2d) {
|
||||
copyArgs.canvas = canvas
|
||||
}
|
||||
if ('toTempFilePath' in canvas) {
|
||||
canvas.toTempFilePath(copyArgs)
|
||||
} else {
|
||||
uni.canvasToTempFilePath(copyArgs, context);
|
||||
}
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
if(!stacks.size) {
|
||||
const flie = 'file-' + Math.random();
|
||||
const stack = {args, time: +new Date()}
|
||||
stacks.set(`${flie}`, stack)
|
||||
canvas.toDataURL(flie)
|
||||
setTimeout(() => {
|
||||
const stack = stacks.get(flie)
|
||||
if(stack && 'fail' in stack.args) {
|
||||
stack.args.fail({
|
||||
error: '超时'
|
||||
})
|
||||
stacks.delete(flie)
|
||||
}
|
||||
},5000)
|
||||
}
|
||||
// #endif
|
||||
})
|
||||
const useCanvasToTempFilePath = () => {
|
||||
if(props.useCanvasToTempFilePath) {
|
||||
canvasToTempFilePath({
|
||||
success(res: UniNamespace.CanvasToTempFilePathRes) {
|
||||
emit('success', res.tempFilePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
const onFinished = () => {
|
||||
const { pixelRatio } = uni.getSystemInfoSync()
|
||||
canvas = {
|
||||
toDataURL(flie: string) {
|
||||
const ref: any = context.refs['qrcodeRef'];
|
||||
if(ref) {
|
||||
ref?.evalJS(`toDataURL('${flie}')`)
|
||||
}
|
||||
}
|
||||
};
|
||||
qrcode = {
|
||||
async render(props: any) {
|
||||
const ref: any = context.refs['qrcodeRef'];
|
||||
const { src } = props.imageSettings || { };
|
||||
if(!ref) return
|
||||
if(src && !isBase64(src) && !/^http/.test(src) && /^\/static/.test(src)) {
|
||||
props.imageSettings.src = await pathToBase64(src)
|
||||
}
|
||||
const _props = JSON.stringify(Object.assign({}, props, {pixelRatio}));
|
||||
ref?.evalJS(`render(${_props})`);
|
||||
}
|
||||
}
|
||||
qrcode.render(qrCodeProps.value)
|
||||
useCanvasToTempFilePath()
|
||||
}
|
||||
const onError = () => {
|
||||
console.warn('lime-qrcode 加载失败')
|
||||
}
|
||||
const onMessage = (e: any) => {
|
||||
const {detail:{data: [res]}} = e
|
||||
if(res.event == 'toDataURL') {
|
||||
const {file, image, msg} = res.data;
|
||||
const stack = stacks.get(file)
|
||||
if(stack && image && 'success' in stack.args) {
|
||||
stack.args.success({tempFilePath: image})
|
||||
stacks.delete(file)
|
||||
} else if(stack && 'fails' in stack.args) {
|
||||
stack.args.fail({error: msg})
|
||||
stacks.delete(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
const propsWatch = watch(props, () => {
|
||||
if (qrcode) {
|
||||
qrcode.render(qrCodeProps.value)
|
||||
useCanvasToTempFilePath()
|
||||
}
|
||||
})
|
||||
onMounted(() => {
|
||||
// #ifndef APP-NVUE
|
||||
getCanvas(canvasId, { context }).then(res => {
|
||||
canvas = res
|
||||
qrcode = new QRCodeCanvas(res, {
|
||||
// #ifdef H5
|
||||
path2D: false,
|
||||
// #endif
|
||||
pixelRatio: isCanvas2d ? uni.getSystemInfoSync().pixelRatio : 1,
|
||||
createImage
|
||||
})
|
||||
qrcode.render(qrCodeProps.value)
|
||||
useCanvasToTempFilePath()
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
onUnmounted(() => {
|
||||
propsWatch && propsWatch()
|
||||
})
|
||||
return {
|
||||
canvasId,
|
||||
styles,
|
||||
props,
|
||||
canvasToTempFilePath,
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
onFinished,
|
||||
onError,
|
||||
onMessage
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.l-qrcode {
|
||||
position: relative;
|
||||
|
||||
&-mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
// inset-block-start: 0;
|
||||
// inset-inline-start: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
line-height: 1.5714285714285714;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
36
src/uni_modules/lime-qrcode/components/l-qrcode/props.ts
Normal file
36
src/uni_modules/lime-qrcode/components/l-qrcode/props.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// @ts-nocheck
|
||||
// import type { PropType } from './vue'
|
||||
export default {
|
||||
value: String,
|
||||
icon: String,
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 160
|
||||
},
|
||||
iconSize: {
|
||||
type: [Number, String],
|
||||
default: 40
|
||||
},
|
||||
marginSize: Number,
|
||||
color: {
|
||||
type: String,
|
||||
default: '#000'
|
||||
},
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'transparent'
|
||||
},
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
errorLevel: {
|
||||
type: String as PropType<'L'|'M'|'Q'|'H'>,
|
||||
default: 'M' // 'L' | 'M' | 'Q' | 'H'
|
||||
},
|
||||
useCanvasToTempFilePath: Boolean
|
||||
// status: {
|
||||
// type: String as PropType<'active'|'expired'|'loading'>,
|
||||
// default: 'active' // active | expired | loading
|
||||
// }
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
25
src/uni_modules/lime-qrcode/components/l-qrcode/type.ts
Normal file
25
src/uni_modules/lime-qrcode/components/l-qrcode/type.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export type ImageSettings = {
|
||||
width: number
|
||||
height: number
|
||||
x?: number
|
||||
y?: number
|
||||
excavate: boolean
|
||||
}
|
||||
export type QRCodePropsTypes = {
|
||||
value?: string
|
||||
size?: number
|
||||
fgColor?: string
|
||||
level?: string
|
||||
marginSize: number
|
||||
includeMargin: boolean
|
||||
imageSettings?: ImageSettings
|
||||
}
|
||||
|
||||
export type QRCodeCallback = (cells : boolean[][]) => void
|
||||
|
||||
export type Excavation = {
|
||||
x: number
|
||||
y: number
|
||||
h: number
|
||||
w: number
|
||||
}
|
||||
78
src/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts
Normal file
78
src/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
// @ts-nocheck
|
||||
import type { ComponentInternalInstance } from './vue'
|
||||
import { getRect } from '@/uni_modules/lime-shared/getRect'
|
||||
import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d'
|
||||
export const isCanvas2d = canIUseCanvas2d()
|
||||
|
||||
export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) {
|
||||
let { context } = options
|
||||
// #ifdef MP || VUE2
|
||||
if (context.proxy) context = context.proxy
|
||||
// #endif
|
||||
return getRect('#' + canvasId, {context, type: isCanvas2d ? 'fields': 'boundingClientRect'}).then(res => {
|
||||
if(res.node){
|
||||
return res.node
|
||||
} else {
|
||||
const ctx = uni.createCanvasContext(canvasId, context)
|
||||
return {
|
||||
getContext(type: string) {
|
||||
if(type == '2d') {
|
||||
return ctx
|
||||
}
|
||||
},
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
}
|
||||
// #ifdef H5
|
||||
// canvas.value = context.proxy.$el.querySelector('#'+ canvasId)
|
||||
// #endif
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// #ifndef H5 || APP-NVUE
|
||||
class Image {
|
||||
currentSrc: string | null = null
|
||||
naturalHeight: number = 0
|
||||
naturalWidth: number = 0
|
||||
width: number = 0
|
||||
height: number = 0
|
||||
tagName: string = 'IMG'
|
||||
path: any = ''
|
||||
crossOrigin: any = ''
|
||||
referrerPolicy: any = ''
|
||||
onload: () => void
|
||||
onerror: () => void
|
||||
constructor() {}
|
||||
set src(src) {
|
||||
this.currentSrc = src
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
this.path = res.path
|
||||
this.naturalWidth = this.width = res.width
|
||||
this.naturalHeight = this.height = res.height
|
||||
this.onload()
|
||||
},
|
||||
fail: () => {
|
||||
this.onerror()
|
||||
}
|
||||
})
|
||||
}
|
||||
get src() {
|
||||
return this.currentSrc
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
|
||||
export function createImage(canvas: WechatMiniprogram.Canvas) {
|
||||
if(canvas && canvas.createImage) {
|
||||
return canvas.createImage()
|
||||
} else if(typeof window != 'undefined' && window.Image) {
|
||||
return new window.Image()
|
||||
}
|
||||
// #ifndef H5 || APP-NVUE
|
||||
return new Image()
|
||||
// #endif
|
||||
}
|
||||
35
src/uni_modules/lime-qrcode/components/l-qrcode/utils.uts
Normal file
35
src/uni_modules/lime-qrcode/components/l-qrcode/utils.uts
Normal file
@@ -0,0 +1,35 @@
|
||||
export function addUnit(value: any|null):string{
|
||||
if(value == null){
|
||||
return ''
|
||||
}
|
||||
value = `${value}`
|
||||
return /^(-)?\d+(\\.\d+)?$/.test(value) ? `${value}px` : value
|
||||
}
|
||||
|
||||
export function unitConvert(value: any|null): number{
|
||||
if(typeof value == 'number'){
|
||||
return value as number
|
||||
}
|
||||
if(typeof value == 'string'){
|
||||
value = `${value}`
|
||||
if(/^(-)?\d+(\\.\d+)?$/.test(value)){
|
||||
return parseFloat(value);
|
||||
}
|
||||
|
||||
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g;
|
||||
const results = reg.exec(value);
|
||||
if (results == null) {
|
||||
return 0;
|
||||
}
|
||||
const unit = results[3];
|
||||
const v = parseFloat(value);
|
||||
if (unit == 'rpx') {
|
||||
const { windowWidth } = uni.getWindowInfo()
|
||||
return windowWidth / 750 * v;
|
||||
}
|
||||
if (unit == 'px') {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text ultra">QRCode</text>
|
||||
<text class="demo-block__desc-text">QRCode</text>
|
||||
<view class="demo-block__body">
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text large">基础</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text large">icon</text>
|
||||
<view class="demo-block__body">
|
||||
<image v-if="image !=''" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<view style="flex-direction: row; justify-content: space-between">
|
||||
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
<l-qrcode :useCanvasToTempFilePath="true" @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
</view>
|
||||
<button @click="onClick">生成图片</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text large">颜色</text>
|
||||
<view class="demo-block__body">
|
||||
<view style="flex-direction: row; justify-content: space-between">
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text large">纠错比例</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
|
||||
<button @click="onToggle">切换纠错等级:{{levels[index]}}</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-block">
|
||||
<text class="demo-block__title-text large">动态</text>
|
||||
<view class="demo-block__body">
|
||||
<l-qrcode :value="text" size="300rpx" :marginSize="1" bgColor="white"></l-qrcode>
|
||||
<button @click="update">更新</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
<script>
|
||||
// import {ComponentPublicInstance} from 'vue'
|
||||
export default {
|
||||
name: 'lime-qrcode',
|
||||
data() {
|
||||
return {
|
||||
text: 'qcoon.com.cn',
|
||||
image: '',
|
||||
index: 0,
|
||||
levels: ['L', 'M', 'Q', 'H']
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
success(src: string) {
|
||||
console.log(`src`, src)
|
||||
},
|
||||
update() {
|
||||
this.text = `qcoon.cn?v=${Math.random()}`
|
||||
},
|
||||
onToggle() {
|
||||
this.index++
|
||||
this.index = this.index % this.levels.length
|
||||
},
|
||||
onClick() {
|
||||
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
|
||||
el.canvasToTempFilePath({
|
||||
success:(res: TakeSnapshotSuccess)=>{
|
||||
this.image = res.tempFilePath
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.demo-block {
|
||||
margin: 32px 20px 0;
|
||||
overflow: visible;
|
||||
&__title {
|
||||
margin: 0;
|
||||
margin-top: 8px;
|
||||
&-text {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
|
||||
&.large {
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 26px;
|
||||
}
|
||||
&.ultra {
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__desc-text {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
margin: 8px 16px 0 0;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
&__body {
|
||||
margin: 16px 0;
|
||||
overflow: visible;
|
||||
.demo-block {
|
||||
// margin-top: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<demo-block title="QRCode" type="ultra">
|
||||
<demo-block title="基础">
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx"></l-qrcode>
|
||||
</demo-block>
|
||||
<demo-block title="icon">
|
||||
<view style="display: flex; gap: 10px">
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
<l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
</view>
|
||||
<button @click="onClick">生成图片</button>
|
||||
</demo-block>
|
||||
<demo-block title="颜色">
|
||||
<view style="display: flex; gap: 10px">
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
|
||||
</view>
|
||||
</demo-block>
|
||||
<demo-block title="纠错比例">
|
||||
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" :errorLevel="levels[index]"></l-qrcode>
|
||||
<button @click="onToggle">切换纠错等级:{{levels[index]}}</button>
|
||||
</demo-block>
|
||||
<demo-block title="动态">
|
||||
<l-qrcode :value="text" size="300rpx" marginSize="20rpx" bgColor="white"></l-qrcode>
|
||||
<button @click="update">更新</button>
|
||||
</demo-block>
|
||||
</demo-block>
|
||||
</template>
|
||||
<script>
|
||||
import {ref, defineComponent} from '../l-qrcode/vue'
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const qrcodeRef = ref(null)
|
||||
const image = ref(null)
|
||||
const text = ref('qcoon.com.cn')
|
||||
const levels = ['L', 'M', 'Q', 'H']
|
||||
let index = ref(0)
|
||||
const onToggle = () => {
|
||||
index.value++
|
||||
index.value = index.value % levels.length
|
||||
}
|
||||
const onClick = () => {
|
||||
if(qrcodeRef.value) {
|
||||
qrcodeRef.value.canvasToTempFilePath({
|
||||
success(res) {
|
||||
image.value = res.tempFilePath
|
||||
console.log('success:::', res)
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const success = (res) => {
|
||||
console.log('res', res)
|
||||
}
|
||||
|
||||
const update = () =>{
|
||||
text.value = `qcoon.cn?v=${Math.random()}`
|
||||
}
|
||||
|
||||
return {
|
||||
levels,
|
||||
index,
|
||||
image,
|
||||
text,
|
||||
qrcodeRef,
|
||||
onClick,
|
||||
update,
|
||||
success,
|
||||
onToggle,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
77
src/uni_modules/lime-qrcode/hybrid/html/index.html
Normal file
77
src/uni_modules/lime-qrcode/hybrid/html/index.html
Normal file
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>lime-qrcode</title>
|
||||
<style>
|
||||
html,body,canvas {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
/* background-color: rgba(255,0,0,0.1) */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="lime-qrcode"></canvas>
|
||||
<script type="text/javascript" src="./uni.webview.1.5.3.js"></script>
|
||||
<script type="text/javascript" src="./qrcode.min.js"></script>
|
||||
<script>
|
||||
var canvas = document.querySelector('#lime-qrcode')
|
||||
var pixelRatio = window.devicePixelRatio || 1
|
||||
function appendWatermark(image) {
|
||||
emit('append', mark.toDataURL())
|
||||
}
|
||||
|
||||
var qrcode = new lime.QRCodeCanvas(canvas, {
|
||||
pixelRatio,
|
||||
})
|
||||
function render(props) {
|
||||
if(props.pixelRatio) {
|
||||
pixelRatio = props.pixelRatio
|
||||
}
|
||||
if(qrcode) {
|
||||
qrcode.render(props)
|
||||
}
|
||||
}
|
||||
function toDataURL(file) {
|
||||
if(qrcode && canvas) {
|
||||
try{
|
||||
const image = canvas.toDataURL()
|
||||
emit('toDataURL', {
|
||||
file,
|
||||
image
|
||||
})
|
||||
}catch(e){
|
||||
emit('toDataURL', {
|
||||
file,
|
||||
msg: e
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
function emit(event, data) {
|
||||
postMessage({
|
||||
event,
|
||||
data
|
||||
});
|
||||
};
|
||||
function postMessage(data) {
|
||||
uni.postMessage({
|
||||
data
|
||||
});
|
||||
};
|
||||
// render({
|
||||
// content: ['Lime UI'],
|
||||
// // rotate: -22,
|
||||
// // baseSize: 2,
|
||||
// // fontGap: 3
|
||||
// })
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
6
src/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js
vendored
Normal file
6
src/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
89
src/uni_modules/lime-qrcode/package.json
Normal file
89
src/uni_modules/lime-qrcode/package.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"id": "lime-qrcode",
|
||||
"displayName": "qrcode 二维码生成",
|
||||
"version": "0.1.2",
|
||||
"description": "全端二维码生成插件,draw api绘制。在H5,微信小程序,uvue,nvue上测试过,非uvue为vue3实现vue2需配置@vue/composition-api",
|
||||
"keywords": [
|
||||
"qrcode",
|
||||
"qr",
|
||||
"uvue",
|
||||
"生成图片",
|
||||
"二维码"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": "305716444"
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [
|
||||
"lime-shared"
|
||||
],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y",
|
||||
"app-android": {
|
||||
"minVersion": "19"
|
||||
}
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
153
src/uni_modules/lime-qrcode/readme.md
Normal file
153
src/uni_modules/lime-qrcode/readme.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# lime-qrcode 二维码
|
||||
- uniapp vue3 生成二维码插件
|
||||
- 仅在H5、微信小程序、APP-NVUE测试过,如果你在其它端遇到问题或其它端也能跑,请告之
|
||||
- uvue 需要导入[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)
|
||||
|
||||
## 使用
|
||||
- 导入插件后直接使用
|
||||
- uvue 需要导入**[lime-qrcodegen](https://ext.dcloud.net.cn/plugin?id=15838)**
|
||||
|
||||
#### 基础使用
|
||||
|
||||
```html
|
||||
<l-qrcode value="http://lime.qcoon.cn" />
|
||||
```
|
||||
|
||||
|
||||
#### ICON
|
||||
- 带 Icon 的二维码
|
||||
|
||||
```html
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" icon="/static/logo.png" iconSize="70rpx"></l-qrcode>
|
||||
```
|
||||
|
||||
#### 颜色
|
||||
- 通过设置 `color` 自定义二维码颜色,通过设置 `bgColor` 自定义背景颜色。
|
||||
|
||||
```html
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(82,196,26)"></l-qrcode>
|
||||
<l-qrcode value="https://limeui.qcoon.cn" size="300rpx" color="rgb(22,119,255)" bgColor="rgb(245,245,245)"></l-qrcode>
|
||||
```
|
||||
|
||||
#### 纠错比例
|
||||
- 通过设置 `errorLevel` 调整不同的容错等级。
|
||||
|
||||
```html
|
||||
<l-qrcode value="img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" size="300rpx" errorLevel="H"></l-qrcode>
|
||||
```
|
||||
|
||||
#### 生成图片
|
||||
- 1、通过调用插件的`canvasToTempFilePath`方法生成图片。
|
||||
|
||||
```html
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode ref="qrcodeRef" value="https://limeui.qcoon.cn" size="300rpx" icon="https://img10.360buyimg.com/img/jfs/t1/182127/16/37474/11761/64659c31F0cd84976/21f25b952f03a49a.jpg" iconSize="70rpx"></l-qrcode>
|
||||
<button @click="onClick">生成图片</button>
|
||||
```
|
||||
```js
|
||||
// vue3
|
||||
const qrcodeRef = ref(null)
|
||||
const onClick = () => {
|
||||
if(!qrcodeRef.value) return
|
||||
qrcodeRef.value.canvasToTempFilePath({
|
||||
success(res) {
|
||||
image.value = res.tempFilePath
|
||||
console.log('success:::', res)
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// vue2
|
||||
const el = this.$refs['qrcodeRef']
|
||||
el.canvasToTempFilePath({
|
||||
success:(res)=>{
|
||||
this.image = res.tempFilePath
|
||||
},
|
||||
fail(err) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
|
||||
// uvue
|
||||
const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance
|
||||
el.canvasToTempFilePath({
|
||||
success:(res: TakeSnapshotSuccess)=>{
|
||||
this.image = res.tempFilePath
|
||||
},
|
||||
fail(err: TakeSnapshotFail) {
|
||||
console.log('err:::', err)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
- 2、通过设置`useCanvasToTempFilePath`在`success`事件里接收图片地址
|
||||
|
||||
```html
|
||||
<image v-if="image" :src="image" style="width: 300rpx;" mode="widthFix"></image>
|
||||
<l-qrcode useCanvasToTempFilePath @success="success" value="https://limeui.qcoon.cn"></l-qrcode>
|
||||
```
|
||||
```js
|
||||
const image = ref(null)
|
||||
const success = (img) => {
|
||||
image.value = img
|
||||
}
|
||||
```
|
||||
|
||||
### 关于vue2的使用方式
|
||||
- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置
|
||||
- 关键代码是: 在main.js中 在vue2部分加上这一段即可,官方是把它单独成了一个文件.
|
||||
|
||||
```js
|
||||
// main.js vue2
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
Vue.use(VueCompositionAPI)
|
||||
```
|
||||
另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录
|
||||
```cmd
|
||||
// \HBuilderX\plugins\compile-typescript
|
||||
yarn add typescript -D
|
||||
- or -
|
||||
npm install typescript -D
|
||||
```
|
||||
|
||||
### 查看示例
|
||||
- 导入后直接使用这个标签查看演示效果
|
||||
|
||||
```html
|
||||
// 代码位于 uni_modules/lime-qrcode/compoents/lime-qrcode
|
||||
<lime-qrcode />
|
||||
```
|
||||
|
||||
### 插件标签
|
||||
- 默认 l-qrcode 为 component
|
||||
- 默认 lime-qrcode 为 demo
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ |
|
||||
| value | 扫描后的文本 | <em>string</em> | `-` |
|
||||
| icon | 二维码中图片的地址 | <em>string</em> | `-` |
|
||||
| size | 二维码大小 | <em>number,string</em> | `160` |
|
||||
| iconSize | 二维码中图片的大小 | <em>number,string</em> | `40` |
|
||||
| color | 二维码颜色 | <em>string</em> | `-` |
|
||||
| bgColor | 二维码背景颜色 | <em>string</em> | `-` |
|
||||
| errorLevel | 二维码纠错等级 | `'L' | 'M' | 'Q' | 'H' ` | `M` |
|
||||
| marginSize | 边距码大小,默认为0码点 | <em>number</em> | `0` |
|
||||
|
||||
### 常见问题
|
||||
- icon 是网络地址时,H5和Nvue需要解决跨域问题,小程序需要配置download
|
||||
|
||||
## 打赏
|
||||
|
||||
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
|
||||

|
||||

|
||||
25
src/uni_modules/lime-shared/addUnit/index.ts
Normal file
25
src/uni_modules/lime-shared/addUnit/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// @ts-nocheck
|
||||
import {isNumeric} from '../isNumeric'
|
||||
import {isDef} from '../isDef'
|
||||
/**
|
||||
* 给一个值添加单位(像素 px)
|
||||
* @param value 要添加单位的值,可以是字符串或数字
|
||||
* @returns 添加了单位的值,如果值为 undefined 则返回 undefined
|
||||
*/
|
||||
export function addUnit(value?: string | number): string | undefined {
|
||||
if (!isDef(value)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
value = String(value); // 将值转换为字符串
|
||||
|
||||
// 如果值是数字,则在后面添加单位 "px",否则保持原始值
|
||||
return isNumeric(value) ? `${value}px` : value;
|
||||
}
|
||||
|
||||
|
||||
// console.log(addUnit(100)); // 输出: "100px"
|
||||
// console.log(addUnit("200")); // 输出: "200px"
|
||||
// console.log(addUnit("300px")); // 输出: "300px"(已经包含单位)
|
||||
// console.log(addUnit()); // 输出: undefined(值为 undefined)
|
||||
// console.log(addUnit(null)); // 输出: undefined(值为 null)
|
||||
63
src/uni_modules/lime-shared/arrayBufferToFile/index.ts
Normal file
63
src/uni_modules/lime-shared/arrayBufferToFile/index.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
// @ts-nocheck
|
||||
import {platform} from '../platform'
|
||||
/**
|
||||
* buffer转路径
|
||||
* @param {Object} buffer
|
||||
*/
|
||||
// @ts-nocheck
|
||||
export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP
|
||||
const fs = uni.getFileSystemManager()
|
||||
//自定义文件名
|
||||
if (!name && !format) {
|
||||
reject(new Error('ERROR_NAME_PARSE'))
|
||||
}
|
||||
const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
|
||||
let pre = platform()
|
||||
const filePath = `${pre.env.USER_DATA_PATH}/${fileName}`
|
||||
fs.writeFile({
|
||||
filePath,
|
||||
data: buffer,
|
||||
success() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
const file = new File([buffer], name, {
|
||||
type: format,
|
||||
});
|
||||
resolve(file)
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
const base64 = uni.arrayBufferToBase64(buffer)
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!name && !format) {
|
||||
reject(new Error('ERROR_NAME_PARSE'))
|
||||
}
|
||||
const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`;
|
||||
const filePath = `_doc/uniapp_temp/${fileNmae}`
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
13
src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts
Normal file
13
src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// @ts-nocheck
|
||||
// 未完成
|
||||
export function base64ToArrayBuffer(base64 : string) {
|
||||
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
|
||||
if (!format) {
|
||||
new Error('ERROR_BASE64SRC_PARSE')
|
||||
}
|
||||
if(uni.base64ToArrayBuffer) {
|
||||
return uni.base64ToArrayBuffer(bodyData)
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
76
src/uni_modules/lime-shared/base64ToPath/index.ts
Normal file
76
src/uni_modules/lime-shared/base64ToPath/index.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
// @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) || [];
|
||||
console.log('format', format)
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP
|
||||
const fs = uni.getFileSystemManager()
|
||||
//自定义文件名
|
||||
if (!filename && !format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
// const time = new Date().getTime();
|
||||
const name = filename || `${new Date().getTime()}.${format}`;
|
||||
let pre = platform()
|
||||
const filePath = `${pre.env.USER_DATA_PATH}/${name}`
|
||||
fs.writeFile({
|
||||
filePath,
|
||||
data: base64.split(',')[1],
|
||||
encoding: 'base64',
|
||||
success() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// mime类型
|
||||
let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
|
||||
//base64 解码
|
||||
let byteString = atob(base64.split(',')[1]);
|
||||
//创建缓冲数组
|
||||
let arrayBuffer = new ArrayBuffer(byteString.length);
|
||||
//创建视图
|
||||
let intArray = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < byteString.length; i++) {
|
||||
intArray[i] = byteString.charCodeAt(i);
|
||||
}
|
||||
resolve(URL.createObjectURL(new Blob([intArray], {
|
||||
type: mimeString
|
||||
})))
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
|
||||
bitmap.loadBase64Data(base64, () => {
|
||||
if (!filename && !format) {
|
||||
reject(new Error('ERROR_BASE64SRC_PARSE'))
|
||||
}
|
||||
// const time = new Date().getTime();
|
||||
const name = filename || `${new Date().getTime()}.${format}`;
|
||||
const filePath = `_doc/uniapp_temp/${name}`
|
||||
bitmap.save(filePath, {},
|
||||
() => {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
},
|
||||
(error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, (error) => {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
21
src/uni_modules/lime-shared/camelCase/index.ts
Normal file
21
src/uni_modules/lime-shared/camelCase/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 将字符串转换为 camelCase 或 PascalCase 风格的命名约定
|
||||
* @param str 要转换的字符串
|
||||
* @param isPascalCase 指示是否转换为 PascalCase 的布尔值,默认为 false
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function camelCase(str: string, isPascalCase: boolean = false): string {
|
||||
// 将字符串分割成单词数组
|
||||
let words: string[] = str.split(/[\s_-]+/);
|
||||
|
||||
// 将数组中的每个单词首字母大写(除了第一个单词)
|
||||
let camelCased: string[] = words.map((word, index) => {
|
||||
if (index === 0 && !isPascalCase) {
|
||||
return word.toLowerCase(); // 第一个单词全小写
|
||||
}
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
});
|
||||
|
||||
// 将数组中的单词拼接成一个字符串
|
||||
return camelCased.join('');
|
||||
};
|
||||
58
src/uni_modules/lime-shared/canIUseCanvas2d/index.ts
Normal file
58
src/uni_modules/lime-shared/canIUseCanvas2d/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
// @ts-nocheck
|
||||
// #ifdef MP-ALIPAY
|
||||
interface My {
|
||||
SDKVersion: string
|
||||
}
|
||||
declare var my: My
|
||||
// #endif
|
||||
|
||||
function compareVersion(v1:string, v2:string) {
|
||||
let a1 = v1.split('.');
|
||||
let a2 = v2.split('.');
|
||||
const len = Math.max(a1.length, a2.length);
|
||||
|
||||
while (a1.length < len) {
|
||||
a1.push('0');
|
||||
}
|
||||
while (a2.length < len) {
|
||||
a2.push('0');
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(a1[i], 10);
|
||||
const num2 = parseInt(a2[i], 10);
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1;
|
||||
}
|
||||
if (num1 < num2) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function gte(version: string) {
|
||||
let {SDKVersion} = uni.getSystemInfoSync();
|
||||
// #ifdef MP-ALIPAY
|
||||
SDKVersion = my.SDKVersion
|
||||
// #endif
|
||||
return compareVersion(SDKVersion, version) >= 0;
|
||||
}
|
||||
|
||||
/** 环境是否支持canvas 2d */
|
||||
export function canIUseCanvas2d() {
|
||||
// #ifdef MP-WEIXIN
|
||||
return gte('2.9.0');
|
||||
// #endif
|
||||
// #ifdef MP-ALIPAY
|
||||
return gte('2.7.0');
|
||||
// #endif
|
||||
// #ifdef MP-TOUTIAO
|
||||
return gte('1.78.0');
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO
|
||||
return false
|
||||
// #endif
|
||||
}
|
||||
30
src/uni_modules/lime-shared/changelog.md
Normal file
30
src/uni_modules/lime-shared/changelog.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## 0.1.4(2023-09-05)
|
||||
- feat: 增加 Hooks `useIntersectionObserver`
|
||||
- feat: 增加 `floatAdd`
|
||||
- feat: 因为本人插件兼容 vue2 需要使用 `composition-api`,故增加vue文件代码插件的条件编译
|
||||
## 0.1.3(2023-08-13)
|
||||
- feat: 增加 `camelCase`
|
||||
## 0.1.2(2023-07-17)
|
||||
- feat: 增加 `getClassStr`
|
||||
## 0.1.1(2023-07-06)
|
||||
- feat: 增加 `isNumeric`, 区别于 `isNumber`
|
||||
## 0.1.0(2023-06-30)
|
||||
- fix: `clamp`忘记导出了
|
||||
## 0.0.9(2023-06-27)
|
||||
- feat: 增加`arrayBufferToFile`
|
||||
## 0.0.8(2023-06-19)
|
||||
- feat: 增加`createAnimation`、`clamp`
|
||||
## 0.0.7(2023-06-08)
|
||||
- chore: 更新注释
|
||||
## 0.0.6(2023-06-08)
|
||||
- chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖
|
||||
## 0.0.5(2023-06-03)
|
||||
- chore: 更新注释
|
||||
## 0.0.4(2023-05-22)
|
||||
- feat: 增加`range`,`exif`,`selectComponent`
|
||||
## 0.0.3(2023-05-08)
|
||||
- feat: 增加`fillZero`,`debounce`,`throttle`,`random`
|
||||
## 0.0.2(2023-05-05)
|
||||
- chore: 更新文档
|
||||
## 0.0.1(2023-05-05)
|
||||
- 无
|
||||
16
src/uni_modules/lime-shared/clamp/index.ts
Normal file
16
src/uni_modules/lime-shared/clamp/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 将一个值限制在指定的范围内
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @param val 要限制的值
|
||||
* @returns 限制后的值
|
||||
*/
|
||||
export function clamp(min: number, max: number, val: number): number {
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
|
||||
// console.log(clamp(0, 10, 5)); // 输出: 5(在范围内,不做更改)
|
||||
// console.log(clamp(0, 10, -5)); // 输出: 0(小于最小值,被限制为最小值)
|
||||
// console.log(clamp(0, 10, 15)); // 输出: 10(大于最大值,被限制为最大值)
|
||||
103
src/uni_modules/lime-shared/cloneDeep/index.ts
Normal file
103
src/uni_modules/lime-shared/cloneDeep/index.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 深度克隆一个对象或数组
|
||||
* @param obj 要克隆的对象或数组
|
||||
* @returns 克隆后的对象或数组
|
||||
*/
|
||||
export function cloneDeep<T>(obj: any): T {
|
||||
// 如果传入的对象为空,返回空
|
||||
if (obj === null) {
|
||||
return null as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Set 类型,则将其转换为数组,并通过新的 Set 构造函数创建一个新的 Set 对象
|
||||
if (obj instanceof Set) {
|
||||
return new Set([...obj]) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Map 类型,则将其转换为数组,并通过新的 Map 构造函数创建一个新的 Map 对象
|
||||
if (obj instanceof Map) {
|
||||
return new Map([...obj]) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 WeakMap 类型,则直接用传入的 WeakMap 对象进行赋值
|
||||
if (obj instanceof WeakMap) {
|
||||
let weakMap = new WeakMap();
|
||||
weakMap = obj;
|
||||
return weakMap as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 WeakSet 类型,则直接用传入的 WeakSet 对象进行赋值
|
||||
if (obj instanceof WeakSet) {
|
||||
let weakSet = new WeakSet();
|
||||
weakSet = obj;
|
||||
return weakSet as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 RegExp 类型,则通过新的 RegExp 构造函数创建一个新的 RegExp 对象
|
||||
if (obj instanceof RegExp) {
|
||||
return new RegExp(obj) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 undefined 类型,则返回 undefined
|
||||
if (typeof obj === 'undefined') {
|
||||
return undefined as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是数组,则递归调用 cloneDeep 函数对数组中的每个元素进行克隆
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(cloneDeep) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是 Date 类型,则通过新的 Date 构造函数创建一个新的 Date 对象
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime()) as unknown as T;
|
||||
}
|
||||
|
||||
// 如果传入的对象是普通对象,则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆
|
||||
if (typeof obj === 'object') {
|
||||
const newObj: any = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
newObj[key] = cloneDeep(value);
|
||||
}
|
||||
const symbolKeys = Object.getOwnPropertySymbols(obj);
|
||||
for (const key of symbolKeys) {
|
||||
newObj[key] = cloneDeep(obj[key]);
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
|
||||
// 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 示例使用
|
||||
|
||||
// // 克隆一个对象
|
||||
// const obj = { name: 'John', age: 30 };
|
||||
// const clonedObj = cloneDeep(obj);
|
||||
|
||||
// console.log(clonedObj); // 输出: { name: 'John', age: 30 }
|
||||
// console.log(clonedObj === obj); // 输出: false (副本与原对象是独立的)
|
||||
|
||||
// // 克隆一个数组
|
||||
// const arr = [1, 2, 3];
|
||||
// const clonedArr = cloneDeep(arr);
|
||||
|
||||
// console.log(clonedArr); // 输出: [1, 2, 3]
|
||||
// console.log(clonedArr === arr); // 输出: false (副本与原数组是独立的)
|
||||
|
||||
// // 克隆一个包含嵌套对象的对象
|
||||
// const person = {
|
||||
// name: 'Alice',
|
||||
// age: 25,
|
||||
// address: {
|
||||
// city: 'New York',
|
||||
// country: 'USA',
|
||||
// },
|
||||
// };
|
||||
// const clonedPerson = cloneDeep(person);
|
||||
|
||||
// console.log(clonedPerson); // 输出: { name: 'Alice', age: 25, address: { city: 'New York', country: 'USA' } }
|
||||
// console.log(clonedPerson === person); // 输出: false (副本与原对象是独立的)
|
||||
// console.log(clonedPerson.address === person.address); // 输出: false (嵌套对象的副本也是独立的)
|
||||
22
src/uni_modules/lime-shared/closest/index.ts
Normal file
22
src/uni_modules/lime-shared/closest/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 在给定数组中找到最接近目标数字的元素。
|
||||
* @param arr 要搜索的数字数组。
|
||||
* @param target 目标数字。
|
||||
* @returns 最接近目标数字的数组元素。
|
||||
*/
|
||||
export function closest(arr: number[], target: number) {
|
||||
return arr.reduce((pre, cur) =>
|
||||
Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur
|
||||
);
|
||||
}
|
||||
|
||||
// 示例
|
||||
// // 定义一个数字数组
|
||||
// const numbers = [1, 3, 5, 7, 9];
|
||||
|
||||
// // 在数组中找到最接近目标数字 6 的元素
|
||||
// const closestNumber = closest(numbers, 6);
|
||||
|
||||
// console.log(closestNumber); // 输出结果: 5
|
||||
149
src/uni_modules/lime-shared/createAnimation/index.ts
Normal file
149
src/uni_modules/lime-shared/createAnimation/index.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
// @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动画太慢~~~无语
|
||||
|
||||
|
||||
|
||||
|
||||
// #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
|
||||
}
|
||||
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
|
||||
// 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
|
||||
return uni.createAnimation({ ...options })
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
return new LimeAnimation(options)
|
||||
// #endif
|
||||
}
|
||||
61
src/uni_modules/lime-shared/createImage/index.ts
Normal file
61
src/uni_modules/lime-shared/createImage/index.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
// @ts-nocheck
|
||||
import {isBrowser} from '../isBrowser'
|
||||
class Image {
|
||||
currentSrc: string | null = null
|
||||
naturalHeight: number = 0
|
||||
naturalWidth: number = 0
|
||||
width: number = 0
|
||||
height: number = 0
|
||||
tagName: string = 'IMG'
|
||||
path: string = ''
|
||||
crossOrigin: string = ''
|
||||
referrerPolicy: string = ''
|
||||
onload: () => void = () => {}
|
||||
onerror: () => void = () => {}
|
||||
complete: boolean = false
|
||||
constructor() {}
|
||||
set src(src: string) {
|
||||
console.log('src', src)
|
||||
if(!src) {
|
||||
return this.onerror()
|
||||
}
|
||||
src = src.replace(/^@\//,'/')
|
||||
this.currentSrc = src
|
||||
uni.getImageInfo({
|
||||
src,
|
||||
success: (res) => {
|
||||
const localReg = /^\.|^\/(?=[^\/])/;
|
||||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
|
||||
res.path = localReg.test(src) ? `/${res.path}` : res.path;
|
||||
// #endif
|
||||
this.complete = true
|
||||
this.path = res.path
|
||||
this.naturalWidth = this.width = res.width
|
||||
this.naturalHeight = this.height = res.height
|
||||
this.onload()
|
||||
},
|
||||
fail: () => {
|
||||
this.onerror()
|
||||
}
|
||||
})
|
||||
}
|
||||
get src() {
|
||||
return this.currentSrc
|
||||
}
|
||||
}
|
||||
interface UniImage extends WechatMiniprogram.Image {
|
||||
complete?: boolean
|
||||
naturalHeight?: number
|
||||
naturalWidth?: number
|
||||
}
|
||||
/** 创建用于 canvas 的 img */
|
||||
export function createImage(canvas?: any): HTMLImageElement | UniImage {
|
||||
if(canvas && canvas.createImage) {
|
||||
return (canvas as WechatMiniprogram.Canvas).createImage()
|
||||
} else if(this.tagName == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){
|
||||
return new Image()
|
||||
} else if(isBrowser) {
|
||||
return new window.Image()
|
||||
}
|
||||
return new Image()
|
||||
}
|
||||
38
src/uni_modules/lime-shared/debounce/index.ts
Normal file
38
src/uni_modules/lime-shared/debounce/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
// @ts-nocheck
|
||||
type Timeout = ReturnType<typeof setTimeout> | null;
|
||||
/**
|
||||
* 防抖函数,通过延迟一定时间来限制函数的执行频率。
|
||||
* @param fn 要防抖的函数。
|
||||
* @param wait 触发防抖的等待时间,单位为毫秒。
|
||||
* @returns 防抖函数。
|
||||
*/
|
||||
export function debounce(fn: (...args: any[]) => void, wait = 300) {
|
||||
let timer: Timeout = null; // 用于存储 setTimeout 的标识符的变量
|
||||
|
||||
return function (this: any, ...args: any[]) {
|
||||
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"
|
||||
1056
src/uni_modules/lime-shared/exif/index.ts
Normal file
1056
src/uni_modules/lime-shared/exif/index.ts
Normal file
File diff suppressed because it is too large
Load Diff
11
src/uni_modules/lime-shared/fillZero/index.ts
Normal file
11
src/uni_modules/lime-shared/fillZero/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 在数字前填充零,返回字符串形式的结果
|
||||
* @param number 要填充零的数字
|
||||
* @param length 填充零后的字符串长度,默认为2
|
||||
* @returns 填充零后的字符串
|
||||
*/
|
||||
export function fillZero(number: number, length: number = 2): string {
|
||||
// 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度
|
||||
return `${number}`.padStart(length, '0');
|
||||
}
|
||||
36
src/uni_modules/lime-shared/floatAdd/index.ts
Normal file
36
src/uni_modules/lime-shared/floatAdd/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import {isNumber} from '../isNumber'
|
||||
/**
|
||||
* 返回两个浮点数相加的结果
|
||||
* @param num1 第一个浮点数
|
||||
* @param num2 第二个浮点数
|
||||
* @returns 两个浮点数的相加结果
|
||||
*/
|
||||
export function floatAdd(num1: number, num2: number): number {
|
||||
// 检查 num1 和 num2 是否为数字类型
|
||||
if (!(isNumber(num1) || isNumber(num2))) {
|
||||
console.warn('Please pass in the number type');
|
||||
return NaN;
|
||||
}
|
||||
|
||||
let r1: number, r2: number, m: number;
|
||||
|
||||
try {
|
||||
// 获取 num1 小数点后的位数
|
||||
r1 = num1.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r1 = 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取 num2 小数点后的位数
|
||||
r2 = num2.toString().split('.')[1].length;
|
||||
} catch (error) {
|
||||
r2 = 0;
|
||||
}
|
||||
|
||||
// 计算需要扩大的倍数
|
||||
m = Math.pow(10, Math.max(r1, r2));
|
||||
|
||||
// 返回相加结果
|
||||
return (num1 * m + num2 * m) / m;
|
||||
}
|
||||
27
src/uni_modules/lime-shared/getClassStr/index.ts
Normal file
27
src/uni_modules/lime-shared/getClassStr/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 获取对象的类名字符串
|
||||
* @param obj - 需要处理的对象
|
||||
* @returns 由对象属性作为类名组成的字符串
|
||||
*/
|
||||
export function getClassStr<T>(obj: T): string {
|
||||
let classNames: string[] = [];
|
||||
|
||||
// 遍历对象的属性
|
||||
for (let key in obj) {
|
||||
// 检查属性确实属于对象自身且其值为true
|
||||
if ((obj as any).hasOwnProperty(key) && obj[key]) {
|
||||
// 将属性名添加到类名数组中
|
||||
classNames.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 将类名数组用空格连接成字符串并返回
|
||||
return classNames.join(' ');
|
||||
}
|
||||
|
||||
|
||||
// 示例
|
||||
// const obj = { foo: true, bar: false, baz: true };
|
||||
// const classNameStr = getClassStr(obj);
|
||||
// console.log(classNameStr); // 输出: "foo baz"
|
||||
6
src/uni_modules/lime-shared/getCurrentPage/index.ts
Normal file
6
src/uni_modules/lime-shared/getCurrentPage/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// @ts-nocheck
|
||||
/** 获取当前页 */
|
||||
export const getCurrentPage = () => {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance;
|
||||
};
|
||||
14
src/uni_modules/lime-shared/getLocalFilePath/index.ts
Normal file
14
src/uni_modules/lime-shared/getLocalFilePath/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// @ts-nocheck
|
||||
export const getLocalFilePath = (path: string) => {
|
||||
if(typeof plus == 'undefined') return path
|
||||
if(/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path
|
||||
if (/^\//.test(path)) {
|
||||
const localFilePath = plus.io.convertAbsoluteFileSystem(path)
|
||||
if (localFilePath !== path) {
|
||||
return localFilePath
|
||||
} else {
|
||||
path = path.slice(1)
|
||||
}
|
||||
}
|
||||
return '_www/' + path
|
||||
}
|
||||
86
src/uni_modules/lime-shared/getRect/index.ts
Normal file
86
src/uni_modules/lime-shared/getRect/index.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
// @ts-nocheck
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
// 当编译环境是 APP-NVUE 时,引入 uni.requireNativePlugin('dom'),具体插件用途未知
|
||||
const dom = uni.requireNativePlugin('dom')
|
||||
// #endif
|
||||
|
||||
interface RectOptions {
|
||||
/**
|
||||
* 上下文
|
||||
*/
|
||||
context ?: any // ComponentInternalInstance 类型,用于指定上下文
|
||||
|
||||
/**
|
||||
* 是否需要获取所有节点,nvue 环境下不支持
|
||||
*/
|
||||
needAll ?: boolean,
|
||||
|
||||
/**
|
||||
* 节点引用对象,类型为 UniNamespace.NodesRef
|
||||
*/
|
||||
nodes ?: UniNamespace.NodesRef
|
||||
|
||||
/**
|
||||
* 节点引用对象的键,类型为 UniNamespace.NodesRef 中的某个键
|
||||
*/
|
||||
type ?: keyof UniNamespace.NodesRef
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点信息
|
||||
* @param selector 选择器字符串
|
||||
* @param options RectOptions 对象,用于配置选项
|
||||
* @returns 包含节点信息的 Promise 对象
|
||||
*/
|
||||
export function getRect(selector : string, options : RectOptions = {}) {
|
||||
// #ifndef APP-NVUE
|
||||
const typeDefault = 'boundingClientRect'
|
||||
let { context, needAll, type = typeDefault } = options
|
||||
// #endif
|
||||
|
||||
// #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)[needAll ? 'selectAll' : 'select'](selector);
|
||||
const result = (rect: UniNamespace.NodeInfo) => {
|
||||
if (rect) {
|
||||
resolve(rect)
|
||||
} else {
|
||||
reject('no rect')
|
||||
}
|
||||
}
|
||||
if (type == typeDefault) {
|
||||
dom[type](result).exec()
|
||||
} else {
|
||||
dom[type]({
|
||||
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
|
||||
});
|
||||
};
|
||||
30
src/uni_modules/lime-shared/getStyleStr/index.ts
Normal file
30
src/uni_modules/lime-shared/getStyleStr/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// @ts-nocheck
|
||||
interface CSSProperties {
|
||||
[key: string]: string | number
|
||||
}
|
||||
/**
|
||||
* 将字符串转换为带有连字符分隔的小写形式
|
||||
* @param key - 要转换的字符串
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function toLowercaseSeparator(key: string) {
|
||||
return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取样式对象对应的样式字符串
|
||||
* @param style - CSS样式对象
|
||||
* @returns 由非空有效样式属性键值对组成的字符串
|
||||
*/
|
||||
export function getStyleStr(style: CSSProperties): string {
|
||||
return Object.keys(style)
|
||||
.filter(key => style[key] !== undefined && style[key] !== null && style[key] !== '')
|
||||
.map((key: string) => `${toLowercaseSeparator(key)}: ${style[key]};`)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
// 示例
|
||||
// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null };
|
||||
// const styleStr = getStyleStr(style);
|
||||
// console.log(styleStr);
|
||||
// 输出: "color: red; font-size: 16px;"
|
||||
30
src/uni_modules/lime-shared/hasOwn/index.ts
Normal file
30
src/uni_modules/lime-shared/hasOwn/index.ts
Normal file
@@ -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' 属性
|
||||
43
src/uni_modules/lime-shared/index.ts
Normal file
43
src/uni_modules/lime-shared/index.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// @ts-nocheck
|
||||
// validator
|
||||
export * from './isString'
|
||||
export * from './isNumber'
|
||||
export * from './isNumeric'
|
||||
export * from './isDef'
|
||||
export * from './isFunction'
|
||||
export * from './isObject'
|
||||
export * from './isPromise'
|
||||
export * from './isBase64'
|
||||
|
||||
export * from './hasOwn'
|
||||
|
||||
// 单位转换
|
||||
export * from './addUnit'
|
||||
export * from './unitConvert'
|
||||
export * from './toNumber'
|
||||
|
||||
export * from './random'
|
||||
export * from './range'
|
||||
export * from './fillZero'
|
||||
|
||||
// image
|
||||
export * from './base64ToPath'
|
||||
export * from './pathToBase64'
|
||||
export * from './exif'
|
||||
|
||||
// canvas
|
||||
export * from './canIUseCanvas2d'
|
||||
|
||||
// page
|
||||
export * from './getCurrentPage'
|
||||
|
||||
// dom
|
||||
export * from './getRect'
|
||||
export * from './selectComponent'
|
||||
export * from './createAnimation'
|
||||
|
||||
// delay
|
||||
export * from './sleep'
|
||||
export * from './debounce'
|
||||
export * from './throttle'
|
||||
|
||||
9
src/uni_modules/lime-shared/isBase64/index.ts
Normal file
9
src/uni_modules/lime-shared/isBase64/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 判断给定的路径是否为Base64编码的图像路径
|
||||
* @param path 图像路径
|
||||
* @returns 如果路径是Base64编码,则返回true;否则返回false
|
||||
*/
|
||||
export const isBase64 = (path: string): boolean => {
|
||||
return /^data:image\/(\w+);base64/.test(path);
|
||||
};
|
||||
2
src/uni_modules/lime-shared/isBrowser/index.ts
Normal file
2
src/uni_modules/lime-shared/isBrowser/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// @ts-nocheck
|
||||
export const isBrowser = typeof window !== 'undefined';
|
||||
9
src/uni_modules/lime-shared/isDef/index.ts
Normal file
9
src/uni_modules/lime-shared/isDef/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否已定义(不为 undefined)且不为 null
|
||||
* @param value 要检查的值
|
||||
* @returns 如果值已定义且不为 null,则返回 true;否则返回 false
|
||||
*/
|
||||
export function isDef(value: unknown): boolean {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
8
src/uni_modules/lime-shared/isFunction/index.ts
Normal file
8
src/uni_modules/lime-shared/isFunction/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为函数类型
|
||||
* @param val 要检查的值
|
||||
* @returns 如果值的类型是函数类型,则返回 true;否则返回 false
|
||||
*/
|
||||
export const isFunction = (val: unknown): val is Function =>
|
||||
typeof val === 'function';
|
||||
9
src/uni_modules/lime-shared/isNumber/index.ts
Normal file
9
src/uni_modules/lime-shared/isNumber/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为数字类型
|
||||
* @param value 要检查的值,可以是 number 类型或 string 类型的数字
|
||||
* @returns 如果值是数字类型且不是 NaN,则返回 true;否则返回 false
|
||||
*/
|
||||
export function isNumber(value: number | string): boolean {
|
||||
return typeof value === 'number' && !isNaN(value);
|
||||
}
|
||||
9
src/uni_modules/lime-shared/isNumeric/index.ts
Normal file
9
src/uni_modules/lime-shared/isNumeric/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为数字类型或表示数字的字符串
|
||||
* @param value 要检查的值,可以是 string 类型或 number 类型
|
||||
* @returns 如果值是数字类型或表示数字的字符串,则返回 true;否则返回 false
|
||||
*/
|
||||
export function isNumeric(value: string | number): boolean {
|
||||
return /^(-)?\d+(\.\d+)?$/.test(value);
|
||||
}
|
||||
8
src/uni_modules/lime-shared/isObject/index.ts
Normal file
8
src/uni_modules/lime-shared/isObject/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为对象类型
|
||||
* @param val 要检查的值
|
||||
* @returns 如果值的类型是对象类型,则返回 true;否则返回 false
|
||||
*/
|
||||
export const isObject = (val : unknown) : val is Record<any, any> =>
|
||||
val !== null && typeof val === 'object';
|
||||
13
src/uni_modules/lime-shared/isPromise/index.ts
Normal file
13
src/uni_modules/lime-shared/isPromise/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// @ts-nocheck
|
||||
import {isFunction} from '../isFunction'
|
||||
import {isObject} from '../isObject'
|
||||
/**
|
||||
* 检查一个值是否为 Promise 类型
|
||||
* @param val 要检查的值
|
||||
* @returns 如果值的类型是 Promise 类型,则返回 true;否则返回 false
|
||||
*/
|
||||
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
|
||||
// 使用 isObject 函数判断值是否为对象类型
|
||||
// 使用 isFunction 函数判断值是否具有 then 方法和 catch 方法
|
||||
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
|
||||
};
|
||||
7
src/uni_modules/lime-shared/isString/index.ts
Normal file
7
src/uni_modules/lime-shared/isString/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 检查一个值是否为字符串类型
|
||||
* @param str 要检查的值
|
||||
* @returns 如果值的类型是字符串类型,则返回 true;否则返回 false
|
||||
*/
|
||||
export const isString = (str: unknown): str is string => typeof str === 'string';
|
||||
17
src/uni_modules/lime-shared/kebabCase/index.ts
Normal file
17
src/uni_modules/lime-shared/kebabCase/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// export function toLowercaseSeparator(key: string) {
|
||||
// return key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 将字符串转换为指定连接符的命名约定
|
||||
* @param str 要转换的字符串
|
||||
* @param separator 指定的连接符,默认为 "-"
|
||||
* @returns 转换后的字符串
|
||||
*/
|
||||
export function kebabCase(str: string, separator: string = "-"): string {
|
||||
return str
|
||||
.replace(/[A-Z]/g, match => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母
|
||||
.replace(/[\s_-]+/g, separator) // 将空格、下划线和短横线替换为指定连接符
|
||||
.replace(new RegExp(`^${separator}|${separator}$`, "g"), "") // 删除开头和结尾的连接符
|
||||
.toLowerCase(); // 将结果转换为全小写
|
||||
}
|
||||
83
src/uni_modules/lime-shared/package.json
Normal file
83
src/uni_modules/lime-shared/package.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"id": "lime-shared",
|
||||
"displayName": "lime-shared",
|
||||
"version": "0.1.4",
|
||||
"description": "本人插件的几个公共函数,获取当前页,图片的base64转临时路径,图片的exif信息等",
|
||||
"keywords": [
|
||||
"lime-shared",
|
||||
"exif",
|
||||
"selectComponent"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "sdk-js",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "u",
|
||||
"Edge": "u",
|
||||
"Firefox": "u",
|
||||
"Safari": "u"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "y",
|
||||
"百度": "y",
|
||||
"字节跳动": "y",
|
||||
"QQ": "y",
|
||||
"钉钉": "y",
|
||||
"快手": "y",
|
||||
"飞书": "y",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
src/uni_modules/lime-shared/pathToBase64/index.ts
Normal file
121
src/uni_modules/lime-shared/pathToBase64/index.ts
Normal file
@@ -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
|
||||
})
|
||||
}
|
||||
2320
src/uni_modules/lime-shared/piexif/index.ts
Normal file
2320
src/uni_modules/lime-shared/piexif/index.ts
Normal file
File diff suppressed because it is too large
Load Diff
27
src/uni_modules/lime-shared/platform/index.ts
Normal file
27
src/uni_modules/lime-shared/platform/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
// @ts-nocheck
|
||||
declare var tt: Uni
|
||||
declare var swan: Uni
|
||||
declare var my: Uni
|
||||
declare var dd: Uni
|
||||
declare var ks: Uni
|
||||
declare var jd: Uni
|
||||
declare var qa: Uni
|
||||
declare var qq: Uni
|
||||
declare var qh: Uni
|
||||
declare var qq: Uni
|
||||
|
||||
export function platform(): Uni | WechatMiniprogram.Wx {
|
||||
const UNDEFINED = 'undefined'
|
||||
if(typeof wx !== UNDEFINED) return wx // 微信
|
||||
if(typeof tt !== UNDEFINED) return tt // 字节 飞书
|
||||
if(typeof swan !== UNDEFINED) return swan // 百度
|
||||
if(typeof my !== UNDEFINED) return my // 支付宝
|
||||
if(typeof dd !== UNDEFINED) return dd // 钉钉
|
||||
if(typeof ks !== UNDEFINED) return ks // 快手
|
||||
if(typeof jd !== UNDEFINED) return jd // 京东
|
||||
if(typeof qa !== UNDEFINED) return qa // 快应用
|
||||
if(typeof qq !== UNDEFINED) return qq // qq
|
||||
if(typeof qh !== UNDEFINED) return qh // 360
|
||||
if(typeof uni !== UNDEFINED) return uni
|
||||
return null
|
||||
}
|
||||
30
src/uni_modules/lime-shared/raf/index.ts
Normal file
30
src/uni_modules/lime-shared/raf/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// @ts-nocheck
|
||||
import {isBrowser} from '../isBrowser'
|
||||
|
||||
// 是否支持被动事件监听
|
||||
export const supportsPassive = true;
|
||||
|
||||
// 请求动画帧
|
||||
export function raf(fn: FrameRequestCallback): number {
|
||||
// 如果是在浏览器环境下,使用 requestAnimationFrame 方法
|
||||
if (isBrowser) {
|
||||
return requestAnimationFrame(fn); // 请求动画帧
|
||||
} else { // 在非浏览器环境下,使用 setTimeout 模拟
|
||||
return setTimeout(fn, 1000 / 30); // 使用 setTimeout 模拟动画帧,每秒钟执行 30 次
|
||||
}
|
||||
}
|
||||
|
||||
// 取消动画帧
|
||||
export function cancelRaf(id: number) {
|
||||
// 如果是在浏览器环境下,使用 cancelAnimationFrame 方法
|
||||
if (isBrowser) {
|
||||
cancelAnimationFrame(id); // 取消动画帧
|
||||
} else { // 在非浏览器环境下,使用 clearTimeout 模拟
|
||||
clearTimeout(id); // 使用 clearTimeout 模拟取消动画帧
|
||||
}
|
||||
}
|
||||
|
||||
// 双倍动画帧
|
||||
export function doubleRaf(fn: FrameRequestCallback): void {
|
||||
raf(() => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果
|
||||
}
|
||||
22
src/uni_modules/lime-shared/random/index.ts
Normal file
22
src/uni_modules/lime-shared/random/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 生成一个指定范围内的随机数
|
||||
* @param min 随机数的最小值
|
||||
* @param max 随机数的最大值
|
||||
* @param fixed 随机数的小数位数,默认为 0
|
||||
* @returns 生成的随机数
|
||||
*/
|
||||
export function random(min: number, max: number, fixed: number = 0) {
|
||||
// 将 min 和 max 转换为数字类型
|
||||
min = +min || 0;
|
||||
max = +max || 0;
|
||||
// 计算随机数范围内的一个随机数
|
||||
const num = Math.random() * (max - min) + min;
|
||||
// 如果 fixed 参数为 0,则返回四舍五入的整数随机数;否则保留固定小数位数
|
||||
return fixed == 0 ? Math.round(num) : Number(num.toFixed(fixed));
|
||||
}
|
||||
|
||||
// 示例
|
||||
// console.log(random(0, 10)); // 输出:在 0 和 10 之间的一个整数随机数
|
||||
// console.log(random(0, 1, 2)); // 输出:在 0 和 1 之间的一个保留两位小数的随机数
|
||||
// console.log(random(1, 100, 3)); // 输出:在 1 和 100 之间的一个保留三位小数的随机数
|
||||
31
src/uni_modules/lime-shared/range/index.ts
Normal file
31
src/uni_modules/lime-shared/range/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 生成一个数字范围的数组
|
||||
* @param start 范围的起始值
|
||||
* @param end 范围的结束值
|
||||
* @param step 步长,默认为 1
|
||||
* @param fromRight 是否从右侧开始生成,默认为 false
|
||||
* @returns 生成的数字范围数组
|
||||
*/
|
||||
export function range(start: number, end: number, step: number = 1, fromRight: boolean = false): number[] {
|
||||
let index = -1;
|
||||
// 计算范围的长度
|
||||
let length = Math.max(Math.ceil((end - start) / (step || 1)), 0);
|
||||
// 创建一个长度为 length 的数组
|
||||
const result = new Array(length);
|
||||
|
||||
// 使用循环生成数字范围数组
|
||||
while (length--) {
|
||||
// 根据 fromRight 参数决定从左侧还是右侧开始填充数组
|
||||
result[fromRight ? length : ++index] = start;
|
||||
start += step;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// 示例
|
||||
// console.log(range(0, 5)); // 输出: [0, 1, 2, 3, 4]
|
||||
// console.log(range(1, 10, 2, true)); // 输出: [9, 7, 5, 3, 1]
|
||||
// console.log(range(5, 0, -1)); // 输出: [5, 4, 3, 2, 1]
|
||||
251
src/uni_modules/lime-shared/readme.md
Normal file
251
src/uni_modules/lime-shared/readme.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# lime-shared 工具库
|
||||
- 本人插件的几个公共函数
|
||||
|
||||
## 引入
|
||||
```js
|
||||
// 按需引入
|
||||
// 这种只会引入相关的方法
|
||||
import {getRect} from '@/uni_modules/lime-shared/getRect'
|
||||
|
||||
// 全量引入
|
||||
// 这种引入方式,会全量打包
|
||||
import {getRect} from '@/uni_modules/lime-shared'
|
||||
```
|
||||
|
||||
## Utils
|
||||
|
||||
#### getRect
|
||||
- 返回节点尺寸信息
|
||||
|
||||
```js
|
||||
// 组件内需要传入上下文
|
||||
// 如果是nvue 则需要在节点上加与id或class同名的ref
|
||||
getRect('#id',{context: this}).then(res => {})
|
||||
```
|
||||
|
||||
#### addUnit
|
||||
- 将未带单位的数值添加px,如果有单位则返回原值
|
||||
|
||||
```js
|
||||
addUnit(10)
|
||||
// 10px
|
||||
```
|
||||
|
||||
#### unitConvert
|
||||
- 将带有rpx|px的字符转成number,若本身是number则直接返回
|
||||
|
||||
```js
|
||||
unitConvert('10rpx')
|
||||
// 5 设备不同 返回的值也不同
|
||||
unitConvert('10px')
|
||||
// 10
|
||||
unitConvert(10)
|
||||
// 10
|
||||
```
|
||||
|
||||
#### canIUseCanvas2d
|
||||
- 环境是否支持使用 canvas 2d
|
||||
|
||||
```js
|
||||
canIUseCanvas2d()
|
||||
// 若支持返回 true 否则 false
|
||||
```
|
||||
|
||||
|
||||
#### getCurrentPage
|
||||
- 获取当前页
|
||||
|
||||
```js
|
||||
const page = getCurrentPage()
|
||||
```
|
||||
|
||||
|
||||
#### base64ToPath
|
||||
- 把base64的图片转成临时路径
|
||||
|
||||
```js
|
||||
base64ToPath(`xxxxx`).then(res => {})
|
||||
```
|
||||
|
||||
#### pathToBase64
|
||||
- 把图片的临时路径转成base64
|
||||
|
||||
```js
|
||||
pathToBase64(`xxxxx/xxx.png`).then(res => {})
|
||||
```
|
||||
|
||||
#### sleep
|
||||
- 睡眠,让 async 内部程序等待一定时间后再执行
|
||||
|
||||
```js
|
||||
async next () => {
|
||||
await sleep(300)
|
||||
console.log('limeui');
|
||||
}
|
||||
```
|
||||
|
||||
#### isBase64
|
||||
- 判断字符串是否为base64
|
||||
|
||||
```js
|
||||
isBase64('xxxxx')
|
||||
```
|
||||
|
||||
#### throttle
|
||||
- 节流
|
||||
|
||||
```js
|
||||
throttle((nama) => {console.log(nama)}, 200)('limeui');
|
||||
```
|
||||
|
||||
#### debounce
|
||||
- 防抖
|
||||
|
||||
```js
|
||||
debounce((nama) => {console.log(nama)}, 200)('limeui');
|
||||
```
|
||||
|
||||
#### random
|
||||
- 返回指定范围的随机数
|
||||
|
||||
```js
|
||||
random(1, 5);
|
||||
```
|
||||
|
||||
#### range
|
||||
- 生成区间数组
|
||||
|
||||
```js
|
||||
range(0, 5)
|
||||
// [0,1,2,3,4,5]
|
||||
```
|
||||
|
||||
#### clamp
|
||||
- 夹在min和max之间的数值,如小于min,返回min, 如大于max,返回max,否侧原值返回
|
||||
|
||||
```js
|
||||
clamp(0, 10, -1)
|
||||
// 0
|
||||
clamp(0, 10, 11)
|
||||
// 10
|
||||
clamp(0, 10, 9)
|
||||
// 9
|
||||
```
|
||||
|
||||
#### floatAdd
|
||||
- 返回两个浮点数相加的结果
|
||||
|
||||
```js
|
||||
floatAdd(0.1, 0.2) // 0.3
|
||||
```
|
||||
|
||||
|
||||
#### fillZero
|
||||
- 补零,如果传入的是`个位数`则在前面补0
|
||||
|
||||
```js
|
||||
fillZero(9);
|
||||
// 09
|
||||
```
|
||||
|
||||
#### exif
|
||||
- 获取图片exif
|
||||
- 支持临时路径、base64
|
||||
|
||||
```js
|
||||
uni.chooseImage({
|
||||
count: 1, //最多可以选择的图片张数
|
||||
sizeType: "original",
|
||||
success: (res) => {
|
||||
exif.getData(res.tempFiles[0], function() {
|
||||
let tagj = exif.getTag(this, "GPSLongitude");
|
||||
let Orientation = exif.getTag(this, 'Orientation');
|
||||
console.log(tagj, Orientation)
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### selectComponent
|
||||
- 获取页面或当前实例的指定组件,会在页面或实例向所有的节点查找(包括子组件或子子组件)
|
||||
- 仅vue3,vue2没有测试过
|
||||
|
||||
```js
|
||||
// 当前页面
|
||||
const page = getCurrentPage()
|
||||
selectComponent('.custom', {context: page}).then(res => {
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
#### createAnimation
|
||||
- 创建动画,与uni.createAnimation使用方法一致,只为了抹平nvue
|
||||
|
||||
```html
|
||||
<view ref="ball" :animation="animationData"></view>
|
||||
```
|
||||
```js
|
||||
const ball = ref(null)
|
||||
const animation = createAnimation({
|
||||
transformOrigin: "50% 50%",
|
||||
duration: 1000,
|
||||
timingFunction: "ease",
|
||||
delay: 0
|
||||
})
|
||||
|
||||
animation.scale(2,2).rotate(45).step()
|
||||
// nvue 无导出数据,这样写只为了平台一致,
|
||||
// nvue 需要把 ref 传入,其它平台不需要
|
||||
const animationData = animation.export(ball.value)
|
||||
```
|
||||
|
||||
|
||||
## composition-api
|
||||
- 因本人插件需要兼容vue2/vue3,故增加一个vue文件,代替条件编译
|
||||
- vue2需要在main.js加上这一段
|
||||
```js
|
||||
// vue2
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
Vue.use(VueCompositionAPI)
|
||||
```
|
||||
|
||||
```js
|
||||
//使用
|
||||
import {computed, onMounted, watch, reactive} from '@/uni_modules/lime-shared/vue'
|
||||
```
|
||||
|
||||
|
||||
## Hooks
|
||||
#### useIntersectionObserver
|
||||
- 使用 Intersection Observer 观察元素可见性的钩子函数
|
||||
|
||||
```html
|
||||
<div class="target">
|
||||
<h1>Hello world</h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
```js
|
||||
// options 接口可传的参数,若在插件里context为必传
|
||||
interface UseIntersectionObserverOptions {
|
||||
root ?: string; // 观察器的根元素选择器字符串
|
||||
rootMargin ?: {
|
||||
top ?: number; // 根元素顶部边距
|
||||
bottom ?: number; // 根元素底部边距
|
||||
left ?: number; // 根元素左侧边距
|
||||
right ?: number; // 根元素右侧边距
|
||||
}; // 根元素的边距
|
||||
thresholds ?: any[]; // 交叉比例数组,用于指定何时触发回调函数
|
||||
context ?: any; // 上下文对象,用于指定观察器的上下文
|
||||
initialRatio ?: number; // 初始的交叉比例
|
||||
observeAll ?: boolean; // 是否同时观察所有交叉对象
|
||||
}
|
||||
const options: UseIntersectionObserverOptions = {
|
||||
rootMargin: {top: 44},
|
||||
context: this
|
||||
}
|
||||
const {stop} = useIntersectionObserver('.target', (result) => {
|
||||
|
||||
}, options)
|
||||
```
|
||||
152
src/uni_modules/lime-shared/selectComponent/index.ts
Normal file
152
src/uni_modules/lime-shared/selectComponent/index.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
// @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 selectComponent(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, node} = options
|
||||
const [,prefix, name] = selector.match(reg)
|
||||
const symbolMappings = {'.': 'class', '#': 'id', '$':'ref', '@':'component'}
|
||||
|
||||
const key = symbolMappings [prefix] //prefix === '.' ? 'class' : prefix === '#' ? 'id' : 'ref';
|
||||
console.log('key', key)
|
||||
// #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
|
||||
}
|
||||
30
src/uni_modules/lime-shared/sleep/index.ts
Normal file
30
src/uni_modules/lime-shared/sleep/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 延迟指定时间后解析的 Promise
|
||||
* @param delay 延迟的时间(以毫秒为单位),默认为 300 毫秒
|
||||
* @returns 一个 Promise,在延迟结束后解析
|
||||
*/
|
||||
export const sleep = (delay: number = 300) =>
|
||||
new Promise(resolve => setTimeout(resolve, delay));
|
||||
|
||||
|
||||
// 示例
|
||||
// async function example() {
|
||||
// console.log("Start");
|
||||
|
||||
// // 延迟 1 秒后执行
|
||||
// await sleep(1000);
|
||||
// console.log("1 second later");
|
||||
|
||||
// // 延迟 500 毫秒后执行
|
||||
// await sleep(500);
|
||||
// console.log("500 milliseconds later");
|
||||
|
||||
// // 延迟 2 秒后执行
|
||||
// await sleep(2000);
|
||||
// console.log("2 seconds later");
|
||||
|
||||
// console.log("End");
|
||||
// }
|
||||
|
||||
// example();
|
||||
41
src/uni_modules/lime-shared/throttle/index.ts
Normal file
41
src/uni_modules/lime-shared/throttle/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 节流函数,用于限制函数的调用频率
|
||||
* @param fn 要进行节流的函数
|
||||
* @param delay 两次调用之间的最小间隔时间
|
||||
* @returns 节流后的函数
|
||||
*/
|
||||
export function throttle(fn: (...args: any[]) => void, delay: number) {
|
||||
let flag = true; // 标记是否可以执行函数
|
||||
|
||||
return (...args: any[]) => {
|
||||
if (flag) {
|
||||
flag = false; // 设置为不可执行状态
|
||||
fn(...args); // 执行传入的函数
|
||||
|
||||
setTimeout(() => {
|
||||
flag = true; // 经过指定时间后,设置为可执行状态
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// // 示例
|
||||
// // 定义一个被节流的函数
|
||||
// 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);
|
||||
13
src/uni_modules/lime-shared/toArray/index.ts
Normal file
13
src/uni_modules/lime-shared/toArray/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 将一个或多个元素转换为数组
|
||||
* @param item 要转换为数组的元素
|
||||
* @returns 转换后的数组
|
||||
*/
|
||||
export const toArray = <T>(item: T | T[]): T[] => Array.isArray(item) ? item : [item];
|
||||
|
||||
// 示例
|
||||
// console.log(toArray(5)); // 输出: [5]
|
||||
// console.log(toArray("hello")); // 输出: ["hello"]
|
||||
// console.log(toArray([1, 2, 3])); // 输出: [1, 2, 3]
|
||||
// console.log(toArray(["apple", "banana"])); // 输出: ["apple", "banana"]
|
||||
15
src/uni_modules/lime-shared/toNumber/index.ts
Normal file
15
src/uni_modules/lime-shared/toNumber/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* 将字符串转换为数字
|
||||
* @param val 要转换的字符串
|
||||
* @returns 转换后的数字或原始字符串
|
||||
*/
|
||||
export function toNumber(val: string): number | string {
|
||||
const n = parseFloat(val); // 使用 parseFloat 函数将字符串转换为浮点数
|
||||
return isNaN(n) ? val : n; // 使用 isNaN 函数判断是否为非数字,返回转换后的数字或原始字符串
|
||||
}
|
||||
|
||||
// 示例
|
||||
// console.log(toNumber("123")); // 输出: 123
|
||||
// console.log(toNumber("3.14")); // 输出: 3.14
|
||||
// console.log(toNumber("hello")); // 输出: "hello"
|
||||
39
src/uni_modules/lime-shared/unitConvert/index.ts
Normal file
39
src/uni_modules/lime-shared/unitConvert/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// @ts-nocheck
|
||||
import {isString} from '../isString'
|
||||
import {isNumeric} from '../isNumeric'
|
||||
|
||||
/**
|
||||
* 单位转换函数,将字符串数字或带有单位的字符串转换为数字
|
||||
* @param value 要转换的值,可以是字符串数字或带有单位的字符串
|
||||
* @returns 转换后的数字,如果无法转换则返回0
|
||||
*/
|
||||
export function unitConvert(value: string | number): number {
|
||||
// 如果是字符串数字
|
||||
if (isNumeric(value)) {
|
||||
return Number(value);
|
||||
}
|
||||
// 如果有单位
|
||||
if (isString(value)) {
|
||||
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g;
|
||||
const results = reg.exec(value);
|
||||
if (!value || !results) {
|
||||
return 0;
|
||||
}
|
||||
const unit = results[3];
|
||||
value = parseFloat(value);
|
||||
if (unit === 'rpx') {
|
||||
return uni.upx2px(value);
|
||||
}
|
||||
if (unit === 'px') {
|
||||
return value * 1;
|
||||
}
|
||||
// 如果是其他单位,可以继续添加对应的转换逻辑
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 示例
|
||||
// console.log(unitConvert("123")); // 输出: 123 (字符串数字转换为数字)
|
||||
// console.log(unitConvert("3.14em")); // 输出: 0 (无法识别的单位)
|
||||
// console.log(unitConvert("20rpx")); // 输出: 根据具体情况而定 (根据单位进行转换)
|
||||
// console.log(unitConvert(10)); // 输出: 10 (数字不需要转换)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user