32 Commits

Author SHA1 Message Date
3d6252d5cb Merge pull request '抽奖' (#34) from dev-qsh into jwl-gift
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/34
2024-01-25 09:07:54 +08:00
qsh
056ca965b3 抽奖 2024-01-25 08:59:04 +08:00
4fd6a21f35 Merge pull request 'dev-hxf' (#33) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/33
2023-11-07 20:58:25 +08:00
7b1db47383 提交 2023-11-07 20:50:15 +08:00
79236493e6 Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-10-30 20:29:32 +08:00
59c49bfdf2 提交 2023-10-30 20:29:18 +08:00
110e9d4adc Merge pull request '屏蔽Ios支付' (#32) from dev-qsh into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/32
2023-10-25 20:33:06 +08:00
qsh
b533581a5d 屏蔽Ios支付 2023-10-25 20:30:58 +08:00
ac8acdccda Merge pull request 'dev-hxf' (#31) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/31
2023-10-17 12:03:51 +08:00
f4b6f96649 Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-10-17 12:01:11 +08:00
9ba3f6ed48 提交 2023-10-17 12:00:58 +08:00
253ab2a80d Merge pull request 'dev-hxf' (#30) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/30
2023-10-10 18:07:01 +08:00
bfdbd3f7f3 Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-10-10 17:56:31 +08:00
e3544ee06e 提交 2023-10-10 17:56:20 +08:00
7e15974ea6 Merge pull request 'dev-hxf' (#29) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/29
2023-09-21 16:34:55 +08:00
46972d5bff Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-09-21 16:31:30 +08:00
851319e24c 提交 2023-09-21 16:30:36 +08:00
19616d56c5 Merge pull request 'dev-hxf' (#28) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/28
2023-09-20 22:32:22 +08:00
89c4b2148e 提交 2023-09-20 22:23:21 +08:00
e68a422cc8 Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-09-20 22:18:41 +08:00
2e93ad93e9 提交 2023-09-20 22:18:19 +08:00
c783ff969d Merge pull request 'dev-hxf' (#27) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/27
2023-09-18 21:44:49 +08:00
4526430a38 Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-09-18 21:43:29 +08:00
3a0545852f 提交 2023-09-18 21:42:50 +08:00
0410e5dda9 Merge pull request 'dev-hxf' (#26) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/26
2023-09-17 23:53:26 +08:00
0c230fa5f1 Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-09-17 23:49:59 +08:00
ded28b62fb 提交 2023-09-17 23:49:42 +08:00
6834b3f9fe Merge pull request 'dev-hxf' (#25) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/25
2023-09-17 13:23:02 +08:00
3379a9b18e Merge branch 'master' of http://114.55.169.15:3000/huxiaofeng666/jwl-applet into dev-hxf 2023-09-17 13:19:26 +08:00
9e871e4079 提交 2023-09-17 13:18:59 +08:00
728fefac03 Merge pull request 'dev-hxf' (#24) from dev-hxf into master
Reviewed-on: http://114.55.169.15:3000/huxiaofeng666/jwl-applet/pulls/24
2023-09-07 09:55:25 +08:00
bf05f771f7 提交 2023-09-07 09:47:38 +08:00
118 changed files with 11451 additions and 1252 deletions

View File

@@ -5,7 +5,6 @@ VITE_APP_TITLE = 金武联驾校
VITE_APP_ENV = 'development'
# 金武联驾校/开发环境
VITE_APP_BASE_API = 'https://jwl.ahduima.com/'
VITE_APP_BASE_API = 'https://jwl.ahduima.com/'
#
VITE_WEB_BASE_URL = 'https://jwl.ahduima.com'
VITE_WEB_BASE_URL = 'https://jwl.ahduima.com'

View File

@@ -5,7 +5,7 @@ VITE_APP_TITLE = 金武联驾校
VITE_APP_ENV = 'production'
# 金武联驾校/开发环境
VITE_APP_BASE_API = 'https://jwl.ahduima.com/'
VITE_APP_BASE_API = 'https://jwl.ahduima.com/'
#
VITE_WEB_BASE_URL = 'https://jwl.ahduima.com'
VITE_WEB_BASE_URL = 'https://jwl.ahduima.com'

View File

@@ -1,16 +1,21 @@
<script>
import useUserStore from '@/jtools/store/user'
import useQuestionStore from '@/jtools/store/question' //引入store
import storage from './jtools/storage'
export default {
onLaunch: function () {
onLaunch: function (options) {
// 如果是二维码扫描过来的需要保存公司id
if (options.query?.scene) {
storage.set('companyId', options.query?.scene)
}
useUserStore().queryVipList()
useQuestionStore().getAllQuestion()
if(useUserStore().isLogin) {
if (useUserStore().isLogin) {
useUserStore().getUserInfo()
useUserStore().searchUserVip()
}
},
onShow: function () {
useQuestionStore().getAllQuestion()
console.log('App Show')
},
onHide: function () {
@@ -23,5 +28,8 @@ export default {
/*每个页面公共css */
@import "uni_modules/uview-plus/index.scss";
@import "static/style/index.scss";
button::after{ border: none;}
button::after {
border: none;
}
</style>

File diff suppressed because it is too large Load Diff

View 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>
```

View 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>

View File

@@ -7,4 +7,21 @@ export function getAliCompanyInfo(data) {
data,
noToken: true
});
}
export function getCarTypeList(data) {
return request({
url: 'driver-api/tdCar/list',
method: 'GET',
data,
noToken: true
});
}
export function addInfo(data) {
return request({
url: 'chaoyuan-api/driver/addInfo',
method: 'POST',
data
});
}

View File

@@ -50,7 +50,14 @@ export function querySysConfigList(carTypeId, configKey) {
noToken: true
});
}
//获取配置
export function querySysConfig(carTypeId, configKey) {
return request({
url: 'driver-api/tdSysConfig/queryConfigByKey?configKey=' + configKey + '&carTypeId=' + carTypeId,
method: 'GET',
noToken: true
});
}
//获取项目列表 (考试项目和基础操作)
export function queryProjectList(data) {
return request({

View File

@@ -64,7 +64,8 @@ export default class JtoolsPay {
"description": this.order.description,
"money": this.order.money,
"outTradeNo": this.order.outTradeNo,
"userId": this.order.userId
"userId": this.order.userId,
"tradeType":'1'
}
if (uni.getStorageSync('openId')) {
params.openId = uni.getStorageSync('openId');

View File

@@ -29,7 +29,7 @@ function service(options = {}) {
title: res?.data?.message || '请重新登录',
icon: 'none'
});
useUserStore().logout()
useUserStore().logoutWithoutToken()
//请求成功
resolved(res.data);
} else if(res.data.code != '0000'&&res.data.code !='4001') {

View File

@@ -4,7 +4,8 @@ import {
import http from '@/jtools/request/index';
import {
queryQuestion,
getVersion
getVersion,
querySysConfig
} from '@/jtools/api/question';
import storage from '@/jtools/storage';
@@ -12,212 +13,224 @@ const question = defineStore({
id: 'question',
state: () => ({
currentCartype: storage.get('carType') || '1001',
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') || ''
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') || ''
}),
actions: {
getAllQuestion(){
getVersion(this.currentCartype).then(resp=>{
if(resp.code==='0000'){
if(this.version!=resp.data){
this.version=resp.data
storage.set('version',resp.data)
this.getOrderQuestion_sub1(true)
this.getOrderQuestion_sub4(true)
}else{
this.getOrderQuestion_sub1()
this.getOrderQuestion_sub4()
}
}
})
},
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;
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)
}
})
}
})
},
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)
changeSubject(val) {
this.curSubject = val
storage.set('curSubject', val)
},
// 获取顺序做题科目1
getOrderQuestion_sub1(isUpdate) {
if(isUpdate){
this.loading_subject1=true
queryQuestion({
carTypeId: this.currentCartype,
subject: '1',
// questionIdList:[10982,10983,10985,10986]
}).then(res => {
if (res.code == '0000') {
this.loading_subject1=false
uni.showToast({
title:'题库加载完成!'
})
this.orderQuestion_subject1 = res.data
const diveList=this.divideArray(this.orderQuestion_subject1,5)
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=[]
this.orderQuestion_subject1.forEach(item=>{
if(falseList.includes(item.questionId)){
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)){
if (trueList.includes(item.questionId)) {
rightArr.push(item.questionId)
}
})
storage.set('wrongList_subject1',falseArr)
storage.set('rightList_subject1',rightArr)
storage.set('wrongList_subject1', falseArr)
storage.set('rightList_subject1', rightArr)
}
})
}else{
}
})
} 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
queryQuestion({
carTypeId: this.currentCartype,
subject: '1',
// questionIdList:[10982,10983,10985,10986]
}).then(res => {
if (res.code == '0000') {
this.loading_subject1=false
uni.showToast({
title:'题库加载完成!'
})
this.orderQuestion_subject1 = res.data
const diveList=this.divideArray(this.orderQuestion_subject1,5)
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=[]
this.orderQuestion_subject1.forEach(item=>{
if(falseList.includes(item.questionId)){
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)){
if (trueList.includes(item.questionId)) {
rightArr.push(item.questionId)
}
})
storage.set('wrongList_subject1',falseArr)
storage.set('rightList_subject1',rightArr)
storage.set('wrongList_subject1', falseArr)
storage.set('rightList_subject1', rightArr)
}
})
}
})
}
}
},
// 获取顺序做题科目4
getOrderQuestion_sub4(isUpdate) {
if(isUpdate){
this.loading_subject4=true
queryQuestion({
carTypeId: this.currentCartype,
subject: '4',
// questionIdList:[10982,10983,10985,10986]
}).then(res => {
if (res.code == '0000') {
this.loading_subject4=false
this.orderQuestion_subject4 = res.data
const diveList=this.divideArray(this.orderQuestion_subject4,5)
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=[]
this.orderQuestion_subject4.forEach(item=>{
if(falseList.includes(item.questionId)){
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)){
if (trueList.includes(item.questionId)) {
rightArr.push(item.questionId)
}
})
storage.set('wrongList_subject4',falseArr)
storage.set('rightList_subject4',rightArr)
storage.set('wrongList_subject4', falseArr)
storage.set('rightList_subject4', rightArr)
}
})
}else{
}
})
} 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
queryQuestion({
carTypeId: this.currentCartype,
subject: '4',
// questionIdList:[10982,10983,10985,10986]
}).then(res => {
if (res.code == '0000') {
this.loading_subject4=false
this.orderQuestion_subject4 = res.data
const diveList=this.divideArray(this.orderQuestion_subject4,5)
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=[]
this.orderQuestion_subject4.forEach(item=>{
if(falseList.includes(item.questionId)){
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)){
if (trueList.includes(item.questionId)) {
rightArr.push(item.questionId)
}
})
storage.set('wrongList_subject4',falseArr)
storage.set('rightList_subject4',rightArr)
storage.set('wrongList_subject4', falseArr)
storage.set('rightList_subject4', rightArr)
}
})
}
})
}
}
},
//获取索引
getCurrentIndex(index,val){
this[`currentIndex_subject${val}`]=index
console.log(`currentIndex_subject${val}`,this[`currentIndex_subject${val}`]);
getCurrentIndex(index, val) {
this[`currentIndex_subject${val}`] = index
}
}
});

View File

@@ -1,6 +1,6 @@
import { defineStore } from 'pinia';
import { login,logout,getInfo } from '@/jtools/api/login';
import { queryVip,getVipList } from '@/jtools/api/vip'
import { login, logout, getInfo } from '@/jtools/api/login';
import { queryVip, getVipList } from '@/jtools/api/vip';
import constants from '@/jtools/constants';
import storage from '@/jtools/storage';
@@ -16,67 +16,77 @@ const useUserStore = defineStore({
}),
actions: {
login(params) {
return new Promise(async (resolve, reject) => {
const resp = await login(params);
if (resp.code === '0000') {
// 保存登录信息,用于重新登录
this.isLogin = true;
this.token = resp.data.token;
this.userInfo = resp.data
storage.set('isLogin', true)
storage.set('token', resp.data.token)
storage.set('userInfo', resp.data)
resolve(resp.data);
} else {
reject();
}
});
},
login(params) {
return new Promise(async (resolve, reject) => {
const resp = await login(params);
if (resp.code === '0000') {
// 保存登录信息,用于重新登录
this.isLogin = true;
this.token = resp.data.token;
this.userInfo = resp.data;
storage.set('isLogin', true);
storage.set('token', resp.data.token);
storage.set('userInfo', resp.data);
storage.remove('companyId');
resolve(resp.data);
} else {
reject();
}
});
},
// 登出
logout(force = false) {
return new Promise((resolve, reject) => {
this.resetUserData();
uni.redirectTo({
url: '/pages/login/login'
})
});
resolve();
})
});
},
//过期登出
logoutWithoutToken(force = false) {
return new Promise((resolve, reject) => {
this.resetUserData();
resolve();
});
},
// 获取用户信息
getUserInfo() {
getInfo().then(resp => {
if(resp.code == '0000') {
this.userInfo = resp.data
storage.set('userInfo', resp.data)
if (resp.code == '0000') {
this.userInfo = resp.data;
storage.set('userInfo', resp.data);
}
})
});
},
resetUserData() {
this.isLogin = false;
this.token = '';
this.userInfo = {}
this.vipOnList = []
storage.remove('isLogin')
storage.remove('token')
storage.remove('userInfo')
this.userInfo = {};
this.vipOnList = [];
storage.remove('isLogin');
storage.remove('token');
storage.remove('userInfo');
},
// 查询当前用户的vip开通情况
async searchUserVip() {
const resp=await queryVip({ carTypeId: this.currentCartype,memberId: null, subject:'' })
if(resp.code == '0000') {
this.vipOnList = resp.data
}
this.currentCartype = storage.get('carType') || '1001';
const resp = await queryVip({ carTypeId: this.currentCartype, memberId: null, subject: '' });
if (resp.code == '0000') {
this.vipOnList = resp.data;
}
},
// 查询所有的vip
queryVipList() {
getVipList({ carTypeId: this.currentCartype,memberId: null, subject:'' }).then(resp => {
if(resp.code == '0000') {
this.vipAllList = resp.data
this.currentCartype = storage.get('carType') || '1001';
getVipList({ carTypeId: this.currentCartype, memberId: null, subject: '' }).then(resp => {
if (resp.code == '0000') {
this.vipAllList = resp.data;
}
})
});
}
},
}
});
export default useUserStore;

18
src/package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"id": "yan-qr",
"name": "动态生成二维码",
"displayName": "动态生成二维码",
"version": "1.0.0",
"description": "动态生成二维码",
"keywords": [
"二维码",
"生成二维码",
"动态二维码"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
}
}

View File

@@ -8,7 +8,8 @@
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "金武联驾考"
"navigationStyle": "custom",
"enablePullDownRefresh": false
}
},
{
@@ -104,6 +105,13 @@
"enablePullDownRefresh": false
}
},
{
"path": "pages/index/testTip",
"style": {
"navigationBarTitleText": "模拟考试",
"enablePullDownRefresh": false
}
},
{
"path": "pages/login/login",
"style": {
@@ -158,6 +166,37 @@
"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
}
}
],

View 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>

View File

@@ -41,11 +41,11 @@
<view class="mt5">VIP课程</view>
</view>
</view>
<view class="wp33 flex ai-c jc-c" @tap="toAnswer('精简500题',true)">
<view class="wp33 flex ai-c jc-c" @tap="toAnswer(`精简${titleNum}题`,true)">
<view class="text-center wp100">
<image style="width: 72rpx;height: 72rpx;margin: 0 auto" src="../../static/image/index/500icon.png">
</image>
<view class="mt5">精简500</view>
<view class="mt5">精简{{titleNum}}</view>
</view>
</view>
<view class="wp33 flex ai-c jc-c" @tap="toExclusive">
@@ -78,11 +78,10 @@
</view>
</view>
</view>
<view style="padding: 0 28rpx;margin-top: 30rpx;" @tap="toClass">
<!-- <view style="padding: 0 28rpx;margin-top: 30rpx;" @tap="toClass">
<view class="video-box">
<view class="flex jc-sb ai-c wp100">
<text style="color: #05C341;font-size: 36rpx;">{{subject==1?'一':'四'}}精品视频课</text>
<!-- <text class="cor-666 fs12">全部10节课 ></text> -->
</view>
<view class="flex ai-c mt20">
<view class="contain-box relative">
@@ -97,7 +96,7 @@
</view>
</view>
</view>
</view>
</view> -->
</view>
</template>
@@ -110,6 +109,7 @@
import storage from '@/jtools/storage';
import useQuestionStore from '@/jtools/store/question' //引入store
import {
querySysConfig,
queryQuestionId,
getTestQuestionId
} from '@/jtools/api/question';
@@ -127,11 +127,12 @@
},
data() {
return {
titleNum:500,
allQuestionNum: 0,
}
},
mounted() {
this.getTitle()
},
computed: {
...mapState(useUserStore, ["vipOnList", "token"]),
@@ -143,6 +144,15 @@
methods: {
...mapActions(useUserStore, ['searchUserVip']),
...mapActions(useQuestionStore, ['getOrderQuestion_sub1', 'getOrderQuestion_sub4', 'getAllQuestion']),
getTitle(){
const carId=storage.get('carType') || '1001'
querySysConfig(carId,'SimplifyQuestionNum').then(resp=>{
if(resp.code==='0000'){
const list = JSON.parse(resp.data.configJson)
this.titleNum=list.find(item=>item.subject==this.subject).num
}
})
},
async toTestRoom() {
// uni.navigateTo({
// url:"/pages/index/trueTest"
@@ -161,11 +171,18 @@
subject: this.subject,
}).then(async (resp) => {
if (resp.code === '0000') {
const arr = resp.data
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/index/trueTest?subject="+ this.subject + "&questionIdList=" + listJson
})
if(resp.data&&resp.data.length){
const arr = resp.data
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/index/trueTest?subject="+ this.subject + "&questionIdList=" + listJson
})
}else{
uni.showToast({
title: '暂无题目',
icon: 'none'
})
}
} else if (resp.code === '4001') {
uni.showToast({
title: '当前题库非最新版,请更新~',
@@ -207,7 +224,7 @@
})
},
toAnswer(title, val) {
if (title == '精简500题') {
if (title == `精简${this.titleNum}`) {
queryQuestionId({
versionId: this.version,
carTypeId: storage.get('carType') || '1001',
@@ -218,23 +235,38 @@
if (this.token) {
await this.searchUserVip()
const result = this.vipOnList.some(item => item.subjects.includes(this.subject))
let arr = []
if (result) {
const listJson = JSON.stringify(resp.data)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject +
"&needVip=" + result + "&questionIdList=" + listJson
})
if(resp.data&&resp.data.length){
const listJson = JSON.stringify(resp.data)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject +
"&needVip=" + !result + "&questionIdList=" + listJson
})
}else{
uni.showToast({
title: '暂无题目',
icon: 'none'
})
}
} else {
if (resp.data && resp.data.length > 3) {
const arr = resp.data.slice(0, 3)
arr = resp.data.slice(0, 3)
} else {
const arr = resp.data
arr = resp.data
}
if(arr&&arr.length){
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject +
"&needVip=" + !result + "&questionIdList=" + listJson
})
}else{
uni.showToast({
title: '暂无题目',
icon: 'none'
})
}
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject +
"&needVip=" + result + "&questionIdList=" + listJson
})
}
} else {
uni.redirectTo({
@@ -268,7 +300,7 @@
const arr = resp.data
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/practiceExams?title=" + title + "&subject=" + this.subject + "&questionIdList=" + listJson
url: "/pages/index/testTip?subject=" + this.subject + "&questionIdList=" + listJson
})
} else if (resp.code === '4001') {
uni.showToast({
@@ -287,6 +319,7 @@
...param
}).then(async (resp) => {
if (resp.code === '0000') {
let arr = []
if (this.token) {
await this.searchUserVip()
const result = this.vipOnList.some(item => item.subjects.includes(this.subject))
@@ -296,13 +329,13 @@
})
} else {
if (resp.data && resp.data.length > 3) {
const arr = resp.data.slice(0, 3)
arr = resp.data.slice(0, 3)
} else {
const arr = resp.data
arr = resp.data
}
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/practiceExams?title=" + title + "&subject=" + this.subject + "&questionIdList=" + listJson+"&needVip="+result
url: "/pages/index/videoVip?subject=" + this.subject
})
}
} else {

View File

@@ -94,7 +94,7 @@
}
},
async mounted() {
await this.getDiverType()
// await this.getDiverType()
},
computed: {
...mapState(useUserStore, ["vipOnList", "token"])
@@ -134,9 +134,9 @@
"subject": String(this.subject),
"type": "2"
}).then(resp => {
if (resp.code === '0000') {
if (resp.code === '0000' && resp.data &&resp.data.length) {
this.baseList = resp.data
this.videoList = resp.data[0] ? resp.data[0].videoList.slice(0, 5) : []
this.videoList = resp.data[0] && resp.data[0].videoList? resp.data[0].videoList.slice(0, 5) : []
}
})
},
@@ -148,20 +148,35 @@
const formatSecond = second > 59 ? 59 : second
return `${hours > 0 ? `${hours}:` : ''}${minute < 10 ? '0' + minute : minute}:${formatSecond < 10 ? '0' + formatSecond : formatSecond}`
},
toVideo() {
let arr = JSON.parse(JSON.stringify(this.operateList))
arr = arr.map(item => {
return {
...item,
videoUrl: item.videoList[0]?.videoUrl,
videoTime: this.formateTime(item.videoList[0]?.videoTime)
async toVideo() {
if (this.token) {
await this.searchUserVip()
const res = this.vipOnList.some(item => item.subjects.includes(this.subject))
if (!res) {
uni.navigateTo({
url:"/pages/index/videoVip?subject="+this.subject
})
}else{
let arr = JSON.parse(JSON.stringify(this.operateList))
arr = arr.map(item => {
return {
...item,
videoUrl: item.videoList[0]?.videoUrl,
videoTime: this.formateTime(item.videoList[0]?.videoTime)
}
})
let jsonString = JSON.stringify(arr)
console.log('arr',jsonString);
uni.navigateTo({
url: "/pages/questionBank/videoDetail?videoList=" + jsonString + "&subject=" + this.subject +
"&projectId=" + this.projectId + "&type=1&driveType=2"
})
}
})
let jsonString = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/videoDetail?videoList=" + jsonString + "&subject=" + this.subject +
"&projectId=" + this.projectId + "&type=1"
})
} else {
uni.redirectTo({
url: '/pages/login/login'
});
}
},
toOperateDetail(val) {
let arr = JSON.parse(JSON.stringify(this.baseList[0].videoList))
@@ -186,8 +201,10 @@
"subject": String(this.subject),
"type": "1"
}).then(resp => {
this.operateList = resp.data
this.projectId = this.operateList[0]?.projectId
if(resp.code==='0000'){
this.operateList = resp.data
this.projectId = this.operateList[0]?.projectId
}
})
},
getDiverType() {

View File

@@ -0,0 +1,147 @@
<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">您今日还剩1次刮奖机会共有999人参加活动</view>
<view class="scraping">
<scraping-card :result="result" watermark="刮一刮" title="刮一刮赢取大奖" ref="reset" ></scraping-card>
<view v-if="showBtn" class="gj">
<view class="btn" @tap="handleScrap">
点我刮奖
</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">
<view v-for="(item, index) in winningList" :key="index" class="card-item">
<text class="item-text">188****8888</text>
<text class="item-text">一等奖</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">
<view class="item-text">1. 中奖率100%</view>
<view class="item-text">2. 报名后可获得一次抽奖机会</view>
<view class="item-text">3. 中奖用户请于"我的-我的奖品"中查看并核销</view>
</view>
</view>
</view>
</template>
<script>
import ScrapingCard from './scraping.vue'
export default {
components: {
ScrapingCard
},
data() {
return {
result: '特等奖',
showBtn: true,
winningList: [1,2,3,4,5,6,7,8]
}
},
methods: {
handleScrap() {
this.showBtn = !this.showBtn
}
},
mounted() {
}
}
</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>

View File

@@ -0,0 +1,266 @@
<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>
</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: 60,
},
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);
}
}
},
/** @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;
}
</style>

View File

@@ -1,11 +1,20 @@
<template>
<view>
<u-loading-page :loading="getLoading" loading-text="题库更新中..."></u-loading-page>
<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="height: 100vh;background-color: rgb(245, 245, 245);">
<view class="m10tb">
<u-swiper :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>
@@ -35,6 +44,7 @@
},
data() {
return {
show:false,
subject: storage.get('curSubject') || '1',
curTab: 0,
searchValue: '',
@@ -42,24 +52,42 @@
categoryList: [],
rightList: storage.get(`rightList_subject${this.subject}`) || [],
wrongList: storage.get(`wrongList_subject${this.subject}`) || [],
activityList: ['https://cdn.uviewui.com/uview/swiper/swiper1.png']
};
},
onLoad() {
this.curTab=Number(this.curSubject)-1
this.getSubjectConfig()
},
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"]), //映射函数取出tagslist
...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']),
//获取科目配置
@@ -73,6 +101,8 @@
name: item.configItemName
}
})
this.subject=storage.get('curSubject') || '1',
this.curTab=this.categoryList.findIndex(item=>item.configItemCode==this.subject)
}
})
},
@@ -89,6 +119,12 @@
}, 100)
}
},
// 去活动
handleToActivity(index) {
uni.navigateTo({
url: '/pages/index/activity'
})
}
}
}
</script>

View File

@@ -0,0 +1,83 @@
<template>
<view class="bc-fff hp100">
<view class="wp100 p14 flex ai-c jc-c">
<view class="text-center">
<view style="width: 64px;height: 64px;border-radius: 50%;overflow: hidden;margin-bottom: 10px;">
<u-avatar class="br-p50 overflow-h" :size="64" mp-avatar shape="circle"></u-avatar>
</view>
<text class="fs18">{{user.userName}}{{ user.userId }}</text>
</view>
</view>
<view class="p14">
<view class="wp100 p14lr p20tb mb14" style="background-color: rgb(248, 248, 248);border-radius: 20rpx;">
<view class="flex ai-c jc-sb pb10">
<text style="color: rgb(175, 175, 175);">考试类型</text>
<text>{{carName}}</text>
</view>
<view class="flex ai-c jc-sb pb10">
<text style="color: rgb(175, 175, 175);">考试标准</text>
<text>{{subject=='1'?100:50}}/45分钟</text>
</view>
<view class="flex ai-c jc-sb pb10">
<text style="color: rgb(175, 175, 175);">合格标准</text>
<text>90分及格(满分100分)</text>
</view>
<view class="flex ai-c jc-sb">
<text style="color: rgb(175, 175, 175);">出题规则</text>
<text>根据公安部出题规则组卷</text>
</view>
</view>
<text>模拟考试不能修改答案每错1题扣{{subject=='1'?1:2}}错题累计超过{{subject=='1'?10:5}}考试不通过</text>
</view>
<view class="p14lr wp100" style="margin-top: 30px;">
<u-button :customStyle="{width: '100%',borderRadius:'40rpx',backgroundColor:'#05C341',color:'#fff'}" text="开始考试"
@click="submit" />
</view>
<view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions
} from 'pinia' //引入映射函数
import useUserStore from '@/jtools/store/user'
import storage from '@/jtools/storage';
import useQuestionStore from '@/jtools/store/question' //引入store
export default {
data() {
return {
carName:storage.get('carName') ||'小车C1/C2/C3',
questionList: '',
subject:'1',
}
},
onLoad(op) {
if (op.questionIdList) {
this.questionList = op.questionIdList
}
if(op.subject){
this.subject = op.subject
}
},
computed: {
...mapState(useQuestionStore, ["currentCarName"]), //映射函数取出tagslist
user() {
return useUserStore().userInfo
},
},
methods: {
submit(){
uni.navigateTo({
url: "/pages/questionBank/practiceExams?title=模拟考试&subject=" + this.subject + "&questionIdList=" + this.questionList
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -133,7 +133,7 @@
onLoad(option) {
this.subject = option.subject || 1
if(option.questionIdList){
const idList=JSON.parse(op.questionIdList)
const idList=JSON.parse(option.questionIdList)
let arr = this[`orderQuestion_subject${this.subject}`].filter(qItem=>idList.includes(qItem.questionId))
this.list = arr.map(it => ({
...it,

View File

@@ -1,38 +1,75 @@
<template>
<view class="relative" style="height: 100vh;">
<image style="width: 100%;height: 600rpx;" src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/vip%E9%A1%B5%E8%83%8C%E6%99%AF%E5%9B%BE_20230830214136.png"></image>
<view class="p14">
<view class="flex jc-sb ai-c wp100">
<view class="option_tem relative" :class="checkedId===item.memberId?'checked_item':''" v-for="(item,index) of priceList" :key="index" @click="checkPrice(item.memberId,item.price)">
<text class="fw600 fs12 cor-333">{{item.memberName}}</text>
<view class="mt5">
<text class="fs14" style="color: #FF6E02;">¥</text>
<text class="fs30 fw600" style="color: #FF6E02;">{{item.price}}</text>
</view>
<text class="fs12 cor-999">长期有效</text>
<view class="bottom_box fs12 cor-333" :class="checkedId===item.memberId?'checked_bottom':''">赠送vip题库</view>
<view class="tag" v-if="item.all">
<text style="transform:scale(0.83);">合买更优惠</text>
<view style="height: 100%;overflow: scroll;background-color: #fff;">
<image style="width: 100%;height: 600rpx;" src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/vip%E9%A2%98%E5%BA%93_20230911211532.png"></image>
<view style="margin-bottom: 100px;">
<view class="flex jc-fa ai-c wp100 p14">
<view style="width: 33.3%;" v-for="(item, index) of priceList" :key="index">
<view class="option_tem relative" :class="checkedId === item.memberId ? 'checked_item' : ''" @click="checkPrice(item.memberId, item.price)">
<text class="fw600 fs12 cor-333">{{ item.memberName }}</text>
<view class="mt5" v-if="!isIOS">
<text class="fs14" style="color: #FF6E02;">¥</text>
<text class="fs30 fw600" style="color: #FF6E02;">{{ item.price }}</text>
</view>
<view v-else class="mt5">
<text class="fs14" style="color: #FF6E02;">iOS暂不支持</text>
</view>
<text class="fs12 cor-999">一年有效</text>
<!-- <view class="bottom_box fs12 cor-333" :class="checkedId===item.memberId?'checked_bottom':''">赠送vip题库</view> -->
<view class="tag" v-if="item.all">
<text style="transform:scale(0.83);">合买更优惠</text>
</view>
</view>
</view>
</view>
<view class="mt15 intr_box">
<view class="fw600 fs16 cor-000">免费获赠<text style="color: #FF6E02;">价值68元</text>的VIP题库</view>
<view class="flex ai-c jc-sb mt15">
<view class="flex ai-c">
<image style="width: 63rpx;height: 63rpx;margin-right: 5px;" src="../../static/image/index/vip500.png"></image>
<view class="vip_item">含精简500题</view>
</view>
<view class="flex ai-c">
<image style="width: 63rpx;height: 63rpx;margin-right: 5px;" src="../../static/image/index/vipmijuan.png"></image>
<view class="vip_item">含考前密卷2套</view>
</view>
</view>
<view class="intr_box p14">
<view class="fw600 fs16 cor-000">尊享以下权益</view>
<view class="flex ai-c jc-sb mt15" v-if="subject == '1' || subject == '4'">
<view class="text-center" style="width: 33%">
<view class="wp100 flex ai-c jc-c mb5">
<image style="width: 63rpx;height: 63rpx;margin-right: 5px;" src="../../static/image/index/vip500.png"></image>
</view>
<text>精简{{ titleNum }}</text>
</view>
<view class="text-center" style="width: 33%;">
<view class="wp100 flex ai-c jc-c mb5">
<image style="width: 63rpx;height: 63rpx;margin-right: 5px;" src="../../static/image/index/vip_trueRoom.png"></image>
</view>
<text>真实考场模拟</text>
</view>
<view class="text-center" style="width: 33%;">
<view class="wp100 flex ai-c jc-c mb5">
<image style="width: 63rpx;height: 63rpx;margin-right: 5px;" src="../../static/image/index/vipmijuan.png"></image>
</view>
<text>考前密卷</text>
</view>
</view>
<view v-else>
<view class="flex ai-c jc-c mt15">
<view class="flex ai-c">
<image style="width: 63rpx;height: 63rpx;margin-right: 5px;" src="../../static/image/index/vip_ksxj.png"></image>
<view class="vip_item">考试项目详解</view>
</view>
</view>
<view class="flex ai-c mt20">
<view class="contain-box relative">
<image class="contain-box" src="https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/%E5%B0%8F%E8%BD%A6_20230911211545.png"></image>
</view>
<view class="ml15" style="text-align: left;">
<view class="cor-333 fw600">考试项目详解</view>
<view class="fs15 mt10" style="color: #9F826C ;">考试实操技巧讲解事半功倍</view>
</view>
</view>
</view>
</view>
<view class="wp100 mt15" v-if="subject == '1' || subject == '4'">
<image class="wp100" mode="widthFix" :src="picUrl"></image>
</view>
</view>
</view>
<view class="wp100 p14" style="position: absolute;left: 0;bottom:20px" @tap="handlePay()">
</view>
<view v-if="!isIOS" class="wp100 p14" style="position: absolute;left: 0;bottom:20px" @tap="handlePay()">
<view class="sub_btn flex ai-c jc-sb">
<text class="cor-fff fs14">¥<text class="fs24 cor-fff">{{nowPrice}}</text></text>
<text class="cor-fff fs14">¥<text class="fs24 cor-fff">{{ nowPrice }}</text></text>
<image style="width: 276rpx;height: 88rpx;margin-top: -5px;" src="../../static/image/index/buy.png"></image>
</view>
</view>
@@ -40,152 +77,197 @@
</template>
<script>
import {
mapState,
mapActions
} from 'pinia' //引入映射函数
import { getVipList } from '@/jtools/api/vip'
import storage from '@/jtools/storage';
import Pay from '@/jtools/pay/index.js';
import useUserStore from '@/jtools/store/user'
export default {
data(){
return{
subject:'1',
loading:true,
nowPrice:168,
checkedId:0,
priceList:[],
order:{
money:0,
description:'会员充值'
}
}
},
onLoad(op){
if(op.subject){
this.subject=op.subject
}
this.loading=true
this.getVipList()
this.getWXOpenId()
this.$set(this.order, 'userId', this.userInfo.userId);
},
computed: {
...mapState(useUserStore, ["userInfo"])
},
methods:{
async handlePay(){
if(this.loading){
this.loading=false
await this.getWXOpenId()
new Pay('wechat', this.order);
this.loading=true
}
import {
mapState,
mapActions
} from 'pinia' //引入映射函数
import { getVipList } from '@/jtools/api/vip'
import { querySysConfig } from '@/jtools/api/question';
import storage from '@/jtools/storage';
import Pay from '@/jtools/pay/index.js';
import useUserStore from '@/jtools/store/user'
export default {
data() {
return {
picUrl: '',
titleNum: 500,
subject: '1',
loading: true,
nowPrice: 168,
checkedId: 0,
priceList: [],
order: {
money: 0,
description: '会员充值'
},
getWXOpenId() {
const that = this
uni.login({
success(res) {
that.$set(that.order, 'code', res.code);
}
})
},
getVipList(){
getVipList({
currentCartype: storage.get('carType') || '1001',
subject:this.subject
}).then(resp=>{
this.priceList=resp.data
this.checkedId=this.priceList[0].memberId
this.order.outTradeNo=this.priceList[0].memberId
this.order.money=this.priceList[0].price
this.nowPrice=this.priceList[0].price
this.priceList.forEach(item=>{
if(item.subjects.length>1){
item.all=true
isIOS: true
}
},
onLoad(op) {
this.isIOS = this.$platform.device().includes('ios')
if (op.subject) {
this.subject = op.subject
}
this.loading = true
this.getVipList()
this.getWXOpenId()
this.getTitle()
this.getPic()
this.$set(this.order, 'userId', this.userInfo.userId);
},
computed: {
...mapState(useUserStore, ["userInfo"])
},
methods: {
getPic() {
const currentCartype = storage.get('carType') || '1001'
querySysConfig(currentCartype, 'VipDescImageUrl').then(res => {
this.picUrl = JSON.parse(res.data.configJson).url
})
},
getTitle() {
const carId = storage.get('carType') || '1001'
querySysConfig(carId, 'SimplifyQuestionNum').then(resp => {
if (resp.code === '0000') {
const list = JSON.parse(resp.data.configJson)
this.titleNum = list.find(item => item.subject == this.subject).num
}
})
},
async handlePay() {
if (this.loading) {
this.loading = false
await this.getWXOpenId()
new Pay('wechat', this.order);
this.loading = true
}
},
getWXOpenId() {
const that = this
uni.login({
success(res) {
that.$set(that.order, 'code', res.code);
}
})
},
getVipList() {
getVipList({
carTypeId: storage.get('carType') || '1001',
subject: this.subject
}).then(resp => {
if (resp.code === '0000') {
this.priceList = resp.data
this.checkedId = this.priceList[0].memberId
this.order.outTradeNo = this.priceList[0].memberId
this.order.money = this.priceList[0].price
this.nowPrice = this.priceList[0].price
this.priceList.forEach(item => {
if (item.subjects.length > 1) {
item.all = true
}
})
})
},
checkPrice(val,price){
this.checkedId=val
this.order.outTradeNo=val
this.nowPrice=price
this.order.money=price
}
}
}
})
},
checkPrice(val, price) {
this.checkedId = val
this.order.outTradeNo = val
this.nowPrice = price
this.order.money = price
}
}
}
</script>
<style scoped>
.option_tem{
width: 220rpx;
height: 241rpx;
text-align: center;
background: #FFFFFF;
border: 2px solid #D8D8D8;
border-radius: 16rpx 46rpx 16rpx 16rpx;
padding: 14px;
}
.checked_item{
width: 228rpx;
background: #FFF0E5;
border: 4px solid #FF6E02;
}
.bottom_box{
width: 214rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
background: rgb(239,239,239);
border-radius: 0 0 16rpx 16rpx;
position: absolute;
bottom: 0;
left: 0;
}
.checked_bottom{
width: 218rpx;
border-radius: 0 0 16rpx 5rpx;
background-color: #FF6E02;
color:#fff
}
.tag{
padding:0 5px;
height: 36rpx;
background: linear-gradient(90deg, #E66501 0%, #F8A42C 100%);
border-radius: 8rpx 20rpx 8rpx 8rpx;
line-height: 36rpx;
text-align: center;
font-size: 12px;
color: #FFFC27;
position: absolute;
left: 10rpx;
top:-18rpx
}
.intr_box{
width: 100%;
text-align: center;
padding: 40rpx;
background: #FFF0E5;
border-radius: 16rpx;
}
.vip_item{
width: 208rpx;
height: 54rpx;
line-height: 54rpx;
text-align: center;
font-size: 14px;
background: #F3D7C2;
border-radius: 0rpx 10rpx 10rpx 10rpx;
}
.sub_btn{
width:100%;
height: 110rpx;
border: 4px solid #F59B26;
background: linear-gradient(0deg, #E66501 0%, #F8A42C 100%);
box-shadow: 0rpx 16rpx 20rpx 1rpx rgba(245,155,38,0.78);
border-radius: 55rpx;
padding: 14rpx;
}
.option_tem {
width: 220rpx;
height: 241rpx;
text-align: center;
background: #FFFFFF;
border: 2px solid #D8D8D8;
border-radius: 16rpx 46rpx 16rpx 16rpx;
padding: 14px;
}
.checked_item {
background: #FFF0E5;
border: 4px solid #FF6E02;
}
.bottom_box {
width: 214rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
background: rgb(239, 239, 239);
border-radius: 0 0 16rpx 16rpx;
position: absolute;
bottom: 0;
left: 0;
}
.checked_bottom {
width: 218rpx;
border-radius: 0 0 16rpx 5rpx;
background-color: #FF6E02;
color: #fff
}
.tag {
padding: 0 5px;
height: 36rpx;
background: linear-gradient(90deg, #E66501 0%, #F8A42C 100%);
border-radius: 8rpx 20rpx 8rpx 8rpx;
line-height: 36rpx;
text-align: center;
font-size: 12px;
color: #FFFC27;
position: absolute;
left: 10rpx;
top: -18rpx
}
.intr_box {
width: 100%;
text-align: center;
padding: 40rpx;
background: #FFF0E5;
border-radius: 16rpx;
}
.vip_item {
width: 208rpx;
height: 54rpx;
line-height: 54rpx;
text-align: center;
font-size: 14px;
background: #F3D7C2;
border-radius: 0rpx 10rpx 10rpx 10rpx;
}
.sub_btn {
width: 100%;
height: 110rpx;
border: 4px solid #F59B26;
background: linear-gradient(0deg, #E66501 0%, #F8A42C 100%);
box-shadow: 0rpx 16rpx 20rpx 1rpx rgba(245, 155, 38, 0.78);
border-radius: 55rpx;
padding: 14rpx;
}
.contain-box {
width: 344rpx;
height: 196rpx;
background: #00B74F;
border-radius: 16rpx;
}
.play_btn_2 {
width: 65rpx;
height: 65rpx;
position: absolute;
left: 165.5rpx;
top: 78rpx
}
</style>

View File

@@ -13,7 +13,7 @@
<view class="list-call">
<u-input class="sl-input" v-model="login.code" type="text" maxlength="6" border="none" placeholder="输入验证码">
<template #suffix>
<text class="fs14 mr10" style="color: #05C341;" @tap="getCode">{{countDown==0?'获取验证码':countDown}}</text>
<text class="fs14 mr10" style="color: #05C341;" @tap="getCode">{{ countDown == 0 ? '获取验证码' : countDown }}</text>
</template>
</u-input>
</view>
@@ -27,138 +27,142 @@
</template>
<script>
import {
isPhone
} from '@/jtools/utils/validate.js'
import {
getCode,
login
} from '@/jtools/api/login'
import useUserStore from '@/jtools/store/user'
export default {
data() {
return {
login: {
phone: '',
code: ''
},
countDown: 0,
js: undefined
};
},
onShow() {
if(useUserStore().isLogin) {
this.toHome()
}
},
methods: {
getCode() {
if (isPhone(this.login.phone) && this.countDown == 0) {
getCode({
phone: this.login.phone
}).then(resp => {
// if (resp.code == '0000') {
uni.showToast({
title: '发送成功!',
icon: 'none'
})
this.countDown = 60
this.js = setInterval(() => {
this.countDown--;
if (this.countDown == 0) {
this.clear()
}
}, 1000)
// }
import {
isPhone
} from '@/jtools/utils/validate.js'
import {
getCode,
login
} from '@/jtools/api/login'
import useUserStore from '@/jtools/store/user'
import storage from '@/jtools/storage';
export default {
data() {
return {
login: {
phone: '',
code: ''
},
countDown: 0,
js: undefined
};
},
onShow() {
if (useUserStore().isLogin) {
this.toHome()
}
},
methods: {
getCode() {
if (isPhone(this.login.phone) && this.countDown == 0) {
getCode({
phone: this.login.phone
}).then(resp => {
// if (resp.code == '0000') {
uni.showToast({
title: '发送成功!',
icon: 'none'
})
}
},
clear() {
clearInterval(js)
this.js = null
this.countDown = 0
},
bindLogin() {
if(isPhone(this.login.phone) && this.login.code) {
useUserStore().login(this.login).then(resp => {
if(resp.userId) {
useUserStore().getUserInfo()
useUserStore().searchUserVip()
this.toHome()
this.countDown = 60
this.js = setInterval(() => {
this.countDown--;
if (this.countDown == 0) {
this.clear()
}
})
}
},
toHome() {
uni.switchTab({
url: '/pages/index/index'
}, 1000)
// }
})
}
},
clear() {
clearInterval(js)
this.js = null
this.countDown = 0
},
bindLogin() {
if (isPhone(this.login.phone) && this.login.code) {
let params = { ...this.login }
if (storage.get('companyId')) {
params.id = storage.get('companyId')
}
useUserStore().login(params).then(resp => {
if (resp.userId) {
useUserStore().getUserInfo()
useUserStore().searchUserVip()
this.toHome()
}
})
}
},
toHome() {
uni.switchTab({
url: '/pages/index/index'
})
}
}
}
</script>
<style>
page {
background-color: #fff;
}
page {
background-color: #fff;
}
.content {
display: flex;
flex-direction: column;
justify-content: center;
}
.content {
display: flex;
flex-direction: column;
justify-content: center;
}
.header {
margin-top: 166rpx;
margin-left: auto;
margin-right: auto;
letter-spacing: 10rpx;
}
.header {
margin-top: 166rpx;
margin-left: auto;
margin-right: auto;
letter-spacing: 10rpx;
}
.header image {
width: 383rpx;
}
.header image {
width: 383rpx;
}
.list {
display: flex;
flex-direction: column;
padding-top: 120rpx;
padding-left: 90rpx;
padding-right: 90rpx;
}
.list {
display: flex;
flex-direction: column;
padding-top: 120rpx;
padding-left: 90rpx;
padding-right: 90rpx;
}
.list-call {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 100rpx;
color: #333333;
border-bottom: 0.5px solid #e2e2e2;
}
.list-call {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 100rpx;
color: #333333;
border-bottom: 0.5px solid #e2e2e2;
}
.list-call .sl-input {
flex: 1;
text-align: left;
font-size: 32rpx;
margin-left: 16rpx;
}
.button-login {
color: #FFFFFF;
font-size: 34rpx;
width: 560rpx;
height: 100rpx;
background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);
border-radius: 50rpx;
line-height: 100rpx;
text-align: center;
margin-top: 100rpx;
margin-left: auto;
margin-right: auto;
}
.list-call .sl-input {
flex: 1;
text-align: left;
font-size: 32rpx;
margin-left: 16rpx;
}
.button-login {
color: #FFFFFF;
font-size: 34rpx;
width: 560rpx;
height: 100rpx;
background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);
border-radius: 50rpx;
line-height: 100rpx;
text-align: center;
margin-top: 100rpx;
margin-left: auto;
margin-right: auto;
}
</style>

View File

@@ -0,0 +1,99 @@
<template>
<view>
<view class="p14">
<view class="wp100 p14lr p20tb bc-fff br8 mb10 relative" style="box-sizing: border-box;" :class="checkedCar==item.carTypeId?'checked':''" v-for="(item,index) of carTypeList" :key="index" @click="changeType(item)">{{item.carName}}{{item.remark}}
<view class="p5 cor-fff fs12 br4" style="background-color:#05C341;position: absolute;right: -1rpx;top:-1rpx" v-if="checkedCar==item.carTypeId">已选</view>
</view>
<view class="wp100" style="margin-top: 40px;">
<u-button :customStyle="{width: '100%',borderRadius:'40rpx',backgroundColor:'#05C341',color:'#fff'}" text="确定"
@click="submit" />
</view>
<u-modal :show="show" title="提示" content="切换题库后上一个题库练习记录将会全部清空,确定要切换吗" showCancelButton @confirm="confirmSubmit" @cancel="cancel"></u-modal>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions
} from 'pinia' //引入映射函数
import {
getCarTypeList,
} from '@/jtools/api/index';
import storage from '@/jtools/storage';
import useQuestionStore from '@/jtools/store/question' //引入store
export default{
data() {
return{
show:false,
carTypeList:[],
checkedCar: storage.get('carType') ||'1001',
checkedCarName:storage.get('carName') || '小车C1/C2/C3'
}
},
mounted() {
this.getCarTypeList()
},
computed:{
...mapState(useQuestionStore, ["loading_subject4", "loading_subject1","curSubject"]), //映射函数取出tagslist
getLoading() {
return this.loading_subject4 && this.loading_subject1
}
},
watch:{
getLoading(newVal){
console.log(newVal);
if(newVal){
uni.hideLoading()
uni.showToast({
title:'切换成功!'
})
this.cancel()
}
}
},
methods:{
getCarTypeList(){
getCarTypeList().then(resp=>{
if(resp.code==='0000'){
this.carTypeList=resp.data
}
})
},
changeType(item){
this.checkedCar=item.carTypeId
this.checkedCarName=item.carName+item.remark
},
submit(){
const id=storage.get('carType') ||'1001'
if(this.checkedCar==id){
uni.showToast({
title:'当前已是该车型题库!',
icon:'error'
})
}else{
this.show=true
}
},
cancel(){
this.show=false
},
confirmSubmit(){
storage.set('carType',this.checkedCar)
storage.set('carName',this.checkedCarName)
uni.showLoading({
title: '加载中'
});
useQuestionStore().resetStorage()
useQuestionStore().getAllQuestion()
}
}
}
</script>
<style scoped>
.checked{
border: 6rpx solid #05C341;
}
</style>

View File

@@ -40,7 +40,7 @@
<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="study fs16 text-center" style="margin: 25px auto 0;color: #F6E99FFF;">立即开通</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">
<view class="fs18 fwb" style="color: #7E4012FF;">{{ vipText }}</view>
@@ -75,17 +75,27 @@
<template #icon>
<img src="/static/image/mine/wdzl.png" style="width: 24px;height: 24px;">
</template>
</u-cell>
<u-cell size="large" title="我的奖品" isLink @tap="handleGift">
<template #icon>
<u-icon size="24" name="gift"></u-icon>
</template>
</u-cell>
<u-cell size="large" title="我的体检" isLink @tap="handleTJ">
<template #icon>
<img src="/static/image/mine/wdtj.png" style="width: 24px;height: 24px;">
</template>
</u-cell>
<u-cell size="large" title="我的题库" value="小车">
<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">
@@ -96,6 +106,7 @@
</template>
<script>
import useUserStore from '@/jtools/store/user'
import storage from '@/jtools/storage';
export default {
components: {},
computed: {
@@ -136,17 +147,35 @@ export default {
},
data() {
return {
};
carName: storage.get('carName') || '小车C1/C2/C3',
isIOS: true
}
},
onShow() {
this.isIOS = this.$platform.device().includes('ios')
this.carName = storage.get('carName') || '小车C1/C2/C3'
},
methods: {
// 拨打电话
callPhoneNumber() {
uni.makePhoneCall({
phoneNumber: '15105693067'
});
},
toChangeCarType() {
uni.navigateTo({
url: "/pages/me/changeCarType"
})
},
handleVip() {
if (this.isIOS) {
return
}
if (this.isLogin) {
// if (this.vipOn.length) {
uni.navigateTo({
url: '/pages/me/vip'
})
uni.navigateTo({
url: '/pages/me/vip'
})
// } else {
// uni.navigateTo({
// url: '/pages/index/videoVip'
@@ -181,8 +210,12 @@ export default {
},
handleTJ() {
if (this.isLogin) {
uni.navigateTo({
url: '/pages/me/tijian'
// uni.navigateTo({
// url: '/pages/me/tijian'
// })
uni.showToast({
title: '敬请期待',
icon: 'none'
})
} else {
this.toLogin()
@@ -196,6 +229,11 @@ export default {
handleLogout() {
useUserStore().logout()
},
handleGift() {
uni.navigateTo({
url: '/pages/me/myGift'
})
}
}
}
</script>

View File

@@ -1,28 +1,50 @@
<template>
<view class="p15 bc-f5">
<view class="br8 bc-fff p15lr">
<view class="flex ai-c bb1" style="height: 110rpx;">
<view class="flex jc-sb ai-c bb1" style="height: 110rpx;">
<view class="title">联系电话</view>
<view class="m30lr fs14 cor-333 fl1">15122305568</view>
<u-icon name="arrow-right" color="#999" />
<view class="flex">
<view class="m30lr fs14 cor-333">15122305568</view>
<u-icon name="arrow-right" color="#999" />
</view>
</view>
<view class="flex ai-c" style="height: 110rpx;">
<view class="flex jc-sb ai-c bb1" style="height: 110rpx;">
<view class="title">地址</view>
<view class="m30lr fs14 cor-333 fl1">安徽省合肥市包河区</view>
<u-icon name="arrow-right" color="#999" />
<view class="flex">
<view class="m30lr fs14 cor-333">安徽省合肥市包河区</view>
<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>
<script>
import storage from '@/jtools/storage';
export default {
data() {
return {
sfzNum:storage.get('sfzNum') || ''
}
},
onShow(){
this.sfzNum=storage.get('sfzNum') || ''
},
methods: {
toUploadPic(){
uni.navigateTo({
url: '/pages/me/uploadPic'
})
}
}
}
</script>

67
src/pages/me/myGift.vue Normal file
View File

@@ -0,0 +1,67 @@
<template>
<view class="p10lr p20tb">
<view 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">一等奖</view>
</view>
<view class="df ai-c jcsb p20tb p10lr">
<view class="item-label">
<view>活动名称幸运刮刮乐开心赢大奖</view>
<view>参与时间2024-01-01 08:00</view>
<view>有效期至2024-03-01 08:00</view>
</view>
<view class="ml20" style="width: 120rpx;">
<img v-if="index%2==0" src="/static/image/mine/writeoff.png" style="width: 120rpx;height: 120rpx;" />
<view v-else class="btn" @tap="handleWriteoff(item)">核销</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: []
}
},
onShow() {
this.handleSearch()
},
methods: {
handleSearch() {
this.list = [1,2,3,4,5]
},
handleWriteoff(item) {
uni.navigateTo({
url: `/pages/me/qrCode?item=${item}`
})
}
}
}
</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>

29
src/pages/me/qrCode.vue Normal file
View File

@@ -0,0 +1,29 @@
<template>
<view class="df jcc" style="padding-top: 100px;">
<qrcode value="123" :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.item
},
methods: {
}
}
</script>
<style>
</style>

View File

@@ -24,11 +24,11 @@
<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="name" borderBottom ref="item1">
<u--input v-model="form.name" border="none"></u--input>
<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="idNum" borderBottom ref="item2">
<u--input v-model="form.idNum" border="none"></u--input>
<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>
@@ -42,6 +42,9 @@
<script>
import storage from '@/jtools/storage';
import {
addInfo,
} from '@/jtools/api/index';
export default {
data() {
const shenfenzhen = (rule, value, callback) => {
@@ -50,20 +53,21 @@
}
return {
form: {
name: '',
idNum: ''
idCardName: '',
sfzmhm: ''
},
driverLicenseImg:'',
fileList:[],
fileList1: [],
uploadList: [],
saving: false,
rules: {
name: [{
idCardName: [{
required: true,
message: '请输入姓名',
trigger: ['blur', 'change']
}],
idNum: [{
sfzmhm: [{
required: true,
message: '请输入身份证号',
trigger: ['blur', 'change']
@@ -78,13 +82,6 @@
};
},
onReady() {
if(storage.get('photoForm')){
this.fileList=storage.get('photoForm').file
this.form={
name: storage.get('photoForm').name,
idNum: storage.get('photoForm').idNum
}
}
this.$refs.form1.setRules(this.rules);
},
methods: {
@@ -95,7 +92,17 @@
},
// 新增图片
async afterRead(event) {
// this.compressImage(event.file);
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
@@ -129,62 +136,47 @@
this.$refs.form1.validate().then((valid) => {
if (valid) {
const param={
file:this.fileList,
...this.form
driverLicenseImg:this.driverLicenseImg,
...this.form,
schoolOrgCode:'340103000700',
operater:'1d08daf852cf4ee28f67cb583f538cbf'
}
storage.set('photoForm',param)
uni.showToast({
title:"提交成功!",
duration:2000,
addInfo(param).then(resp=>{
uni.showToast({
title:"提交成功!",
duration:2000,
})
setTimeout(()=>{
uni.navigateBack()
},1000)
})
setTimeout(()=>{
uni.navigateBack()
},1000)
} else {
console.log('验证失败');
}
});
},
async compressImage(image) {
const img = new Image();
img.src = image.url;
img.onload = async () => {
const canvas = document.createElement('canvas'); // 创建Canvas对象(画布)
const context = canvas.getContext('2d');
// 默认按比例压缩
const cw = img.width;
const ch = img.height;
let w = img.width;
let h = img.height;
canvas.width = w;
canvas.height = h;
if (cw > 400 && cw > ch) {
w = 400;
h = (400 * ch) / cw;
canvas.width = w;
canvas.height = h;
}
if (ch > 400 && ch > cw) {
h = 400;
w = (400 * cw) / ch;
canvas.width = w;
canvas.height = h;
}
// 生成canvas
let base64;
// 创建属性节点
context.clearRect(0, 0, 0, w, h);
context.drawImage(img, 0, 0, w, h);
if (image.size > 2000000) {
// 如果图片超过2m,则进行压缩
base64 = canvas.toDataURL(image['type'], 0.5);
}
const result = await this.uploadFilePromise(base64 || image.url);
this.uploadList.push(result);
};
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>

View File

@@ -5,36 +5,36 @@
<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="fileList1&&fileList1.length" style="width: 240rpx;height: 240rpx;" :src="fileList1[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: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" :maxSize="500 * 1024" @oversize="oversize" @afterRead="afterRead"
@delete="deletePic" multiple :maxCount="1" width="150" height="150">
<view
style="width: 320rpx;height:60rpx;line-height:60rpx;background-color: #3C9CFF;border-radius: 0 0 20rpx 20rpx;"
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>
</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="name" borderBottom ref="item1">
<u--input v-model="form.name" border="none"></u--input>
<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="idNum" borderBottom ref="item2">
<u--input v-model="form.idNum" border="none"></u--input>
<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'}" :disabled="saving" text="提交"
@click="submit" />
<u-button type="primary" :style="{width: '100%',borderRadius:'40rpx',backgroundColor:'#05C341'}"
:disabled="saving" text="提交" @click="submit" />
</view>
</view>
</view>
@@ -42,6 +42,9 @@
<script>
import storage from '@/jtools/storage';
import {
addInfo,
} from '@/jtools/api/index';
export default {
data() {
const shenfenzhen = (rule, value, callback) => {
@@ -50,23 +53,25 @@
}
return {
form: {
name: '',
idNum: ''
idCardName: '',
sfzmhm: storage.get('sfzNum') || ''
},
driverLicenseImg: '',
fileList: [],
fileList1: [],
uploadList: [],
saving: false,
rules: {
name: [{
idCardName: [{
required: true,
message: '请输入姓名',
trigger: ['blur', 'change']
}],
idNum: [{
sfzmhm: [{
required: true,
message: '请输入身份证号',
trigger: ['blur', 'change']
},{
}, {
// 自定义验证函数,见上说明
validator: shenfenzhen,
message: '身份证号码不正确',
@@ -77,17 +82,17 @@
};
},
onReady() {
console.log(storage.get('photoForm'));
if(storage.get('photoForm')){
this.fileList1=storage.get('photoForm').file
this.form={
name: storage.get('photoForm').name,
idNum: storage.get('photoForm').idNum
}
}
this.$refs.form1.setRules(this.rules);
},
methods: {
// 文件超出大小限制
oversize() {
uni.showToast({
title: "图片最大不能超过500k",
icon: 'none'
})
},
// 删除图片
deletePic(event) {
this.fileList1.splice(event.index, 1);
@@ -95,95 +100,94 @@
},
// 新增图片
async afterRead(event) {
// this.compressImage(event.file);
let imageInfoObj = await uni.getImageInfo({
src: event.file[0].url
})
console.log(imageInfoObj);
if (imageInfoObj.width != 260 && imageInfoObj.height != 378) {
uni.showToast({
icon: 'error',
title: '证件照尺寸有误'
})
this.fileList = []
return false
}
this.driverLicenseImg = await this.urlTobase64(event.file[0].url)
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
const lists = [].concat(event.file);
this.fileList1 = lists;
this.fileList = lists
this.fileList1 = [];
uni.showToast({
title:'上传成功!'
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={
file:this.fileList1,
...this.form
const param = {
driverLicenseImg: this.driverLicenseImg,
...this.form,
schoolOrgCode: '340103000700',
operater: '1d08daf852cf4ee28f67cb583f538cbf'
}
storage.set('photoForm',param)
uni.showToast({
title:"提交成功!",
duration:2000,
})
setTimeout(()=>{
uni.navigateBack()
},1000)
storage.set('sfzNum', this.form.sfzmhm)
uni.request({
url: 'https://www.hfcgs.cn:20223/driver/addInfo',
method: 'POST',
data: param,
success: res => {
console.log(res);
uni.showToast({
title: "提交成功!",
duration: 2000,
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
},
fail: () => {},
});
// addInfo(param).then(resp=>{
// if(resp.code==='0000'){
// uni.showToast({
// title:"提交成功!",
// duration:2000,
// })
// setTimeout(()=>{
// uni.navigateBack()
// },1000)
// }
// })
} else {
console.log('验证失败');
}
});
},
async compressImage(image) {
const img = new Image();
img.src = image.url;
img.onload = async () => {
const canvas = document.createElement('canvas'); // 创建Canvas对象(画布)
const context = canvas.getContext('2d');
// 默认按比例压缩
const cw = img.width;
const ch = img.height;
let w = img.width;
let h = img.height;
canvas.width = w;
canvas.height = h;
if (cw > 400 && cw > ch) {
w = 400;
h = (400 * ch) / cw;
canvas.width = w;
canvas.height = h;
}
if (ch > 400 && ch > cw) {
h = 400;
w = (400 * cw) / ch;
canvas.width = w;
canvas.height = h;
}
// 生成canvas
let base64;
// 创建属性节点
context.clearRect(0, 0, 0, w, h);
context.drawImage(img, 0, 0, w, h);
if (image.size > 2000000) {
// 如果图片超过2m,则进行压缩
base64 = canvas.toDataURL(image['type'], 0.5);
}
const result = await this.uploadFilePromise(base64 || image.url);
this.uploadList.push(result);
};
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>
@@ -214,4 +218,9 @@
::v-deep .u-button--square {
border-radius: 40rpx !important;
}
::v-deep .u-button--primary {
background-color: #05C341 !important;
border-color: #05C341 !important;
}
</style>

View File

@@ -1,345 +1,393 @@
<template>
<view class="flex fld-c p15">
<u-sticky>
<view class="flex ai-c">
<view v-for="(item, index) in vipAllList" :key="index" class="km-item" :class="{ actived: index == current }"
@click="current = index">
{{ getKmTitle(item.subjects) }}
</view>
</view>
</u-sticky>
<view class="mt15">
<swiper class="swiper" :current="current" style="height: 120px;" :autoplay="false" :display-multiple-items="1.45"
:disable-programmatic-animation="true" @change="onChange">
<swiper-item v-for="(item, index) in vipAllList" :key="index">
<view class="relative" style="background-image: url(https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/vip%E8%83%8C%E6%99%AF_20230831010348.png);width: 95%;height: 204rpx;background-size: 100% 100%;">
<!-- <image src="" mode="widthFix" style="width:95%;"></image> -->
<view class="vip-info">
<u-avatar class="br-p50 overflow-h" style="border: 3px solid #873E1D;" :size="35" mp-avatar></u-avatar>
<view class="ml10">
<view class="fs16 fwb" style="color: #7E4012;line-height: 16px;">
{{ item.memberName }}
</view>
<view class="mt5 fs12" style="color: #7E4012;line-height: 12px;">
{{ getExpireText(item) }}
</view>
</view>
</view>
<view v-if="vipHasOpened(item)" class="corner">
VIP已开通
</view>
<view v-if="vipHasOpened(item)" @tap="chargeVip(item)" class="renew">
{{ item.price }}元立即续费
</view>
<view v-else class="buy" @tap="chargeVip(item)">
立即充值
</view>
</view>
</swiper-item>
<swiper-item>
</swiper-item>
</swiper>
</view>
<view class="mt20">
<swiper class="swiper" :current="current" style="height: 300px;" :autoplay="false"
:disable-programmatic-animation="true" @change="onChange">
<swiper-item v-for="(item, index) in vipAllList" :key="index">
<view v-if="index == 0 || index == 3" class="p15 br8 cor-fff bc-fff">
<view class="fs18 cor-000 fwb">
3步轻松学{{ getKmTitle(item.subjects) }}
</view>
<view class="mt25 flex ai-c jc-sb">
<view class="flex ai-c">
<image src="../../static/image/mine/1.png" mode="widthFix" style="width:25px;"></image>
<view class="ml7">
<view class="fs16 cor-000 fwb" style="line-height: 16px;">
精简500题
</view>
<view class="mt7 fs12 cor-666" style="line-height: 12px;">
题目全部做对视为完成
</view>
</view>
</view>
<view class="study" @tap="to500(item)">
去学习
</view>
</view>
<view class="mt45 flex ai-c jc-sb">
<view class="flex ai-c">
<image src="../../static/image/mine/2.png" mode="widthFix" style="width:25px;"></image>
<view class="ml7">
<view class="fs16 cor-000 fwb" style="line-height: 16px;">
真实考场模拟
</view>
<view class="mt7 fs12 cor-666" style="line-height: 12px;">
10次95分以上视为完成
</view>
</view>
</view>
<view class="study" @tap="toRealExam(item)">
去学习
</view>
</view>
<view class="mt45 flex ai-c jc-sb">
<view class="flex ai-c">
<image src="../../static/image/mine/3.png" mode="widthFix" style="width:25px;"></image>
<view class="ml7">
<view class="fs16 cor-000 fwb" style="line-height: 16px;">
考前密卷
</view>
<view class="mt7 fs12 cor-666" style="line-height: 12px;">
2套试卷95分以上视为完成
</view>
</view>
</view>
<view class="study" @tap="toSpecExam(item)">
去学习
</view>
</view>
</view>
<view v-else style="margin-top: 30rpx;">
<view class="video-box">
<view class="flex jc-sb ai-c wp100">
<text style="color: #873E1D;font-size: 36rpx;">考场实况</text>
<text class="cor-666 fs12">全部 ></text>
</view>
<view class="flex ai-c mt20">
<view class="contain-box relative">
<image class="contain-box" src="../../../static/image/index/jpsp.png"></image>
<image class="play_btn_2" src="../../static/image/index/play.png" />
</view>
<view class="ml15 text-center">
<u-button :customStyle="{width:'200rpx',height:'66rpx',borderRadius: '33rpx'}" iconColor="#fff"
text="去看视频" color="linear-gradient(90deg, #E66501 0%, #F8A42C 100%)" icon="play-circle" @click="toPlaceLive(item)">
</u-button>
<view class="cor-333 fs15 fw600 mt10">真实考场模拟</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
<view>
<u-loading-page :loading="getLoading" loading-text="题库更新中..."></u-loading-page>
<view class="flex fld-c p15" v-if="!getLoading">
<u-sticky>
<view class="flex ai-c">
<view v-for="(item, index) in vipAllList" :key="index" class="km-item" :class="{ actived: index == current }"
@click="current = index">
{{ getKmTitle(item.subjects) }}
</view>
</view>
</u-sticky>
<view class="mt15">
<swiper class="swiper" :current="current" style="height: 120px;" :autoplay="false"
:display-multiple-items="1.45" :disable-programmatic-animation="true" @change="onChange">
<swiper-item v-for="(item, index) in vipAllList" :key="index">
<view class="relative"
style="background-image: url(https://oss-bq.ahduima.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%9B%BE%E7%89%87/vip%E8%83%8C%E6%99%AF_20230831010348.png);width: 95%;height: 204rpx;background-size: 100% 100%;">
<!-- <image src="" mode="widthFix" style="width:95%;"></image> -->
<view class="vip-info">
<u-avatar class="br-p50 overflow-h" style="border: 3px solid #873E1D;" :size="35" mp-avatar></u-avatar>
<view class="ml10">
<view class="fs16 fwb" style="color: #7E4012;line-height: 16px;">
{{ item.memberName }}
</view>
<view class="mt5 fs12" style="color: #7E4012;line-height: 12px;">
{{ getExpireText(item) }}
</view>
</view>
</view>
<view v-if="vipHasOpened(item)" class="corner">
VIP已开通
</view>
<view v-if="vipHasOpened(item)" @tap="chargeVip(item)" class="renew">
{{ item.price }}元立即续费
</view>
<view v-else class="buy" @tap="chargeVip(item)">
立即充值
</view>
</view>
</swiper-item>
<swiper-item>
</swiper-item>
</swiper>
</view>
<view class="mt20">
<swiper class="swiper" :current="current" style="height: 300px;" :autoplay="false"
:disable-programmatic-animation="true" @change="onChange">
<swiper-item v-for="(item, index) in vipAllList" :key="index">
<view v-if="index == 0 || index == 3" class="p15 br8 cor-fff bc-fff">
<view class="fs18 cor-000 fwb">
3步轻松学{{ getKmTitle(item.subjects) }}
</view>
<view class="mt25 flex ai-c jc-sb">
<view class="flex ai-c">
<image src="../../static/image/mine/1.png" mode="widthFix" style="width:25px;"></image>
<view class="ml7">
<view class="fs16 cor-000 fwb" style="line-height: 16px;">
精简500题
</view>
<view class="mt7 fs12 cor-666" style="line-height: 12px;">
题目全部做对视为完成
</view>
</view>
</view>
<view class="study" @tap="to500(item)">
去学习
</view>
</view>
<view class="mt45 flex ai-c jc-sb">
<view class="flex ai-c">
<image src="../../static/image/mine/2.png" mode="widthFix" style="width:25px;"></image>
<view class="ml7">
<view class="fs16 cor-000 fwb" style="line-height: 16px;">
真实考场模拟
</view>
<view class="mt7 fs12 cor-666" style="line-height: 12px;">
10次95分以上视为完成
</view>
</view>
</view>
<view class="study" @tap="toRealExam(item)">
去学习
</view>
</view>
<view class="mt45 flex ai-c jc-sb">
<view class="flex ai-c">
<image src="../../static/image/mine/3.png" mode="widthFix" style="width:25px;"></image>
<view class="ml7">
<view class="fs16 cor-000 fwb" style="line-height: 16px;">
考前密卷
</view>
<view class="mt7 fs12 cor-666" style="line-height: 12px;">
2套试卷95分以上视为完成
</view>
</view>
</view>
<view class="study" @tap="toSpecExam(item)">
去学习
</view>
</view>
</view>
<view v-else style="margin-top: 30rpx;">
<view class="video-box">
<view class="flex jc-sb ai-c wp100">
<text style="color: #873E1D;font-size: 36rpx;">考场实况</text>
<text class="cor-666 fs12">全部 ></text>
</view>
<view class="flex ai-c mt20">
<view class="contain-box relative">
<image class="contain-box" src="../../../static/image/index/jpsp.png"></image>
<image class="play_btn_2" src="../../static/image/index/play.png" />
</view>
<view class="ml15 text-center">
<u-button :customStyle="{width:'200rpx',height:'66rpx',borderRadius: '33rpx'}" iconColor="#fff"
text="去看视频" color="linear-gradient(90deg, #E66501 0%, #F8A42C 100%)" icon="play-circle"
@click="toPlaceLive(item)">
</u-button>
<view class="cor-333 fs15 fw600 mt10">真实考场模拟</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</view>
</template>
<script>
import useUserStore from '@/jtools/store/user'
export default {
computed: {
vipAllList() {
return useUserStore().vipAllList.filter(it => [1, 2, 3, 4].includes(Number(it.subjects)))
},
vipOnList() {
return useUserStore().vipOnList
},
import {
mapState,
mapActions
} from 'pinia' //引入映射函数
import useQuestionStore from '@/jtools/store/question' //引入store
import useUserStore from '@/jtools/store/user'
import {
queryQuestionId,
getTestQuestionId
} from '@/jtools/api/question';
import storage from '@/jtools/storage';
export default {
computed: {
...mapState(useQuestionStore, ["loading_subject4", "loading_subject1", "version"]), //映射函数取出tagslist
getLoading() {
return this.loading_subject4 && this.loading_subject1
},
vipAllList() {
return useUserStore().vipAllList.filter(it => [1, 2, 3, 4].includes(Number(it.subjects)))
},
vipOnList() {
return useUserStore().vipOnList
},
},
data() {
return {
current: 0
}
},
methods: {
chargeVip(item){
uni.navigateTo({
url:"/pages/index/videoVip?subject="+item.subjects
})
},
onChange(e) {
this.current = e.detail.current
},
getKmTitle(km) {
return {
'1': '科一',
'2': '科二',
'3': '科三',
'4': '科四',
}[km]
},
getExpireText(item) {
const vipOn = this.vipOnList.find(it => it.memberId == item.memberId)
if (vipOn) {
return `${vipOn.endDate.split(' ')[0]}到期`
} else {
return '开通vip助您快速拿证'
}
},
vipHasOpened(item) {;
return !!this.vipOnList.some(it => it.subjects.includes(item.subjects) )
},
// 去精选500题 item=> 当前科目vip信息
to500(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
data() {
return {
current: 0
}
},
methods: {
...mapActions(useQuestionStore, ['getOrderQuestion_sub1', 'getOrderQuestion_sub4', 'getAllQuestion']),
chargeVip(item) {
uni.navigateTo({
url:"/pages/questionBank/questionBank?navTitle=精简500题&subject="+item.subjects+"&needVip=true&isVip=1"
url: "/pages/index/videoVip?subject=" + item.subjects
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
},
// 去真实考场 item=> 当前科目vip信息
toRealExam(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
uni.navigateTo({
url: "/pages/index/trueTest?subject="+item.subjects
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
},
// 去秘卷 item=> 当前科目vip信息
toSpecExam(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
uni.navigateTo({
url:"/pages/questionBank/practiceExams?subject="+item.subjects+'&title=考前密卷&isExam1=1'
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
},
// 去考场实况 item=> 当前科目vip信息
toPlaceLive(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
uni.showToast({
title:'敬请期待',
icon:'none'
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
}
}
}
},
onChange(e) {
this.current = e.detail.current
},
getKmTitle(km) {
return {
'1': '科一',
'2': '科二',
'3': '科三',
'4': '科四',
} [km]
},
getExpireText(item) {
const vipOn = this.vipOnList.find(it => it.memberId == item.memberId)
if (vipOn) {
return `${vipOn.endDate.split(' ')[0]}到期`
} else {
return '开通vip助您快速拿证'
}
},
vipHasOpened(item) {
;
return !!this.vipOnList.some(it => it.subjects.includes(item.subjects))
},
// 去精选500题 item=> 当前科目vip信息
to500(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
queryQuestionId({
versionId: this.version,
carTypeId: storage.get('carType') || '1001',
subject: item.subjects,
isVip: '1'
}).then(async (resp) => {
if (resp.code === '0000') {
const arr = resp.data
if (arr && arr.length) {
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=精简500题&subject=" + item.subjects +
"&questionIdList=" + listJson
})
} else {
uni.showToast({
title: '暂无题目',
icon: 'none'
})
}
} else if (resp.code === '4001') {
uni.showToast({
title: '当前题库非最新版,请更新~',
icon: 'none'
})
this.getAllQuestion()
}
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
},
// 去真实考场 item=> 当前科目vip信息
toRealExam(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
uni.navigateTo({
url: "/pages/index/trueTest?subject=" + item.subjects
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
},
// 去秘卷 item=> 当前科目vip信息
toSpecExam(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
uni.navigateTo({
url: "/pages/index/secretPapers?subject=" + item.subjects
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
},
// 去考场实况 item=> 当前科目vip信息
toPlaceLive(item) {
// 当前vip是否已开通
if (this.vipHasOpened(item)) {
// 跳转
uni.showToast({
title: '敬请期待',
icon: 'none'
})
} else {
uni.showToast({
title: `请先充值${this.getKmTitle(item.subjects)}vip`,
icon: 'none'
})
}
}
}
}
</script>
<style lang="scss" scoped>
.km-item {
margin-right: 8px;
width: 72px;
height: 30px;
line-height: 30px;
font-size: 12px;
color: #333;
text-align: center;
border-radius: 15px;
border: 1px solid #CCCCCC;
}
.km-item {
margin-right: 8px;
width: 72px;
height: 30px;
line-height: 30px;
font-size: 12px;
color: #333;
text-align: center;
border-radius: 15px;
border: 1px solid #CCCCCC;
}
.actived {
background: linear-gradient(90deg, #E66501 0%, #F8A42C 100%);
color: #fff;
}
.actived {
background: linear-gradient(90deg, #E66501 0%, #F8A42C 100%);
color: #fff;
}
.km-item:last-of-type {
margin-right: 0;
}
.km-item:last-of-type {
margin-right: 0;
}
.vip-info {
position: absolute;
top: 15px;
left: 10px;
display: flex;
align-items: center;
z-index: 1;
}
.vip-info {
position: absolute;
top: 15px;
left: 10px;
display: flex;
align-items: center;
z-index: 1;
}
.corner {
position: absolute;
bottom: 0;
left: 0;
width: 77px;
height: 23px;
background: linear-gradient(0deg, #E66501 0%, #F8A42C 100%);
opacity: 0.86;
border-radius: 0px 8px 0px 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #fff;
z-index: 1;
}
.corner {
position: absolute;
bottom: 0;
left: 0;
width: 77px;
height: 23px;
background: linear-gradient(0deg, #E66501 0%, #F8A42C 100%);
opacity: 0.86;
border-radius: 0px 8px 0px 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #fff;
z-index: 1;
}
.renew {
position: absolute;
right: 10%;
bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
width: 185rpx;
height: 52rpx;
background: #873E1D;
border-radius: 26rpx;
font-size: 12px;
color: #F6E99F;
z-index: 1;
}
.renew {
position: absolute;
right: 10%;
bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
width: 185rpx;
height: 52rpx;
background: #873E1D;
border-radius: 26rpx;
font-size: 12px;
color: #F6E99F;
z-index: 1;
}
.buy {
position: absolute;
left: 45%;
transform: translateX(-50%);
bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
width: 200rpx;
height: 60rpx;
background: #873E1D;
border-radius: 30rpx;
font-size: 14px;
color: #F6E99F;
z-index: 1;
}
.buy {
position: absolute;
left: 45%;
transform: translateX(-50%);
bottom: 15px;
display: flex;
align-items: center;
justify-content: center;
width: 200rpx;
height: 60rpx;
background: #873E1D;
border-radius: 30rpx;
font-size: 14px;
color: #F6E99F;
z-index: 1;
}
.study {
display: flex;
align-items: center;
justify-content: center;
width: 161rpx;
height: 67rpx;
background: #F6E99F;
border-radius: 34rpx;
font-size: 12px;
color: #873E1D;
}
.study {
display: flex;
align-items: center;
justify-content: center;
width: 161rpx;
height: 67rpx;
background: #F6E99F;
border-radius: 34rpx;
font-size: 12px;
color: #873E1D;
}
.video-box {
padding: 20rpx;
width: 694rpx;
height: 369rpx;
background: #F9F3E7;
border: 2rpx solid #CF8B6D;
border-radius: 16rpx;
}
.video-box {
padding: 20rpx;
width: 694rpx;
height: 369rpx;
background: #F9F3E7;
border: 2rpx solid #CF8B6D;
border-radius: 16rpx;
}
.contain-box {
width: 406rpx;
height: 228rpx;
background: #00B74F;
border-radius: 16rpx;
}
.play_btn_2 {
width: 65rpx;
height: 65rpx;

View File

@@ -49,11 +49,18 @@
...param,
}).then(async (resp) => {
if (resp.code === '0000') {
const arr = resp.data
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject + "&questionIdList=" + listJson
})
if(resp.data&&resp.data.length){
const arr = resp.data
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject + "&questionIdList=" + listJson
})
}else{
uni.showToast({
title: '暂无题目',
icon: 'none'
})
}
}else if (resp.code === '4001') {
uni.showToast({
title: '当前题库非最新版,请更新~',

View File

@@ -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"
:disable-programmatic-animation="true" @change="onChange" @animationfinish="onAnimationfinish"
@touchend="touchEnd">
<swiper-item v-for="(quesItem,quesIndex) in swiperList" :key="quesIndex">
<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">
<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 style="width: 100%;" mode="widthFix" :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,21 @@
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 class="fs18 cor-000" style="text-indent:2em;"> {{quesItem.bestAnswer}}</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>
<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>
@@ -82,7 +97,21 @@
<view class="m14lr mt30" v-if="isShowAnswer">
<view class="answer_box">
<text class="fs18 fw600 cor-000">答案:{{getRightOp(quesItem.trueAnswer)}}</text>
<view class="fs18 cor-000" 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>
@@ -90,6 +119,7 @@
</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>
@@ -99,17 +129,17 @@
</view>
</view>
<view class="text-center flex jc-c ai-c" style="flex-direction: column;" @tap="toCollect">
<u-icon name="star-fill" v-if="questionList[topicIndex].isCollect" color="rgb(249,236,141)"
size="24"></u-icon>
<u-icon name="star" v-else size="24"></u-icon>
<text class="cor-666">{{questionList[topicIndex].isCollect?'已收藏':'收藏'}}</text>
<u-icon name="star-fill" v-if="collectList.includes(questionList[topicIndex].questionId)"
color="rgb(249,236,141)" size="20"></u-icon>
<u-icon name="star" v-else size="20"></u-icon>
<text class="cor-666">{{collectList.includes(questionList[topicIndex].questionId)?'已收藏':'收藏'}}</text>
</view>
<view class="text-center">
<view style="color: #00B74F;">{{rightList.length}}</view>
<view style="color: #00B74F;">{{navTitle !== '顺序答题'?rightList.length:storageRightList.length}}</view>
<text class="cor-666">答对</text>
</view>
<view class="text-center">
<view style="color: #FF6E02;">{{wrongList.length}}</view>
<view style="color: #FF6E02;">{{navTitle !== '顺序答题'?wrongList.length:storageWrongList.length}}</view>
<text class="cor-666">答错</text>
</view>
<view class="text-center" @tap="popupShow=!popupShow">
@@ -191,17 +221,17 @@
</view>
</view>
<view class="text-center flex jc-c ai-c" style="flex-direction: column;" @tap="toCollect">
<u-icon name="star-fill" v-if="questionList[topicIndex].isCollect" color="rgb(249,236,141)"
size="24"></u-icon>
<u-icon name="star" v-else size="24"></u-icon>
<text class="cor-666">{{questionList[topicIndex].isCollect?'已收藏':'收藏'}}</text>
<u-icon name="star-fill" v-if="collectList.includes(questionList[topicIndex].questionId)"
color="rgb(249,236,141)" size="20"></u-icon>
<u-icon name="star" v-else size="20"></u-icon>
<text class="cor-666">{{collectList.includes(questionList[topicIndex].questionId)?'已收藏':'收藏'}}</text>
</view>
<view class="text-center">
<view style="color: #00B74F;">{{rightList.length}}</view>
<view style="color: #00B74F;">{{navTitle !== '顺序答题'?rightList.length:storageRightList.length}}</view>
<text class="cor-666">答对</text>
</view>
<view class="text-center">
<view style="color: #FF6E02;">{{wrongList.length}}</view>
<view style="color: #FF6E02;">{{navTitle !== '顺序答题'?wrongList.length:storageWrongList.length}}</view>
<text class="cor-666">答错</text>
</view>
<view class="text-center" @tap="popupShow=!popupShow">
@@ -212,15 +242,17 @@
</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="index" style="width:20%;" 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':rightList.includes(item.questionId),
'error':wrongList.includes(item.questionId)
'success':type=='exam'?rightList.includes(item.questionId):storageRightList.includes(item.questionId),
'error':type=='exam'?wrongList.includes(item.questionId):storageWrongList.includes(item.questionId)
}">
{{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>
</view>
</view>
</view>
@@ -272,6 +304,7 @@
import storage from '@/jtools/storage';
import useQuestionStore from '@/jtools/store/question' //引入store
import {
querySysConfig,
submitTest
} from '@/jtools/api/question';
export default {
@@ -290,14 +323,15 @@
isShowAll: {
type: Boolean,
default: true
},
subject: {
type: [String, Number],
default: 1,
},
}
},
data() {
return {
imgs: [],
subject: '1',
showSkillInfo: 'hidden',
currentType: storage.get('carType') || '1001',
onoff: '0',
navTitle: '',
originArray: '',
showBestAnswer: false,
@@ -310,6 +344,8 @@
tCurrent: 0,
index: 0,
qIndex: 0,
storageRightList: storage.get(`rightList_subject${this.subject}`) || [],
storageWrongList: storage.get(`wrongList_subject${this.subject}`) || [],
rightList: [],
wrongList: [],
collectList: storage.get(`collectList_subject${this.subject}`) || [],
@@ -322,7 +358,14 @@
time: 0,
}
},
created() {
const carType = storage.get('carType') || '1001'
querySysConfig(carType, 'NeedSkillInfo').then(resp => {
if (resp.code === '0000') {
this.showSkillInfo = resp.data.configValue
}
})
},
computed: {
...mapState(useQuestionStore, ["currentIndex_subject1", "currentIndex_subject4"]), //映射函数取出tagslist
isShowAnswer() {
@@ -345,23 +388,33 @@
},
timeCount() {
const time = this.subject == '1' ? 60 * 60 * 1000 : 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 = []
arr.forEach(item => {
let isCollect = false
if (this.collectList.includes(item.questionId)) {
isCollect = true
}
arr1.push({
isChoose: false,
isCollect: isCollect,
...item
})
})
@@ -381,8 +434,8 @@
if (this.tCurrent != 1) {
this.originArray = JSON.stringify(this.questionList)
}
const falseList = storage.get(`wrongList_subject${this.subject}`) || []
const trueList = storage.get(`rightList_subject${this.subject}`) || []
// const falseList = storage.get(`wrongList_subject${this.subject}`) || []
// const trueList = storage.get(`rightList_subject${this.subject}`) || []
if (this.tCurrent !== 1) {
const arr1 = this.questionList[this.topicIndex].clickAnswer.split('')
const arr2 = this.questionList[this.topicIndex].trueAnswer.split('')
@@ -392,18 +445,18 @@
if (!this.rightList.includes(this.questionList[this.topicIndex].questionId)) {
this.rightList.push(this.questionList[this.topicIndex].questionId)
}
if (!trueList.includes(this.questionList[this.topicIndex].questionId)) {
trueList.push(this.questionList[this.topicIndex].questionId)
storage.set(`rightList_subject${this.subject}`, trueList)
if (!this.storageRightList.includes(this.questionList[this.topicIndex].questionId)) {
this.storageRightList.push(this.questionList[this.topicIndex].questionId)
storage.set(`rightList_subject${this.subject}`, this.storageRightList)
}
if (this.wrongList.includes(this.questionList[this.topicIndex].questionId)) {
const wIndex = this.wrongList.indexOf(this.questionList[this.topicIndex].questionId)
this.wrongList.splice(wIndex, 1)
}
if (falseList.includes(this.questionList[this.topicIndex].questionId)) {
const wIndex = falseList.indexOf(this.questionList[this.topicIndex].questionId)
falseList.splice(wIndex, 1)
storage.set(`wrongList_subject${this.subject}`, falseList)
if (this.storageWrongList.includes(this.questionList[this.topicIndex].questionId)) {
const wIndex = this.storageWrongList.indexOf(this.questionList[this.topicIndex].questionId)
this.storageWrongList.splice(wIndex, 1)
storage.set(`wrongList_subject${this.subject}`, this.storageWrongList)
}
//答对题目 如果不是最后一题,跳下一题
if (this.topicIndex < this.questionList.length - 1) {
@@ -429,14 +482,14 @@
if (!this.wrongList.includes(this.questionList[this.topicIndex].questionId)) {
this.wrongList.push(this.questionList[this.topicIndex].questionId)
}
if (!falseList.includes(this.questionList[this.topicIndex].questionId)) {
falseList.push(this.questionList[this.topicIndex].questionId)
storage.set(`wrongList_subject${this.subject}`, falseList)
if (!this.storageWrongList.includes(this.questionList[this.topicIndex].questionId)) {
this.storageWrongList.push(this.questionList[this.topicIndex].questionId)
storage.set(`wrongList_subject${this.subject}`, this.storageWrongList)
}
if (trueList.includes(this.questionList[this.topicIndex].questionId)) {
const rIndex = trueList.indexOf(this.questionList[this.topicIndex].questionId)
trueList.splice(rIndex, 1)
storage.set(`rightList_subject${this.subject}`, trueList)
if (this.storageRightList.includes(this.questionList[this.topicIndex].questionId)) {
const rIndex = this.storageRightList.indexOf(this.questionList[this.topicIndex].questionId)
this.storageRightList.splice(rIndex, 1)
storage.set(`rightList_subject${this.subject}`, this.storageRightList)
}
}
}
@@ -490,6 +543,7 @@
this.$refs.countDown_1.pause();
this.$refs.countDown_2.pause();
}
this.popupShow = false
this.$refs.countDown_3.pause();
this.$emit('update:isSubmit', true)
},
@@ -508,21 +562,27 @@
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)
submitTest({
"carTypeId": storage.get('carType') || '1001',
"score": score,
"testTime": 60 * 60 - restTime,
subject: this.subject
}).then(resp => {
const doNotNum = this.questionList.length - this.rightList.length - this.wrongList.length
const list = JSON.stringify(this.wrongList)
if (resp.code === '0000') {
uni.navigateTo({
url: "/pages/questionBank/examResult?doNotNum=" + doNotNum + "&wrongList=" + list + "&score=" +
score + "&subject=" + this.subject + "&navTitle=" + this.navTitle
})
}
})
if (this.rightList.length + this.wrongList.length == 0) {
uni.navigateBack({
delta: 1
})
} else {
submitTest({
"carTypeId": storage.get('carType') || '1001',
"score": score,
"testTime": 45 * 60 - restTime,
subject: this.subject
}).then(resp => {
const doNotNum = this.questionList.length - this.rightList.length - this.wrongList.length
const list = JSON.stringify(this.wrongList)
if (resp.code === '0000') {
uni.navigateTo({
url: "/pages/questionBank/examResult?doNotNum=" + doNotNum + "&wrongList=" + list + "&score=" +
score + "&subject=" + this.subject + "&navTitle=" + this.navTitle
})
}
})
}
},
//查看考试结果
toResult() {
@@ -534,12 +594,9 @@
})
},
toCollect() {
if (this.questionList[this.topicIndex].isCollect) {
if (this.collectList.includes(this.questionList[this.topicIndex].questionId)) {
const idx = this.collectList.indexOf(this.questionList[this.topicIndex].questionId)
this.collectList.splice(idx, 1)
}
this.questionList[this.topicIndex].isCollect = false
if (this.collectList.includes(this.questionList[this.topicIndex].questionId)) {
const idx = this.collectList.indexOf(this.questionList[this.topicIndex].questionId)
this.collectList.splice(idx, 1)
uni.showToast({
title: "取消收藏",
icon: 'none'
@@ -593,6 +650,7 @@
// 轮播图切换
onChange(e) {
// this.onoff='0'
// 非触摸事件不做轮播图状态更新
if (e.detail.source != "touch") return;
@@ -644,8 +702,8 @@
},
//答题
answerQues(op, index) {
const falseList = storage.get(`wrongList_subject${this.subject}`) || []
const trueList = storage.get(`rightList_subject${this.subject}`) || []
// const falseList = storage.get(`wrongList_subject${this.subject}`) || []
// const trueList = storage.get(`rightList_subject${this.subject}`) || []
if (!this.questionList[this.topicIndex].clickAnswer) {
this.questionList[this.topicIndex].optionList[index].chooseOption =
`${this.questionList[this.topicIndex].optionList[index].chooseOption?this.questionList[this.topicIndex].optionList[index].chooseOption:''}${op}`
@@ -678,18 +736,18 @@
if (!this.rightList.includes(this.questionList[this.topicIndex].questionId)) {
this.rightList.push(this.questionList[this.topicIndex].questionId)
}
if (!trueList.includes(this.questionList[this.topicIndex].questionId)) {
trueList.push(this.questionList[this.topicIndex].questionId)
storage.set(`rightList_subject${this.subject}`, trueList)
if (!this.storageRightList.includes(this.questionList[this.topicIndex].questionId)) {
this.storageRightList.push(this.questionList[this.topicIndex].questionId)
storage.set(`rightList_subject${this.subject}`, this.storageRightList)
}
if (this.wrongList.includes(this.questionList[this.topicIndex].questionId)) {
const wIndex = this.wrongList.indexOf(this.questionList[this.topicIndex].questionId)
this.wrongList.splice(wIndex, 1)
}
if (falseList.includes(this.questionList[this.topicIndex].questionId)) {
const wIndex = falseList.indexOf(this.questionList[this.topicIndex].questionId)
falseList.splice(wIndex, 1)
storage.set(`wrongList_subject${this.subject}`, falseList)
if (this.storageWrongList.includes(this.questionList[this.topicIndex].questionId)) {
const wIndex = this.storageWrongList.indexOf(this.questionList[this.topicIndex].questionId)
this.storageWrongList.splice(wIndex, 1)
storage.set(`wrongList_subject${this.subject}`, this.storageWrongList)
}
//答对题目 如果不是最后一题,跳下一题
if (this.topicIndex < this.questionList.length - 1) {
@@ -715,14 +773,14 @@
if (!this.wrongList.includes(this.questionList[this.topicIndex].questionId)) {
this.wrongList.push(this.questionList[this.topicIndex].questionId)
}
if (!falseList.includes(this.questionList[this.topicIndex].questionId)) {
falseList.push(this.questionList[this.topicIndex].questionId)
storage.set(`wrongList_subject${this.subject}`, falseList)
if (!this.storageWrongList.includes(this.questionList[this.topicIndex].questionId)) {
this.storageWrongList.push(this.questionList[this.topicIndex].questionId)
storage.set(`wrongList_subject${this.subject}`, this.storageWrongList)
}
if (trueList.includes(this.questionList[this.topicIndex].questionId)) {
const rIndex = trueList.indexOf(this.questionList[this.topicIndex].questionId)
trueList.splice(rIndex, 1)
storage.set(`rightList_subject${this.subject}`, trueList)
if (this.storageRightList.includes(this.questionList[this.topicIndex].questionId)) {
const rIndex = this.storageRightList.indexOf(this.questionList[this.topicIndex].questionId)
this.storageRightList.splice(rIndex, 1)
storage.set(`rightList_subject${this.subject}`, this.storageRightList)
}
}
}
@@ -733,17 +791,30 @@
// this.renderSwiper(0)
this.$emit('changeTab', index)
},
getQuestionList(val, title) {
getQuestionList(val, title, subject) {
if (title) {
this.navTitle = title
}
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}`) || []
}
if (val && val.length) {
this.questionList = JSON.parse(val)
} else {
this.questionList = JSON.parse(this.originArray)
}
console.log(this.questionList);
if (this.navTitle === '顺序答题') {
this.pickerTopic(this[`currentIndex_subject${this.subject}`])
if (subject) {
this.pickerTopic(this[`currentIndex_subject${subject}`])
} else {
this.pickerTopic(this[`currentIndex_subject${this.subject}`])
}
} else {
this.pickerTopic(this.topicIndex)
}

View File

@@ -15,7 +15,7 @@
<image style="width: 363rpx;height: 170rpx;" src="../../static/image/practice/chapter_bg.png"></image>
<view style="position: absolute;left: 0;top: 0;" class="p10">
<view style="color: #FF6E02;font-size: 18px;">章节练习</view>
<text style="color: #FF6E02;font-size: 14px;">5</text>
<text style="color: #FF6E02;font-size: 14px;">{{chapterNum}}</text>
</view>
</view>
</view>
@@ -67,6 +67,7 @@
export default {
data() {
return {
chapterNum:0,
errorIcon,
newRulesIcon,
neverWriteIcon,
@@ -111,6 +112,7 @@
}
this.getExamPoint()
this.getQuestionNum()
this.getChapterList()
},
computed: {
...mapState(useQuestionStore, ["loading_subject4", "loading_subject1", "version"]), //映射函数取出tagslist
@@ -120,6 +122,15 @@
},
methods: {
...mapActions(useQuestionStore, ['getAllQuestion']),
getChapterList(){
const carTypeId=storage.get('carType') || '1001'
const key=this.subject=='1'?'ChapterOfSubjectOne':'ChapterOfSubjectFour'
querySysConfigList(carTypeId,'ChapterOfSubjectOne').then(resp=>{
if(resp.code==='0000'&&resp.data){
this.chapterNum=resp.data.length
}
})
},
getQuestionNum() {
querySpecialNum({
carTypeId: storage.get('carType') || '1001',
@@ -218,11 +229,18 @@
...param,
}).then(async (resp) => {
if (resp.code === '0000') {
const arr = resp.data
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject + "&questionIdList=" + listJson
})
if(resp.data&&resp.data.length){
const arr = resp.data
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + title + "&subject=" + this.subject + "&questionIdList=" + listJson
})
}else{
uni.showToast({
title: '暂无题目',
icon: 'none'
})
}
}else if (resp.code === '4001') {
uni.showToast({
title: '当前题库非最新版,请更新~',

View File

@@ -3,8 +3,7 @@
<!-- <u-navbar title="模拟考试" @rightClick="rightClick" :autoBack="true">
</u-navbar> -->
<j-navbar :isDefineBack="true" @toBack="toBack">{{title}}</j-navbar>
<Question ref="question" :tabsList="tabsList" v-model:isSubmit="isSubmit" :type="type" :isShowAll="isShowAll"
:subject="subject" @changeTab="changeTab" />
<Question ref="question" :tabsList="tabsList" v-model:isSubmit="isSubmit" :type="type" :isShowAll="isShowAll" @changeTab="changeTab" />
</view>
</template>
@@ -29,7 +28,7 @@
return {
type: '',
collectList: storage.get(`collectList_subject${this.subject}`) || [],
questionArr:[],
questionArr: [],
isShowAll: true,
title: "模拟考试",
subject: 1,
@@ -53,31 +52,30 @@
if (op.isExam1) {
param.isExam1 = op.isExam1
}
if(op.needVip){
this.isShowAll = op.needVip
}
let arr=[]
if(op.questionIdList){
const idList=JSON.parse(op.questionIdList)
arr = this[`orderQuestion_subject${this.subject}`].filter(qItem=>idList.includes(qItem.questionId))
if (op.needVip) {
this.isShowAll = op.needVip
}
let arr = []
arr = [...this[`orderQuestion_subject${this.subject}`]]
let questionObj = {}
arr.forEach(item => {
let isCollect = false
if (this.collectList.includes(item.questionId)) {
isCollect = true
}
this.questionArr.push({
isChoose: false,
isCollect: isCollect,
...item
})
item.isChoose = false
questionObj[item.questionId] = item
})
if (op.questionIdList) {
const idList = JSON.parse(op.questionIdList)
if (idList && idList.length > 0) {
idList.forEach(item => {
this.questionArr.push(questionObj[item])
})
}
}
this.type = 'exam'
this.$refs.question.getQuestionList(JSON.stringify(this.questionArr),this.title)
this.$refs.question.getQuestionList(JSON.stringify(this.questionArr), this.title,this.subject)
}
},
computed: {
...mapState(useUserStore, ["vipOnList","token"]),
...mapState(useUserStore, ["vipOnList", "token"]),
...mapState(useQuestionStore, ["orderQuestion_subject1", "orderQuestion_subject4", "version"]), //映射函数取出tagslist
},
methods: {
@@ -85,48 +83,19 @@
toBack() {
this.$refs.question.submitPaper()
},
changeTab(val) {
async changeTab(val) {
if (val == 1) {
const param=this.subject=='1'?{isExam1: '1'}:{isExam2: '1'}
queryQuestionId({
versionId: this.version,
carTypeId: storage.get('carType') || '1001',
subject: this.subject,
...param
}).then(async (resp) => {
if (resp.code === '0000') {
if (this.token) {
await this.searchUserVip()
const result = this.vipOnList.some(item => item.subjects.includes(this.subject))
if (result) {
const listJson = JSON.stringify(resp.data)
uni.navigateTo({
url: "/pages/questionBank/practiceExams?title=考前密卷" + "&subject=" + this.subject + "&questionIdList=" + listJson
})
} else {
if (resp.data && resp.data.length > 3) {
const arr = resp.data.slice(0, 3)
} else {
const arr = resp.data
}
const listJson = JSON.stringify(arr)
uni.navigateTo({
url: "/pages/questionBank/practiceExams?title=" + title + "&subject=" + this.subject + "&questionIdList=" + listJson+"&needVip="+result
})
}
} else {
uni.redirectTo({
url: '/pages/login/login'
});
}
} else if (resp.code === '4001') {
uni.showToast({
title: '当前题库非最新版,请更新~',
icon: 'none'
})
this.getAllQuestion()
}
})
await this.searchUserVip()
const result = this.vipOnList.some(item => item.subjects.includes(this.subject))
if (result) {
uni.navigateTo({
url: "/pages/index/secretPapers?subject=" + this.subject
})
} else {
uni.navigateTo({
url: "/pages/index/videoVip?subject=" + this.subject
})
}
}
}
}

View File

@@ -6,7 +6,7 @@
<GradesChart :titleName="rightPencentDesc" :actualValue="Number(rightPencent)" />
<view class="top_box flex jc-c" style="flex-direction: column;">
<view class="wp100 text-center" style="margin-top: -80px;">
<text v-if="Number(rightPencent)>=90">太棒了正确率很高了</text>
<text v-if="Number(rightPencent * 100)>=90">太棒了正确率很高了</text>
<text v-else>继续努力吧正确率有点低~</text>
<view class="flex ai-c jc-c mt10">
<view class="text-center wp50" @tap="toQuestionBank">

View File

@@ -28,9 +28,9 @@
},
data() {
return {
loadTxt:'加载中...',
loadTxt: '加载中...',
collectList: storage.get(`collectList_subject${this.subject}`) || [],
loading:false,
loading: false,
isShowAll: true,
needVip: false,
subject: 1,
@@ -46,7 +46,7 @@
}
},
async onLoad(op) {
this.loading=true
this.loading = true
if (op.needVip) {
this.needVip = op.needVip
}
@@ -55,63 +55,34 @@
}
if (op && op.navTitle) {
this.navTitle = op.navTitle
let arr=[]
let param={}
if(op.needVip){
this.isShowAll = !Boolean(op.needVip=='true')
}
if (this.navTitle === '顺序答题') {
if (this.subject == '1') {
arr = [...this.orderQuestion_subject1]
} else if (this.subject == '4') {
arr = [...this.orderQuestion_subject4]
}
} else if(op.questionIdList){
const idList=JSON.parse(op.questionIdList)
arr = this[`orderQuestion_subject${this.subject}`].filter(qItem=>idList.includes(qItem.questionId))
}else{
if(op.isVip){
param.isVip=op.isVip
}
const resp=await queryQuestionId({
subject:this.subject,
carTypeId:storage.get('carType') || '1001',
versionId:this.version,
...params
})
let list=[]
if(resp.code==='0000'){
await this.searchUserVip()
const res = this.vipOnList.some(item => item.subjects.includes(this.subject))
if (!res) {
list=resp.data.slice(0,3)
}else{
list=resp.data
}
}
if(op.needVip){
this.isShowAll = op.needVip
}
arr=this[`orderQuestion_subject${this.subject}`].filter(qItem=>list.includes(qItem.questionId))
let arr = []
let param = {}
if (op.needVip) {
this.isShowAll = !Boolean(op.needVip == 'true')
}
arr = [...this[`orderQuestion_subject${this.subject}`]]
let questionObj={}
arr.forEach(item => {
let isCollect = false
if (this.collectList.includes(item.questionId)) {
isCollect = true
}
this.questionArr.push({
isChoose: false,
isCollect: isCollect,
...item
})
item.isChoose=false
questionObj[item.questionId]=item
})
this.loading=false
this.$refs.question.getQuestionList(JSON.stringify(this.questionArr),this.navTitle)
if(op.navTitle==='顺序答题'){
this.questionArr=arr
}else if (op.questionIdList) {
const idList = JSON.parse(op.questionIdList)
if(idList&&idList.length>0){
idList.forEach(item=>{
this.questionArr.push(questionObj[item])
})
}
}
this.loading = false
this.$refs.question.getQuestionList(JSON.stringify(this.questionArr), this.navTitle , this.subject)
this.$refs.question.getOriginArr(JSON.stringify(this.questionArr))
}
},
computed: {
...mapState(useQuestionStore, ["orderQuestion_subject1", "orderQuestion_subject4","version"]), //映射函数取出tagslist
...mapState(useQuestionStore, ["orderQuestion_subject1", "orderQuestion_subject4", "version"]), //映射函数取出tagslist
...mapState(useUserStore, ["vipOnList", "token"]),
},
methods: {
@@ -128,7 +99,7 @@
}
})
this.$refs.question.isShowBest(true)
this.$refs.question.getQuestionList(JSON.stringify(list),this.navTitle)
this.$refs.question.getQuestionList(JSON.stringify(list), this.navTitle)
} else {
this.$refs.question.isShowBest(false)
this.$refs.question.getQuestionList()

View File

@@ -96,6 +96,7 @@
label: "庐江考场",
value: 6
}],
driveType:'',
videoType: '',
popupShow: false,
videoList: [{
@@ -128,6 +129,10 @@
}
},
onLoad(op) {
if(op.driveType){
this.driveType=op.driveType
this.param.driveType=op.driveType
}
if (op.type) {
this.videoType = op.type
}
@@ -139,6 +144,7 @@
}
if (op.type) {
this.param.type = op.type
this.getVideoList()
}
if (op.type == '1') {
if (this.param.subject == '2') {
@@ -149,10 +155,7 @@
} else {
this.title = '基础操作讲解'
}
if (op.videoList) {
this.videoList = JSON.parse(op.videoList)
this.currentIndex = this.videoList.findIndex(item => item.projectId == this.projectId)
}
},
computed: {
getScrollLeft() {
@@ -161,6 +164,46 @@
}
},
methods: {
formateTime(time) {
const h = parseInt(time / 3600)
const minute = parseInt(time / 60 % 60)
const second = Math.ceil(time % 60)
const hours = h < 10 ? '0' + h : h
const formatSecond = second > 59 ? 59 : second
return `${hours > 0 ? `${hours}:` : ''}${minute < 10 ? '0' + minute : minute}:${formatSecond < 10 ? '0' + formatSecond : formatSecond}`
},
getVideoList() {
queryProjectList({
"carTypeId": storage.get('carType') || '1001',
...this.param,
}).then(resp => {
if(resp.code==='0000'){
let arr=[]
if(this.param.type=='1'){
arr = JSON.parse(JSON.stringify(resp.data))
arr = arr.map(item => {
return {
...item,
videoUrl: item.videoList[0]?.videoUrl,
videoTime: this.formateTime(item.videoList[0]?.videoTime)
}
})
}else{
arr = JSON.parse(JSON.stringify(resp.data[0].videoList))
arr = arr.map(item => {
return {
...item,
projectId: item.videoId,
subDesc: resp.data[0].description,
videoTime: this.formateTime(item.videoTime)
}
})
}
this.videoList = arr
this.currentIndex = this.videoList.findIndex(item => item.projectId == this.projectId)
}
})
},
checkTest(val) {
this.videoIndex = val
},

View File

@@ -68,7 +68,9 @@
<view class="flex ai-c jc-sb mt10" style="flex-wrap: wrap;">
<!-- 这个点击效果没加 -->
<view v-for="(item,index) of typeList" :key="index" class="category_item p14 flex jc-sb ai-c mb10" @tap="toCategoryQuestion(item)">
<text class="cor-000">{{item.categoryName}}</text>
<view class="topic_cont_text" style="max-width: 75%;">
<text class="cor-000">{{item.categoryName}}</text>
</view>
<text class="cor-666">{{item.num}}</text>
</view>
</view>
@@ -139,8 +141,9 @@
},
toCategoryQuestion(item){
const jsonString = JSON.stringify(item.errorQuestionIdList)
console.log(item);
uni.navigateTo({
url: "/pages/questionBank/questionBank?navTitle=" + item.categoryName + "&questionIdList=" + jsonString
url: "/pages/questionBank/questionBank?navTitle=" + item.categoryName + "&questionIdList=" + jsonString+"&subject="+this.subject
})
},
toPractice() {
@@ -264,4 +267,18 @@
left: 165.5rpx;
top: 78rpx
}
.topic_cont_text {
overflow: hidden;
word-break: break-all;
/* break-all(允许在单词内换行。) */
text-overflow: ellipsis;
/* 超出部分省略号 */
display: -webkit-box;
/** 对象作为伸缩盒子模型显示 **/
-webkit-box-orient: vertical;
/** 设置或检索伸缩盒对象的子元素的排列方式 **/
-webkit-line-clamp: 1;
/** 显示的行数 **/
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,11 @@
## 1.0.42022-12-07
修改判断在APP端不监听document的滚动
## 1.0.32022-12-05
新增:支持视频预览,视频图片混用
新增支持预览单张handlePreviewImg方法直接传入图片或视频地址即可单张预览
## 1.0.22022-12-05
## 1.0.12022-12-05
文档错误修改
## 1.0.02022-11-29
初始化插件

View File

@@ -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>

View File

@@ -0,0 +1,84 @@
{
"id": "g-preview-img",
"displayName": "g-preview-img一款兼容vue2vue3的图片预览插件视频预览支持单张多张左右滑动放大缩小",
"version": "1.0.4",
"description": "g-preview-img一款兼容vue2vue3的图片预览插件视频预览支持单张多张左右滑动放大缩小",
"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"
}
}
}
}
}

View File

@@ -0,0 +1,64 @@
![image](https://deaon-saasvideo.oss-cn-shanghai.aliyuncs.com/11111.gif)
### 一款兼容vue2vue3的图片预览插件视频预览支持图片视频混用支持单张多张左右滑动放大缩小
### 基础使用方法
```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会及时修复**

View File

@@ -0,0 +1,25 @@
## 0.1.22023-12-14
- fix: uvue 引入 API 自定义包出错
## 0.1.12023-12-11
- chore: uvue的二维码API独立需要单独下载
## 0.1.02023-12-07
- fix: 修复因utssdk目录导致无法运行
## 0.0.92023-12-06
- feat: 支持uvue
## 0.0.82023-12-06
- feat: 支持uvue
## 0.0.72023-12-06
- feat: 支持uvue
## 0.0.62023-12-06
- feat: 支持uvue
## 0.0.52023-07-30
- fix: 修复再次生成前没有清空,导致图形叠加
## 0.0.42023-07-27
- fix: 修复相同尺寸无法再次生成
## 0.0.32023-06-09
- feat: 支持通过`@vue/composition-api``vue2`上使用
- chore: 更新文档
## 0.0.22023-06-08
- chore: 更新文档
## 0.0.12023-06-08
- 首次

View 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>

View 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>

View 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

View 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
}

View 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
}

View 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;
}

View File

@@ -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>

View File

@@ -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>

View 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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,89 @@
{
"id": "lime-qrcode",
"displayName": "qrcode 二维码生成",
"version": "0.1.2",
"description": "全端二维码生成插件draw api绘制。在H5微信小程序uvuenvue上测试过非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"
}
}
}
}
}

View 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)
```
另外插件也用到了TSvue2可能会遇过官方的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>numberstring</em> | `160` |
| iconSize | 二维码中图片的大小 | <em>numberstring</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
## 打赏
如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png)
![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png)

View 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

View 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
})
}

View 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 {
}
}

View 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
})
}

View 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('');
};

View 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
}

View File

@@ -0,0 +1,30 @@
## 0.1.42023-09-05
- feat: 增加 Hooks `useIntersectionObserver`
- feat: 增加 `floatAdd`
- feat: 因为本人插件兼容 vue2 需要使用 `composition-api`故增加vue文件代码插件的条件编译
## 0.1.32023-08-13
- feat: 增加 `camelCase`
## 0.1.22023-07-17
- feat: 增加 `getClassStr`
## 0.1.12023-07-06
- feat: 增加 `isNumeric` 区别于 `isNumber`
## 0.1.02023-06-30
- fix: `clamp`忘记导出了
## 0.0.92023-06-27
- feat: 增加`arrayBufferToFile`
## 0.0.82023-06-19
- feat: 增加`createAnimation``clamp`
## 0.0.72023-06-08
- chore: 更新注释
## 0.0.62023-06-08
- chore: 增加`createImage``lime-watermark``lime-qrcode`提供依赖
## 0.0.52023-06-03
- chore: 更新注释
## 0.0.42023-05-22
- feat: 增加`range`,`exif`,`selectComponent`
## 0.0.32023-05-08
- feat: 增加`fillZero`,`debounce`,`throttle`,`random`
## 0.0.22023-05-05
- chore: 更新文档
## 0.0.12023-05-05
-

View 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大于最大值被限制为最大值

View 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 (嵌套对象的副本也是独立的)

View 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

View 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
}

View 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()
}

View 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"

File diff suppressed because it is too large Load Diff

View 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');
}

View 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;
}

View 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"

View File

@@ -0,0 +1,6 @@
// @ts-nocheck
/** 获取当前页 */
export const getCurrentPage = () => {
const pages = getCurrentPages();
return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance;
};

View 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
}

View 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
});
};

View 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;"

View 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' 属性

View 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'

View 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);
};

View File

@@ -0,0 +1,2 @@
// @ts-nocheck
export const isBrowser = typeof window !== 'undefined';

View 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;
}

View File

@@ -0,0 +1,8 @@
// @ts-nocheck
/**
* 检查一个值是否为函数类型
* @param val 要检查的值
* @returns 如果值的类型是函数类型,则返回 true否则返回 false
*/
export const isFunction = (val: unknown): val is Function =>
typeof val === 'function';

View 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);
}

View 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);
}

View 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';

View 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);
};

View File

@@ -0,0 +1,7 @@
// @ts-nocheck
/**
* 检查一个值是否为字符串类型
* @param str 要检查的值
* @returns 如果值的类型是字符串类型,则返回 true否则返回 false
*/
export const isString = (str: unknown): str is string => typeof str === 'string';

View 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(); // 将结果转换为全小写
}

View 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"
}
}
}
}
}

View 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
})
}

Some files were not shown because too many files have changed in this diff Show More