<template>
<view
@click="back"
class=""
:style="{
height: _windowHeight - _statusHeight + 'px',
paddingTop: _statusHeight + 'px'
}"
>
<view class="container"
id="close"
:style="{
maxHeight: _windowHeight - _statusHeight - 200 + 'px'
}">
<scroll-view scroll-y class="content">
<image-container ref="imageContainer">
<view v-if="!image">
<ShareImageNews v-if="id==='news'"></ShareImageNews>
</view>
<view v-else>
<image :src="image" style="width: 100%;min-height:700px;" mode="widthFix"></image>
</view>
</image-container>
</scroll-view>
</view>
<view
ref="platformContainer"
class="platformContainer"
:animation="animationData"
@click.stop="(e)=>e.stopPropagation()"
:style="{
width: _windowWidth,
'padding-bottom': _isIphoneX ? '20px' : 0
}">
<view class="platformList">
<view class="platformItem" @click.stop="(e)=>share(e, 1)">
<image mode="aspectFill" :src="require('@/static/images/share-save@3x.png')" class="platformImage" />
<text class="platformItemText">保存图片</text>
</view>
<view class="platformItem" @click.stop="(e)=>share(e, 2)">
<image mode="aspectFill" :src="require('@/static/images/share-wechat@3x.png')" class="platformImage" />
<text class="platformItemText">微信好友</text>
</view>
<view class="platformItem" @click.stop="(e)=>share(e, 3)">
<image mode="aspectFill" :src="require('@/static/images/share-moments@3x.png')" class="platformImage" />
<text class="platformItemText">朋友圈</text>
</view>
<view class="platformItem" @click.stop="(e)=>share(e, 4)">
<image mode="aspectFill" :src="require('@/static/images/share-qq@3x.png')" class="platformImage" />
<text class="platformItemText">QQ好友</text>
</view>
<!-- <view class="platformItem" @click.stop="(e)=>share(e, 5)">
<image mode="aspectFill" :src="require('@/static/images/share-weibo@3x.png')" class="platformImage" />
<text class="platformItemText">微博</text>
</view> -->
</view>
<view
class="btn"
hover-class="btnHover"
@click.stop="back"
>
<text class="btnText" id="close">取消</text>
</view>
</view>
<GlobalPermissionsInfo></GlobalPermissionsInfo>
</view>
</template>
<script>
//html2canvas
import ImageContainer from './ImageContainer.vue'
//卡片页面
import ShareImageNews from '@/pagesNew/NewsDetail/Base/ShareImage'
//打开应用/应用没安装
import { checkApp } from '@/utils/common'
//是否无存储权限/允许读写图片文件
import { photoCheck2, photoPermissionCheck2 } from '@/utils/check'
//隐私权限
import privacy from '@/config/privacy'
//申请授权
import androidPrivacy from '@/mixins/androidPrivacy'
//获取小程序全局权限设置信息的接口
import GlobalPermissionsInfo from '@/components/GlobalPermissionsInfo'
export default {
components: { ImageContainer, ShareImageNews, GlobalPermissionsInfo},
mixins: [androidPrivacy],
data () {
return {
imageData: '',
animationData: {},
animation: {},
id: '',
isImg: false,
image: ''
}
},
onLoad (option) {
this.id = getApp().globalData.shareInfo.id
this.image = option.image
console.log(this.image, '-test')
},
mounted () {
this.animation = uni.createAnimation({
duration: 400,
timingFunction: 'ease',
})
this.animation.translateY(-180).opacity(1).step()
this.animationData = this.animation.export()
},
computed:{
_windowWidth () {
return uni.$deviceInfo.windowWidth
},
_windowHeight () {
return uni.$deviceInfo.windowHeight
},
_statusHeight () {
return uni.$deviceInfo.statusBarHeight
},
_isIphoneX () {
let model = uni.$deviceInfo.model.toUpperCase()
if (model.indexOf('IPHONE X') >= 0 || model.indexOf('IPHONE11') >= 0) {
return true
} else {
return false
}
},
_hasNotchInScreen () {
// 是否为刘海屏
// #ifdef APP-PLUS
return plus.navigator.hasNotchInScreen()
// #endif
},
_isIos () {
return uni.$deviceInfo.platform === 'ios'
},
},
methods: {
back (e) {
if (e.target.id === 'close') {
uni.navigateBack({
delta: 1,
})
}
},
async downloadFile(image) {
try {
const data = await uni.downloadFile({url: image})
return data.tempFilePath
} catch (error) {
return ''
}
},
share (e, index) {
const type = (index === 2 || index === 3) ? 'weixin' : (index === 4) ? 'mqq' : index === 5 ? 'sinaweibo' : ''
const boo = checkApp(type)
if (!boo) return
uni.showLoading({})
setTimeout(async() => {
try {
const imageContainer = this.$refs.imageContainer
const filePath = !this.image ? await imageContainer.getImagePath() : await this.downloadFile(this.image)
let strProvider = ''
let strScene = ''
switch (index) {
case 2:
strProvider = 'weixin'
strScene = 'WXSceneSession'
break
case 3:
strProvider = 'weixin'
strScene = 'WXSceneTimeline'
break
case 4:
strProvider = 'qq'
strScene = 'WXSceneSession'
break
case 5:
strProvider = 'sinaweibo'
break
}
if (index === 1) {
// 权限判断 --start
const granted = await photoPermissionCheck2(() => {
uni.setStorageSync(privacy.FILES_SUB_SAVE_IMAGE, true)
})
if (granted === 2) {
photoCheck2()
uni.hideLoading()
return
}
const canSave = await this.getPrivacyItem(privacy.FILES_SUB_SAVE_IMAGE)
if (!canSave) {
const res = await this.requestPrivacyItem(privacy.FILES_SUB_SAVE_IMAGE, privacy.FILES)
if (!res) {
uni.hideLoading()
return
}
}
// 权限判断 --end
uni.saveImageToPhotosAlbum({
filePath: filePath,
success: function () {
uni.showToast({
title: '图片保存成功',
icon: '',
duration: 2000,
})
},
fail: function (error) {
if(error.code === 2) {
photoCheck2()
}
console.log('error = ', error)
},
})
} else {
// 纯图片分享不要传href;如果传了href默认按图文分享;
uni.share({
provider: strProvider, // 分享服务提供商(即weixin|qq|sinaweibo)
scene: strScene,
type: 2, // 分享形式
imageUrl: filePath, // 图片地址
success: (res) => {
console.log(res)
},
fail: (err) => {
console.log(err)
},
})
}
uni.hideLoading()
} catch(err) {
this.share(e, index)
}
}, 2000)
},
},
}
</script>
<style lang="scss" scoped>
.mask{
background: rgba(0,0,0,.6);
width: 100%;
height: 100%;
flex: 1;
}
.platformContainer{
position: absolute;
bottom: -180px;
background: #fff;
width: 100%;
padding: 14px 0;
opacity:0
}
.platformList{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.platformItem{
flex:1;
text-align: center;
padding: 0 10px;
image{
width: 50px;
height: 50px;
}
.platformItemText{
display: block;
font-size: 12px;
text-align: center;
}
}
.btn{
height: 44px;
width: 100%;
text-align: center;
margin-top: 30px;
margin-bottom: 10px;
}
.btnText{
display: block;
width: 100%;
height: 44px;
line-height: 44px;
text-align: center;
color: $uni-color-primary;
}
.container{
display: flex;
flex: 1;
padding:44px 20px;
}
.content{
flex: 1;
background: #fff;
}
</style>
<template>
<view>
<view
id="imageContainer"
class="imageContainer"
>
<slot></slot>
</view>
</view>
</template>
<script>
export default {
props: {
},
data () {
return {
imageData: '',
canvas: '',
filePath: '',
}
},
methods: {
setImageData (base64Data) {
this.imageData = base64Data.replace(/[\r\n]/g, '')
},
getImageDate () {
return this.imageData
},
//将 Base64 编码格式表示的图像数据(this.imageData)转换为实际的图像文件,
//并将其保存到设备上的指定路径下,最后返回该图像文件的保存路径
async getImagePath () {
//生成图像文件路径
const filePath = '_doc/' + new Date().getTime() + '.jpg'
//创建了一个plus.nativeObj.Bitmap类型的位图对象,用于后续加载和处理 Base64 编码的图像数据
const bitmap = new plus.nativeObj.Bitmap('imageTemp')
//因为整个图像保存的过程涉及到异步操作(如加载 Base64 数据、保存图像文件等)
//使用 Promise 处理异步操作
return new Promise((resolve, reject) => {
//调用位图对象的loadBase64Data方法,将Base64 编码图像数据加载到位图对象中
bitmap.loadBase64Data(this.imageData, () => {
// 加载成功后的处理逻辑
bitmap.save(filePath, {
overwrite: true,
quality: 100,
}, (i) => {
resolve(filePath)
bitmap.clear()
}, (err) => {
//保存失败,bitmap.clear()清理位图对象资源
reject(err)
bitmap.clear()
})
}, (err) => {
// 加载失败后的处理逻辑
reject(err)
bitmap.clear()
})
})
},
},
}
</script>
<script module="html2canvas" lang="renderjs">
export default {
mounted(){
//设备的像素比
console.log('-----html2canvas--------',window.devicePixelRatio )
//是否为函数来判断html2canvas库是否已经加载到页面中
if (typeof window.html2canvas === 'function') {
this.init()
} else {
// 动态引入较大类库避免影响页面展示
const script = document.createElement('script')
// view 层的页面运行在 www 根目录,其相对路径相对于 www 计算
script.src = 'static/scripts/html2canvas.min.js'
//将script.onload事件绑定到this.init.bind(this),这样当脚本成功加载后就会自动执行this.init()方法
script.onload = this.init.bind(this)
document.head.appendChild(script)
}
},
methods: {
async init() {
//使用定时器的目的是:等待元素完全加载完成后进行html2canvas操作,确保准别的将目标元素转化成图像
setTimeout(async ()=>{
let dpr = window.devicePixelRatio || 2
html2canvas(document.getElementById('imageContainer'),
{
allowTaint:true,
useCORS:true,
scale: dpr
})
//html2canvas函数返回一个Promise,当转换完成后,会执行then块中的内容
.then((canvas)=>{
console.log(canvas)
/*
renderjs和service层的通信
具体分为三部分:
1.在template中通过用户手动操作触发事件
2.在service层中调用方法
3.在renderjs中调用方法
从renderjs到service层:通过this.$ownerInstance.callMethod()方法可以调用service中的方法,第一个参数是方法名,第二个参数是传过去的参数*/
//canvas.toDataURL("image/jpeg", 1):将生成的canvas对象转换为image/jpeg格式的数据 URL
this.$ownerInstance.callMethod('setImageData', canvas.toDataURL("image/jpeg", 1))
}).catch((error)=>{
console.log('error11= ',error)
})
},1500)
},
}
}
</script>
<style lang="scss">
.imageContainer{
width: 100%;
background: #666;
}
</style>
export function checkApp (type) {
if (!type) return true
let boo = true
let text = ''
switch (type) {
case 'weixin':
//plus.runtime.isApplicationExist:用于检查指定的应用程序是否在设备上已经安装
boo = plus.runtime.isApplicationExist({ pname: 'com.tencent.mm', action: 'weixin://' })
text = '微信'
break
case 'mqq':
boo = plus.runtime.isApplicationExist({ pname: 'com.tencent.mobileqq', action: 'mqq://' })
text = 'QQ'
break
case 'sinaweibo':
text = '新浪微博'
boo = plus.runtime.isApplicationExist({ pname: 'com.sina.weibo', action: 'sinaweibo://' })
break
}
if (!boo) {
uni.showToast({
title: `${text}应用未安装`,
icon: 'none',
})
}
return boo
}
//下载本插件(App权限判断和提示)并存放到项目目录下
import permision from '@/js_sdk/wa-permission/permission.js'
export async function photoPermissionCheck2 (callback) {
if (plus.os.name !== 'Android') { return Promise.resolve(1) }
const storageCheck = await permision.checkPermission('WRITE_EXTERNAL_STORAGE')
const datetime = await uni.getStorageSync('WRITE_EXTERNAL_STORAGE_DATA')
let granted
if (!storageCheck) {
if ((!datetime || (new Date().getTime() - parseInt(datetime)) > 24 * 3600 * 1000)) {
await uni.setStorageSync('WRITE_EXTERNAL_STORAGE_DATA', new Date().getTime() + '')
uni.$emit('show_global_permissions_info', {
visible: true,
message: '存储权限使用说明:允许读取或者写入图片、文件等内容,用于用户下载/保存图片或修改头像',
})
//获取Android设备上当前App是否有某项权限
//WRITE_EXTERNAL_STORAGE:外部存储(含相册)写入权限
granted = await permision.requestAndroidPermission('android.permission.WRITE_EXTERNAL_STORAGE')
uni.$emit('show_global_permissions_info', {
visible: false,
})
if(granted === 1){
callback && callback()
}else{
return Promise.resolve(2)
}
} else {
return Promise.resolve(2)
}
}
if (granted === 0 || granted === -1) {
return Promise.resolve(0)
}
return Promise.resolve(1)
}
export function photoCheck2 () {
const text = plus.os.name === 'iOS' ? '请进入系统「设置」>「隐私」>「相册」中打开开关,允许”数字新能源“访问你的存储' : '请开启存储权限,以获得更好的服务'
uni.showModal({
title: '无存储权限',
content: text,
cancelText: '取消',
confirmText: '立即开启',
success: async (res) => {
if (res.confirm) {
permision.gotoAppPermissionSetting()
}
},
})
}
const privacy = {
// CAN_PUSH: 'canPush',
// CAN_SHARE: 'canShare',
// CAN_SHANYAN: 'canShanyan',
// DEVICE_NUMBER: 'deviceNumber',
// ALLOW_WIFI_AND_IP: 'allowWifiAndIp',
// ALLOW_GET_ANDROID_ID: 'allowGetAndroidId',
LOCATION: 'location',
LOCATION_SUB_PRICE: 'location_sub_price',
LOCATION_SUB_MAP: 'location_sub_map',
// CALL_PHONE:'callPhone',
CAMERA: 'camera',
// CAMERA_SUB_SCAN: 'camera_sub_scan',
CAMERA_SUB_AVATAR: 'camera_sub_avatar',
FILES: 'files',
// FILES_SUB_SCAN: 'files_sub_scan',
FILES_SUB_AVATAR: 'files_sub_avatar',
FILES_SUB_SAVE_IMAGE: 'files_sub_saveImage',
PHONE: 'phone',
PHONE_SUB_CONTACTUS:'phone_sub_contactus',
// PHONE_SUB_FEEDBACK:'phone_sub_feedback'
}
export default privacy
- @/mixins/androidPrivacy.js
import privacy from '@/config/privacy'
import permision from '@/js_sdk/wa-permission/permission.js'
export default {
data () {
return {
privaceList: [
{
name: '相机',
key: privacy.CAMERA,
desc: '用于实现拍摄照片功能',
type: 'nav',
state: false,
page: '/pagesMy/SetSubPrivacy/index?pkey=' + privacy.CAMERA,
androidPermission: 'CAMERA',
},
{
name: '拨打电话',
key: privacy.PHONE,
desc: '用于联系客服热线',
type: 'nav',
state: false,
page: '/pagesMy/SetSubPrivacy/index?pkey=' + privacy.PHONE,
androidPermission: 'CALL_PHONE',
},
{
name: '文件存储及访问',
key: privacy.FILES,
desc: '用于实现图片及文件的读取与写入',
type: 'nav',
state: false,
page: '/pagesMy/SetSubPrivacy/index?pkey=' + privacy.FILES,
androidPermission: [
'READ_EXTERNAL_STORAGE',
'WRITE_EXTERNAL_STORAGE',
],
}
],
cameraPrivaceList: [
{
name: '更换头像',
key: privacy.CAMERA_SUB_AVATAR,
check: true,
desc: '用于帮助您通过拍摄照片完成头像更换',
type: 'swith',
state: false,
alert: {
title: '',
desc: '更改头像是否允许访问相机?',
no: {
label: '禁止',
onPress: async () => {
const findItem = this.cameraPrivaceList.find((item) => item.key === privacy.CAMERA_SUB_AVATAR)
findItem.state = false
await uni.removeStorage({ key: privacy.CAMERA_SUB_AVATAR })
},
},
yes: {
label: '允许',
onPress: async () => {
const findItem = this.cameraPrivaceList.find((item) => item.key === privacy.CAMERA_SUB_AVATAR)
findItem.state = true
await uni.setStorageSync(privacy.CAMERA_SUB_AVATAR, new Date().getTime() + '')
},
},
},
} ],
filesPrivaceList: [
{
name: '更换头像',
key: privacy.FILES_SUB_AVATAR,
check: true,
desc: '用于帮助您通过上传图片完成头像更换',
type: 'swith',
state: false,
alert: {
title: '',
desc: '是否允许访问本地文件?',
no: {
label: '禁止',
onPress: async () => {
const findItem = this.filesPrivaceList.find((item) => item.key === privacy.FILES_SUB_AVATAR)
findItem.state = false
await uni.removeStorage({ key: privacy.FILES_SUB_AVATAR })
},
},
yes: {
label: '允许',
onPress: async () => {
const findItem = this.filesPrivaceList.find((item) => item.key === privacy.FILES_SUB_AVATAR)
findItem.state = true
await uni.setStorageSync(privacy.FILES_SUB_AVATAR, new Date().getTime() + '')
},
},
},
},
{
name: '保存图片',
key: privacy.FILES_SUB_SAVE_IMAGE,
check: true,
desc: '用于把APP内图片保存至本地相册',
type: 'swith',
state: false,
alert: {
title: '',
desc: '是否允许访问本地文件?',
no: {
label: '禁止',
onPress: async () => {
const findItem = this.filesPrivaceList.find((item) => item.key === privacy.FILES_SUB_SAVE_IMAGE)
findItem.state = false
await uni.removeStorage({ key: privacy.FILES_SUB_SAVE_IMAGE })
},
},
yes: {
label: '允许',
onPress: async () => {
const findItem = this.filesPrivaceList.find((item) => item.key === privacy.FILES_SUB_SAVE_IMAGE)
findItem.state = true
await uni.setStorageSync(privacy.FILES_SUB_SAVE_IMAGE, new Date().getTime() + '')
},
},
},
},
],
phonePrivaceList: [
{
name: '联系我们',
key: privacy.PHONE_SUB_CONTACTUS,
check: true,
desc: '用于帮助您联系客服',
type: 'swith',
state: false,
alert: {
title: '',
desc: '是否允许访问拨号功能?',
no: {
label: '禁止',
onPress: async () => {
const findItem = this.phonePrivaceList.find((item) => item.key === privacy.PHONE_SUB_CONTACTUS)
findItem.state = false
await uni.removeStorage({ key: privacy.PHONE_SUB_CONTACTUS })
},
},
yes: {
label: '允许',
onPress: async () => {
const findItem = this.phonePrivaceList.find((item) => item.key === privacy.PHONE_SUB_CONTACTUS)
findItem.state = true
await uni.setStorageSync(privacy.PHONE_SUB_CONTACTUS, new Date().getTime() + '')
},
},
},
} ,
],
}
},
methods: {
getModule (module) {
if (module === privacy.LOCATION) return this.locationPrivaceList
if (module === privacy.CAMERA) return this.cameraPrivaceList
if (module === privacy.FILES) return this.filesPrivaceList
if (module === privacy.PHONE) return this.phonePrivaceList
return this.privaceList
},
/**
* 申请授权
* @param module
* @param key
*/
requestPrivacyItem (key, module, successFunc, failedFunc, alert) {
if (plus.os.name === 'iOS') return Promise.resolve(true)
let list = this.privaceList
if (module) {
list = this.getModule(module)
}
const currentItem = list.find((item) => item.key === key)
return new Promise((resolve) => {
uni.showModal({
title: alert?.title || currentItem.alert?.title || '',
content: alert?.desc || currentItem.alert?.desc,
cancelText: '禁止',
confirmText: '允许',
success: async (res) => {
if (res.confirm) {
await currentItem.alert?.yes?.onPress()
successFunc && successFunc()
resolve(true)
} else if (res.cancel) {
await currentItem.alert?.no?.onPress()
failedFunc && failedFunc()
resolve(false)
}
},
})
})
},
/**
* 获取权限
* @param module
* @param key
*/
async getPrivacyItem (key) {
if (plus.os.name === 'iOS') return true
const res = await uni.getStorageSync(key)
if (res === 'false' || res === 'unknown' || res === '0' || !res) return false
return true
},
},
}
- @/components/GlobalPermissionsInfo/index.vue
<template>
<view class="modal" v-if="visible" ref="globalInfo" :style="{
width: windowWidth - 14 * 2 + 'px',
marginTop: statusHeight + 'px'
}">
<view class="box">
<text class="message">{{message}}</text>
</view>
</view>
</template>
<script lang="js">
export default {
data () {
return {
// #ifdef APP-PLUS
windowWidth: plus.screen.resolutionWidth,
isAndroid: plus.os.name === 'Android',
// #endif
// #ifdef MP-WEIXIN
windowWidth: 0,
isAndroid: false,
// #endif
message: '',
visible: false,
}
},
computed: {
statusHeight () {
// #ifdef APP-PLUS
return plus.navigator.getStatusbarHeight()
// #endif
// #ifdef MP-WEIXIN
return 0
// #endif
},
},
mounted () {
uni.$on('show_global_permissions_info' , (data) => {
if(data) {
this.visible = data.visible
this.message = data.message
}
})
},
}
</script>
<style lang="scss">
.modal {
left: 14px;
top: 0;
position: absolute;
border-radius: 10px;
background-color: #fff;
z-index: 1002;
box-shadow: 0px 1px 2px rgba(0, 0, 0, .1);
padding: 10px;
box-sizing: border-box;
}
.message {
line-height: 24px;
font-size: 14px;
color: 333;
}
</style>