Browse Source

feat: h5;

h5
Fox-33 13 hours ago
parent
commit
e49e74698d
23 changed files with 266 additions and 2876 deletions
  1. +0
    -147
      components/base/navbar.vue
  2. +0
    -244
      components/base/posterPopup.vue
  3. +0
    -116
      components/base/servicePopup.vue
  4. +0
    -135
      components/base/tabbar.vue
  5. +0
    -168
      components/config/PrivacyAgreementPoup.vue
  6. +0
    -48
      components/config/configPopup.vue
  7. +0
    -122
      components/config/customerServicePopup.vue
  8. +0
    -114
      components/growing/recordsView.vue
  9. +0
    -220
      components/growing/userCard.vue
  10. +0
    -40
      components/home/bgSwiperView.vue
  11. +0
    -110
      components/home/categoryView.vue
  12. +0
    -177
      components/home/pictureLiveView.vue
  13. +0
    -112
      components/home/productView.vue
  14. +0
    -98
      components/home/recommendView.vue
  15. +0
    -54
      components/home/swiperView.vue
  16. +0
    -18
      components/member/styles/tag.scss
  17. +0
    -434
      components/partner/posterPopup copy.vue
  18. +0
    -270
      components/product/productCard.vue
  19. +0
    -107
      components/product/sortBar.vue
  20. +93
    -0
      components/progressSegment.vue
  21. +0
    -7
      main.js
  22. +1
    -0
      package.json
  23. +172
    -135
      pages/index/index.vue

+ 0
- 147
components/base/navbar.vue View File

@ -1,147 +0,0 @@
<template>
<!-- <view class="navbar"
:style="{backgroundColor : bgColor}"> -->
<view class="title nav-bar__view"
:style="{backgroundColor : bgColor,color}">
<view class="left">
<uv-icon name="home"
v-if="leftClick && length == 1"
@click="toHome"
:color="color" size="46rpx"></uv-icon>
<uv-icon name="arrow-left"
v-else-if="leftClick"
@click="$emit('leftClick')"
:color="color" size="46rpx"></uv-icon>
</view>
<view>
<slot>
{{ title }}
</slot>
</view>
<view class="icon">
<uv-icon name="search"
v-if="isSearch"
:color="color" size="58rpx"></uv-icon>
<uv-icon name="plus-circle" :color="color"
v-if="isPlus"
@click="plusCircleShow = true"
size="46rpx" style="margin-left: 30rpx;"></uv-icon>
<view v-if="moreClick" style="margin-left: 30rpx;">
<uv-icon name="more-dot-fill" :color="color"
v-if="!moreText"
@click="moreClick()"
size="46rpx"></uv-icon>
<view v-else @click="moreClick"
style="font-weight: 400;font-size: 30rpx;">
{{ moreText }}
</view>
</view>
</view>
</view>
<!-- </view> -->
</template>
<script>
export default {
name:"navbar",
props : {
title : {
type : String,
default : ''
},
leftClick : {
type : Boolean,
},
moreClick : {
type : Function,
},
isSearch : {
type : Boolean,
default : false,
},
isPlus : {
type : Boolean,
default : false,
},
moreText : {
},
bgColor : {
default : 'transparent'
},
color : {
default : '#191919'
}
},
created() {
},
beforeDestroy() {
},
data() {
return {
length : getCurrentPages().length
};
},
methods : {
toHome(){
if(this.length != 1){
return
}
uni.reLaunch({
url: '/pages/index/index'
})
}
}
}
</script>
<style lang="scss" scoped>
// .navbar{
// width: 100%;
// height: 120rpx;
// padding-top: var(--status-bar-height);
// }
.title{
position: sticky;
top: 0;
left: 0;
padding-top: calc(var(--status-bar-height) + 20rpx);
width: 100%;
height: $navbar-height;
display: flex;
justify-content: center;
font-size: 32rpx;
font-weight: 500;
align-items: center;
z-index: 999;
.left{
position: absolute;
left: 40rpx;
display: flex;
justify-content: flex-start;
}
.icon{
position: absolute;
right: 40rpx;
display: flex;
justify-content: flex-end;
}
}
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>

+ 0
- 244
components/base/posterPopup.vue View File

@ -1,244 +0,0 @@
<template>
<uv-popup
ref="popup"
:overlayOpacity="0.6"
mode="center"
bgColor="none"
:zIndex="1000000"
@change="onPopupChange"
>
<view class="popup__view">
<view class="canvas" style="width: 566rpx; height: 1060rpx; overflow: hidden;">
<canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas>
</view>
<button class="btn" @click="saveImg">
<view class="content">保存到相册</view>
</button>
</view>
</uv-popup>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
path: '',
wxCodeImage: '',
baseUrl: 'https://image.hhlm1688.com/',
canvas: {},
retry: 10,
}
},
computed: {
...mapState(['userInfo', 'configList']),
},
async onLoad() {
},
methods: {
open(path) {
this.path = path
this.retry = 10
this.$refs.popup.open();
},
close() {
this.$refs.popup.close();
},
async fetchQrCode() {
try {
this.wxCodeImage = (await this.$fetch('getInviteCode', { path: this.path }))?.url
} catch (err) {
}
},
drawCoverImg(canvas, ctx, x, y, width, height) {
return new Promise(resolve => {
//
const paperImage = canvas.createImage()
console.log('paperImage', paperImage)
paperImage.src = this.configList.poster_image
paperImage.onload = () => {
console.log('paperImage onload')
ctx.drawImage(paperImage, x, y, width, height)
resolve()
}
})
},
drawQrCodeImg(canvas, ctx, x, y, size) {
return new Promise(resolve => {
//
const coderImage = canvas.createImage()
coderImage.src = this.wxCodeImage
coderImage.onload = () => {
console.log('coderImage onload')
ctx.drawImage(coderImage, x, y, size, size)
resolve()
}
})
},
draw() {
wx.createSelectorQuery().in(this)
.select('#myCanvas') // canvasid
.fields({
node: true,
size: true
})
.exec(async (res) => {
console.log('res', res)
if (!res?.[0]?.node) {
if (!this.retry) {
console.log('retry fail')
return
}
console.log('retry')
this.retry -= 1
setTimeout(() => {
this.draw()
}, 200)
return
}
const canvas = res[0].node
//
const ctx = canvas.getContext('2d')
// Canvas
const width = res[0].width
const height = res[0].height
//
const dpr = wx.getWindowInfo().pixelRatio
//dpr
// dpr 2 4
// 3 6
console.log("--dpr", dpr)
canvas.width = width * dpr
canvas.height = height * dpr
let Ratio = canvas.width / 566
this.canvas = canvas
ctx.scale(dpr, dpr)
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = 'transparent'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.save()
let x = 0
let y = 0
let w = 566 * Ratio / dpr
let h = 1060 * Ratio / dpr
await this.drawCoverImg(canvas, ctx, x, y, w, h)
ctx.restore();
ctx.save()
x = 316 * Ratio / dpr
y = 810 * Ratio / dpr
let size = 210 * Ratio / dpr
await this.drawQrCodeImg(canvas, ctx, x, y, size)
uni.hideLoading()
})
},
async init() {
uni.showLoading({
title: '加载中...'
});
await this.fetchQrCode()
uni.hideLoading();
uni.showLoading({
title: "拼命绘画中..."
})
this.draw()
},
saveImg() {
this.$authorize('scope.writePhotosAlbum').then((res) => {
this.imgApi()
})
},
imgApi() {
uni.showLoading({
title: '保存中...'
});
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this.canvas.width,
height: this.canvas.height,
canvas: this.canvas,
success: (res) => {
let tempFilePath = res.tempFilePath;
this.saveImgToPhone(tempFilePath)
},
fail: (err) => {
console.log('--canvasToTempFilePath--fail', err)
uni.hideLoading();
}
}, this);
},
saveImgToPhone(image) {
/* 获取图片的信息 */
uni.getImageInfo({
src: image,
success: function(image) {
/* 保存图片到手机相册 */
uni.saveImageToPhotosAlbum({
filePath: image.path,
success: function() {
uni.showModal({
title: '保存成功',
content: '图片已成功保存到相册',
showCancel: false
});
},
complete(res) {
console.log(res);
uni.hideLoading();
}
});
}
});
},
onPopupChange(e) {
if (!e.show) {
return
}
this.init()
},
},
}
</script>
<style scoped lang="scss">
.canvas {
border-radius: 48rpx;
}
.btn {
margin-top: 32rpx;
width: 100%;
padding: 22rpx 0;
box-sizing: border-box;
font-family: PingFang SC;
font-weight: 500;
font-size: 36rpx;
line-height: 1.4;
color: #FFFFFF;
background: linear-gradient(to right, #21FEEC, #019AF9);
border: 2rpx solid #00A9FF;
border-radius: 41rpx;
}
</style>

+ 0
- 116
components/base/servicePopup.vue View File

@ -1,116 +0,0 @@
<template>
<view>
<uv-popup ref="popup" mode="bottom" bgColor="none"
:zIndex="1000000"
>
<view class="popup__view">
<view class="flex header">
<view class="title">申请售后</view>
<button class="btn" @click="close">关闭</button>
</view>
<view class="flex content">
<view>{{ phone }}</view>
<button plain class="flex btn" @click="onCall">
<image class="btn-icon" src="@/pages_order/static/order/icon-phone.png" mode="widthFix"></image>
</button>
</view>
</view>
</uv-popup>
</view>
</template>
<script>
export default {
data() {
return {
phone: null,
}
},
methods: {
open() {
this.phone = this.configList.after_sale_service_hotline
this.$refs.popup.open()
},
close() {
this.$refs.popup.close()
},
onCall() {
uni.makePhoneCall({
phoneNumber: this.phone,
success() {
console.log('安卓拨打成功');
},
fail() {
console.log('安卓拨打失败');
}
})
},
},
}
</script>
<style lang="scss" scoped>
.popup__view {
width: 100vw;
display: flex;
flex-direction: column;
box-sizing: border-box;
font-family: PingFang SC;
font-weight: 400;
line-height: 1.4;
background: #FFFFFF;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
}
.header {
position: relative;
width: 100%;
padding: 24rpx 0;
box-sizing: border-box;
border-bottom: 2rpx solid #EEEEEE;
.title {
font-family: PingFang SC;
font-weight: 500;
font-size: 34rpx;
line-height: 1.4;
color: #181818;
}
.btn {
font-family: PingFang SC;
font-weight: 500;
font-size: 32rpx;
line-height: 1.4;
color: #8B8B8B;
position: absolute;
top: 26rpx;
left: 40rpx;
}
}
.content {
padding: 84rpx;
column-gap: 12rpx;
font-size: 36rpx;
color: #181818;
.btn {
border: none;
width: 72rpx;
height: 72rpx;
background: #F6F6F6;
border-radius: 50%;
overflow: hidden;
&-icon {
width: 40rpx;
height: auto;
}
}
}
</style>

+ 0
- 135
components/base/tabbar.vue View File

@ -1,135 +0,0 @@
<template>
<view class="tabbar-box">
<view class="tabbar">
<view :class="{ 'tabbar-active' : select == item.key}" v-for="(item, index) in list" :key="index"
v-if="!item.isNotShop || !userShop" @click="toPath(item, index)" class="tabbar-item">
<view class="tabbar-icon">
<image :src="select == item.key ?
item.selectedIconPath :
item.iconPath" class="tabbar-icon-image" mode="aspectFill"></image>
</view>
<view class="tabbar-title">
{{ item.title }}
</view>
</view>
</view>
</view>
</template>
<script>
import {
mapGetters
} from 'vuex'
export default {
name: "tabbar",
props: ['select'],
computed: {
...mapGetters(['userShop']),
},
data() {
return {
list: [
{
"selectedIconPath": "/static/image/tabbar/home-active.png",
"iconPath": "/static/image/tabbar/home.png",
"pagePath": "/pages/index/index",
"title": "首页",
key: 'home',
},
{
"selectedIconPath": "/static/image/tabbar/category-active.png",
"iconPath": "/static/image/tabbar/category.png",
"pagePath": "/pages/index/category",
"title": "分类",
key: 'category',
},
{
"selectedIconPath": "/static/image/tabbar/growing-active.png",
"iconPath": "/static/image/tabbar/growing.png",
"pagePath": "/pages/index/growing",
"title": "成长档案",
key: 'growing',
},
{
"selectedIconPath": "/static/image/tabbar/partner-active.png",
"iconPath": "/static/image/tabbar/partner.png",
"pagePath": "/pages/index/partner",
"title": "合伙人",
key: 'partner',
},
{
"selectedIconPath": "/static/image/tabbar/user-center-active.png",
"iconPath": "/static/image/tabbar/user-center.png",
"pagePath": "/pages/index/center",
"title": "我的",
key: 'center',
},
]
};
},
methods: {
toPath(item, index) {
if (item.key == this.select) {
return
}
uni.reLaunch({
url: item.pagePath
})
},
}
}
</script>
<style scoped lang="scss">
.tabbar-box {
height: 120rpx;
padding-bottom: env(safe-area-inset-bottom);
.tabbar {
position: fixed;
width: 750rpx;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
height: 120rpx;
padding-bottom: env(safe-area-inset-bottom);
z-index: 999999;
bottom: 0;
left: 0;
color: #999999;
.tabbar-item {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.tabbar-icon {
width: 54rpx;
height: 54rpx;
.tabbar-icon-image {
width: 54rpx;
height: 54rpx;
}
}
.tabbar-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
font-size: 23rpx;
line-height: 35rpx;
}
}
.tabbar-active {
color: #181818 !important;
}
}
}
</style>

+ 0
- 168
components/config/PrivacyAgreementPoup.vue View File

@ -1,168 +0,0 @@
<template>
<uv-popup ref="popup" z-index="99999" :closeOnClickOverlay="false" :customStyle="{ backgroundColor: 'transparent' }">
<view class="privacyPopup">
<view class="icon">
<image src="/static/image/PrivacyAgreementPoup/icon.png"
mode=""></image>
</view>
<view class="title">
<view>协议与隐私政策</view>
</view>
<view class="content_pri">
<view class="text">
欢迎来到酒店布草!我们根据最新的法律法规监管政策要求更新了用户协议隐私政策,请您认真阅读
</view>
</view>
<view class="config">
<uv-checkbox-group v-model="checkboxValue" shape="circle">
<view class="content">
<view style="display: flex;">
<!-- <uv-checkbox size="30rpx" :name="1"></uv-checkbox> -->
同意<text @click="goToPrivacy">酒店布草隐私政策</text>
</view>
<view class="">
以及<text @click="goToPrivacy">用户协议</text>
</view>
</view>
</uv-checkbox-group>
</view>
<view class="pri_btn">
<button class="confuse_btn" @click="confusePrivacy">拒绝</button>
<button
class="confirm_btn" id="agree-btn"
open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handleAgreePrivacyAuthorization">同意</button>
</view>
</view>
</uv-popup>
</template>
<script>
export default {
name: 'PrivacyAgreementPoup',
data() {
return {
resolvePrivacyAuthorization: {},
checkboxValue : false
}
},
mounted() {
if(wx.getPrivacySetting){
wx.getPrivacySetting({
success: res => {
if (res.needAuthorization) {
//
this.init()
}
},
fail: () => {}
})
}
},
methods: {
//
init(resolve) {
this.$refs.popup.open('center')
this.resolvePrivacyAuthorization = resolve
},
//
goToPrivacy() {
wx.openPrivacyContract({
success: () => {
console.log('打开成功');
}, //
fail: () => {
uni.showToast({
title: '打开失败,稍后重试',
icon: 'none'
})
} //
})
},
//
confusePrivacy() {
this.$refs.popup.close()
// this.resolvePrivacyAuthorization({
// event: 'disagree'
// })
},
//
handleAgreePrivacyAuthorization() {
// id
// this.resolvePrivacyAuthorization({
// buttonId: 'agree-btn',
// event: 'agree'
// })
this.$refs.popup.close()
}
}
}
</script>
<style lang="scss" scoped>
.privacyPopup {
width: 90%;
margin: 0rpx auto;
background: white;
border-radius: 20rpx;
box-sizing: border-box;
padding: 40rpx 30rpx;
.icon{
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 30rpx;
image{
width: 90rpx;
height: 90rpx;
}
}
.title {
text-align: center;
font-size: 36rpx;
}
.content_pri {
padding: 30rpx 0rpx;
font-size: 28rpx;
}
.config {
font-size: 28rpx;
text-align: center;
line-height: 40rpx;
margin-bottom: 30rpx;
text {
color: $uni-color;
}
.content{
display: flex;
}
}
.pri_btn {
display: flex;
.confuse_btn{
background-color: #F2F2F2;
color: #BDBDBD;
}
button {
margin: 10rpx;
flex: 1;
background: $uni-color;
outline: none;
color: white;
font-size: 30rpx;
}
}
}
</style>

+ 0
- 48
components/config/configPopup.vue View File

@ -1,48 +0,0 @@
<template>
<view class="configPopup">
<uv-popup ref="popup" :round="30" :customStyle="{height: '50vh'}">
<view class="content">
<uv-parse :content="content"></uv-parse>
</view>
</uv-popup>
</view>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'configPoup',
data() {
return {
content : ''
}
},
onShow(){
},
methods: {
//
open(key){
this.content = this.configList[key]
this.$refs.popup.open('bottom');
}
},
computed : {
...mapState(['configList'])
}
}
</script>
<style lang="scss" scoped>
.configPopup {
.content{
padding: 30rpx 20rpx;
overflow: scroll;
height: 100%;
box-sizing: border-box;
}
}
</style>

+ 0
- 122
components/config/customerServicePopup.vue View File

@ -1,122 +0,0 @@
<template>
<!-- 联系客服弹框 -->
<uv-popup ref="popup"
:safeAreaInsetBottom="false"
:round="30">
<view class="warp">
<view class="rect" @tap.stop>
<view class="title">联系{{ bTitle || title }}</view>
<view class="center">确定拨打{{ bTitle || title }}电话?</view>
<view class="bottom">
<view class="btn1"
@click="close">
取消
</view>
<view class="btn2"
@click="confirm">
确定
</view>
</view>
</view>
</view>
</uv-popup>
</template>
<script>
export default {
data() {
return {
phone:'',
title : '客服',
bPhone : '',
bTitle : '',
}
},
mounted() {
this.getCustomPhone()
},
methods: {
getCustomPhone(){
this.$api('customUser', {}, res => {
this.phone = res.result.phone
})
},
open(phone, title) {
this.bPhone = phone || this.phone
this.bTitle = title || this.title
this.$refs.popup.open()
},
close() {
this.$refs.popup.close()
},
//
confirm() {
this.$refs.popup.close()
uni.makePhoneCall({
phoneNumber: this.bPhone || this.phone,
success() {
console.log('安卓拨打成功');
},
fail() {
console.log('安卓拨打失败');
}
})
},
}
}
</script>
<style scoped lang="scss">
.warp {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.rect {
width: 600rpx;
height: 300rpx;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
.title {
padding: 10rpx 0 0 15rpx;
background-color: $uni-color;
color: #FFF;
text-align: left;
width: 100%;
height: 18%;
font-size: 36rpx;
}
.center {
height: 40%;
display: flex;
justify-content: center;
align-items: center;
font-size: 36rpx;
}
.bottom {
display: flex;
justify-content: center;
gap: 50rpx;
view{
height: 60rpx;
line-height: 60rpx;
padding: 0 50rpx;
border-radius: 30rpx;
}
.btn1{
background-color: #fff;
}
.btn2{
background-color: $uni-color;
color: #fff;
}
}
}
</style>

+ 0
- 114
components/growing/recordsView.vue View File

@ -1,114 +0,0 @@
<template>
<view class="records__view">
<uv-steps direction="column">
<uv-steps-item v-for="(record, index) in list" :key="record.id">
<template #icon>
<view class="flex mark is-active" v-if="index === 0">
<image class="icon" src="@/static/image/icon-mark-highlight.png" mode="widthFix"></image>
</view>
<view class="flex mark" v-else>
<image class="icon" src="@/static/image/icon-mark.png" mode="widthFix"></image>
</view>
</template>
<template #title>
<view class="title" @click="onClickActivity(record.id)">{{ $dayjs(record.createTime).format('YYYY-MM-DD') }}</view>
</template>
<template #desc>
<view class="content" @click="onClickActivity(record.id)">
<view class="desc">{{ record.name }}</view>
<view class="image">
<view class="image-item" v-for="(image, imgIdx) in record.images" :key="imgIdx">
<image class="img" :src="image" mode="aspectFill"></image>
</view>
</view>
</view>
</template>
</uv-steps-item>
</uv-steps>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default() {
return []
}
}
},
methods: {
onClickActivity(id) {
this.$utils.navigateTo(`/pages_order/growing/activity/index?id=${id}`)
},
},
}
</script>
<style scoped lang="scss">
.records__view {
padding-left: 40rpx;
}
.mark {
margin-top: 8rpx;
width: 36rpx;
height: 36rpx;
background: #F3F3F3;
border-radius: 50%;
.icon {
width: 24rpx;
height: auto;
}
&.is-active {
background: linear-gradient(to right, #21FEEC, #019AF9);
}
}
.title {
font-family: PingFang SC;
font-size: 36rpx;
font-weight: 600;
line-height: 1.4;
color: $uni-color;
}
.content {
width: calc(100vw - 92rpx);
}
.desc {
padding: 12rpx 0 8rpx 0;
font-family: PingFang SC;
font-size: 28rpx;
font-weight: 400;
line-height: 1.4;
color: #191919;
}
.image {
display: flex;
column-gap: 16rpx;
flex-wrap: nowrap;
overflow-x: auto;
&-item {
flex: none;
width: 208rpx;
height: 296rpx;
border: 2rpx solid #CDCDCD;
border-radius: 12rpx;
overflow: hidden;
.img {
width: 100%;
height: 100%;
}
}
}
</style>

+ 0
- 220
components/growing/userCard.vue View File

@ -1,220 +0,0 @@
<template>
<view class="card">
<view class="flex card-header">
<view>
<view class="title">我的档案</view>
<button class="flex btn btn-question">
<image class="btn-icon" src="@/static/image/icon-question.png" mode="widthFix"></image>
<view>如何完善我的档案</view>
</button>
</view>
<view class="flex">
<button class="btn btn-switch" @click="onSwitch">切换</button>
<!-- <button class="btn btn-add" @click="onAdd">新增记录</button> -->
</view>
</view>
<view class="card-content">
<view class="flex info">
<view class="avatar">
<image class="img" :src="info.headImage" mode="scaleToFill"></image>
<view :class="['tag', `tag-${info.role}`]">{{ info.roleDesc || '' }}</view>
</view>
<view class="flex summary">
<view class="flex flex-column summary-item name">
<view class="summary-item-content">{{ info.nickName || '' }}</view>
<view class="summary-item-label">{{ `ID:${info.id}` }}</view>
</view>
<view class="flex flex-column summary-item" @click="jumpToAchievement">
<view class="summary-item-content">{{ medalCount }}</view>
<view class="summary-item-label nowrap">成就</view>
</view>
<view class="flex flex-column summary-item">
<view class="summary-item-content">{{ experienceCount }}</view>
<view class="summary-item-label nowrap">足迹</view>
</view>
</view>
</view>
<view class="flex medal" @click="jumpToAchievement">
<image class="medal-item" v-for="item in medalList" :key="item.id" :src="item.medal.icon1" mode="widthFix"></image>
</view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex'
export default {
props: {
medalList: {
type: Array,
default() {
return []
}
},
medalCount: {
type: Number,
default: 0
},
experienceCount: {
type: Number,
default: 0
},
},
data() {
return {
}
},
onLoad() {
},
computed: {
...mapState(['userInfo', 'memberInfo']),
info() {
return this.memberInfo || this.userInfo
},
},
methods: {
onAdd() {
this.$emit('addRecord')
},
onSwitch() {
this.$emit('switchMember')
},
jumpToAchievement() {
uni.navigateTo({
url: '/pages_order/growing/achievement/index'
})
},
},
}
</script>
<style scoped lang="scss">
@import '../member/styles/tag.scss';
.card {
font-family: PingFang SC;
font-weight: 400;
line-height: 1.4;
background: linear-gradient(to right, #DDF4FF, #9FE1FF);
border-radius: 48rpx;
&-header {
justify-content: space-between;
padding: 24rpx 40rpx 20rpx 40rpx;
.title {
font-size: 32rpx;
font-weight: 600;
color: #000000;
}
.btn-question {
margin-top: 4rpx;
column-gap: 8rpx;
font-size: 26rpx;
color: #21607D;
.btn-icon {
width: 32rpx;
height: auto;
}
}
.btn-switch {
padding: 6rpx 22rpx;
font-size: 28rpx;
font-weight: 400;
line-height: 1.4;
color: #252545;
border: 2rpx solid #252545;
border-radius: 28rpx;
}
.btn-add {
padding: 8rpx 24rpx;
font-size: 28rpx;
font-weight: 500;
line-height: 1.4;
color: #FFFFFF;
background: linear-gradient(to right, #21FEEC, #019AF9);
border-radius: 28rpx;
}
.btn + .btn {
margin-left: 26rpx;
}
}
&-content {
padding: 38rpx 38rpx 14rpx 38rpx;
background: linear-gradient(to right, #FBFEFF, #DAF3FF);
border: 2rpx solid #FFFFFF;
border-radius: 48rpx;
box-shadow: 0 2px 12px 0 #009AE717;
.info {
column-gap: 24rpx;
.avatar {
flex: none;
position: relative;
width: 128rpx;
height: 128rpx;
border-radius: 24rpx;
overflow: hidden;
.img {
width: 100%;
height: 100%;
}
}
.summary {
flex: 1;
column-gap: 26rpx;
&-item {
flex: 1;
row-gap: 8rpx;
&.name {
flex: none;
}
&-content {
font-size: 32rpx;
font-weight: 600;
color: #000000;
}
&-label {
font-size: 24rpx;
color: #939393;
}
}
}
}
.medal {
margin-top: 16rpx;
justify-content: flex-start;
flex-wrap: wrap;
gap: 16rpx;
&-item {
width: 50rpx;
height: auto;
}
}
}
}
.nowrap {
white-space: nowrap;
}
</style>

+ 0
- 40
components/home/bgSwiperView.vue View File

@ -1,40 +0,0 @@
<template>
<view class="swiper">
<uv-swiper
:list="bannerList" keyName="image"
indicator
indicatorMode="dot"
indicatorInactiveColor="rgba(255, 255, 255, 0.7)"
height="382rpx"
></uv-swiper>
</view>
</template>
<script>
export default {
data() {
return {
bannerList: [],
}
},
created() {
this.getData()
},
methods: {
async getData() {
try {
this.bannerList = (await this.$fetch('queryBannerList', { type: 0 })).records // type0- 1-
} catch (err) {
}
},
},
}
</script>
<style scoped lang="scss">
.swiper {
border-radius: 40rpx;
overflow: hidden;
}
</style>

+ 0
- 110
components/home/categoryView.vue View File

@ -1,110 +0,0 @@
<template>
<view class="category">
<view class="flex flex-column category-item" v-for="item in categoryOptions" :key="item.id" @click="onClick(item)">
<image class="img" :src="item.icon" mode="aspectFit"></image>
<view>{{ item.label }}</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
categoryOptions: [],
}
},
created() {
this.getData()
},
methods: {
async getData() {
try {
const categorys = (await this.$fetch('queryCategoryList', { pageSize: 6, isShow: '1' }))?.records?.map(item => {
const { id, icon, title } = item
return {
id,
icon,
label: title,
path: `/pages/index/category?categoryId=${id}`,
}
})
this.categoryOptions = [
...categorys.slice(0, 5),
{
id: '006',
icon: '/static/image/temp-6.png',
label: '研学日记',
path: `/pages_order/article/search?api=queryJournalList&title=研学日记`,
},
{
id: '007',
icon: '/static/image/temp-7.png',
label: '研学政策',
path: `/pages_order/article/search?api=queryPolicyList&title=研学政策`,
},
categorys[5],
{
id: '009',
icon: '/static/image/temp-9.png',
label: '公司动态',
path: `/pages_order/article/search?api=queryNewsList&title=公司动态`,
},
{
id: '010',
icon: '/static/image/temp-10.png',
label: '全部',
path: `/pages/index/category`,
},
]
} catch(err) {
}
},
onClick(target) {
const { path } = target
if (path) {
uni.navigateTo({
url: path
})
}
},
},
}
</script>
<style scoped lang="scss">
.category {
margin-top: 158rpx;
width: 100%;
padding: 24rpx 32rpx;
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(5, 1fr);
column-gap: 16rpx;
row-gap: 4rpx;
background: linear-gradient(#DAF3FF, #FBFEFF 50rpx, #FBFEFF);
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
&-item {
padding: 16rpx 0;
row-gap: 12rpx;
font-size: 22rpx;
color: #9B9B9B;
.img {
width: 72rpx;
height: 72rpx;
}
}
}
</style>

+ 0
- 177
components/home/pictureLiveView.vue View File

@ -1,177 +0,0 @@
<template>
<view class="live">
<view class="flex live-header">
<view>图片直播</view>
<button class="flex btn" @click="showAll">
<view>查看全部</view>
<image class="img" src="@/static/image/icon-arrow-right.png" mode="widthFix"></image>
</button>
</view>
<!-- <view class="live-content">
<view class="live-item" v-for="item in list" :key="item.id">
<image class="live-item-bg" :src="item.image" mode="aspectFill"></image>
<view class="live-item-info">
<view class="text-ellipsis live-item-info-title">{{ item.title }}</view>
<view class="live-item-info-time">{{ item.time }}</view>
</view>
</view>
</view> -->
<view class="live-content">
<swiper
class="swiper"
:current="current"
:autoplay="true"
:display-multiple-items="3.2"
>
<swiper-item v-for="item in list" :key="item.id" style="display: inline-block;">
<view class="swiper-item">
<view class="swiper-item-content" @click="jumpToLive(item.id, item.activityId)">
<image class="live-item-bg" :src="item.url" mode="aspectFill"></image>
<view class="live-item-info">
<view class="text-ellipsis live-item-info-title">{{ item.title }}</view>
<view class="live-item-info-time">{{ item.createTime }}</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: [],
}
},
created() {
this.getData()
},
methods: {
async getData() {
try {
this.list = (await this.$fetch('queryImageList', { pageNo: 1, pageSize: 8 }))?.records?.map(item => {
const { id, image, activityId, activityId_dictText, createTime } = item
const images = image?.split?.(',') || []
return {
id,
activityId,
url: images?.[0],
images,
title: activityId_dictText,
createTime: this.$dayjs(createTime).format('YYYY-MM-DD'),
}
})
} catch (err) {
}
},
jumpToLive(id, activityId) {
this.$store.commit('setLiveInfo', this.list.find(item => item.id === id))
this.$utils.navigateTo(`/pages_order/live/index?imageId=${id}&activityId=${activityId}`)
},
showAll() {
this.$utils.navigateTo(`/pages_order/live/list`)
}
},
}
</script>
<style scoped lang="scss">
.live {
width: 100%;
padding: 32rpx;
box-sizing: border-box;
background-image: linear-gradient(164deg,#DAF3FF, #FBFEFF , #FBFEFF);
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
&-header {
justify-content: space-between;
font-size: 36rpx;
font-weight: 500;
color: #191919;
.btn {
column-gap: 4rpx;
font-size: 24rpx;
color: #8B8B8B;
.img {
width: 32rpx;
height: auto;
}
}
}
&-content {
margin-top: 16rpx;
white-space: nowrap;
width: 100%;
overflow-x: auto;
font-size: 0;
}
&-item {
flex: none;
display: inline-block;
width: 180rpx;
height: 240rpx;
border-radius: 12rpx;
overflow: hidden;
position: relative;
& + & {
margin-left: 16rpx;
}
&-bg {
width: 100%;
height: 100%;
}
&-info {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
padding: 8rpx 12rpx;
box-sizing: border-box;
&-title {
font-size: 26rpx;
font-weight: 600;
color: #FFFFFF;
}
&-time {
font-size: 22rpx;
color: #FFFFFF;
}
}
}
}
.swiper {
width: 100%;
height: 240rpx;
&-item {
width: 180rpx;
height: 240rpx;
&-content {
position: relative;
width: 100%;
height: 100%;
border-radius: 12rpx;
overflow: hidden;
}
}
}
</style>

+ 0
- 112
components/home/productView.vue View File

@ -1,112 +0,0 @@
<template>
<view>
<view class="tabs">
<uv-tabs
:list="tabs"
:activeStyle="{
'font-family': 'PingFang SC',
'font-weight': 500,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#FFFFFF',
'background-color': '#191919',
'border-radius': '34rpx',
'padding': '12rpx 32rpx',
}"
:inactiveStyle="{
'font-family': 'PingFang SC',
'font-weight': 400,
'font-size': '28rpx',
'line-height': 1.5,
'color': '#191919',
'background-color': '#E4E7EB',
'border-radius': '34rpx',
'padding': '12rpx 32rpx',
}"
lineWidth="0"
lineHeight="0"
:current="current"
@click="onClickTab"
></uv-tabs>
</view>
<view v-if="list.length" class="content">
<view v-for="item in list" :key="item.id">
<productCard
:data="item"
size="small"
@collect="$emit('collect')"
></productCard>
</view>
</view>
<template v-else>
<uv-empty mode="list"></uv-empty>
</template>
</view>
</template>
<script>
import productCard from '@/components/product/productCard.vue'
export default {
components: {
productCard,
},
props: {
list: {
type: Array,
default() {
return []
}
},
},
data() {
return {
tabs: [],
current: 0,
}
},
created() {
this.fetchRecommend()
},
methods: {
fetchRecommend() {
// todo: fetch
this.tabs = [
{ name: '全部' },
// { id: '1962342791093227522', name: '', disabled: true, },
]
},
onClickTab(e) {
this.current = 0
const index = e.index
if (index > 0) {
this.$utils.navigateTo(`/pages_order/product/productDetail?id=${this.tabs[e.index].id}`)
}
this.$nextTick(() => {
this.current = 0
})
setTimeout(() => {
this.current = 0
}, 800)
},
},
}
</script>
<style scoped lang="scss">
.tabs {
width: calc(100% + 22px);
transform: translateX(-11px);
}
.content {
margin-top: 24rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
}
</style>

+ 0
- 98
components/home/recommendView.vue View File

@ -1,98 +0,0 @@
<template>
<view class="recommend">
<view class="flex recommend-item" v-for="item in recommendList" :key="item.id" @click="onClick(item)">
<image class="img" :src="item.icon" mode="widthFix"></image>
<view>
<view class="label">{{ item.label }}</view>
<view :class="['desc', item.highlight ? 'highlight' : '']">{{ item.desc }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
recommendList: [],
}
},
created() {
this.getData()
},
methods: {
getData() {
this.recommendList = [
{
id: '001',
icon: '/static/image/temp-11.png',
label: '限时优惠',
desc: '速抢折扣',
highlight: true,
path: `/pages_order/product/search?isDiscount=1&title=限时优惠`,
},
{
id: '002',
icon: '/static/image/temp-12.png',
label: '口碑爆款',
desc: '热销推荐',
path: `/pages_order/product/search?isHot=1&title=口碑爆款`,
},
{
id: '003',
icon: '/static/image/temp-13.png',
label: '新上线路',
desc: '全新上线',
path: `/pages_order/product/search?isNew=1&title=新上线路`,
},
]
},
onClick(target) {
const { path } = target
if (path) {
uni.navigateTo({
url: path
})
}
},
},
}
</script>
<style scoped lang="scss">
.recommend {
display: grid;
grid-template-columns: repeat(3, 1fr);
column-gap: 12rpx;
&-item {
justify-content: flex-start;
column-gap: 8rpx;
padding: 16rpx;
box-sizing: border-box;
background: linear-gradient(to right, #FFF5EA, #FFFCF9 70%, #FFFCF9);
border-radius: 16rpx;
.img {
width: 56rpx;
height: auto;
}
.label {
font-size: 26rpx;
color: #181818;
}
.desc {
font-size: 22rpx;
color: #9B9B9B;
&.highlight {
color: #FF9035;
}
}
}
}
</style>

+ 0
- 54
components/home/swiperView.vue View File

@ -1,54 +0,0 @@
<template>
<view class="swiper">
<uv-swiper
:list="bannerList" keyName="image"
indicator
indicatorMode="dot"
indicatorInactiveColor="rgba(255, 255, 255, 0.7)"
height="228rpx"
@click="onClickBanner"
></uv-swiper>
</view>
</template>
<script>
export default {
data() {
return {
bannerList: [],
}
},
created() {
this.getData()
},
methods: {
async getData() {
try {
this.bannerList = (await this.$fetch('queryBannerList', { type: 1 })).records // type0- 1-
} catch (err) {
}
},
onClickBanner(index) {
console.log('onClickBanner', index)
},
},
}
</script>
<style scoped lang="scss">
.swiper {
border-radius: 32rpx;
overflow: hidden;
/deep/ .uv-swiper-indicator__wrapper__dot,
/deep/ .uv-swiper-indicator__wrapper__dot--active {
margin: 0 4rpx;
width: 30rpx;
height: 10rpx;
}
/deep/ .uv-swiper-indicator__wrapper__dot--active {
background: linear-gradient(to right, #21FEEC 40%, #019AF9);
}
}
</style>

+ 0
- 18
components/member/styles/tag.scss View File

@ -1,18 +0,0 @@
.tag {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
padding: 2rpx 0;
box-sizing: border-box;
text-align: center;
font-size: 24rpx;
color: #03C25C;
background: #E9FFF5;
&-1 {
color: #00A9FF;
background: #E5F2F9;
}
}

+ 0
- 434
components/partner/posterPopup copy.vue View File

@ -1,434 +0,0 @@
<template>
<uv-popup
ref="popup"
:overlayOpacity="0.6"
mode="center"
bgColor="none"
:zIndex="1000000"
@change="onPopupChange"
>
<view class="popup__view">
<view class="canvas" style="width: 566rpx; height: 1060rpx; overflow: hidden;">
<canvas id="myCanvas" canvas-id="firstCanvas1" type="2d" style="width: 100%; height: 100%;"></canvas>
</view>
<button class="btn" @click="saveImg">
<view class="content">保存到相册</view>
</button>
</view>
</uv-popup>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
wxCodeImage: '',
baseUrl: 'https://image.hhlm1688.com/',
canvas: {},
retry: 10,
}
},
computed: {
...mapState(['userInfo', 'configList']),
},
async onLoad() {
},
methods: {
open() {
this.retry = 10
this.$refs.popup.open();
},
close() {
this.$refs.popup.close();
},
async fetchQrCode() {
// // todo: delete
// this.wxCodeImage = 'https://uploadfile.bizhizu.cn/up/e3/64/e0/e364e0f7f6af11f11abdafc22d17b15c.jpg'
// return
try {
const path = `pages/index/index?shareId=${this.userInfo.id}`
this.wxCodeImage = (await this.$fetch('getInviteCode', { path }))?.url
} catch (err) {
}
},
//
drawBg(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
ctx.lineTo(width - radius + x, y);
ctx.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2);
ctx.lineTo(width + x, height + y - radius);
ctx.arc(width - radius + x, height - radius + y, radius, 0, Math.PI * 1 / 2);
ctx.lineTo(radius + x, height + y);
ctx.arc(radius + x, height - radius + y, radius, Math.PI * 1 / 2, Math.PI);
ctx.closePath();
ctx.fillStyle = '#fff'
ctx.fill()
},
drawCoverImg(canvas, ctx, x, y, width, height, radius, lineWidth) {
return new Promise(resolve => {
//
const paperImage = canvas.createImage()
console.log('paperImage', paperImage)
// todo: fetch
paperImage.src = 'https://i1.hdslb.com/bfs/archive/c0101b4ce06e6bdda803728408e79c8f8b8d0725.jpg'
paperImage.onload = () => {
console.log('paperImage onload')
ctx.beginPath();
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
ctx.lineTo(width - radius + x, y);
ctx.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2);
ctx.lineTo(width + x, height + y);
ctx.lineTo(x, height + y);
ctx.closePath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = '#F8F8F8';
ctx.stroke();
ctx.clip();
ctx.drawImage(paperImage, x, y, width, height)
resolve()
}
})
},
drawContentBg(ctx, x, y, width, height, radius) {
ctx.beginPath();
// ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2);
ctx.moveTo(x, y);
ctx.lineTo(width + x, y);
ctx.lineTo(width + x, height + y - radius);
ctx.arc(width - radius + x, height - radius + y, radius, 0, Math.PI * 1 / 2);
ctx.lineTo(radius + x, height + y);
ctx.arc(radius + x, height - radius + y, radius, Math.PI * 1 / 2, Math.PI);
ctx.closePath();
let gradient = ctx.createLinearGradient(x, y, x, y + height);
gradient.addColorStop('0', '#DAF3FF');
gradient.addColorStop('0.2', '#FBFEFF');
gradient.addColorStop('1.0', '#FBFEFF');
ctx.fillStyle = gradient
ctx.fill()
},
drawAvatar(canvas, ctx, x, y, r) {
console.log('drawAvatar', 'x', x, 'y', y, 'r', r)
return new Promise(resolve => {
//
const avatarImage = canvas.createImage()
avatarImage.src = this.userInfo.headImage
avatarImage.onload = () => {
console.log('avatarImage onload')
ctx.beginPath();
console.log('arc', 'x', x + r, 'y', y + r, 'r', r)
ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);
ctx.clip();
const size = r*2
console.log('size', size)
ctx.drawImage(avatarImage, x, y, size, size)
resolve()
}
})
},
drawMultilineText(ctx, text, x, y, maxWidth, lineHeight) {
console.log('drawMultilineText', 'x', x, 'y', y)
let line = '';
for (let n = 0; n < text.length; n++) {
const testLine = line + text[n];
const metrics = ctx.measureText(testLine);
const testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
ctx.fillText(line, x, y);
console.log(n, 'line', line, 'x', x, 'y', y)
line = text[n];
y += lineHeight;
} else {
line = testLine;
}
}
console.log('line', line, 'x', x, 'y', y)
ctx.fillText(line, x, y);
},
drawQrCodeImg(canvas, ctx, x, y, size) {
return new Promise(resolve => {
//
const coderImage = canvas.createImage()
coderImage.src = this.wxCodeImage
coderImage.onload = () => {
console.log('coderImage onload')
ctx.drawImage(coderImage, x, y, size, size)
resolve()
}
})
},
draw() {
wx.createSelectorQuery().in(this)
.select('#myCanvas') // canvasid
.fields({
node: true,
size: true
})
.exec(async (res) => {
console.log('res', res)
if (!res?.[0]?.node) {
if (!this.retry) {
console.log('retry fail')
return
}
console.log('retry')
this.retry -= 1
setTimeout(() => {
this.draw()
}, 200)
return
}
const canvas = res[0].node
//
const ctx = canvas.getContext('2d')
// Canvas
const width = res[0].width
const height = res[0].height
//
const dpr = wx.getWindowInfo().pixelRatio
//dpr
// dpr 2 4
// 3 6
console.log("--dpr", dpr)
canvas.width = width * dpr
canvas.height = height * dpr
let Ratio = canvas.width / 566
this.canvas = canvas
ctx.scale(dpr, dpr)
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = 'transparent'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.save()
let radius = 48 * Ratio / dpr
let w = 566 * Ratio / dpr
let h = 1060 * Ratio / dpr
this.drawBg(ctx, 0, 0, w, h, radius)
ctx.restore();
ctx.save()
let lineWidth = 2 * Ratio / dpr
let x = lineWidth
let y = lineWidth
w = 566 * Ratio / dpr - lineWidth * 2
h = 400 * Ratio / dpr - lineWidth * 2
await this.drawCoverImg(canvas, ctx, x, y, w, h, radius, lineWidth)
ctx.restore();
ctx.save()
x = lineWidth
y = 400 * Ratio / dpr + lineWidth
h = 660 * Ratio / dpr - lineWidth * 2
this.drawContentBg(ctx, x, y, w, h, radius, lineWidth)
ctx.restore();
ctx.save()
radius = Math.floor(27 * Ratio / dpr)
x = Math.floor(40 * Ratio / dpr)
y = Math.floor(440 * Ratio / dpr)
await this.drawAvatar(canvas, ctx, x, y, radius)
ctx.restore();
ctx.save()
let text = this.userInfo.nickName
let maxWidth = 560 * Ratio / dpr
let lineHeight = 17 * Ratio / dpr
x = 100 * Ratio / dpr
y = 474 * Ratio / dpr
ctx.font = "normal normal normal 11px normal";
ctx.fillStyle = "#7D7D7D";
this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
ctx.restore();
ctx.save()
maxWidth = 560 * Ratio / dpr
lineHeight = 60 * Ratio / dpr
ctx.font = "normal normal 600 16px normal";
ctx.fillStyle = "#181818";
text = '邀请您'
x = 40 * Ratio / dpr
y = 564 * Ratio / dpr
this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
y += lineHeight
text = '探索新世界,开启研学之旅!'
this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
ctx.restore();
ctx.save()
text = '是否渴望一场充满知识与乐趣的冒险?现在,我们诚挚邀请你加入我们的研学小程序,开启一场别开生面的学习之旅!'
maxWidth = 486 * Ratio / dpr
lineHeight = 30 * Ratio / dpr
ctx.font = "normal normal normal 10px normal";
ctx.fillStyle = "#7D7D7D";
x = 40 * Ratio / dpr
y = 690 * Ratio / dpr
this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
ctx.restore();
ctx.save()
// 线
ctx.setLineDash([4, 4]); // 线,
ctx.lineDashOffset = 2; // 线
ctx.lineWidth = lineWidth;
ctx.strokeStyle = '#DADADA';
//
ctx.beginPath();
x = 40 * Ratio / dpr
y = 778 * Ratio / dpr
ctx.moveTo(x, y);
x += 486 * Ratio / dpr
ctx.lineTo(x, y);
ctx.stroke();
ctx.restore();
ctx.save()
maxWidth = 250 * Ratio / dpr
lineHeight = 44 * Ratio / dpr
ctx.font = "normal normal 600 13px normal";
ctx.fillStyle = "#181818";
text = '立即加入我们,'
x = 40 * Ratio / dpr
y = 879 * Ratio / dpr
this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
text = '开启你的研学之旅!'
y += lineHeight
this.drawMultilineText(ctx, text, x, y, maxWidth, lineHeight)
ctx.restore();
ctx.save()
x = 316 * Ratio / dpr
y = 810 * Ratio / dpr
let size = 210 * Ratio / dpr
await this.drawQrCodeImg(canvas, ctx, x, y, size)
uni.hideLoading()
})
},
async init() {
uni.showLoading({
title: '加载中...'
});
await this.fetchQrCode()
uni.hideLoading();
uni.showLoading({
title: "拼命绘画中..."
})
this.draw()
},
saveImg() {
this.$authorize('scope.writePhotosAlbum').then((res) => {
this.imgApi()
})
},
imgApi() {
uni.showLoading({
title: '保存中...'
});
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: this.canvas.width,
height: this.canvas.height,
canvas: this.canvas,
success: (res) => {
let tempFilePath = res.tempFilePath;
this.saveImgToPhone(tempFilePath)
},
fail: (err) => {
console.log('--canvasToTempFilePath--fail', err)
uni.hideLoading();
}
}, this);
},
saveImgToPhone(image) {
/* 获取图片的信息 */
uni.getImageInfo({
src: image,
success: function(image) {
/* 保存图片到手机相册 */
uni.saveImageToPhotosAlbum({
filePath: image.path,
success: function() {
uni.showModal({
title: '保存成功',
content: '图片已成功保存到相册',
showCancel: false
});
},
complete(res) {
console.log(res);
uni.hideLoading();
}
});
}
});
},
onPopupChange(e) {
if (!e.show) {
return
}
this.init()
},
},
}
</script>
<style scoped lang="scss">
.canvas {
border-radius: 48rpx;
}
.btn {
margin-top: 32rpx;
width: 100%;
padding: 22rpx 0;
box-sizing: border-box;
font-family: PingFang SC;
font-weight: 500;
font-size: 36rpx;
line-height: 1.4;
color: #FFFFFF;
background: linear-gradient(to right, #21FEEC, #019AF9);
border: 2rpx solid #00A9FF;
border-radius: 41rpx;
}
</style>

+ 0
- 270
components/product/productCard.vue View File

@ -1,270 +0,0 @@
<template>
<view :class="['product', size]"
@touchstart="onTouchstart"
@touchmove="onTouchmove"
@touchend="onTouchend"
>
<image class="product-img" :src="data.image" mode="aspectFill"></image>
<view class="flex flex-column product-info">
<view class="product-info-top">
<view class="product-name text-ellipsis-2">{{ data.title }}</view>
<view class="product-desc text-ellipsis" v-if="tagDesc">{{ tagDesc }}</view>
</view>
<view class="flex product-info-bottom">
<view class="product-detail">
<view class="flex product-price">
<view class="product-price-val">
<text>¥</text>
<text class="highlight">{{ priceInt }}</text>
<text>{{ `${priceFrac}` }}</text>
</view>
<view class="product-price-bef" v-if="data.priceOrigin">
{{ `¥${data.priceOrigin}` }}
</view>
</view>
<view class="product-registered">
{{ `${data.applyNum}人已报名` }}
</view>
</view>
<button class="btn" @click="onRegistrate">报名</button>
</view>
</view>
<button class="flex btn btn-collect"
:style="collectBtnStyle"
@click.stop="onCollect"
@touchstart.stop="onCollect"
>
<view>{{ isCollected ? '移除收藏' : '收藏' }}</view>
</button>
</view>
</template>
<script>
export default {
props: {
data: {
type: Object,
default() {
return {}
}
},
size: {
type: String,
default: 'normal' // normal | small
}
},
data() {
return {
isMove: false,
startClientX: null,
displayX: 0,
collectBtnVisible: false,
}
},
computed: {
tagDesc() {
const { tagList } = this.data
return tagList?.length ? tagList.split('、').join('·') : ''
},
priceInt() {
return Math.floor(this.data.priceDiscount)
},
priceFrac() {
let frac = this.data.priceDiscount % this.priceInt
return frac > 0 ? frac.toFixed(2).slice(1) : ''
},
isCollected() {
return this.data.isCollection == '1'
},
collectBtnWidth() {
return this.isCollected ? 80 : 56
},
collectBtnStyle() {
const width = this.collectBtnWidth
const background = this.isCollected ? '#26334E' : '#FF9035'
let display = Math.ceil(this.displayX / width * 100)
display > 100 && (display = 100)
const translateX = 100 - display
return `width: ${width}px; transform: translateX(${translateX}%); background: ${background};`
},
},
methods: {
onTouchstart(e) {
const clientX = e.changedTouches[0].clientX
this.isMove = false
this.startClientX = clientX
this.displayX = 0
},
onTouchmove(e) {
const clientX = e.changedTouches[0].clientX
if (clientX < this.startClientX) {
this.displayX = this.startClientX - clientX
} else {
this.displayX = 0
}
this.isMove = true
},
onTouchend() {
if (!this.isMove && !this.collectBtnVisible) {
this.onRegistrate()
}
if (this.displayX < this.collectBtnWidth) {
this.displayX = 0
}
if (this.displayX) {
this.collectBtnVisible = true
} else {
this.collectBtnVisible = false
}
this.isMove = false
},
showCollectBtn() {
this.displayX = 100
},
hiddenCollectBtn() {
this.displayX = 0
},
async onCollect() {
try {
let succ = await this.$store.dispatch('collect', this.data.id)
succ && this.hiddenCollectBtn()
this.$emit('collect', !this.isCollected)
} catch (err) {
console.log('collect err', err)
}
},
onRegistrate() {
this.$utils.navigateTo(`/pages_order/product/productDetail?id=${this.data.id}`)
},
},
}
</script>
<style scoped lang="scss">
.product {
position: relative;
height: 464rpx;
background: #FFFFFF;
border: 2rpx solid #FFFFFF;
border-radius: 32rpx;
overflow: hidden;
font-size: 0;
&-img {
width: 100%;
height: 220rpx;
}
&-info {
height: 244rpx;
padding: 16rpx 16rpx 24rpx 16rpx;
box-sizing: border-box;
justify-content: space-between;
&-top {
width: 100%;
}
&-bottom {
width: 100%;
justify-content: space-between;
}
}
&-name {
font-size: 28rpx;
font-weight: 500;
color: #000000;
}
&-desc {
margin-top: 8rpx;
font-size: 24rpx;
color: #8B8B8B;
}
&-detail {
}
&-price {
justify-content: flex-start;
align-items: baseline;
column-gap: 12rpx;
flex-wrap: wrap;
&-val {
font-size: 24rpx;
font-weight: 500;
color: #FF4800;
white-space: nowrap;
.highlight {
font-size: 32rpx;
}
}
&-bef {
text-decoration: line-through;
font-size: 24rpx;
color: #8B8B8B;
}
}
&-registered {
font-size: 24rpx;
color: #8B8B8B;
}
.btn {
padding: 11rpx 32rpx;
font-size: 26rpx;
font-weight: 500;
color: #FFFFFF;
background: #00A9FF;
border-radius: 24rpx;
white-space: nowrap;
}
&.small {
.btn {
padding: 11rpx 16rpx;
}
}
}
.btn.btn-collect {
position: absolute;
top: 0;
right: 0;
row-gap: 8rpx;
// width: 112rpx;
height: 100%;
font-size: 24rpx;
line-height: 1;
color: #FFFFFF;
// background: #FF9035;
border-radius: 0;
}
</style>

+ 0
- 107
components/product/sortBar.vue View File

@ -1,107 +0,0 @@
<template>
<view class="flex sort" :style="style">
<view :class="['flex', 'sort-item', sort == 'comprehensive' ? 'is-active' : '']" @click="onClickSort('comprehensive')">综合</view>
<view :class="['flex', 'sort-item', ['sale-asc', 'sale-desc'].includes(sort) ? 'is-active' : '']" @click="onClickSort('sale')">
<view>销量</view>
<view class="sort-item-icon">
<uv-icon v-if="sort == 'sale-asc'" name="arrow-up-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<uv-icon v-else-if="sort == 'sale-desc'" name="arrow-down-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<image v-else style="width: 8rpx; height: auto;" src="/static/image/icon-sort.png" mode="widthFix"></image>
</view>
</view>
<view :class="['flex', 'sort-item', ['price-asc', 'price-desc'].includes(sort) ? 'is-active' : '']" @click="onClickSort('price')">
<view>价格</view>
<view class="sort-item-icon">
<uv-icon v-if="sort == 'price-asc'" name="arrow-up-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<uv-icon v-else-if="sort == 'price-desc'" name="arrow-down-fill" color="#00A9FF" size="16rpx" :bold="true"></uv-icon>
<image v-else style="width: 8rpx; height: auto;" src="/static/image/icon-sort.png" mode="widthFix"></image>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
value: {
type: String,
default: 'comprehensive'
},
style: {
type: String,
default: ''
}
},
computed: {
sort: {
set(val) {
this.$emit('input', val)
},
get() {
return this.value
}
}
},
methods: {
onClickSort(key) {
let sort = 'comprehensive'
switch(key) {
case 'comprehensive':
sort = 'comprehensive'
break;
case 'sale':
if (this.sort == 'sale-desc') {
sort = 'sale-asc'
} else {
sort = 'sale-desc'
}
break;
case 'price':
if (this.sort == 'price-desc') {
sort = 'price-asc'
} else {
sort = 'price-desc'
}
break;
default:
break;
}
this.sort = sort
this.$emit('change', sort)
},
},
}
</script>
<style scoped lang="scss">
.sort {
width: 100%;
justify-content: space-between;
&-item {
padding: 12rpx 32rpx;
font-size: 28rpx;
line-height: 1.5;
color: #191919;
column-gap: 4rpx;
&.is-active {
font-weight: 600;
color: #00A9FF;
}
&-icon {
width: 32rpx;
display: inline-flex;
align-items: center;
justify-content: center;
}
}
}
</style>

+ 93
- 0
components/progressSegment.vue View File

@ -0,0 +1,93 @@
<template>
<div class="progress">
<div class="progress-item flex" v-for="(item, index) in arr" :key="`${code}-${index}`"
:style="getStyle(item.index)"
>
{{ item.text }}
</div>
<div class="progress-text" :style="textStyle">
<div class="flex" v-for="(item, index) in arr" :key="`${code}-text-${index}`" >
{{ item.text }}
</div>
</div>
</div>
</template>
<script>
const WIDTH = 40;
export default {
props: {
value: {
default: 0
}
},
data() {
return {
code: Math.floor(Math.random() * 100).toString()
}
},
computed: {
current() {
return Math.floor(this.value / 20)
},
arr() {
let arr = new Array(this.current).fill(1).map((item, index) => {
return {
text: `${(index + 1) * 20}%`,
index,
}
})
return arr.reverse()
},
textStyle() {
const n = this.arr.length
const width = WIDTH * n
return `width: ${width}rpx; grid-template-columns: repeat(${n}, 1fr);`
},
},
methods: {
getStyle(index) {
const width = WIDTH * (index + 1)
return `width: ${width}rpx;`
},
},
}
</script>
<style lang="scss" scoped>
.progress {
position: relative;
font-size: 0;
width: 100%;
box-sizing: border-box;
&-item {
position: absolute;
top: 0;
left: 0;
padding: 2rpx 0;
font-size: 6rpx;
color: transparent;
background: #ee751c;
border: 2rpx solid #FFFFFF;
border-radius: 20rpx;
&:first-child {
}
}
&-text {
position: absolute;
display: grid;
padding: 2rpx 0;
font-size: 6rpx;
color: #FFFFFF;
background: transparent;
border: 2rpx solid transparent;
}
}
</style>

+ 0
- 7
main.js View File

@ -18,13 +18,6 @@ import mixinConfigList from '@/mixins/configList.js'
Vue.mixin(mixinConfigList)
//组件注册
import configPopup from '@/components/config/configPopup.vue'
import navbar from '@/components/base/navbar.vue'
Vue.component('configPopup',configPopup)
Vue.component('navbar',navbar)
const app = new Vue({
...App,
store,


+ 1
- 0
package.json View File

@ -14,6 +14,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@wtto00/html2canvas": "^1.4.3",
"ali-oss": "^6.21.0",
"dayjs": "^1.11.12",
"html2canvas": "^1.4.1",


+ 172
- 135
pages/index/index.vue View File

@ -4,161 +4,168 @@
<div class="card">
<div class="report" :style="style">
<!-- 01 -->
<view class="export">
<div class="export">
<img class="img fg" :src="configList.report_page_01" crossorigin=anonymous />
</view>
</div>
<!-- 02 -->
<view class="export">
<div class="export">
<img class="img fg" :src="configList.report_page_02" crossorigin=anonymous />
</view>
</div>
<!-- 03 -->
<view class="export">
<div class="export">
<img class="img" :src="configList.report_page_03" crossorigin=anonymous />
<img class="img fg" style="top: 122rpx; left: 144rpx; width: 132rpx; height: 165rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<div class="text" style="top: 128rpx; left: 330rpx;">姓名</div>
<div class="text" style="top: 128rpx; left: 476rpx;">性别</div>
<div class="text" style="top: 192rpx; left: 330rpx;">年龄</div>
<div class="text" style="top: 192rpx; left: 476rpx;">学段</div>
<div class="text" style="top: 256rpx; left: 330rpx;">学校</div>
<div class="text" style="top: 300rpx; left: 190rpx;">城市</div>
<div class="text" style="top: 344rpx; left: 220rpx;">出勤情况</div>
</view>
<img class="img fg" style="top: 76rpx; left: 90rpx; width: 82rpx; height: 104rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<div class="text" style="top: 76rpx; left: 206rpx;">姓名</div>
<div class="text" style="top: 76rpx; left: 298rpx;">性别</div>
<div class="text" style="top: 116rpx; left: 206rpx;">年龄</div>
<div class="text" style="top: 116rpx; left: 298rpx;">学段</div>
<div class="text" style="top: 156rpx; left: 206rpx;">学校</div>
<div class="text" style="top: 180rpx; left: 118rpx;">城市</div>
<div class="text" style="top: 208rpx; left: 138rpx;">出勤情况</div>
</div>
<!-- 04 -->
<view class="export">
<img class="img fg" style="top: 112rpx; left: 193rpx; width: 414rpx; height: 246rpx; border-radius: 12rpx; overflow: hidden;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 70rpx; left: 120rpx; width: 258rpx; height: 154rpx; border-radius: 8rpx; overflow: hidden;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_04" crossorigin=anonymous />
</view>
</div>
<!-- 05 -->
<view class="export">
<img class="img fg" style="top: 344rpx; left: 100rpx; width: 96rpx; height: 96rpx; transform: rotate(14deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 278rpx; left: 20rpx; width: 96rpx; height: 96rpx; transform: rotate(-7deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 49rpx; left: 92rpx; width: 118rpx; height: 118rpx; transform: rotate(15deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 134rpx; left: 138rpx; width: 118rpx; height: 118rpx; transform: rotate(-6deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 194rpx; left: 316rpx; width: 134rpx; height: 134rpx; transform: rotate(-14deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 280rpx; left: 430rpx; width: 134rpx; height: 134rpx; transform: rotate(14deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 40rpx; left: unset; right: -38rpx; width: 213rpx; height: 213rpx; transform: rotate(14deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 216rpx; left: 62rpx; width: 60rpx; height: 60rpx; transform: rotate(14deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 170rpx; left: 12rpx; width: 60rpx; height: 60rpx; transform: rotate(-7deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 30rpx; left: 56rpx; width: 74rpx; height: 74rpx; transform: rotate(15deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 80rpx; left: 84rpx; width: 74rpx; height: 74rpx; transform: rotate(-6deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 120rpx; left: 196rpx; width: 84rpx; height: 84rpx; transform: rotate(-14deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 174rpx; left: 262rpx; width: 84rpx; height: 84rpx; transform: rotate(14deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 26rpx; left: unset; right: -24rpx; width: 134rpx; height: 134rpx; transform: rotate(14deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_05" crossorigin=anonymous />
</view>
<div class="text text-5" style="top: 228rpx; left: 6rpx; transform: rotate(-7deg);">标签1</div>
<div class="text text-5" style="top: 160rpx; left: 136rpx; transform: rotate(7deg);">标签2</div>
<div class="text text-5" style="top: 266rpx; left: 318rpx; transform: rotate(7deg);">标签3</div>
<div class="text text-5" style="top: 42rpx; left: unset; right: 100rpx; transform: rotate(-7deg);">标签4</div>
</div>
<!-- 06 -->
<view class="export">
<img class="img fg" style="top: 190rpx; left: 146rpx; width: 126rpx; height: 80rpx; transform: skew(50deg, -34deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 176rpx; left: 390rpx; width: 110rpx; height: 96rpx; transform: skew(50deg, 317deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 120rpx; left: 276rpx; width: 110rpx; height: 74rpx; transform: skew(50deg, -34deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 280rpx; left: 260rpx; width: 148rpx; height: 96rpx; transform: skew(54deg, 324deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 118rpx; left: 92rpx; width: 78rpx; height: 50rpx; transform: skew(50deg, -34deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 110rpx; left: 244rpx; width: 68rpx; height: 60rpx; transform: skew(50deg, 317deg);" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 76rpx; left: 173rpx; width: 68rpx; height: 46rpx; transform: skew(50deg, -34deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 176rpx; left: 162rpx; width: 92rpx; height: 60rpx; transform: skew(54deg, 324deg);" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_06" crossorigin=anonymous />
</view>
</div>
<!-- 07 -->
<view class="export">
<img class="img fg" style="top: 86rpx; left: 168rpx; width: 154rpx; height: 118rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 86rpx; left: 326rpx; width: 154rpx; height: 118rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 86rpx; left: 478rpx; width: 154rpx; height: 118rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 244rpx; left: 138rpx; width: 226rpx; height: 154rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 244rpx; left: 402rpx; width: 226rpx; height: 154rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 54rpx; left: 106rpx; width: 96rpx; height: 74rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 54rpx; left: 204rpx; width: 96rpx; height: 74rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 54rpx; left: 300rpx; width: 96rpx; height: 74rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 152rpx; left: 84rpx; width: 142rpx; height: 96rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 152rpx; left: 250rpx; width: 142rpx; height: 96rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_07" crossorigin=anonymous />
</view>
</div>
<!-- 08 -->
<view class="export">
<img class="img fg" style="top: 76rpx; left: 112rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 194rpx; left: 112rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 76rpx; left: 242rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 194rpx; left: 242rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 312rpx; left: 242rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 138rpx; left: 372rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 258rpx; left: 372rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 194rpx; left: 504rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 312rpx; left: 504rpx; width: 110rpx; height: 110rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 46rpx; left: 68rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 118rpx; left: 68rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 46rpx; left: 148rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 118rpx; left: 148rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 192rpx; left: 148rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 86rpx; left: 228rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 158rpx; left: 228rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 118rpx; left: 310rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 192rpx; left: 310rpx; width: 70rpx; height: 70rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_08" crossorigin=anonymous />
</view>
</div>
<!-- 09 -->
<view class="export">
<div class="export">
<img class="img fg" style="top: 0; left: unset; right: 0; width: auto; height: 100%;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="width: 260rpx; height: 180rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="width: 162rpx; height: 112rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img img-08 fg" :src="configList.report_page_09" crossorigin=anonymous />
</view>
</div>
<!-- 10 -->
<view class="export">
<img class="img fg" style="top: 126rpx; left: 106rpx; width: 158rpx; height: 106rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 132rpx; left: 450rpx; width: 148rpx; height: 100rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 132rpx; left: 604rpx; width: 90rpx; height: 100rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 238rpx; left: 54rpx; width: 210rpx; height: 140rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 242rpx; left: 450rpx; width: 122rpx; height: 80rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 242rpx; left: 576rpx; width: 120rpx; height: 200rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 326rpx; left: 270rpx; width: 156rpx; height: 106rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 68rpx; left: 274rpx; width: 170rpx; height: 256rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 78rpx; left: 64rpx; width: 98rpx; height: 66rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 80rpx; left: 276rpx; width: 94rpx; height: 64rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 80rpx; left: 372rpx; width: 56rpx; height: 62rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 148rpx; left: 32rpx; width: 132rpx; height: 88rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 148rpx; left: 276rpx; width: 76rpx; height: 50rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 148rpx; left: 356rpx; width: 75rpx; height: 125rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 202rpx; left: 166rpx; width: 98rpx; height: 66rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 42rpx; left: 166rpx; width: 108rpx; height: 160rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_10" crossorigin=anonymous />
</view>
</div>
<!-- 11 -->
<view class="export">
<img class="img fg" style="top: unset; bottom: 268rpx; left: 54rpx; width: 118rpx; height: 80rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: unset; bottom: 268rpx; left: 180rpx; width: 78rpx; height: 110rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: unset; bottom: 268rpx; left: 262rpx; width: 210rpx; height: 160rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 240rpx; left: 54rpx; width: 170rpx; height: 142rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 240rpx; left: 380rpx; width: 118rpx; height: 80rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: unset; bottom: 164rpx; left: 34rpx; width: 74rpx; height: 50rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: unset; bottom: 164rpx; left: 112rpx; width: 48rpx; height: 70rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: unset; bottom: 164rpx; left: 164rpx; width: 135rpx; height: 100rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 148rpx; left: 34rpx; width: 106rpx; height: 88rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 148rpx; left: 232rpx; width: 74rpx; height: 50rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_11" crossorigin=anonymous />
</view>
</div>
<!-- 12 -->
<view class="export">
<img class="img fg" style="top: 130rpx; left: 122rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 130rpx; left: 224rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 130rpx; left: 328rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 130rpx; left: 432rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 130rpx; left: 536rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 264rpx; left: 122rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 264rpx; left: 224rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 264rpx; left: 328rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 264rpx; left: 432rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 264rpx; left: 536rpx; width: 80rpx; height: 100rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 82rpx; left: 76rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 82rpx; left: 140rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 82rpx; left: 206rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 82rpx; left: 270rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 82rpx; left: 336rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 166rpx; left: 76rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 166rpx; left: 140rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 166rpx; left: 206rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" style="top: 166rpx; left: 270rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" style="top: 166rpx; left: 336rpx; width: 50rpx; height: 62rpx;" src="@/static/image/temp-14.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_12" crossorigin=anonymous />
</view>
</div>
<!-- 13 -->
<view class="export">
<div class="export">
<img class="img" :src="configList.report_page_13" crossorigin=anonymous />
<div class="text text-12" style="top: 134rpx; left: 160rpx;">技能01</div>
<div class="text text-12" style="top: 134rpx; left: 260rpx;">技能01描述</div>
<div class="text text-12" style="top: 182rpx; left: 160rpx;">技能02</div>
<div class="text text-12" style="top: 182rpx; left: 260rpx;">技能02描述</div>
<div class="text text-12" style="top: 232rpx; left: 160rpx;">技能03</div>
<div class="text text-12" style="top: 232rpx; left: 260rpx;">技能03描述</div>
<div class="text text-12" style="top: 282rpx; left: 160rpx;">技能04</div>
<div class="text text-12" style="top: 282rpx; left: 260rpx;">技能04描述</div>
<div class="text text-12" style="top: 330rpx; left: 160rpx;">技能05</div>
<div class="text text-12" style="top: 330rpx; left: 260rpx;">技能05描述</div>
</view>
<div class="text text-12" style="top: 84rpx; left: 100rpx;">技能01</div>
<progressSegment class="progress" style="top: 80rpx; left: 162rpx;" :value="20"></progressSegment>
<div class="text text-12" style="top: 114rpx; left: 100rpx;">技能02</div>
<progressSegment class="progress" style="top: 112rpx; left: 162rpx;" :value="40"></progressSegment>
<div class="text text-12" style="top: 146rpx; left: 100rpx;">技能03</div>
<progressSegment class="progress" style="top: 144rpx; left: 162rpx;" :value="60"></progressSegment>
<div class="text text-12" style="top: 176rpx; left: 100rpx;">技能04</div>
<progressSegment class="progress" style="top: 174rpx; left: 162rpx;" :value="80"></progressSegment>
<div class="text text-12" style="top: 206rpx; left: 100rpx;">技能05</div>
<progressSegment class="progress" style="top: 204rpx; left: 162rpx;" :value="100"></progressSegment>
</div>
<!-- 14 -->
<view class="export">
<div class="export">
<img class="img fg" :src="configList.report_page_14" crossorigin=anonymous />
<div class="text text-13" style="top: 150rpx; left: 116rpx;">同学寄语同学寄语同学寄语同学寄语同学寄语同学寄语同学寄语同学寄语</div>
<div class="text text-13" style="top: 260rpx; left: 400rpx;">导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语</div>
</view>
<div class="text text-13" style="top: 94rpx; left: 74rpx;">同学寄语同学寄语同学寄语同学寄语同学寄语同学寄语同学寄语同学寄语</div>
<div class="text text-13" style="top: 162rpx; left: 248rpx;">导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语导师寄语</div>
</div>
<!-- 15 -->
<view class="export">
<img class="img fg" style="top: 46rpx; left: unset; right: 30rpx; width: 506rpx; height: 390rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<div class="export">
<img class="img fg" style="top: 28rpx; left: unset; right: 18rpx; width: 316rpx; height: 244rpx;" src="@/static/image/temp-20.png" crossorigin=anonymous />
<img class="img fg" :src="configList.report_page_15" crossorigin=anonymous />
</view>
</div>
<!-- 00 -->
<view class="export">
<div class="export">
<img class="img fg" :src="configList.report_page_00" crossorigin=anonymous />
</view>
</div>
</div>
</div>
<img v-if="current > 0" class="img turn left" src="@/static/image/icon-left.png" @click="prev" />
<img v-if="current < 15" class="img turn right" src="@/static/image/icon-right.png" @click="next" />
<div class="flex bottom">
<button class="btn" @click="createPdf">生成pdf</button>
</div>
<!-- <img v-if="current > 0" class="img turn left" src="@/static/image/icon-left.png" @click="prev" />
<img v-if="current < 15" class="img turn right" src="@/static/image/icon-right.png" @click="next" /> -->
</div>
</template>
<script>
import html2canvas from 'html2canvas'
// import html2canvas from 'html2canvas'
import html2canvas from '@wtto00/html2canvas'
import { jsPDF } from "jspdf";
import progressSegment from '@/components/progressSegment.vue';
export default {
components: {
progressSegment,
},
data() {
return {
mode: null,
id: null,
details: {},
current: 0,
@ -166,8 +173,7 @@
},
computed: {
style() {
return `transform: translateX(calc(-${this.current}*(100vw - 24rpx * 2)))`
// return `transform: translateX(calc(-${this.current}*(100vh * 841.89 / 595.28)))`
},
},
onLoad(arg) {
@ -178,15 +184,24 @@
});
})
const { id, token } = arg
const { id, mode, token } = arg
token && uni.setStorageSync('token', token)
this.id = id
this.mode = mode
},
mounted() {
this.getData(this.id)
async mounted() {
await this.getData(this.id)
console.log('mode', this.mode)
if (this.mode === 'export') {
// todo: await load image
setTimeout(() => {
this.createPdf()
}, 3000)
}
},
methods: {
getData(id) {
async getData(id) {
// todo
},
prev() {
@ -253,11 +268,14 @@
// PDF Blob
const pdfBlob = PDF.output('blob');
return this.upload(pdfBlob)
}).then(url => {
}).then(pdfUrl => {
this.$jWeixin.miniProgram.postMessage({
data: url
data: pdfUrl
});
this.$jWeixin.miniProgram.navigateBack()
// this.$jWeixin.miniProgram.navigateBack()
this.$jWeixin.miniProgram.redirectTo({
url: `/pages_order/growing/activity/applyEmail?id=${this.id}&pdfUrl=${pdfUrl}`
})
}).catch(err => {
console.log('err', err)
}).finally(() => {
@ -272,8 +290,10 @@
<style lang="scss" scoped>
$pages: 16;
$w: calc(100vw - 24rpx * 2);
$h: calc(#{$w} / 841.89 * 595.28);
// $w: calc(100vw - 24rpx * 2);
// $h: calc(#{$w} / 841.89 * 595.28);
$h: 100vh;
$w: calc(#{$h} * 841.89 / 595.28);
.page__view {
width: 100vw;
@ -287,20 +307,26 @@
}
.card {
position: absolute;
top: 50vh;
transform: translateY(-50%);
width: $w;
margin: 0 24rpx;
padding: 250rpx 0;
overflow: hidden;
background: #FFFFFF;
// padding: 0 calc((100vw - #{$w}) / 2);
overflow-x: auto;
// position: absolute;
// top: 50vh;
// transform: translateY(-50%);
// width: $w;
// margin: 0 24rpx;
// padding: 250rpx 0;
// overflow: hidden;
// background: #FFFFFF;
}
.report {
width: calc(#{$pages}*#{$w});
height: auto;
overflow-x: visible;
height: 100vh;
// overflow-x: visible;
padding: 0 calc((100vw - #{$w}) / 2);
// overflow-x: auto;
overflow-y: hidden;
}
.export {
@ -321,6 +347,7 @@
display: block;
width: $w;
height: auto;
object-fit: cover;
&-08 {
width: auto;
@ -330,23 +357,33 @@
.text {
position: absolute;
font-size: 20rpx;
font-size: 16rpx;
font-weight: 600;
color: #FFFFFF;
&-5 {
font-size: 8rpx;
color: #000000;
}
&-12 {
font-size: 14rpx;
font-size: 8rpx;
color: #000000;
}
&-13 {
width: 206rpx;
width: 118rpx;
white-space: pre-wrap;
font-size: 14rpx;
font-size: 8rpx;
color: #000000;
}
}
.progress {
position: absolute;
width: 200rpx;
}
// .export__view {
// position: fixed;
// top: 100vh;


Loading…
Cancel
Save