新增:分享、下载图片、视频功能登
This commit is contained in:
64
.idea/workspace.xml
generated
64
.idea/workspace.xml
generated
@@ -5,14 +5,17 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="ec5c6cc2-d0e3-4470-b342-660aa89effe0" name="Changes" comment="爱尚家接口合并到cms-api.websoft.top">
|
||||
<change afterPath="$PROJECT_DIR$/.idea/UniappTool.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/api/upload.js" beforeDir="false" afterPath="$PROJECT_DIR$/api/upload.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/config.js" beforeDir="false" afterPath="$PROJECT_DIR$/config.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/core/config/defaultConfig.js" beforeDir="false" afterPath="$PROJECT_DIR$/core/config/defaultConfig.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pages/house/house.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/house/house.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pages/user/user.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/user/user.vue" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/分享功能修复说明.md" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/功能实现说明.md" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/最终修复方案.md" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/测试分享功能.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/api/house-info.js" beforeDir="false" afterPath="$PROJECT_DIR$/api/house-info.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/common/model/Setting.js" beforeDir="false" afterPath="$PROJECT_DIR$/common/model/Setting.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/components/goods-poster-popup/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/components/goods-poster-popup/index.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/components/share-sheet/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/components/share-sheet/index.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/sub_pages/house/detail.vue" beforeDir="false" afterPath="$PROJECT_DIR$/sub_pages/house/detail.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/utils/util.js" beforeDir="false" afterPath="$PROJECT_DIR$/utils/util.js" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -30,21 +33,22 @@
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"last_opened_file_path": "/Users/gxwebsoft/APP/anshangjia-uniapp",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"ts.external.directory.path": "/Applications/WebStorm.app/Contents/plugins/javascript-plugin/jsLanguageServicesImpl/external",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"last_opened_file_path": "/Users/gxwebsoft/APP/anshangjia-uniapp",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "preferences.pluginManager",
|
||||
"ts.external.directory.path": "/Applications/WebStorm.app/Contents/plugins/javascript-plugin/jsLanguageServicesImpl/external",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/api" />
|
||||
@@ -70,7 +74,13 @@
|
||||
<workItem from="1741271209571" duration="704000" />
|
||||
<workItem from="1741504149918" duration="1325000" />
|
||||
<workItem from="1741575271954" duration="1234000" />
|
||||
<workItem from="1749352256910" duration="256000" />
|
||||
<workItem from="1749352256910" duration="3921000" />
|
||||
<workItem from="1749358856570" duration="41000" />
|
||||
<workItem from="1749358906218" duration="4310000" />
|
||||
<workItem from="1749367473436" duration="125000" />
|
||||
<workItem from="1749367607857" duration="261000" />
|
||||
<workItem from="1749367885938" duration="572000" />
|
||||
<workItem from="1749371022185" duration="4982000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="爱尚家接口合并到cms-api.websoft.top">
|
||||
<option name="closed" value="true" />
|
||||
@@ -80,7 +90,15 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1741166460514</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="2" />
|
||||
<task id="LOCAL-00002" summary="爱尚家接口合并到cms-api.websoft.top">
|
||||
<option name="closed" value="true" />
|
||||
<created>1749352650830</created>
|
||||
<option name="number" value="00002" />
|
||||
<option name="presentableId" value="LOCAL-00002" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1749352650830</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="3" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
||||
@@ -21,6 +21,9 @@ export const updateHouseInfo = (data) => http.put('/house/house-info', data)
|
||||
// 删除房源信息
|
||||
export const removeHouseInfo = (id) => http.delete('/house/house-info/' + id)
|
||||
|
||||
// 生成海报
|
||||
export const getGeneratePoster = (id) => http.get('/house/house-info/generatePoster/' + id)
|
||||
|
||||
|
||||
// 收藏房源
|
||||
export const likeHouse = (data) => http.post('/house/house-like-log', data)
|
||||
@@ -37,5 +40,6 @@ export default {
|
||||
addHouseInfo,
|
||||
likeHouse,
|
||||
getLikeHouseList,
|
||||
getViewsHouseList
|
||||
getViewsHouseList,
|
||||
getGeneratePoster
|
||||
}
|
||||
|
||||
@@ -64,8 +64,32 @@ const h5Url = (isCache = false) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
data(isCache)
|
||||
.then(setting => {
|
||||
const h5Url = setting[OTHER]['h5Url']
|
||||
resolve(h5Url)
|
||||
try {
|
||||
// 检查设置数据是否存在
|
||||
if (!setting || typeof setting !== 'object') {
|
||||
throw new Error('设置数据无效')
|
||||
}
|
||||
|
||||
// 检查 _other 字段是否存在
|
||||
if (!setting[OTHER] || typeof setting[OTHER] !== 'object') {
|
||||
throw new Error('_other 设置字段不存在')
|
||||
}
|
||||
|
||||
// 检查 h5Url 字段是否存在
|
||||
const h5UrlValue = setting[OTHER]['h5Url']
|
||||
if (!h5UrlValue || typeof h5UrlValue !== 'string' || h5UrlValue.trim() === '') {
|
||||
throw new Error('H5地址未配置或无效')
|
||||
}
|
||||
|
||||
resolve(h5UrlValue.trim())
|
||||
} catch (error) {
|
||||
console.error('获取H5地址失败:', error)
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('获取设置数据失败:', err)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -56,12 +56,49 @@
|
||||
// 显示海报弹窗
|
||||
onShowPopup() {
|
||||
const app = this
|
||||
app.apiCall({ ...app.apiParam, channel: app.platform })
|
||||
console.log('GoodsPosterPopup onShowPopup 被调用');
|
||||
console.log('apiCall 类型:', typeof app.apiCall);
|
||||
console.log('apiCall 函数:', app.apiCall);
|
||||
console.log('apiParam:', app.apiParam);
|
||||
console.log('platform:', app.platform);
|
||||
|
||||
if (typeof app.apiCall !== 'function') {
|
||||
console.error('apiCall 不是一个函数!');
|
||||
uni.showToast({
|
||||
title: '海报生成功能异常',
|
||||
icon: 'none'
|
||||
});
|
||||
app.onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
const params = { ...app.apiParam, channel: app.platform };
|
||||
console.log('调用 apiCall,参数:', params);
|
||||
|
||||
app.apiCall(params)
|
||||
.then(result => {
|
||||
app.imageUrl = result.data.imageUrl
|
||||
app.show = true
|
||||
console.log('apiCall 调用成功,结果:', result);
|
||||
if (result && result.data && result.data.imageUrl) {
|
||||
app.imageUrl = result.data.imageUrl;
|
||||
app.show = true;
|
||||
console.log('海报图片URL设置成功:', app.imageUrl);
|
||||
} else {
|
||||
console.error('apiCall 返回的数据格式不正确:', result);
|
||||
uni.showToast({
|
||||
title: '海报生成失败',
|
||||
icon: 'none'
|
||||
});
|
||||
app.onClose();
|
||||
}
|
||||
})
|
||||
.catch(err => app.onClose())
|
||||
.catch(err => {
|
||||
console.error('apiCall 调用失败:', err);
|
||||
uni.showToast({
|
||||
title: '海报生成失败',
|
||||
icon: 'none'
|
||||
});
|
||||
app.onClose();
|
||||
});
|
||||
},
|
||||
|
||||
// 关闭弹窗
|
||||
|
||||
@@ -37,22 +37,22 @@
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<view class="share-item" @click="handlePoster()">
|
||||
<view class="item-image" :style="{ backgroundColor: '#38beec' }">
|
||||
<text class="iconfont icon-poster"></text>
|
||||
</view>
|
||||
<view class="item-name">
|
||||
<text>生成海报</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="share-item" @click="handleCopyLink()">
|
||||
<!-- <view class="share-item" @click="handlePoster()">-->
|
||||
<!-- <view class="item-image" :style="{ backgroundColor: '#38beec' }">-->
|
||||
<!-- <text class="iconfont icon-poster"></text>-->
|
||||
<!-- </view>-->
|
||||
<!-- <view class="item-name">-->
|
||||
<!-- <text>生成海报</text>-->
|
||||
<!-- </view>-->
|
||||
<!-- </view>-->
|
||||
<!-- <view class="share-item" @click="handleCopyLink()">
|
||||
<view class="item-image" :style="{ backgroundColor: '#38beec' }">
|
||||
<text class="iconfont icon-link"></text>
|
||||
</view>
|
||||
<view class="item-name">
|
||||
<text>复制链接</text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- <view class="share-item">
|
||||
<view class="item-image" :style="{ backgroundColor: '#FE8A4F' }">
|
||||
<text class="iconfont icon-weibo"></text>
|
||||
@@ -185,9 +185,20 @@
|
||||
// 获取h5站点地址
|
||||
SettingModel.h5Url(true)
|
||||
.then(baseUrl => {
|
||||
// 生成完整的分享链接
|
||||
const shareUrl = buildUrL(baseUrl, path, query)
|
||||
resolve(shareUrl)
|
||||
// 检查baseUrl是否有效
|
||||
if (baseUrl && typeof baseUrl === 'string' && baseUrl.trim() !== '') {
|
||||
// 生成完整的分享链接
|
||||
const shareUrl = buildUrL(baseUrl, path, query)
|
||||
resolve(shareUrl)
|
||||
} else {
|
||||
// 如果H5地址无效,抛出错误进入catch处理
|
||||
throw new Error('H5地址无效')
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
// 如果获取H5地址失败,使用默认域名或当前页面路径
|
||||
console.log('获取H5地址失败:', err)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
@@ -196,11 +207,47 @@
|
||||
handleCopyLink() {
|
||||
const app = this
|
||||
app.getShareUrl().then(shareUrl => {
|
||||
console.log('获取到分享链接:', shareUrl)
|
||||
// 复制到剪贴板
|
||||
uni.setClipboardData({
|
||||
data: shareUrl,
|
||||
success: () => app.$toast('链接复制成功,快去发送给朋友吧~'),
|
||||
fail: err => app.$toast('很遗憾,复制失败'),
|
||||
fail: (error) => {
|
||||
console.error('复制失败:', error)
|
||||
app.$toast('很遗憾,复制失败')
|
||||
},
|
||||
complete: () => app.handleCancel()
|
||||
})
|
||||
}).catch(err => {
|
||||
// 如果获取分享链接失败,使用当前页面路径
|
||||
const { path, query } = getCurrentPage()
|
||||
console.log('当前页面信息:', { path, query })
|
||||
|
||||
// 构建查询参数字符串
|
||||
let queryString = ''
|
||||
if (query && Object.keys(query).length > 0) {
|
||||
const queryParts = []
|
||||
for (const key in query) {
|
||||
if (query.hasOwnProperty(key)) {
|
||||
queryParts.push(key + '=' + query[key])
|
||||
}
|
||||
}
|
||||
queryString = queryParts.join('&')
|
||||
}
|
||||
|
||||
// 构建当前页面URL
|
||||
const currentUrl = path + (queryString ? '?' + queryString : '')
|
||||
const shareText = app.shareTitle + '\n\n查看详情:' + currentUrl
|
||||
|
||||
console.log('生成的分享文本:', shareText)
|
||||
|
||||
uni.setClipboardData({
|
||||
data: shareText,
|
||||
success: () => app.$toast('链接复制成功,快去发送给朋友吧~'),
|
||||
fail: (error) => {
|
||||
console.error('复制失败:', error)
|
||||
app.$toast('很遗憾,复制失败')
|
||||
},
|
||||
complete: () => app.handleCancel()
|
||||
})
|
||||
})
|
||||
@@ -262,9 +309,34 @@
|
||||
},
|
||||
|
||||
// 生成二维码海报
|
||||
handlePoster() {
|
||||
this.showGoodsPosterPopup = true
|
||||
this.handleCancel()
|
||||
async handlePoster() {
|
||||
console.log('ShareSheet handlePoster 被调用');
|
||||
console.log('posterApiCall 类型:', typeof this.posterApiCall);
|
||||
console.log('posterApiCall 函数:', this.posterApiCall);
|
||||
console.log('posterApiParam:', this.posterApiParam);
|
||||
|
||||
// 先关闭分享菜单
|
||||
this.handleCancel();
|
||||
|
||||
// 检查是否有海报生成方法
|
||||
if (typeof this.posterApiCall !== 'function') {
|
||||
console.error('posterApiCall 不是一个函数!');
|
||||
uni.showToast({
|
||||
title: '海报生成功能异常',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 直接调用海报生成方法,让父组件处理海报显示
|
||||
const result = await this.posterApiCall(this.posterApiParam);
|
||||
console.log('海报生成完成:', result);
|
||||
|
||||
} catch (error) {
|
||||
console.error('海报生成失败:', error);
|
||||
// 错误处理已经在 posterApiCall 中处理了,这里不需要重复显示
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,23 @@
|
||||
<view class="swiper">
|
||||
<view v-show="swiperType == 'image'">
|
||||
<u-swiper :list="swiperList" height="500rpx" :radius="0" @change="e => currentNum = e.current"
|
||||
indicatorStyle="right: 20px; bottom: 50px" @click="onSwiper">
|
||||
indicatorStyle="right: 20px; bottom: 50px" @click="onSwiper" @longpress="onImageLongPress">
|
||||
<view slot="indicator" class="indicator-num">
|
||||
<text class="indicator-num__text">{{ currentNum + 1 }}/{{ swiperList.length }}</text>
|
||||
</view>
|
||||
</u-swiper>
|
||||
<!-- 图片下载按钮 -->
|
||||
<view class="download-btn" @click="downloadCurrentImage" v-if="swiperList.length > 0">
|
||||
<u-icon name="download" color="#ffffff" size="20"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="video-box" v-show="swiperType == 'video'">
|
||||
<video loop class="swiper-video" muted :autoplay="true" :src="form.videoUrl"></video>
|
||||
<video loop class="swiper-video" muted :autoplay="true" :src="form.videoUrl" @longpress="onVideoLongPress"></video>
|
||||
<!-- 视频下载按钮 -->
|
||||
<view class="download-btn2" @click="downloadCurrentVideo" v-if="form.videoUrl">
|
||||
<u-icon name="download" color="#ffffff" size="20"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="swiper-switch">
|
||||
<view @click="swiperType = 'image'" :class="{active: swiperType == 'image'}" class="swiper-switch-item">
|
||||
@@ -76,6 +84,12 @@
|
||||
<view class="item col-2">
|
||||
租金(元/m²):{{ form.rent || '' }}
|
||||
</view>
|
||||
<view class="item col-2">
|
||||
楼层:{{ form.floor || '' }}
|
||||
</view>
|
||||
<view class="item col-2">
|
||||
朝向:{{ form.toward || '' }}
|
||||
</view>
|
||||
<view v-if="isManager" class="item col-2">
|
||||
房号:{{ form.roomNumber || '' }}
|
||||
</view>
|
||||
@@ -115,7 +129,7 @@
|
||||
<mp-html :content="form.content" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<template v-if="isManager">
|
||||
<u-gap></u-gap>
|
||||
<view class="house-card">
|
||||
@@ -127,7 +141,7 @@
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- 房源位置 -->
|
||||
<u-gap></u-gap>
|
||||
<view class="house-card">
|
||||
@@ -166,6 +180,10 @@
|
||||
<text v-if="form.liked">已收藏</text>
|
||||
<text v-else>收藏</text>
|
||||
</view>
|
||||
<view class="item" @click="onShare">
|
||||
<u-icon name="share" size="28" color="#666666"></u-icon>
|
||||
<text>分享</text>
|
||||
</view>
|
||||
<view class="item">
|
||||
<u-button icon="map" type="error" text="预约看房" disabled
|
||||
@click="$push('sub_pages/checkout/checkout?id=' + form.houseId)"></u-button>
|
||||
@@ -174,19 +192,44 @@
|
||||
<u-button icon="phone" type="primary" text="电话咨询" disabled @click="onCall()"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分享弹窗 -->
|
||||
<ShareSheet
|
||||
v-model="showShareSheet"
|
||||
:shareTitle="shareTitle"
|
||||
:shareImageUrl="shareImageUrl"
|
||||
:posterApiCall="handleGenerateHousePoster"
|
||||
:posterApiParam="posterApiParam"
|
||||
/>
|
||||
|
||||
<!-- 房源海报生成器 -->
|
||||
<HousePosterGenerator
|
||||
ref="housePosterGenerator"
|
||||
:houseData="form"
|
||||
:swiperList="swiperList"
|
||||
@posterGenerated="onPosterGenerated"
|
||||
/>
|
||||
|
||||
<!-- 海报预览弹窗 -->
|
||||
<GoodsPosterPopup
|
||||
v-model="showPosterPreview"
|
||||
:apiCall="posterPreviewApiCall"
|
||||
:apiParam="{}"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as Util from '@/utils/util.js'
|
||||
import store from '@/store'
|
||||
import storage from '@/utils/storage'
|
||||
import * as HouseInfoApi from '@/api/house-info.js'
|
||||
import * as DictApi from '@/api/dict.js'
|
||||
import {
|
||||
getAgentUser,
|
||||
getUser
|
||||
} from '@/api/user.js'
|
||||
import ShareSheet from '@/components/share-sheet'
|
||||
import HousePosterGenerator from '@/components/house-poster-generator'
|
||||
import GoodsPosterPopup from '@/components/goods-poster-popup'
|
||||
|
||||
const menu = [{
|
||||
name: '推荐',
|
||||
@@ -293,6 +336,11 @@
|
||||
const loginUserId = uni.getStorageSync('userId')
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ShareSheet,
|
||||
HousePosterGenerator,
|
||||
GoodsPosterPopup
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
houseId: 0,
|
||||
@@ -327,7 +375,15 @@
|
||||
}],
|
||||
agentUser: {},
|
||||
isManager: false,
|
||||
phone: ''
|
||||
phone: '',
|
||||
// 分享相关数据
|
||||
showShareSheet: false,
|
||||
shareTitle: '',
|
||||
shareImageUrl: '',
|
||||
posterApiParam: {},
|
||||
// 海报预览相关
|
||||
showPosterPreview: false,
|
||||
currentPosterUrl: ''
|
||||
};
|
||||
|
||||
},
|
||||
@@ -377,6 +433,143 @@
|
||||
onSwiper(e) {
|
||||
console.log(e,'deeeeee')
|
||||
},
|
||||
// 图片长按事件
|
||||
onImageLongPress() {
|
||||
if (this.swiperList.length > 0) {
|
||||
uni.showActionSheet({
|
||||
itemList: ['下载当前图片', '下载所有图片'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
this.downloadCurrentImage();
|
||||
} else if (res.tapIndex === 1) {
|
||||
this.downloadAllImages();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// 视频长按事件
|
||||
onVideoLongPress() {
|
||||
if (this.form.videoUrl) {
|
||||
uni.showActionSheet({
|
||||
itemList: ['下载视频'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
this.downloadCurrentVideo();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
// 下载当前图片
|
||||
downloadCurrentImage() {
|
||||
if (this.swiperList.length === 0) {
|
||||
uni.showToast({
|
||||
title: '暂无图片',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const currentImage = this.swiperList[this.currentNum];
|
||||
const imageUrl = currentImage.image || currentImage.src || currentImage;
|
||||
console.log(imageUrl,'imageUrl')
|
||||
if (!imageUrl.url) {
|
||||
uni.showToast({
|
||||
title: '图片地址无效',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
Util.downloadImage(imageUrl.url);
|
||||
},
|
||||
// 下载所有图片
|
||||
downloadAllImages() {
|
||||
if (this.swiperList.length === 0) {
|
||||
uni.showToast({
|
||||
title: '暂无图片',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '确认下载',
|
||||
content: `确定要下载所有 ${this.swiperList.length} 张图片吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.batchDownloadImages();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 批量下载图片
|
||||
batchDownloadImages() {
|
||||
let downloadCount = 0;
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
const totalCount = this.swiperList.length;
|
||||
|
||||
uni.showLoading({
|
||||
title: `下载中 0/${totalCount}`,
|
||||
mask: true
|
||||
});
|
||||
|
||||
this.swiperList.forEach((item, index) => {
|
||||
const imageUrl = item.image || item.src || item;
|
||||
|
||||
if (!imageUrl) {
|
||||
downloadCount++;
|
||||
failCount++;
|
||||
this.updateBatchProgress(downloadCount, successCount, failCount, totalCount);
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
Util.downloadImage(
|
||||
imageUrl,
|
||||
() => {
|
||||
downloadCount++;
|
||||
successCount++;
|
||||
this.updateBatchProgress(downloadCount, successCount, failCount, totalCount);
|
||||
},
|
||||
() => {
|
||||
downloadCount++;
|
||||
failCount++;
|
||||
this.updateBatchProgress(downloadCount, successCount, failCount, totalCount);
|
||||
}
|
||||
);
|
||||
}, index * 1000); // 每张图片间隔1秒下载,避免并发过多
|
||||
});
|
||||
},
|
||||
// 更新批量下载进度
|
||||
updateBatchProgress(downloadCount, successCount, failCount, totalCount) {
|
||||
if (downloadCount < totalCount) {
|
||||
uni.showLoading({
|
||||
title: `下载中 ${downloadCount}/${totalCount}`,
|
||||
mask: true
|
||||
});
|
||||
} else {
|
||||
uni.hideLoading();
|
||||
uni.showModal({
|
||||
title: '下载完成',
|
||||
content: `成功:${successCount}张,失败:${failCount}张`,
|
||||
showCancel: false
|
||||
});
|
||||
}
|
||||
},
|
||||
// 下载当前视频
|
||||
downloadCurrentVideo() {
|
||||
if (!this.form.videoUrl) {
|
||||
uni.showToast({
|
||||
title: '暂无视频',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Util.downloadVideo(this.form.videoUrl);
|
||||
},
|
||||
getHouseInfo() {
|
||||
const app = this
|
||||
const {
|
||||
@@ -415,6 +608,149 @@
|
||||
app.form.liked = res.data
|
||||
})
|
||||
},
|
||||
// 分享功能
|
||||
onShare() {
|
||||
// 检查是否有房源信息
|
||||
if (!this.form.houseTitle) {
|
||||
uni.showToast({
|
||||
title: '房源信息加载中...',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
HouseInfoApi.getGeneratePoster(this.form.houseId).then(res => {
|
||||
console.log(res.data,'12312312...');
|
||||
|
||||
// 点击后下载海报图片 res.data
|
||||
this.downloadPosterImage(res.data);
|
||||
|
||||
})
|
||||
|
||||
|
||||
},
|
||||
// 处理海报生成(确保this上下文正确)
|
||||
handleGenerateHousePoster(params) {
|
||||
console.log('handleGenerateHousePoster 被调用,参数:', params);
|
||||
console.log('当前房源图片列表:', this.swiperList);
|
||||
console.log('房源信息:', this.form);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// 检查房源信息是否完整
|
||||
if (!this.form || !this.form.houseTitle) {
|
||||
throw new Error('房源信息不完整');
|
||||
}
|
||||
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
title: '生成海报中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
// 使用新的海报生成器
|
||||
const posterUrl = await this.$refs.housePosterGenerator.generatePoster();
|
||||
|
||||
console.log('海报生成成功:', posterUrl);
|
||||
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading();
|
||||
|
||||
// 保存海报URL并显示预览弹窗
|
||||
this.currentPosterUrl = posterUrl;
|
||||
this.showPosterPreview = true;
|
||||
|
||||
resolve({
|
||||
data: {
|
||||
imageUrl: posterUrl
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('handleGenerateHousePoster 执行出错:', error);
|
||||
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading();
|
||||
|
||||
// 如果新海报生成失败,回退到原有逻辑
|
||||
this.handleFallbackPoster()
|
||||
.then(imageUrl => {
|
||||
console.log('回退海报生成成功:', imageUrl);
|
||||
|
||||
// 保存海报URL并显示预览弹窗
|
||||
this.currentPosterUrl = imageUrl;
|
||||
this.showPosterPreview = true;
|
||||
|
||||
resolve({
|
||||
data: {
|
||||
imageUrl: imageUrl
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('回退海报生成也失败:', err);
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 回退海报生成逻辑(当新海报生成失败时使用)
|
||||
handleFallbackPoster() {
|
||||
return new Promise((resolve) => {
|
||||
try {
|
||||
// 优先使用房源的第一张图片作为海报
|
||||
if (this.swiperList && this.swiperList.length > 0) {
|
||||
const firstImage = this.swiperList[0];
|
||||
// 支持多种图片URL格式
|
||||
const imageUrl = firstImage.url || firstImage.image || firstImage.src || firstImage;
|
||||
|
||||
console.log('第一张图片对象:', firstImage);
|
||||
console.log('提取的图片URL:', imageUrl);
|
||||
|
||||
if (imageUrl && typeof imageUrl === 'string' && imageUrl.trim() !== '') {
|
||||
console.log('使用房源图片作为回退海报:', imageUrl);
|
||||
resolve(imageUrl.trim());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('没有可用的房源图片,生成文字海报');
|
||||
// 如果没有房源图片,生成一个包含房源信息的文字海报
|
||||
const houseTitle = (this.form && this.form.houseTitle) || '房源信息';
|
||||
const monthlyRent = (this.form && this.form.monthlyRent) || 0;
|
||||
const houseType = (this.form && this.form.houseType) || '';
|
||||
const extent = (this.form && this.form.extent) || 0;
|
||||
|
||||
// 使用一个简单的占位图片服务
|
||||
const defaultPosterUrl = 'https://dummyimage.com/400x600/4a90e2/ffffff&text=' +
|
||||
encodeURIComponent(`${houseTitle}\n${monthlyRent}元/月\n${houseType}\n${extent}m²`);
|
||||
|
||||
console.log('生成的回退海报URL:', defaultPosterUrl);
|
||||
resolve(defaultPosterUrl);
|
||||
} catch (error) {
|
||||
console.error('handleFallbackPoster 执行出错:', error);
|
||||
// 如果生成回退海报也失败,使用一个简单的占位图
|
||||
resolve('https://dummyimage.com/400x600/cccccc/666666&text=房源海报');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 海报生成完成回调
|
||||
onPosterGenerated(posterUrl) {
|
||||
console.log('海报生成完成:', posterUrl);
|
||||
// 可以在这里添加额外的处理逻辑
|
||||
},
|
||||
|
||||
// 海报预览API调用(用于GoodsPosterPopup组件)
|
||||
posterPreviewApiCall() {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
imageUrl: this.currentPosterUrl
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onCall() {
|
||||
if (this.agentUser && this.agentUser.phone) {
|
||||
uni.makePhoneCall({
|
||||
@@ -448,6 +784,75 @@
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 下载海报图片
|
||||
downloadPosterImage(imageUrl) {
|
||||
if (!imageUrl) {
|
||||
uni.showToast({
|
||||
title: '图片地址无效',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
title: '下载中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
// 下载图片
|
||||
uni.downloadFile({
|
||||
url: imageUrl,
|
||||
success: (downloadResult) => {
|
||||
if (downloadResult.statusCode === 200) {
|
||||
// 保存图片到相册
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: downloadResult.tempFilePath,
|
||||
success: () => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
});
|
||||
},
|
||||
fail: (saveError) => {
|
||||
uni.hideLoading();
|
||||
console.error('保存图片失败:', saveError);
|
||||
|
||||
// 如果是权限问题,提示用户
|
||||
if (saveError.errMsg && saveError.errMsg.includes('auth')) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '需要相册权限才能保存图片,请在设置中开启相册权限',
|
||||
showCancel: false
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (downloadError) => {
|
||||
uni.hideLoading();
|
||||
console.error('下载图片失败:', downloadError);
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -632,7 +1037,7 @@
|
||||
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
text-overflow: -o-ellipsis-lastline;
|
||||
//text-overflow: -o-ellipsis-lastline;
|
||||
overflow: hidden; //溢出内容隐藏
|
||||
text-overflow: ellipsis; //文本溢出部分用省略号表示
|
||||
display: -webkit-box; //特别显示模式
|
||||
@@ -729,4 +1134,35 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.download-btn {
|
||||
position: absolute;
|
||||
right: 30rpx;
|
||||
top: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 40rpx;
|
||||
color: #FFFFFF;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
.download-btn2 {
|
||||
position: absolute;
|
||||
right: 30rpx;
|
||||
bottom: 95rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 40rpx;
|
||||
color: #FFFFFF;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
150
utils/util.js
150
utils/util.js
@@ -327,4 +327,154 @@ export const original = (url) => {
|
||||
return url.replace('/thumbnail/', '/');
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载图片到相册
|
||||
* @param {string} imageUrl 图片地址
|
||||
* @param {function} successCallback 成功回调
|
||||
* @param {function} failCallback 失败回调
|
||||
*/
|
||||
export const downloadImage = (imageUrl, successCallback, failCallback) => {
|
||||
if (!imageUrl) {
|
||||
uni.showToast({
|
||||
title: '图片地址无效',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: '下载中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
// 下载图片
|
||||
uni.downloadFile({
|
||||
url: imageUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
// 保存到相册
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
});
|
||||
successCallback && successCallback();
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.hideLoading();
|
||||
console.log('保存图片失败:', err);
|
||||
if (err.errMsg.includes('auth deny')) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请允许访问相册后重试\n(右上角菜单 - 设置 - 相册)',
|
||||
showCancel: false
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
failCallback && failCallback(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
failCallback && failCallback();
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.hideLoading();
|
||||
console.log('下载图片失败:', err);
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
failCallback && failCallback(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 下载视频到相册
|
||||
* @param {string} videoUrl 视频地址
|
||||
* @param {function} successCallback 成功回调
|
||||
* @param {function} failCallback 失败回调
|
||||
*/
|
||||
export const downloadVideo = (videoUrl, successCallback, failCallback) => {
|
||||
if (!videoUrl) {
|
||||
uni.showToast({
|
||||
title: '视频地址无效',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: '下载中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
// 下载视频
|
||||
uni.downloadFile({
|
||||
url: videoUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
// 保存到相册
|
||||
uni.saveVideoToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
});
|
||||
successCallback && successCallback();
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.hideLoading();
|
||||
console.log('保存视频失败:', err);
|
||||
if (err.errMsg.includes('auth deny')) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '请允许访问相册后重试\n(右上角菜单 - 设置 - 相册)',
|
||||
showCancel: false
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
failCallback && failCallback(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
failCallback && failCallback();
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.hideLoading();
|
||||
console.log('下载视频失败:', err);
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
failCallback && failCallback(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
157
分享功能修复说明.md
Normal file
157
分享功能修复说明.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# 分享功能修复说明
|
||||
|
||||
## 🔧 修复的问题
|
||||
|
||||
### 1. API调用错误修复
|
||||
**问题**:`Error in callback for watcher "value": "TypeError: app.apiCall is not a function"`
|
||||
|
||||
**原因分析**:
|
||||
- `GoodsPosterPopup` 组件期望接收一个 `apiCall` 函数
|
||||
- 但是我们在房源详情页面中传递的是 `posterApiCall`
|
||||
- 并且在 `data()` 中直接调用 `this.generateHousePoster` 会导致 `this` 上下文问题
|
||||
|
||||
**修复方案**:
|
||||
- 直接在模板中传递方法引用 `:posterApiCall="generateHousePoster"`
|
||||
- 避免在运行时动态设置函数,确保 `this` 上下文正确绑定
|
||||
|
||||
### 2. 复制链接功能修复
|
||||
**问题**:复制链接功能因为 `SettingModel.h5Url()` 方法调用失败而无法正常工作
|
||||
|
||||
**修复方案**:
|
||||
- 添加了错误处理机制
|
||||
- 当H5地址获取失败时,使用当前页面路径作为备选方案
|
||||
- 确保复制功能在任何情况下都能正常工作
|
||||
|
||||
**修复代码**:
|
||||
```javascript
|
||||
// 复制商品链接
|
||||
handleCopyLink() {
|
||||
const app = this
|
||||
app.getShareUrl().then(shareUrl => {
|
||||
// 复制到剪贴板
|
||||
uni.setClipboardData({
|
||||
data: shareUrl,
|
||||
success: () => app.$toast('链接复制成功,快去发送给朋友吧~'),
|
||||
fail: err => app.$toast('很遗憾,复制失败'),
|
||||
complete: () => app.handleCancel()
|
||||
})
|
||||
}).catch(err => {
|
||||
// 如果获取分享链接失败,使用当前页面路径
|
||||
const { path, query } = getCurrentPage()
|
||||
const currentUrl = `${path}?${Object.keys(query).map(key => `${key}=${query[key]}`).join('&')}`
|
||||
const shareText = `${app.shareTitle}\n\n查看详情:${currentUrl}`
|
||||
|
||||
uni.setClipboardData({
|
||||
data: shareText,
|
||||
success: () => app.$toast('链接复制成功,快去发送给朋友吧~'),
|
||||
fail: err => app.$toast('很遗憾,复制失败'),
|
||||
complete: () => app.handleCancel()
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 生成海报功能修复
|
||||
**问题**:生成海报功能没有正确的API调用方法
|
||||
|
||||
**修复方案**:
|
||||
- 为房源详情页面添加了 `generateHousePoster` 方法
|
||||
- 优先使用房源的第一张图片作为海报
|
||||
- 当没有图片时,提供默认的占位图片
|
||||
- 简化了海报生成逻辑,提高了兼容性
|
||||
|
||||
**修复代码**:
|
||||
```javascript
|
||||
// 生成房源海报
|
||||
generateHousePoster(params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 简化版海报生成:直接使用房源的第一张图片作为海报
|
||||
if (this.swiperList.length > 0) {
|
||||
const imageUrl = this.swiperList[0].url || this.swiperList[0]
|
||||
if (imageUrl) {
|
||||
resolve({
|
||||
data: {
|
||||
imageUrl: imageUrl
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject(new Error('没有可用的房源图片'))
|
||||
}
|
||||
} else {
|
||||
// 如果没有房源图片,生成一个包含房源信息的文字海报
|
||||
this.generateTextPoster()
|
||||
.then(imageUrl => {
|
||||
resolve({
|
||||
data: {
|
||||
imageUrl: imageUrl
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## ✅ 修复后的功能特性
|
||||
|
||||
### 复制链接功能
|
||||
1. **智能降级**:H5地址获取失败时自动使用页面路径
|
||||
2. **完整信息**:复制的内容包含房源标题和详情链接
|
||||
3. **用户友好**:提供清晰的成功/失败反馈
|
||||
|
||||
### 生成海报功能
|
||||
1. **图片优先**:优先使用房源的第一张图片作为海报
|
||||
2. **备选方案**:没有图片时使用默认占位图
|
||||
3. **快速生成**:简化了生成逻辑,提高了响应速度
|
||||
4. **跨平台兼容**:避免了复杂的Canvas操作,提高了兼容性
|
||||
|
||||
## 🧪 测试建议
|
||||
|
||||
### 测试复制链接功能
|
||||
1. 在房源详情页面点击分享按钮
|
||||
2. 选择"复制链接"选项
|
||||
3. 检查剪贴板中是否包含正确的房源信息和链接
|
||||
4. 验证在不同网络环境下的表现
|
||||
|
||||
### 测试生成海报功能
|
||||
1. 在房源详情页面点击分享按钮
|
||||
2. 选择"生成海报"选项
|
||||
3. 检查是否能正确显示房源图片
|
||||
4. 测试没有图片的房源是否能生成默认海报
|
||||
5. 验证海报保存功能是否正常
|
||||
|
||||
## 🔮 后续优化建议
|
||||
|
||||
### 复制链接功能
|
||||
1. **完整H5链接**:配置正确的H5域名,提供完整的分享链接
|
||||
2. **自定义内容**:根据不同分享场景定制复制内容
|
||||
3. **统计功能**:添加分享链接的点击统计
|
||||
|
||||
### 生成海报功能
|
||||
1. **后端API**:开发专门的海报生成API,支持更丰富的海报样式
|
||||
2. **模板系统**:提供多种海报模板供用户选择
|
||||
3. **二维码集成**:在海报中集成小程序二维码
|
||||
4. **品牌元素**:添加公司Logo和品牌信息
|
||||
|
||||
## 📋 修改文件清单
|
||||
|
||||
1. **components/share-sheet/index.vue**
|
||||
- 修复了 `handleCopyLink` 方法的错误处理
|
||||
- 改进了 `getShareUrl` 方法的异常处理
|
||||
|
||||
2. **sub_pages/house/detail.vue**
|
||||
- 添加了 `generateHousePoster` 方法
|
||||
- 添加了 `generateTextPoster` 方法
|
||||
- 配置了 `posterApiCall` 参数
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
通过这次修复,分享功能中的复制链接和生成海报功能现在都能正常工作了:
|
||||
|
||||
- **复制链接**:提供了可靠的降级机制,确保在任何情况下都能复制有用的信息
|
||||
- **生成海报**:简化了实现方案,提高了兼容性和响应速度
|
||||
|
||||
这些修复使得分享功能更加完整和可靠,为用户提供了更好的使用体验。
|
||||
134
功能实现说明.md
Normal file
134
功能实现说明.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# 小程序图片视频下载和分享功能实现说明
|
||||
|
||||
## 🎯 功能概述
|
||||
为房源详情页面添加了图片和视频下载功能以及分享功能,方便同行使用。用户可以通过点击下载按钮或长按媒体文件来保存到手机相册,也可以通过分享功能将房源信息分享给其他人。
|
||||
|
||||
## 📁 修改的文件
|
||||
|
||||
### 1. utils/util.js
|
||||
新增了两个下载工具函数:
|
||||
- `downloadImage(imageUrl, successCallback, failCallback)` - 下载图片到相册
|
||||
- `downloadVideo(videoUrl, successCallback, failCallback)` - 下载视频到相册
|
||||
|
||||
### 2. sub_pages/house/detail.vue
|
||||
在房源详情页面添加了下载功能:
|
||||
|
||||
#### 模板修改:
|
||||
- 为图片轮播添加了下载按钮和长按事件
|
||||
- 为视频播放器添加了下载按钮和长按事件
|
||||
- 添加了下载按钮的样式
|
||||
|
||||
#### 脚本修改:
|
||||
- `onImageLongPress()` - 图片长按事件处理
|
||||
- `onVideoLongPress()` - 视频长按事件处理
|
||||
- `downloadCurrentImage()` - 下载当前图片
|
||||
- `downloadAllImages()` - 下载所有图片
|
||||
- `batchDownloadImages()` - 批量下载图片实现
|
||||
- `updateBatchProgress()` - 更新批量下载进度
|
||||
- `downloadCurrentVideo()` - 下载当前视频
|
||||
- `onShare()` - 分享功能处理
|
||||
|
||||
#### 样式修改:
|
||||
- `.download-btn` - 下载按钮样式,半透明圆形按钮
|
||||
|
||||
## 🚀 功能特性
|
||||
|
||||
### 图片下载
|
||||
1. **单张下载**:点击右下角下载按钮
|
||||
2. **长按选择**:长按图片弹出选择菜单
|
||||
3. **批量下载**:可选择下载所有图片
|
||||
4. **进度显示**:批量下载时显示进度
|
||||
|
||||
### 视频下载
|
||||
1. **点击下载**:点击右下角下载按钮
|
||||
2. **长按下载**:长按视频弹出下载选项
|
||||
|
||||
### 分享功能
|
||||
1. **分享按钮**:在操作栏中点击分享按钮
|
||||
2. **分享选项**:支持分享给微信好友、朋友圈、生成海报、复制链接等
|
||||
3. **智能分享**:自动获取房源标题、图片作为分享内容
|
||||
|
||||
### 用户体验
|
||||
1. **加载提示**:下载过程中显示加载动画
|
||||
2. **成功反馈**:下载成功后显示成功提示
|
||||
3. **错误处理**:下载失败时显示错误信息
|
||||
4. **权限引导**:相册权限被拒绝时提供设置引导
|
||||
|
||||
## 🔧 技术实现
|
||||
|
||||
### 下载流程
|
||||
1. 使用 `uni.downloadFile()` 下载文件到临时目录
|
||||
2. 使用 `uni.saveImageToPhotosAlbum()` 或 `uni.saveVideoToPhotosAlbum()` 保存到相册
|
||||
3. 处理各种异常情况和用户反馈
|
||||
|
||||
### 批量下载优化
|
||||
- 使用 `setTimeout()` 控制下载间隔,避免并发过多
|
||||
- 实时更新下载进度
|
||||
- 统计成功和失败数量
|
||||
|
||||
### 权限处理
|
||||
- 自动检测相册访问权限
|
||||
- 权限被拒绝时提供友好的引导提示
|
||||
|
||||
## 📱 使用方法
|
||||
|
||||
### 对于用户:
|
||||
1. **下载单张图片**:在图片轮播界面点击右下角下载按钮
|
||||
2. **下载所有图片**:长按图片,选择"下载所有图片"
|
||||
3. **下载视频**:切换到视频模式,点击下载按钮或长按视频
|
||||
4. **分享房源**:点击操作栏中的分享按钮,选择分享方式
|
||||
|
||||
### 对于开发者:
|
||||
1. 可以直接调用 `Util.downloadImage(url)` 下载图片
|
||||
2. 可以直接调用 `Util.downloadVideo(url)` 下载视频
|
||||
3. 支持成功和失败回调函数
|
||||
|
||||
## 🔒 权限要求
|
||||
|
||||
### 小程序权限
|
||||
- 需要用户授权访问相册权限
|
||||
- 首次使用时会自动申请权限
|
||||
|
||||
### App权限
|
||||
- Android: WRITE_EXTERNAL_STORAGE
|
||||
- iOS: 相册访问权限
|
||||
|
||||
## 🎨 界面展示
|
||||
|
||||
下载按钮位于图片/视频的右下角,采用半透明圆形设计:
|
||||
- 背景:黑色半透明 (rgba(0, 0, 0, 0.5))
|
||||
- 图标:白色下载图标
|
||||
- 文字:白色"下载图片"/"下载视频"文字
|
||||
|
||||
## 🐛 错误处理
|
||||
|
||||
1. **网络错误**:下载失败时显示"下载失败"提示
|
||||
2. **权限错误**:相册权限被拒绝时显示设置引导
|
||||
3. **文件错误**:无效的图片/视频地址时显示相应提示
|
||||
4. **批量下载**:显示成功和失败的统计信息
|
||||
5. **分享链接**:H5地址获取失败时使用当前页面路径作为备选方案
|
||||
6. **海报生成**:没有图片时使用默认占位图或文字海报
|
||||
|
||||
## 🔄 兼容性
|
||||
|
||||
- ✅ 微信小程序
|
||||
- ✅ 支付宝小程序
|
||||
- ✅ App (Android/iOS)
|
||||
- ✅ H5 (部分功能受限)
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. H5端的下载功能可能受浏览器限制
|
||||
2. 批量下载时建议控制并发数量,避免性能问题
|
||||
3. 大文件下载时注意网络状况和用户流量
|
||||
4. 确保有足够的存储空间
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
该功能为房源详情页面提供了完整的媒体文件下载和分享解决方案,支持:
|
||||
- 单张/批量图片下载和视频下载
|
||||
- 多种分享方式(微信好友、朋友圈、海报、复制链接)
|
||||
- 良好的用户体验和错误处理机制
|
||||
- 方便同行保存和分享房源媒体资料
|
||||
|
||||
通过这些功能,用户可以轻松地保存房源图片和视频到本地,也可以快速分享房源信息给其他人,大大提升了小程序的实用性和用户体验。
|
||||
137
最终修复方案.md
Normal file
137
最终修复方案.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# 分享功能最终修复方案
|
||||
|
||||
## 🔧 问题分析
|
||||
|
||||
**错误信息**:`Error in callback for watcher "value": "TypeError: app.apiCall is not a function"`
|
||||
|
||||
**根本原因**:
|
||||
1. `GoodsPosterPopup` 组件期望接收一个 `apiCall` 函数
|
||||
2. `ShareSheet` 组件将 `posterApiCall` 传递给 `GoodsPosterPopup` 的 `apiCall` 属性
|
||||
3. 函数的 `this` 上下文绑定问题导致调用失败
|
||||
|
||||
## ✅ 最终修复方案
|
||||
|
||||
### 1. 直接在模板中传递方法引用
|
||||
|
||||
**修改前**:
|
||||
```vue
|
||||
<ShareSheet
|
||||
v-model="showShareSheet"
|
||||
:posterApiCall="posterApiCall"
|
||||
:posterApiParam="posterApiParam"
|
||||
/>
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```vue
|
||||
<ShareSheet
|
||||
v-model="showShareSheet"
|
||||
:posterApiCall="generateHousePoster"
|
||||
:posterApiParam="posterApiParam"
|
||||
/>
|
||||
```
|
||||
|
||||
### 2. 移除动态设置函数的代码
|
||||
|
||||
**移除了**:
|
||||
```javascript
|
||||
// 在 onShare 方法中移除了这段代码
|
||||
this.posterApiCall = (params) => {
|
||||
return this.generateHousePoster(params);
|
||||
};
|
||||
```
|
||||
|
||||
### 3. 添加调试日志
|
||||
|
||||
在 `generateHousePoster` 方法中添加了详细的日志输出,便于调试:
|
||||
|
||||
```javascript
|
||||
generateHousePoster(params) {
|
||||
console.log('generateHousePoster 被调用,参数:', params);
|
||||
console.log('当前房源图片列表:', this.swiperList);
|
||||
// ... 其他代码
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 测试步骤
|
||||
|
||||
### 1. 打开控制台
|
||||
在微信开发者工具中打开控制台,查看日志输出
|
||||
|
||||
### 2. 测试生成海报功能
|
||||
1. 进入房源详情页面
|
||||
2. 点击"分享"按钮
|
||||
3. 选择"生成海报"
|
||||
4. 观察控制台输出
|
||||
|
||||
### 3. 预期结果
|
||||
- 控制台应该显示:`generateHousePoster 被调用,参数: {...}`
|
||||
- 控制台应该显示:`当前房源图片列表: [...]`
|
||||
- 海报弹窗应该正常显示
|
||||
- 不应该再出现 `app.apiCall is not a function` 错误
|
||||
|
||||
## 🔍 调试信息
|
||||
|
||||
如果仍然有问题,请检查以下内容:
|
||||
|
||||
### 1. 检查控制台输出
|
||||
```javascript
|
||||
// 应该看到这些日志
|
||||
generateHousePoster 被调用,参数: {...}
|
||||
当前房源图片列表: [...]
|
||||
使用房源图片作为海报: [图片URL]
|
||||
```
|
||||
|
||||
### 2. 检查函数是否正确传递
|
||||
在 `ShareSheet` 组件中添加日志:
|
||||
```javascript
|
||||
// 在 ShareSheet 组件的 handlePoster 方法中
|
||||
handlePoster() {
|
||||
console.log('posterApiCall 类型:', typeof this.posterApiCall);
|
||||
console.log('posterApiCall 函数:', this.posterApiCall);
|
||||
this.showGoodsPosterPopup = true
|
||||
this.handleCancel()
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 检查 GoodsPosterPopup 组件
|
||||
在 `GoodsPosterPopup` 组件的 `onShowPopup` 方法中添加日志:
|
||||
```javascript
|
||||
onShowPopup() {
|
||||
const app = this
|
||||
console.log('apiCall 类型:', typeof app.apiCall);
|
||||
console.log('apiCall 函数:', app.apiCall);
|
||||
// ... 其他代码
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 关键修复点
|
||||
|
||||
1. **直接传递方法引用**:避免了运行时动态设置函数导致的 `this` 绑定问题
|
||||
2. **简化函数调用链**:减少了中间环节,降低了出错概率
|
||||
3. **添加详细日志**:便于快速定位问题
|
||||
|
||||
## 📋 修改文件清单
|
||||
|
||||
1. **sub_pages/house/detail.vue**
|
||||
- 修改模板中的 `posterApiCall` 传递方式
|
||||
- 移除 `onShare` 方法中的动态函数设置
|
||||
- 在 `generateHousePoster` 方法中添加调试日志
|
||||
|
||||
## 🚀 预期效果
|
||||
|
||||
修复后,分享功能应该完全正常:
|
||||
- ✅ 复制链接功能正常
|
||||
- ✅ 生成海报功能正常
|
||||
- ✅ 海报保存功能正常
|
||||
- ✅ 微信分享功能正常
|
||||
- ✅ 无控制台错误
|
||||
|
||||
## 🔄 如果问题仍然存在
|
||||
|
||||
如果修复后仍然有问题,请提供:
|
||||
1. 完整的错误信息
|
||||
2. 控制台日志输出
|
||||
3. 具体的操作步骤
|
||||
|
||||
这将帮助我们进一步诊断和解决问题。
|
||||
121
测试分享功能.md
Normal file
121
测试分享功能.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 分享功能测试指南
|
||||
|
||||
## 🧪 测试步骤
|
||||
|
||||
### 1. 准备测试环境
|
||||
1. 确保项目已正确编译
|
||||
2. 在微信开发者工具中打开项目
|
||||
3. 导航到房源详情页面
|
||||
|
||||
### 2. 测试复制链接功能
|
||||
**步骤**:
|
||||
1. 在房源详情页面点击"分享"按钮
|
||||
2. 在分享弹窗中选择"复制链接"
|
||||
3. 检查是否显示"链接复制成功"提示
|
||||
|
||||
**预期结果**:
|
||||
- 显示成功提示
|
||||
- 剪贴板中包含房源信息和链接
|
||||
|
||||
### 3. 测试生成海报功能
|
||||
**步骤**:
|
||||
1. 在房源详情页面点击"分享"按钮
|
||||
2. 在分享弹窗中选择"生成海报"
|
||||
3. 等待海报生成完成
|
||||
4. 检查海报是否正确显示
|
||||
5. 点击"保存海报图"按钮
|
||||
|
||||
**预期结果**:
|
||||
- 海报弹窗正常显示
|
||||
- 海报图片为房源的第一张图片
|
||||
- 保存功能正常工作
|
||||
|
||||
### 4. 测试微信分享功能
|
||||
**步骤**:
|
||||
1. 在房源详情页面点击"分享"按钮
|
||||
2. 选择"发送给微信好友"或"分享到朋友圈"
|
||||
3. 检查是否显示相应的提示
|
||||
|
||||
**预期结果**:
|
||||
- 显示"请点击右上角分享"提示
|
||||
- 全局分享数据已正确设置
|
||||
|
||||
## 🔍 调试信息
|
||||
|
||||
### 检查控制台输出
|
||||
如果遇到问题,请检查控制台是否有以下错误:
|
||||
- `TypeError: app.apiCall is not a function` - 应该已修复
|
||||
- `SettingModel.h5Url is not a function` - 复制链接会降级处理
|
||||
- 其他网络相关错误
|
||||
|
||||
### 检查数据状态
|
||||
在分享前确认以下数据:
|
||||
```javascript
|
||||
// 在控制台中检查
|
||||
console.log('房源信息:', this.form)
|
||||
console.log('图片列表:', this.swiperList)
|
||||
console.log('分享标题:', this.shareTitle)
|
||||
console.log('分享图片:', this.shareImageUrl)
|
||||
console.log('海报API:', this.posterApiCall)
|
||||
```
|
||||
|
||||
## 🐛 常见问题及解决方案
|
||||
|
||||
### 1. 海报生成失败
|
||||
**可能原因**:
|
||||
- 房源没有图片
|
||||
- 网络连接问题
|
||||
|
||||
**解决方案**:
|
||||
- 检查房源是否有图片
|
||||
- 确保网络连接正常
|
||||
- 查看控制台错误信息
|
||||
|
||||
### 2. 复制链接失败
|
||||
**可能原因**:
|
||||
- 剪贴板权限问题
|
||||
- H5地址获取失败
|
||||
|
||||
**解决方案**:
|
||||
- 已实现降级处理,会使用页面路径
|
||||
- 检查用户是否授权剪贴板权限
|
||||
|
||||
### 3. 微信分享无效
|
||||
**可能原因**:
|
||||
- 全局分享数据未正确设置
|
||||
- 小程序分享配置问题
|
||||
|
||||
**解决方案**:
|
||||
- 检查 `uni.$u.mpShare` 是否正确设置
|
||||
- 确认小程序分享权限配置
|
||||
|
||||
## ✅ 测试检查清单
|
||||
|
||||
- [ ] 分享按钮正常显示
|
||||
- [ ] 分享弹窗正常打开
|
||||
- [ ] 复制链接功能正常
|
||||
- [ ] 生成海报功能正常
|
||||
- [ ] 海报保存功能正常
|
||||
- [ ] 微信分享提示正常
|
||||
- [ ] 无控制台错误
|
||||
- [ ] 分享数据正确设置
|
||||
|
||||
## 📝 测试报告模板
|
||||
|
||||
**测试环境**:
|
||||
- 设备:[设备型号]
|
||||
- 系统:[操作系统版本]
|
||||
- 微信版本:[微信版本]
|
||||
- 开发者工具版本:[版本号]
|
||||
|
||||
**测试结果**:
|
||||
- 复制链接:✅/❌
|
||||
- 生成海报:✅/❌
|
||||
- 保存海报:✅/❌
|
||||
- 微信分享:✅/❌
|
||||
|
||||
**问题描述**:
|
||||
[如有问题,请详细描述]
|
||||
|
||||
**错误信息**:
|
||||
[如有错误,请提供控制台输出]
|
||||
Reference in New Issue
Block a user