html
<view class="container">
<web-view wx:if="{{showWebview}}" src="{{templateUrl}}" bindload="onWebViewLoad">
<cover-view class="floating-btn-container">
<cover-view class="btn-content">
<cover-view class="btn" bindtap="bindFile">
<cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/download_icon.png" />
<cover-view class="btn-text">保存至附件</cover-view>
<cover-view class="divider"></cover-view>
</cover-view>
<cover-view class="btn" bindtap="handleBack">
<cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/detail_icon.png" />
<cover-view class="btn-text">更换样式</cover-view>
<cover-view class="divider"></cover-view>
</cover-view>
<cover-view class="btn" bindtap="uploadFile">
<cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/upload_icon.png" />
<cover-view class="btn-text">上传简历</cover-view>
<cover-view class="divider"></cover-view>
</cover-view>
<cover-view class="btn btn-last" bindtap="goToEdit">
<cover-image class="btn-icon" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/edit_icon.png" />
<cover-view class="btn-text">编辑</cover-view>
</cover-view>
</cover-view>
</cover-view>
</web-view>
<cover-view class="tpl-content" wx:if="{{show}}">
<cover-view class="menu-overlay">
<cover-view class="menu-content" style="transform: translateX({{translateX}}px)" bind:touchstart="onTouchStart" bind:touchmove="onTouchMove">
<block wx:if="{{isIOS}}">
<cover-view wx:for="{{pdfList}}" wx:key="{{item.id}}" class="items" data-id="{{item.id}}" data-imgUrl="{{item.img_src}}" id="item-{{item.id}}" data-title="{{item.name}}" bind:tap="choose">
<cover-image class="items-image" src="{{item.img_src}}" mode="" />
<cover-view class="items-text">{{item.name}}</cover-view>
<cover-image wx:if="{{item.id==id}}" class="choose-image" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/choose-icon.png" mode="" />
<cover-image wx:else class="choose-image" src="//static.solarbe.com/talentNetwork/miniProgram/resume_preview/no-choose-icon.png" mode="" />
</cover-view>
</block>
<block wx:else>
<cover-view wx:for="{{pdfList}}" wx:key="{{item.id}}" id="item-{{item.id}}" class="items {{item.id==id?'selected-box': 'un-selected-box'}}" data-id="{{item.id}}" data-imgUrl="{{item.img_src}}" data-title="{{item.name}}" bind:tap="choose">
<cover-image class="items-image" src="{{item.img_src}}" mode="" />
<cover-view class="items-text">{{item.name}}</cover-view>
</cover-view>
</block>
</cover-view>
</cover-view>
<cover-view class="tpl-btn" bind:tap="chooseClose">使用此模版</cover-view>
</cover-view>
</view>
css
.mask {
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, .3);
width: 100%;
height: 100%;
}
/* 容器样式 */
.container {
height: 100vh;
background: #f3f3f3;
}
/* 悬浮按钮容器 */
.floating-btn-container {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
z-index: 9999;
background: #f3f3f3;
padding: 30rpx 0;
}
/* 按钮内容区域 */
.btn-content {
display: flex;
justify-content: space-around;
/* 横向均匀分布 */
align-items: center;
width: 90%;
height: 120rpx;
/* 增加高度容纳图标+文字 */
margin: 0 auto;
background: #fff;
border-radius: 56rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
padding: 0 40rpx;
box-sizing: border-box;
}
.btn {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 25%;
}
.btn-last {
width: 20%;
}
/* 图标样式 */
.btn-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 8rpx;
/* 图标与文字间距 */
}
/* 文字样式 */
.btn-text {
font-size: 24rpx;
color: #333;
white-space: nowrap;
/* 防止文字换行 */
}
.divider {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 1rpx;
height: 60%;
/* 控制竖线高度为按钮区域的60% */
background: #e5e5e5;
}
.items {
flex-shrink: 0;
/* 禁止收缩 */
display: flex;
flex-direction: column;
position: relative;
width: 240rpx;
/* 与图片宽度一致 */
margin-right: 10rpx;
}
.choose-image {
width: 32rpx;
height: 34rpx;
position: absolute;
z-index: 10002;
right: 20rpx;
top: 20rpx;
}
.selected-box {
border: 2px solid #1787FB !important;
border-radius: 8rpx;
}
.un-selected-box {
border: 0px solid #1787FB !important;
border-radius: 8rpx;
}
.ios-style {
border: none;
border-radius: 0;
}
.items-image {
width: 240rpx;
height: 360rpx;
}
.items-text {
font-size: 24rpx;
color: #333333;
line-height: 34rpx;
text-align: center;
}
.tpl-content {
position: fixed;
left: 0;
bottom: 0;
height: 610rpx;
background: rgba(205, 231, 255, 1);
z-index: 10000;
width: 100%;
}
.tpl-btn {
height: 88rpx;
background: #1787FB;
border-radius: 8rpx;
width: 90%;
font-weight: 500;
font-size: 28rpx;
color: #FFFFFF;
line-height: 88rpx;
text-align: center;
position: fixed;
bottom: 60rpx;
left: 50%;
transform: translate(-50%, 0);
width: 90%;
z-index: 10001;
}
/* 覆盖层容器 */
.menu-overlay {
position: absolute;
top: 0;
visibility: visible !important;
overflow: scroll;
left: 0;
height: 450rpx;
padding: 30rpx 30rpx 0rpx 30rpx;
z-index: 10001;
}
/* 菜单内容容器 */
.menu-content {
display: flex;
flex-direction: row;
width: 100%;
height: 400rpx;
padding-bottom: 10rpx;
transition: transform 0.3s;
will-change: transform; /* 提前声明动画属性 */
-webkit-overflow-scrolling: touch; /* 启用 iOS 弹性滚动 */
transform: translateZ(0); /* 强制开启 GPU 加速 */
/* 平滑动画 */
}
js
var $ = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
showWebview: true,
resumeUrl: '',
title: '',
id: '0', //模版id
resumeId: 0, //简历id
loading: !1,
pdfList: [],
templateUrl: '',
show: false,
translateX: 0,
maxScroll: 0,
startX: 0,
currentIndex: 0,
lastUpdate: 0,
isIOS: false,
currentX: 0
},
onTouchStart(e) {
this.setData({
startX: this.data.isIOS ? e.touches[0].clientX : e.touches[0].pageX,
currentX: this.data.translateX
});
},
onTouchMove(e) {
const deltaX = (this.data.isIOS ? e.touches[0].clientX : e.touches[0].pageX) - this.data.startX;
let newTranslateX = this.data.currentX + deltaX;
// 限制滚动范围(示例值需根据内容宽度调整)
const maxScroll = -(140 * this.data.pdfList.length - 375)
newTranslateX = Math.min(0, Math.max(maxScroll, newTranslateX))
// 节流更新频率(每 16ms 更新一次)
if (Date.now() - this.data.lastUpdate > 16) {
this.setData({
translateX: newTranslateX,
lastUpdate: Date.now()
})
}
},
onLoad(options) {
const systemInfo = wx.getSystemInfoSync()
const isIOS = systemInfo.system.includes('iOS')
this.setData({
isIOS: isIOS
})
this.getMakePDF()
if (options?.id) {
this.setData({
id: options.id
})
}
if (options?.resumeId) {
this.setData({
resumeId: options.resumeId
})
}
if (options?.title) {
this.setData({
title: options.title
})
}
this.setData({
templateUrl: url
})
this.getResumePdfUrl()
// 动态计算高度(示例:屏幕高度 - 按钮区域高度)
const buttonHeight = 60; // 根据实际按钮高度调整
this.setData({
webviewHeight: systemInfo.windowHeight - buttonHeight
});
},
centerSelectedItem() {
wx.createSelectorQuery()
.select(`#item-${this.data.id}`)
.boundingClientRect(res => {
if (res) {
const screenWidth = wx.getSystemInfoSync().screenWidth
const translateX = screenWidth / 2 - res.left - res.width / 2
this.setData({
translateX
})
}
}).exec()
},
getMakePDF(type) {
$.http('home/resume_tpl/getTplList', (r) => {
this.setData({
pdfList: r.data
}, () => {
setTimeout(() => {
// this.centerSelectedItem()
}, 200)
})
if (type == 'init') {
this.setData({
show: true
})
}
})
},
onWebViewLoad() {
// 再次强制设置标题
wx.setNavigationBarTitle({
title: this.data.title + "简历模版-预览"
});
},
getResumePdfUrl() {
let params = {
uid: $.visitor.uid,
tpl_id: this.data.id
},
params2 = {
id: this.data.resumeId,
is_export: 0
}
let url = this.data.id == -1 ? 'home/resume/exportPdfByPhp' : 'home/poster_two/makePdf'
if (this.data.id == -1) {
wx.showLoading({
title: '加载中...',
})
}
$.http(url, this.data.id == -1 ? params2 : params, (r) => {
if (this.data.id == -1) {
wx.downloadFile({
url: r.data.url, // 文件的本身url
filePath: wx.env.USER_DATA_PATH + "/" + this.data.title, // 本地自定义的文件名
success: function (res) {
let filePath = res.filePath; // 微信临时文件路径(这里要使用自定义的名字文件名,否则打开的文件名是乱码)
wx.openDocument({
filePath: filePath,
fileType: 'pdf',
showMenu: true, // 是否显示右上角菜单按钮 默认为false(看自身需求,可要可不要。后期涉及到右上角分享功能)
success: function () {
wx.hideLoading()
},
fail: function (e) {
wx.hideLoading()
$.msg(0, '网络繁忙,请稍后重试~')
}
});
},
fail: function (r) {
wx.hideLoading()
$.msg(0, '网络繁忙,请稍后重试~')
}
});
return
}
this.setData({
resumeUrl: r.data
})
}, 'post')
},
choose(e) {
let id = e.currentTarget.dataset.id,
title = e.currentTarget.dataset.title
wx.setNavigationBarTitle({
title: title + "简历模版-预览"
})
this.setData({
id,
title
}, () => {
this.setData({})
})
console.log(this.data.id)
},
chooseClose() {
this.setData({
showWebview: false
}, () => {
setTimeout(() => {
this.setData({
show: false,
showWebview: true,
templateUrl: `https://www.hsolar.com/member/resume/template/index${this.data.id}?id=${$.visitor.uid}`
})
}, 200)
this.getResumePdfUrl()
})
},
// 换样式
handleBack() {
if (this.data.pdfList.length) {
this.setData({
show: true
})
} else {
this.getMakePDF('init')
}
},
// 跳转
goToEdit() {
wx.redirectTo({
url: '/pages/personal/resume_edit/resume_edit',
})
},
// 上传附件
uploadFile() {
wx.navigateTo({
url: '/subPackages/pages/personal/resume_accessory/resume_accessory',
})
},
detail(f) {
$.http('personal/resume_two/detail', function (r) {
let d = r.data
f && f(d)
})
},
bindFile() {
wx.showLoading({
title: '生成中',
})
this.setData({
loading: !0
})
let that = this
wx.downloadFile({
url: this.data.resumeUrl, // 文件的本身url
filePath: wx.env.USER_DATA_PATH + "/" + this.data.title, // 本地自定义的文件名
success: function (res) {
let filePath = res.filePath; // 微信临时文件路径(这里要使用自定义的名字文件名,否则打开的文件名是乱码)
wx.openDocument({
filePath: filePath,
fileType: 'pdf',
showMenu: true, // 是否显示右上角菜单按钮 默认为false(看自身需求,可要可不要。后期涉及到右上角分享功能)
success: function () {
wx.hideLoading()
that.setData({
loading: !1
})
$.msg(0, '生成成功')
},
fail: function (e) {
wx.hideLoading()
that.setData({
loading: !1
})
$.msg(0, '网络繁忙,请稍后重试~')
}
});
},
fail: function (r) {
wx.hideLoading()
that.setData({
loading: !1
})
$.msg(0, '网络繁忙,请稍后重试~')
}
});
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// wx.onAppRoute((route) => {
// const pages = getCurrentPages();
// console.log(route, pages, '===')
// if (pages.length > 1) {
// // 覆盖左上角返回按钮行为
// // wx.enableAlertBeforeUnload({
// // message: '是否直接返回上一页?',
// // success: (res) => {
// // console.log(res,999)
// // if (res.errMsg=='enableAlertBeforeUnload:ok') {
// // wx.redirectTo({
// // url: '/subPackagesC/pages/resumeTetemplate/resumeTetemplate',
// // })
// // }
// // },
// // });
// }
// });
}
})