Compare commits

..

No commits in common. '7b1db47383ba799455d2792203e6e26a2a4fc4bd' and 'f4b6f96649f7892a9336dfca38a2e74384ee103c' have entirely different histories.

  1. 16
      src/App.vue
  2. 97
      src/jtools/store/user.js
  3. 375
      src/pages/index/videoVip.vue
  4. 236
      src/pages/login/login.vue
  5. 54
      src/pages/me/index.vue
  6. 106
      src/pages/questionBank/components/Question.vue
  7. BIN
      src/static/image/mine/callme.png
  8. 11
      src/uni_modules/g-preview-img/changelog.md
  9. 235
      src/uni_modules/g-preview-img/components/g-preview-img/g-preview-img.vue
  10. 84
      src/uni_modules/g-preview-img/package.json
  11. 64
      src/uni_modules/g-preview-img/readme.md
  12. 24
      src/uni_modules/q-previewImage/changelog.md
  13. 121
      src/uni_modules/q-previewImage/components/q-previewImage/q-previewImage.vue
  14. 81
      src/uni_modules/q-previewImage/package.json
  15. 244
      src/uni_modules/q-previewImage/readme.md

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

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

@ -4,15 +4,12 @@
<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> <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 style="margin-bottom: 100px;">
<view class="flex jc-fa ai-c wp100 p14"> <view class="flex jc-fa ai-c wp100 p14">
<view style="width: 33.3%;" v-for="(item, index) of priceList" :key="index"> <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)"> <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> <text class="fw600 fs12 cor-333">{{item.memberName}}</text>
<view class="mt5" v-if="!isIOS"> <view class="mt5">
<text class="fs14" style="color: #FF6E02;">¥</text> <text class="fs14" style="color: #FF6E02;">¥</text>
<text class="fs30 fw600" style="color: #FF6E02;">{{ item.price }}</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> </view>
<text class="fs12 cor-999">一年有效</text> <text class="fs12 cor-999">一年有效</text>
<!-- <view class="bottom_box fs12 cor-333" :class="checkedId===item.memberId?'checked_bottom':''">赠送vip题库</view> --> <!-- <view class="bottom_box fs12 cor-333" :class="checkedId===item.memberId?'checked_bottom':''">赠送vip题库</view> -->
@ -24,12 +21,12 @@
</view> </view>
<view class="intr_box p14"> <view class="intr_box p14">
<view class="fw600 fs16 cor-000">尊享以下权益</view> <view class="fw600 fs16 cor-000">尊享以下权益</view>
<view class="flex ai-c jc-sb mt15" v-if="subject == '1' || subject == '4'"> <view class="flex ai-c jc-sb mt15" v-if="subject=='1'||subject=='4'">
<view class="text-center" style="width: 33%"> <view class="text-center" style="width: 33%">
<view class="wp100 flex ai-c jc-c mb5"> <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> <image style="width: 63rpx;height: 63rpx;margin-right: 5px;" src="../../static/image/index/vip500.png"></image>
</view> </view>
<text>精简{{ titleNum }}</text> <text>精简{{titleNum}}</text>
</view> </view>
<view class="text-center" style="width: 33%;"> <view class="text-center" style="width: 33%;">
<view class="wp100 flex ai-c jc-c mb5"> <view class="wp100 flex ai-c jc-c mb5">
@ -62,14 +59,14 @@
</view> </view>
</view> </view>
</view> </view>
<view class="wp100 mt15" v-if="subject == '1' || subject == '4'"> <view class="wp100 mt15" v-if="subject=='1'||subject=='4'">
<image class="wp100" mode="widthFix" :src="picUrl"></image> <image class="wp100" mode="widthFix" :src="picUrl"></image>
</view> </view>
</view> </view>
</view> </view>
<view v-if="!isIOS" class="wp100 p14" style="position: absolute;left: 0;bottom:20px" @tap="handlePay()"> <view class="wp100 p14" style="position: absolute;left: 0;bottom:20px" @tap="handlePay()">
<view class="sub_btn flex ai-c jc-sb"> <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> <image style="width: 276rpx;height: 88rpx;margin-top: -5px;" src="../../static/image/index/buy.png"></image>
</view> </view>
</view> </view>
@ -77,197 +74,187 @@
</template> </template>
<script> <script>
import { import {
mapState, mapState,
mapActions mapActions
} from 'pinia' // } from 'pinia' //
import { getVipList } from '@/jtools/api/vip' import { getVipList } from '@/jtools/api/vip'
import { querySysConfig } from '@/jtools/api/question'; import { querySysConfig } from '@/jtools/api/question';
import storage from '@/jtools/storage'; import storage from '@/jtools/storage';
import Pay from '@/jtools/pay/index.js'; import Pay from '@/jtools/pay/index.js';
import useUserStore from '@/jtools/store/user' import useUserStore from '@/jtools/store/user'
export default { export default {
data() { data(){
return { return{
picUrl: '', picUrl:'',
titleNum: 500, titleNum:500,
subject: '1', subject:'1',
loading: true, loading:true,
nowPrice: 168, nowPrice:168,
checkedId: 0, checkedId:0,
priceList: [], priceList:[],
order: { order:{
money: 0, money:0,
description: '会员充值' description:'会员充值'
},
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() { onLoad(op){
if (this.loading) { if(op.subject){
this.loading = false this.subject=op.subject
await this.getWXOpenId()
new Pay('wechat', this.order);
this.loading = true
} }
this.loading=true
this.getVipList()
this.getWXOpenId()
this.getTitle()
this.getPic()
this.$set(this.order, 'userId', this.userInfo.userId);
}, },
getWXOpenId() { computed: {
const that = this ...mapState(useUserStore, ["userInfo"])
uni.login({
success(res) {
that.$set(that.order, 'code', res.code);
}
})
}, },
getVipList() { methods:{
getVipList({ getPic(){
carTypeId: storage.get('carType') || '1001', const currentCartype = storage.get('carType') || '1001'
subject: this.subject querySysConfig(currentCartype, 'VipDescImageUrl').then(res => {
}).then(resp => { this.picUrl = JSON.parse(res.data.configJson).url
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
}
}) })
},
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() {
checkPrice(val, price) { const that = this
this.checkedId = val uni.login({
this.order.outTradeNo = val success(res) {
this.nowPrice = price that.$set(that.order, 'code', res.code);
this.order.money = price }
})
},
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
}
}
} }
}
}
</script> </script>
<style scoped> <style scoped>
.option_tem { .option_tem{
width: 220rpx; width: 220rpx;
height: 241rpx; height: 241rpx;
text-align: center; text-align: center;
background: #FFFFFF; background: #FFFFFF;
border: 2px solid #D8D8D8; border: 2px solid #D8D8D8;
border-radius: 16rpx 46rpx 16rpx 16rpx; border-radius: 16rpx 46rpx 16rpx 16rpx;
padding: 14px; padding: 14px;
} }
.checked_item{
.checked_item { background: #FFF0E5;
background: #FFF0E5; border: 4px solid #FF6E02;
border: 4px solid #FF6E02; }
} .bottom_box{
width: 214rpx;
.bottom_box { height: 40rpx;
width: 214rpx; line-height: 40rpx;
height: 40rpx; text-align: center;
line-height: 40rpx; background: rgb(239,239,239);
text-align: center; border-radius: 0 0 16rpx 16rpx;
background: rgb(239, 239, 239); position: absolute;
border-radius: 0 0 16rpx 16rpx; bottom: 0;
position: absolute; left: 0;
bottom: 0; }
left: 0; .checked_bottom{
} width: 218rpx;
border-radius: 0 0 16rpx 5rpx;
.checked_bottom { background-color: #FF6E02;
width: 218rpx; color:#fff
border-radius: 0 0 16rpx 5rpx; }
background-color: #FF6E02; .tag{
color: #fff padding:0 5px;
} height: 36rpx;
background: linear-gradient(90deg, #E66501 0%, #F8A42C 100%);
.tag { border-radius: 8rpx 20rpx 8rpx 8rpx;
padding: 0 5px; line-height: 36rpx;
height: 36rpx; text-align: center;
background: linear-gradient(90deg, #E66501 0%, #F8A42C 100%); font-size: 12px;
border-radius: 8rpx 20rpx 8rpx 8rpx; color: #FFFC27;
line-height: 36rpx; position: absolute;
text-align: center; left: 10rpx;
font-size: 12px; top:-18rpx
color: #FFFC27; }
position: absolute; .intr_box{
left: 10rpx; width: 100%;
top: -18rpx text-align: center;
} padding: 40rpx;
background: #FFF0E5;
.intr_box { border-radius: 16rpx;
width: 100%; }
text-align: center; .vip_item{
padding: 40rpx; width: 208rpx;
background: #FFF0E5; height: 54rpx;
border-radius: 16rpx; line-height: 54rpx;
} text-align: center;
font-size: 14px;
.vip_item { background: #F3D7C2;
width: 208rpx; border-radius: 0rpx 10rpx 10rpx 10rpx;
height: 54rpx; }
line-height: 54rpx; .sub_btn{
text-align: center; width:100%;
font-size: 14px; height: 110rpx;
background: #F3D7C2; border: 4px solid #F59B26;
border-radius: 0rpx 10rpx 10rpx 10rpx; background: linear-gradient(0deg, #E66501 0%, #F8A42C 100%);
} box-shadow: 0rpx 16rpx 20rpx 1rpx rgba(245,155,38,0.78);
border-radius: 55rpx;
.sub_btn { padding: 14rpx;
width: 100%; }
height: 110rpx; .contain-box {
border: 4px solid #F59B26; width: 344rpx;
background: linear-gradient(0deg, #E66501 0%, #F8A42C 100%); height: 196rpx;
box-shadow: 0rpx 16rpx 20rpx 1rpx rgba(245, 155, 38, 0.78); background: #00B74F;
border-radius: 55rpx; border-radius: 16rpx;
padding: 14rpx; }
}
.contain-box {
width: 344rpx;
height: 196rpx;
background: #00B74F;
border-radius: 16rpx;
}
.play_btn_2 { .play_btn_2 {
width: 65rpx; width: 65rpx;
height: 65rpx; height: 65rpx;
position: absolute; position: absolute;
left: 165.5rpx; left: 165.5rpx;
top: 78rpx top: 78rpx
} }
</style> </style>

@ -13,7 +13,7 @@
<view class="list-call"> <view class="list-call">
<u-input class="sl-input" v-model="login.code" type="text" maxlength="6" border="none" placeholder="输入验证码"> <u-input class="sl-input" v-model="login.code" type="text" maxlength="6" border="none" placeholder="输入验证码">
<template #suffix> <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> </template>
</u-input> </u-input>
</view> </view>
@ -27,142 +27,138 @@
</template> </template>
<script> <script>
import { import {
isPhone isPhone
} from '@/jtools/utils/validate.js' } from '@/jtools/utils/validate.js'
import { import {
getCode, getCode,
login login
} from '@/jtools/api/login' } from '@/jtools/api/login'
import useUserStore from '@/jtools/store/user' import useUserStore from '@/jtools/store/user'
import storage from '@/jtools/storage'; export default {
export default { data() {
data() { return {
return { login: {
login: { phone: '',
phone: '', code: ''
code: '' },
}, countDown: 0,
countDown: 0, js: undefined
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)
// }
})
}
}, },
clear() { onShow() {
clearInterval(js) if(useUserStore().isLogin) {
this.js = null this.toHome()
this.countDown = 0 }
}, },
bindLogin() { methods: {
if (isPhone(this.login.phone) && this.login.code) { getCode() {
let params = { ...this.login } if (isPhone(this.login.phone) && this.countDown == 0) {
if (storage.get('companyId')) { getCode({
params.id = storage.get('companyId') 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)
// }
})
}
},
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()
}
})
} }
useUserStore().login(params).then(resp => { },
if (resp.userId) { toHome() {
useUserStore().getUserInfo() uni.switchTab({
useUserStore().searchUserVip() url: '/pages/index/index'
this.toHome()
}
}) })
} }
},
toHome() {
uni.switchTab({
url: '/pages/index/index'
})
} }
} }
}
</script> </script>
<style> <style>
page { page {
background-color: #fff; background-color: #fff;
} }
.content {
display: flex;
flex-direction: column;
justify-content: center;
}
.content { .header {
display: flex; margin-top: 166rpx;
flex-direction: column; margin-left: auto;
justify-content: center; margin-right: auto;
} letter-spacing: 10rpx;
}
.header { .header image {
margin-top: 166rpx; width: 383rpx;
margin-left: auto; }
margin-right: auto;
letter-spacing: 10rpx;
}
.header image { .list {
width: 383rpx; display: flex;
} flex-direction: column;
padding-top: 120rpx;
padding-left: 90rpx;
padding-right: 90rpx;
}
.list { .list-call {
display: flex; display: flex;
flex-direction: column; flex-direction: row;
padding-top: 120rpx; justify-content: space-between;
padding-left: 90rpx; align-items: center;
padding-right: 90rpx; 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;
}
.list-call .sl-input { .button-login {
flex: 1; color: #FFFFFF;
text-align: left; font-size: 34rpx;
font-size: 32rpx; width: 560rpx;
margin-left: 16rpx; 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;
}
.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> </style>

@ -40,7 +40,7 @@
<view v-if="!isLogin || !vipOn.length" class="text-center"> <view v-if="!isLogin || !vipOn.length" class="text-center">
<view class="fs18 fwb" style="color: #7E4012FF;">开通VIP尊享以下权益</view> <view class="fs18 fwb" style="color: #7E4012FF;">开通VIP尊享以下权益</view>
<view class="fs15" style="color: #7E4012FF;">精选500题 / 真是模考 / 考前密卷</view> <view class="fs15" style="color: #7E4012FF;">精选500题 / 真是模考 / 考前密卷</view>
<view class="study fs16 text-center" style="margin: 25px auto 0;color: #F6E99FFF;">{{ isIOS ? 'iOS暂不可用' : '立即开通' }}</view> <view class="study fs16 text-center" style="margin: 25px auto 0;color: #F6E99FFF;">立即开通</view>
</view> </view>
<view v-else-if="vipOn.length" class="text-center"> <view v-else-if="vipOn.length" class="text-center">
<view class="fs18 fwb" style="color: #7E4012FF;">{{ vipText }}</view> <view class="fs18 fwb" style="color: #7E4012FF;">{{ vipText }}</view>
@ -81,16 +81,11 @@
<img src="/static/image/mine/wdtj.png" style="width: 24px;height: 24px;"> <img src="/static/image/mine/wdtj.png" style="width: 24px;height: 24px;">
</template> </template>
</u-cell> </u-cell>
<u-cell size="large" title="我的题库" :value="carName" @tap="toChangeCarType"> <u-cell size="large" title="我的题库" :value="carName" isLink @tap="toChangeCarType">
<template #icon> <template #icon>
<img src="/static/image/mine/wdtk.png" style="width: 24px;height: 24px;"> <img src="/static/image/mine/wdtk.png" style="width: 24px;height: 24px;">
</template> </template>
</u-cell> </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> </u-cell-group>
</view> </view>
<view v-if="isLogin" class="flex ai-c jc-c mt12 br8 bc-fff" style="height: 50px;" @tap="handleLogout"> <view v-if="isLogin" class="flex ai-c jc-c mt12 br8 bc-fff" style="height: 50px;" @tap="handleLogout">
@ -140,37 +135,26 @@ export default {
} }
} }
}, },
data() { data(){
return { return{
carName: storage.get('carName') || '小车C1/C2/C3', carName:storage.get('carName') || '小车C1/C2/C3'
isIOS: true }
} },
},
onShow() { onShow() {
this.isIOS = this.$platform.device().includes('ios') this.carName=storage.get('carName') || '小车C1/C2/C3'
this.carName = storage.get('carName') || '小车C1/C2/C3'
}, },
methods: { methods: {
// toChangeCarType(){
callPhoneNumber() { uni.navigateTo({
uni.makePhoneCall({ url:"/pages/me/changeCarType"
phoneNumber: '15105693067' })
});
}, },
toChangeCarType() {
uni.navigateTo({
url: "/pages/me/changeCarType"
})
},
handleVip() { handleVip() {
if (this.isIOS) {
return
}
if (this.isLogin) { if (this.isLogin) {
// if (this.vipOn.length) { // if (this.vipOn.length) {
uni.navigateTo({ uni.navigateTo({
url: '/pages/me/vip' url: '/pages/me/vip'
}) })
// } else { // } else {
// uni.navigateTo({ // uni.navigateTo({
// url: '/pages/index/videoVip' // url: '/pages/index/videoVip'
@ -208,10 +192,10 @@ export default {
// uni.navigateTo({ // uni.navigateTo({
// url: '/pages/me/tijian' // url: '/pages/me/tijian'
// }) // })
uni.showToast({ uni.showToast({
title: '敬请期待', title:'敬请期待',
icon: 'none' icon:'none'
}) })
} else { } else {
this.toLogin() this.toLogin()
} }

@ -7,8 +7,9 @@
:class="tCurrent==item.value?'checked':'unchecked'" @tap="sectionChange(item.value)">{{item.label}}</view> :class="tCurrent==item.value?'checked':'unchecked'" @tap="sectionChange(item.value)">{{item.label}}</view>
</view> </view>
</view> </view>
<swiper class="swiper mt20" :current="swiperIndex" :duration="duration" :autoplay="false" @change="onChange" <swiper class="swiper mt20" :current="swiperIndex" :duration="duration" :autoplay="false"
@animationfinish="onAnimationfinish" @touchend="touchEnd"> @change="onChange" @animationfinish="onAnimationfinish"
@touchend="touchEnd">
<swiper-item v-for="(quesItem,quesIndex) in swiperList" :key="quesIndex.questionId"> <swiper-item v-for="(quesItem,quesIndex) in swiperList" :key="quesIndex.questionId">
<scroll-view scroll-y="true" class="swiper-scroll"> <scroll-view scroll-y="true" class="swiper-scroll">
<view> <view>
@ -16,10 +17,8 @@
<text class="tag_box">{{getQuestType(quesItem.type)}}</text> <text class="tag_box">{{getQuestType(quesItem.type)}}</text>
<text class="fs18" style="line-height: 42rpx;vertical-align: middle;">{{quesItem.question}}</text> <text class="fs18" style="line-height: 42rpx;vertical-align: middle;">{{quesItem.question}}</text>
</view> </view>
<view class="p14 flex jc-c ai-c" v-if="quesItem.imageUrl"> <view class="p14" v-if="quesItem.imageUrl">
<image v-show="quesItem.imageUrl" style="width: auto;max-height:40vh;" mode="heightFix" <image v-show="quesItem.imageUrl" style="width: 100%;height: auto;" mode="widthFix" :lazy-load="true" @load="onoff='1'" :src="quesItem.imageUrl"></image>
:lazy-load="true" @load="onoff='1'" :src="quesItem.imageUrl" @click="preview(quesItem.imageUrl)">
</image>
</view> </view>
<template v-if="quesItem.type!='3'"> <template v-if="quesItem.type!='3'">
<view class="flex m14lr ai-c mt20" v-for="(item,index) in quesItem.optionList" :key="item.op" <view class="flex m14lr ai-c mt20" v-for="(item,index) in quesItem.optionList" :key="item.op"
@ -42,8 +41,7 @@
v-if="quesItem.clickAnswer&&!quesItem.trueAnswer.includes(quesItem.clickAnswer) || showBestAnswer"> v-if="quesItem.clickAnswer&&!quesItem.trueAnswer.includes(quesItem.clickAnswer) || showBestAnswer">
<view class="answer_box"> <view class="answer_box">
<text class="fs18 fw600 cor-000">答案:{{getRightOp(quesItem.trueAnswer)}}</text> <text class="fs18 fw600 cor-000">答案:{{getRightOp(quesItem.trueAnswer)}}</text>
<view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000 mt5"> <view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000 mt5">答题技巧{{quesItem.skillInfo}}</view>
答题技巧{{quesItem.skillInfo}}</view>
</view> </view>
<view class="flex ai-c jc-c mt10"> <view class="flex ai-c jc-c mt10">
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view> <view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
@ -52,10 +50,7 @@
</view> </view>
<view class="mt10"> <view class="mt10">
<view class="fw600 cor-000 mb10 flex ai-c"> <view class="fw600 cor-000 mb10 flex ai-c">
<view <view style="background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);height: 36rpx;width: 8rpx;" class="mr5"></view>题目解析</view>
style="background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);height: 36rpx;width: 8rpx;"
class="mr5"></view>题目解析
</view>
<view style="text-indent:2em;">{{quesItem.bestAnswer}}</view> <view style="text-indent:2em;">{{quesItem.bestAnswer}}</view>
</view> </view>
</view> </view>
@ -97,29 +92,24 @@
<view class="m14lr mt30" v-if="isShowAnswer"> <view class="m14lr mt30" v-if="isShowAnswer">
<view class="answer_box"> <view class="answer_box">
<text class="fs18 fw600 cor-000">答案:{{getRightOp(quesItem.trueAnswer)}}</text> <text class="fs18 fw600 cor-000">答案:{{getRightOp(quesItem.trueAnswer)}}</text>
<view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000"> <view v-if="showSkillInfo==='show'&&quesItem.skillInfo" class="fs18 cor-000"> 答题技巧{{quesItem.skillInfo}}</view>
答题技巧{{quesItem.skillInfo}}</view> </view>
</view> <view class="flex ai-c jc-c mt10">
<view class="flex ai-c jc-c mt10"> <view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view> <view class="fs18 fw600 cor-000 p15lr">试题详解</view>
<view class="fs18 fw600 cor-000 p15lr">试题详解</view> <view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view>
<view style="height: 6rpx;width: 120rpx;background-color: rgb(232, 232, 232);"></view> </view>
</view> <view class="mt10">
<view class="mt10"> <view class="fw600 cor-000 mb10 flex ai-c">
<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 <view style="text-indent:2em;">{{quesItem.bestAnswer}}</view>
style="background: linear-gradient(90deg, #11DF20 0%, #00B74F 100%);height: 36rpx;width: 8rpx;"
class="mr5"></view>题目解析
</view> </view>
<view style="text-indent:2em;">{{quesItem.bestAnswer}}</view>
</view>
</view> </view>
</template> </template>
</view> </view>
</scroll-view> </scroll-view>
</swiper-item> </swiper-item>
</swiper> </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 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 style="width: 220rpx;">
<view v-if="type==='practice'" style="width: 220rpx;height: 80rpx;"></view> <view v-if="type==='practice'" style="width: 220rpx;height: 80rpx;"></view>
@ -242,8 +232,8 @@
</view> </view>
</view> </view>
<view class="flex ai-c jc-fs p14" style="flex-wrap: wrap;max-height: 400px;overflow-y: scroll;"> <view class="flex ai-c jc-fs p14" style="flex-wrap: wrap;max-height: 400px;overflow-y: scroll;">
<view v-for="(item,index) of questionList" :key="item.questionId" style="width:20%;position: relative;" <view v-for="(item,index) of questionList" :key="item.questionId" style="width:20%;position: relative;" class="flex ai-c jc-c"
class="flex ai-c jc-c" @tap="chooseQueston(index)"> @tap="chooseQueston(index)">
<view class="tCircle mb10" :class="{ <view class="tCircle mb10" :class="{
'active':index == topicIndex, 'active':index == topicIndex,
'success':type=='exam'?rightList.includes(item.questionId):storageRightList.includes(item.questionId), 'success':type=='exam'?rightList.includes(item.questionId):storageRightList.includes(item.questionId),
@ -251,8 +241,8 @@
}"> }">
{{index+1}} {{index+1}}
</view> </view>
<u-icon name="star-fill" v-if="collectList.includes(item.questionId)" <u-icon name="star-fill" v-if="collectList.includes(item.questionId)" style="position: absolute;right: 5px;top:-3px"
style="position: absolute;right: 5px;top:-3px" color="rgb(249,236,141)" size="24"></u-icon> color="rgb(249,236,141)" size="24"></u-icon>
</view> </view>
</view> </view>
</view> </view>
@ -327,11 +317,10 @@
}, },
data() { data() {
return { return {
imgs: [], subject:'1',
subject: '1', showSkillInfo:'hidden',
showSkillInfo: 'hidden', currentType:storage.get('carType') || '1001',
currentType: storage.get('carType') || '1001', onoff:'0',
onoff: '0',
navTitle: '', navTitle: '',
originArray: '', originArray: '',
showBestAnswer: false, showBestAnswer: false,
@ -344,7 +333,7 @@
tCurrent: 0, tCurrent: 0,
index: 0, index: 0,
qIndex: 0, qIndex: 0,
storageRightList: storage.get(`rightList_subject${this.subject}`) || [], storageRightList:storage.get(`rightList_subject${this.subject}`) || [],
storageWrongList: storage.get(`wrongList_subject${this.subject}`) || [], storageWrongList: storage.get(`wrongList_subject${this.subject}`) || [],
rightList: [], rightList: [],
wrongList: [], wrongList: [],
@ -359,9 +348,9 @@
} }
}, },
created() { created() {
const carType = storage.get('carType') || '1001' const carType=storage.get('carType') || '1001'
querySysConfig(carType, 'NeedSkillInfo').then(resp => { querySysConfig(carType, 'NeedSkillInfo').then(resp=>{
if (resp.code === '0000') { if(resp.code === '0000'){
this.showSkillInfo = resp.data.configValue this.showSkillInfo = resp.data.configValue
} }
}) })
@ -388,27 +377,12 @@
}, },
timeCount() { timeCount() {
const time = 45 * 60 * 1000 const time = 45 * 60 * 1000
return time return time
} }
}, },
methods: { methods: {
...mapActions(useQuestionStore, ['getCurrentIndex']), ...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) { getOriginArr(val) {
const arr = JSON.parse(val) const arr = JSON.parse(val)
let arr1 = [] let arr1 = []
@ -562,11 +536,11 @@
toSubmit() { toSubmit() {
const restTime = this.time.hours * 60 * 60 + this.time.minutes * 60 + this.time.seconds 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) const score = (this.rightList.length / this.questionList.length * 100).toFixed(0)
if (this.rightList.length + this.wrongList.length == 0) { if(this.rightList.length+this.wrongList.length==0){
uni.navigateBack({ uni.navigateBack({
delta: 1 delta:1
}) })
} else { }else{
submitTest({ submitTest({
"carTypeId": storage.get('carType') || '1001', "carTypeId": storage.get('carType') || '1001',
"score": score, "score": score,
@ -795,12 +769,12 @@
if (title) { if (title) {
this.navTitle = title this.navTitle = title
} }
if (subject) { if(subject){
this.subject = subject this.subject = subject
console.log(this.subject); console.log(this.subject);
this.storageRightList = storage.get(`rightList_subject${subject}`) || [] this.storageRightList=storage.get(`rightList_subject${subject}`) || []
this.storageWrongList = storage.get(`wrongList_subject${subject}`) || [] this.storageWrongList=storage.get(`wrongList_subject${subject}`) || []
this.collectList = storage.get(`collectList_subject${subject}`) || [] this.collectList=storage.get(`collectList_subject${subject}`) || []
} }
if (val && val.length) { if (val && val.length) {
this.questionList = JSON.parse(val) this.questionList = JSON.parse(val)
@ -809,9 +783,9 @@
} }
console.log(this.questionList); console.log(this.questionList);
if (this.navTitle === '顺序答题') { if (this.navTitle === '顺序答题') {
if (subject) { if(subject){
this.pickerTopic(this[`currentIndex_subject${subject}`]) this.pickerTopic(this[`currentIndex_subject${subject}`])
} else { }else{
this.pickerTopic(this[`currentIndex_subject${this.subject}`]) this.pickerTopic(this[`currentIndex_subject${this.subject}`])
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

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

@ -1,235 +0,0 @@
<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>

@ -1,84 +0,0 @@
{
"id": "g-preview-img",
"displayName": "g-preview-img一款兼容vue2,vue3的图片预览插件,视频预览,支持单张多张,左右滑动,放大缩小",
"version": "1.0.4",
"description": "g-preview-img一款兼容vue2,vue3的图片预览插件,视频预览,支持单张多张,左右滑动,放大缩小",
"keywords": [
"vue2",
"vue3",
"图片预览",
"视频预览"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

@ -1,64 +0,0 @@
![image](https://deaon-saasvideo.oss-cn-shanghai.aliyuncs.com/11111.gif)
### 一款兼容vue2,vue3的图片预览插件,视频预览,支持图片视频混用,支持单张多张,左右滑动,放大缩小
### 基础使用方法
```javascript
<template>
<image v-for="(item,idx) in imgList" :src="item" :key="idx" @tap="handleClick(idx)"></image>
<g-preview-img :imgList="imgList" ref="preview"><g-preview-img>
</template>
<script setup>
import { ref } from 'vue'
const preview = ref(null)
const imgList = ['图片路径1','图片路径2']
const handleClick = (idx)=>{
// idx为要打开的图片的索引,也可以不传,默认打开第一张
// handlePreviewImg的参数支持传入单张图片地址或单个视频地址
preview.value.handlePreviewImg(idx)
}
</script>
```
| 属性名/事件 | 类型 | 默认值 | 说明 |
| ----------------- | ------------ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| modeClass | Array/String | \['fade', 'zoom-out'] | uni-transition组件过渡效果,可选值见 <https://uniapp.dcloud.net.cn/component/uniui/uni-transition.html#mode-class-%E5%86%85%E7%BD%AE%E8%BF%87%E6%B8%A1%E5%8A%A8%E7%94%BB%E7%B1%BB%E5%9E%8B%E8%AF%B4%E6%98%8E> |
| indicatorDotsType | Boolean | false | 多张图片的指示器,ture为圆点,false数字,当图片列表只有一张图片时,默认不展示指示器 |
| imgList | Array | | 图片列表 |
| disabledScale | Boolean | false | 是否禁止双指放大缩小 |
| @preview | 打开关闭事件 | | 接受一个参数,ture为开启,false为关闭 |
| @changeImg | 图片切换的事件 | | 参数为当前的图片索引 |
#### 插槽,自定义翻页按钮
```js
<g-preview-img :imgList="imgList" ref="preview">
<template>
<!--你的翻页按钮-->
<view @tap.stop="previousImg">上一页</view>
<view @tap.stop="nextImg">下一页</view>
</template>
</g-preview-img>
<script>
const preview = ref(null)
//上一页的方法
const previousImg = ()=>{
preview.value.previousImg()
}
//下一页的方法
const previousImg = ()=>{
preview.value.nextImg()
}
</script>
```
#### 支持uniapp原生swiper的属性
插件内部swiper标签上绑定了$attrs,所以在使用时可以传入一些swiper的属性
注意:不要传disable-touch这个属性,会有意想不到的错误
**插件bug会及时修复!!**

@ -1,24 +0,0 @@
## 1.1.1(2023-08-01)
优化文档
## 1.1.0(2023-08-01)
优化文档
## 1.0.9(2023-07-10)
优化文档
## 1.0.8(2023-06-25)
优化文档
## 1.0.7(2023-06-25)
优化文档
## 1.0.6(2023-05-26)
优化文档
## 1.0.5(2023-05-22)
优化文档
## 1.0.4(2023-04-30)
新增图片放大功能,解决原生组件和tabbar导航栏等无法覆盖的问题
## 1.0.3(2023-04-28)
优化文档
## 1.0.2(2023-04-28)
优化文档
## 1.0.1(2023-04-28)
新增长按事件
## 1.0.0(2023-04-28)
插件上线

@ -1,121 +0,0 @@
<template>
<view class="previewImage" v-if="show" @tap="close">
<view class="page" v-if="urls.length > 0">
<text class="text">{{ current + 1 }} / {{ urls.length }}</text>
</view>
<swiper class="swiper" :current="current" @change="swiperChange" @touchstart="handleTouchStart" @touchend="handleTouchEnd">
<swiper-item v-for="(item, index) in urls" :key="index">
<movable-area class="movable-area" scale-area>
<movable-view class="movable-view" direction="all" :inertia="true" damping="100" scale="true" scale-min="1" scale-max="4" :scale-value="scale">
<scroll-view scroll-y="true" class="uni-scroll-view">
<view class="scroll-view"><image :key="index" class="image" :src="item" mode="widthFix" @longpress="onLongpress(item)" /></view>
</scroll-view>
</movable-view>
</movable-area>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
props: {
urls: {
type: Array,
required: true,
default: () => {
return [];
}
}
},
data() {
return {
show: false,
current: 0, //
scale: 1,
isZooming: false //
};
},
methods: {
//
open(current) {
this.current = this.urls.findIndex(item => item === current);
this.show = true;
this.$emit('open');
},
//
close() {
if (!this.isZooming) {
this.show = false;
this.current = 0;
this.$emit('close');
}
},
//
swiperChange(e) {
this.current = e.detail.current;
},
//
onLongpress(e) {
this.$emit('onLongpress', e);
},
handleTouchStart() {
this.isZooming = true;
},
handleTouchEnd() {
this.isZooming = false;
}
}
};
</script>
<style lang="scss" scoped>
.previewImage {
z-index: 9999;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #000000;
.swiper {
width: 100%;
height: 100vh;
swiper-item {
.movable-area {
height: 100%;
width: 100%;
.movable-view {
width: 100%;
min-height: 100%;
.scroll-view {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
.image {
width: 100%;
height: auto;
}
}
}
}
}
}
.page {
position: absolute;
z-index: 9999;
width: 100%;
top: 60rpx;
text-align: center;
.text {
color: #fff;
font-size: 32rpx;
background-color: rgba(0, 0, 0, 0.5);
padding: 3rpx 16rpx;
border-radius: 20rpx;
user-select: none;
}
}
}
</style>

@ -1,81 +0,0 @@
{
"id": "q-previewImage",
"displayName": "图片预览、多图左右滑动、图片放大、支持覆盖原生组件、原生导航栏、tabbar",
"version": "1.1.1",
"description": "最简洁的模拟图片预览,支持长按事件,多图左右滑动,大图上下滑动查看,支持图片放大,支持覆盖原生组件/原生导航栏/tabbar 支持vue2/vue3/app/小程序/h5",
"keywords": [
"图片预览"
],
"repository": "",
"engines": {
"HBuilderX": "^3.4.14"
},
"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": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

@ -1,244 +0,0 @@
# 最简洁的模拟图片预览,支持长按事件,多图左右滑动,大图上下滑动查看,支持图片放大,支持覆盖原生组件/原生导航栏/tabbar 支持vue2/vue3/app/小程序/h5
- 为了解决项目中因一些特殊原因无法使用uni.previewImage,例如App.onShow或者页面的oShow中写了方法。
- 如果用uni.previewImage,每次预览图片都会进到onShow的方法里
- 可以基本实现官方的预览图片功能,但是体验不如uni.previewImage()
- 如没有特殊原因,还是推荐官方的uni.previewImage()
## 安装指引
##1. 在插件市场打开本插件页面,在右侧点击`使用 HBuilderX 导入插件`,选择要导入的项目点击确定
##2. 使用方法 vue2写法
```
<template>
<view>
<video v-if="videoShow" id="myVideo" src="https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317.mp4" controls></video>
<image v-for="(item, index) in imgs" :key="index" :src="item" @click="preview(item)"></image>
<q-previewImage ref="previewImage" :urls="imgs" @onLongpress="onLongpress" @open="open" @close="close"></q-previewImage>
</view>
</template>
<script>
export default {
data() {
return {
videoShow:true,//video组件是否显示
imgs: [],
};
},
methods: {
preview(url) {
this.imgs = ['https://web-assets.dcloud.net.cn/unidoc/zh/multiport-20210812.png', 'https://web-assets.dcloud.net.cn/unidoc/zh/uni-function-diagram.png'] //设置图片数组
// #ifdef MP-WEIXIN
this.$nextTick(()=>{
this.$refs.previewImage.open(url); // 传入当前选中的图片地址(小程序必须添加$nextTick,解决组件首次加载无图)
})
// #endif
// #ifndef MP-WEIXIN
this.$refs.previewImage.open(url); // 传入当前选中的图片地址
// #endif
},
onLongpress(e){ //长按事件
console.log('当前长按的图片是' + e);
uni.showActionSheet({
itemList: ['转发给朋友', '保存到手机'],
success: function (res) {
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
},
fail: function (res) {
console.log(res.errMsg);
}
});
},
/* open和close方法一般用不到,但是在一些特殊场景会用到,
* 比如预览图片时你需要覆盖 NavigationBar和 TabBar,
* 或者在app中需要预览图片时覆盖住原生组件,比如video或者map等,
* 你可以根据open和close去做一些操作,例如隐藏导航栏或者隐藏一些原生组件等
*/
open(){ //监听组件显示 (隐藏TabBar和NavigationBar,隐藏video原生组件)
// uni.hideTabBar()
// uni.setNavigationBarColor({
// frontColor: '#000000', // 设置前景色为黑色
// backgroundColor: '#000000', // 设置背景色为黑色
// })
// this.videoShow = false
},
close(){ //监听组件隐藏 (显示TabBar和NavigationBar,显示video原生组件)
// uni.showTabBar()
// uni.setNavigationBarColor({
// frontColor: '#ffffff', // 设置前景色为白色
// backgroundColor: '#000000', // 设置背景色为黑色
// })
// this.videoShow = true
}
}
};
</script>
```
##3. vue3 setup写法
```
<template>
<view>
<video v-if="videoShow" id="myVideo" src="https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317.mp4" controls></video>
<image v-for="(item, index) in imgs" :key="index" :src="item" @click="preview(item)"></image>
<q-previewImage ref="previewImage" :urls="imgs" @onLongpress="onLongpress" @open="open" @close="close"></q-previewImage>
</view>
</template>
<script setup>
import { reactive, ref, toRefs,nextTick } from 'vue';
const data = reactive({
videoShow:true,//video组件是否显示
imgs: [],
});
const previewImage = ref(null);
const { imgs,videoShow } = toRefs(data)// 解构
const preview = url => {
data.imgs = ['https://web-assets.dcloud.net.cn/unidoc/zh/multiport-20210812.png', 'https://web-assets.dcloud.net.cn/unidoc/zh/uni-function-diagram.png'] //设置图片数组
// #ifdef MP-WEIXIN
nextTick(()=>{
previewImage.value.open(url); // 传入当前选中的图片地址(小程序必须添加nextTick,解决组件首次加载无图)
})
// #endif
// #ifndef MP-WEIXIN
previewImage.value.open(url); // 传入当前选中的图片地址
// #endif
};
const onLongpress = e =>{
console.log('当前长按的图片是' + e);
uni.showActionSheet({
itemList: ['转发给朋友', '保存到手机'],
success: function (res) {
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
},
fail: function (res) {
console.log(res.errMsg);
}
});
}
/* open和close方法一般用不到,但是在一些特殊场景会用到,
* 比如预览图片时你需要覆盖 NavigationBar和 TabBar,
* 或者在app中需要预览图片时覆盖住原生组件,比如video或者map等,
* 你可以根据open和close去做一些操作,例如隐藏导航栏或者隐藏一些原生组件等
*/
const open = () => { //监听组件显示 (隐藏TabBar和NavigationBar,隐藏video原生组件)
// uni.hideTabBar()
// uni.setNavigationBarColor({
// frontColor: '#000000', // 设置前景色为黑色
// backgroundColor: '#000000', // 设置背景色为黑色
// })
// data.videoShow = false
}
const close = () => { //监听组件隐藏 (显示TabBar和NavigationBar,显示video原生组件)
// uni.showTabBar()
// uni.setNavigationBarColor({
// frontColor: '#ffffff', // 设置前景色为白色
// backgroundColor: '#000000', // 设置背景色为黑色
// })
// data.videoShow = true
}
</script>
```
##4. 项目示例 (一般返回的数据图片是以逗号或特殊字符分割的字符串,点击时就需要传两个参数,一个是图片数组,一个是当前图片的index)
## 注意q-previewImage不要写在循环体中,imgs其实就是用来存放当前图片的数组,每次点击每次赋值就行
```
<template>
<view>
<video v-if="videoShow" id="myVideo" src="https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317.mp4" controls></video>
<view v-for="(item, index) in list" :key="index" class="list">
<image :src="i" mode="aspectFill" v-for="(i,imgindex) in item.urls.split(',')" @click.stop="preimg(item.urls.split(','),imgindex)"></image>
<view>
<q-previewImage ref="previewImage" :urls="imgs" @onLongpress="onLongpress" @open="open" @close="close"></q-previewImage>
</view>
</template>
<script>
export default {
data() {
return {
videoShow:true,//是否显示video组件
imgs: [],//imgs其实就是用来存放当前图片的数组,每次点击每次赋值就行
};
},
methods: {
preimg(urls,index){
this.imgs = urls //imgs其实就是用来存放当前图片的数组,每次点击每次赋值就行
// #ifdef MP-WEIXIN
this.$nextTick(()=>{
this.$refs.previewImage.open(this.imgs[index]); // 传入当前选中的图片地址(小程序必须添加$nextTick,解决组件首次加载无图)
})
// #endif
// #ifndef MP-WEIXIN
this.$refs.previewImage.open(this.imgs[index]); // 传入当前选中的图片地址
// #endif
},
onLongpress(e){ //长按事件
console.log('当前长按的图片是' + e);
uni.showActionSheet({
itemList: ['转发给朋友', '保存到手机'],
success: function (res) {
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
},
fail: function (res) {
console.log(res.errMsg);
}
});
},
/* open和close方法一般用不到,但是在一些特殊场景会用到,
* 比如预览图片时你需要覆盖 NavigationBar和 TabBar,
* 或者在app中需要预览图片时覆盖住原生组件,比如video或者map等,
* 你可以根据open和close去做一些操作,例如隐藏导航栏或者隐藏一些原生组件等
*/
open(){ //监听组件显示 (隐藏TabBar和NavigationBar,隐藏video原生组件)
// uni.hideTabBar()
// uni.setNavigationBarColor({
// frontColor: '#000000', // 设置前景色为黑色
// backgroundColor: '#000000', // 设置背景色为黑色
// })
// this.videoShow = false
},
close(){ //监听组件隐藏 (显示TabBar和NavigationBar,显示video原生组件)
// uni.showTabBar()
// uni.setNavigationBarColor({
// frontColor: '#ffffff', // 设置前景色为白色
// backgroundColor: '#000000', // 设置背景色为黑色
// })
// this.videoShow = true
}
}
};
</script>
```
## 如果插件对您有一点帮助,请给个五星好评,感谢支持
## 如有问题,请加qq 965969604
Loading…
Cancel
Save