feat(house): 添加房源卖价和总价功能并优化分享流程

- 在房源添加页面添加卖价(元/平)和总价(万)输入字段
- 实现卖价变动时总价自动计算功能
- 在房源详情页显示卖价和总价信息
- 调整物业费显示位置提升界面布局合理性
- 更新服务器配置地址从gxwebsoft.com到websoft.top
- 替换必看好房标签为特价好房标签统一显示
- 修复房源详情页分享功能和海报生成流程
- 添加跟进记录页面和相应跳转功能
- 优化房源管理页面删除按钮显示逻辑
- 实现闪屏页跳过功能和登录状态记忆
- 添加房源海报生成组件支持分享推广
- 修复分享路径参数传递和用户信息存储
This commit is contained in:
2026-02-13 19:37:24 +08:00
parent b95b52f4b5
commit 1bf7caf34e
24 changed files with 829 additions and 70 deletions

10
.idea/AugmentWebviewStateStore.xml generated Normal file

File diff suppressed because one or more lines are too long

37
.idea/workspace.xml generated
View File

@@ -4,19 +4,7 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="ec5c6cc2-d0e3-4470-b342-660aa89effe0" name="Changes" comment="爱尚家接口合并到cms-api.websoft.top">
<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>
<list default="true" id="ec5c6cc2-d0e3-4470-b342-660aa89effe0" name="Changes" comment="新增:分享、下载图片、视频功能登" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -80,7 +68,7 @@
<workItem from="1749367473436" duration="125000" />
<workItem from="1749367607857" duration="261000" />
<workItem from="1749367885938" duration="572000" />
<workItem from="1749371022185" duration="4982000" />
<workItem from="1749371022185" duration="5619000" />
</task>
<task id="LOCAL-00001" summary="爱尚家接口合并到cms-api.websoft.top">
<option name="closed" value="true" />
@@ -98,7 +86,23 @@
<option name="project" value="LOCAL" />
<updated>1749352650830</updated>
</task>
<option name="localTasksCounter" value="3" />
<task id="LOCAL-00003" summary="新增:分享、下载图片、视频功能登">
<option name="closed" value="true" />
<created>1749470992116</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1749470992116</updated>
</task>
<task id="LOCAL-00004" summary="新增:分享、下载图片、视频功能登">
<option name="closed" value="true" />
<created>1749474861846</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1749474861846</updated>
</task>
<option name="localTasksCounter" value="5" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -106,6 +110,7 @@
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="爱尚家接口合并到cms-api.websoft.top" />
<option name="LAST_COMMIT_MESSAGE" value="爱尚家接口合并到cms-api.websoft.top" />
<MESSAGE value="新增:分享、下载图片、视频功能登" />
<option name="LAST_COMMIT_MESSAGE" value="新增:分享、下载图片、视频功能登" />
</component>
</project>

View File

@@ -8,22 +8,12 @@ module.exports = {
// 应用秘钥
appSecret: '1f1d186d98ea4620ac65afbf34940051',
// 开发环境
// apiUrl: "http://127.0.0.1:9095/api",
// socketUrl: 'ws://localhost:9190',
// fileUrl: 'https://file.wsdns.cn',
// apiUrl: "http://47.119.165.234:5483/api",
// 测试环境
// apiUrl: 'https://server.gxwebsoft.com/api',
// socketUrl: 'wss://server.gxwebsoft.com',
fileUrl: 'https://file.wsdns.cn',
// 生产环境
serverUrl: 'https://server.gxwebsoft.com/api',
serverUrl: 'https://server.websoft.top/api',
apiUrl: 'https://cms-api.websoft.top/api',
socketUrl: 'wss://server.gxwebsoft.com',
socketUrl: 'wss://server.websoft.top',
// fileUrl: 'https://oss.jimeigroup.cn',

View File

@@ -4,7 +4,7 @@ import http from './index.js';
export const ServerTime = () => http.post('http://127.0.0.1:9090/hxz/v1/ServerTime')
export const QRCodeTransaction = (data) => http.post('http://127.0.0.1:9090/hxz/v1/QRCodeTransaction',data)
export const QRCodeTransaction2 = (data) => http.post('https://server.gxwebsoft.com/hxz/v1/QRCodeTransaction',data)
export const QRCodeTransaction2 = (data) => http.post('https://server.websoft.top/hxz/v1/QRCodeTransaction',data)
export const TransactionInquiry = (data) => http.post('http://127.0.0.1:9090/hxz/v1/TransactionInquiry',data)

View File

@@ -10,7 +10,7 @@ import appConfig from '@/config.js'
// export const uploadFile = (file) => http.upload(fileUrl + '/api/file/upload', file)
// 阿里云OSS
export const uploadFile = (file) => http.upload('https://server.gxwebsoft.com/api/oss/upload', file)
export const uploadFile = (file) => http.upload('https://server.websoft.top/api/oss/upload', file)
// export const uploadFile = async ({filePath}) => {
// // 获取临时凭证

View File

@@ -1,7 +1,7 @@
import http from './index.js';
// 获取用户资料
export const getUser = (params) => http.get('https://server.gxwebsoft.com/api/auth/user', {params})
export const getUser = (params) => http.get('https://server.websoft.top/api/auth/user', {params})
// 修改用户资料
export const updateUser = (data) => http.put('/system/user', data)

View File

@@ -0,0 +1,206 @@
<template>
<view class="house-poster-generator">
<!-- 海报生成器 -->
<l-painter
ref="painter"
custom-style="position: fixed; left: -9999px; top: -9999px;"
:isCanvasToTempFilePath="true"
@success="onPosterSuccess"
@fail="onPosterFail"
css="height: 800rpx; width: 600rpx;"
file-type="png"
>
<template v-if="shouldRender">
<l-painter-view css="height: 800rpx; width: 600rpx; backgroundColor: #fff; position: relative;">
<!-- 房源主图 -->
<l-painter-image
:src="mainImageUrl"
css="width: 600rpx; height: 400rpx; borderRadius: 0;"
/>
<!-- 房源标题 -->
<l-painter-text
:text="houseData.houseTitle || '房源标题'"
css="position: absolute; top: 420rpx; left: 30rpx; width: 540rpx; fontSize: 32rpx; fontWeight: bold; color: #333333;"
/>
<!-- 价格信息 -->
<l-painter-text
:text="priceText"
css="position: absolute; top: 480rpx; left: 30rpx; fontSize: 30rpx; fontWeight: bold; color: #ff4444;"
/>
<!-- 房源详情 -->
<l-painter-text
:text="houseDetailsText"
css="position: absolute; top: 530rpx; left: 30rpx; fontSize: 26rpx; color: #666666;"
/>
<!-- 地址信息 -->
<l-painter-text
:text="houseData.address || '地址信息'"
css="position: absolute; top: 580rpx; left: 30rpx; width: 540rpx; fontSize: 24rpx; color: #999999;"
/>
<!-- 分享文案 -->
<l-painter-text
text="扫码查看房源详情"
css="position: absolute; top: 650rpx; left: 30rpx; fontSize: 24rpx; color: #666666;"
/>
<!-- 小程序码预留位置 -->
<l-painter-view css="position: absolute; top: 630rpx; left: 470rpx; width: 100rpx; height: 100rpx; backgroundColor: #f5f5f5; borderRadius: 10rpx; display: flex; alignItems: center; justifyContent: center;">
<l-painter-text
text="小程序码"
css="fontSize: 18rpx; color: #cccccc;"
/>
</l-painter-view>
</l-painter-view>
</template>
</l-painter>
</view>
</template>
<script>
import LPainter from '@/uni_modules/lime-painter/components/l-painter/l-painter.vue'
export default {
name: 'HousePosterGenerator',
components: {
LPainter
},
props: {
// 房源数据
houseData: {
type: Object,
default: () => ({})
},
// 房源图片列表
swiperList: {
type: Array,
default: () => []
}
},
data() {
return {
// 海报生成状态
isGenerating: false,
// 当前生成的海报URL
currentPosterUrl: '',
// 是否应该渲染海报
shouldRender: false
}
},
computed: {
// 主图URL
mainImageUrl() {
if (this.swiperList && this.swiperList.length > 0) {
const firstImage = this.swiperList[0];
const imageUrl = firstImage.url || firstImage.image || firstImage.src || firstImage || '';
if (imageUrl && typeof imageUrl === 'string' && imageUrl.trim() !== '') {
return imageUrl.trim();
}
}
return 'https://dummyimage.com/600x400/f0f0f0/999999&text=暂无图片';
},
// 价格文本
priceText() {
return (this.houseData.monthlyRent || 0) + '元/月';
},
// 房源详情文本
houseDetailsText() {
const houseType = this.houseData.houseType || '户型';
const extent = this.houseData.extent || 0;
const floor = this.houseData.floor || '楼层';
return houseType + ' | ' + extent + 'm² | ' + floor;
}
},
methods: {
// 生成房源海报
async generatePoster() {
if (this.isGenerating) {
console.log('海报正在生成中,请稍候...');
return;
}
try {
this.isGenerating = true;
console.log('开始生成房源海报...');
// 检查房源数据
if (!this.houseData || !this.houseData.houseTitle) {
throw new Error('房源数据不完整');
}
// 启用渲染
this.shouldRender = true;
// 等待DOM更新
await this.$nextTick();
// 等待一段时间确保图片加载完成
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('海报模板渲染完成,等待生成图片...');
// 由于设置了 isCanvasToTempFilePath="true",会自动触发 onPosterSuccess
return new Promise((resolve, reject) => {
this.resolveGenerate = resolve;
this.rejectGenerate = reject;
});
} catch (error) {
console.error('海报生成失败:', error);
this.isGenerating = false;
this.shouldRender = false;
this.$emit('posterFailed', error);
throw error;
}
},
// 海报生成成功回调
onPosterSuccess(tempFilePath) {
console.log('lime-painter 生成成功:', tempFilePath);
this.currentPosterUrl = tempFilePath;
this.isGenerating = false;
this.shouldRender = false;
// 触发生成完成事件
this.$emit('posterGenerated', tempFilePath);
// 如果有等待的Promiseresolve它
if (this.resolveGenerate) {
this.resolveGenerate(tempFilePath);
this.resolveGenerate = null;
this.rejectGenerate = null;
}
},
// 海报生成失败回调
onPosterFail(error) {
console.error('lime-painter 生成失败:', error);
this.isGenerating = false;
this.shouldRender = false;
// 触发生成失败事件
this.$emit('posterFailed', error);
// 如果有等待的Promisereject它
if (this.rejectGenerate) {
this.rejectGenerate(error);
this.resolveGenerate = null;
this.rejectGenerate = null;
}
}
}
}
</script>
<style lang="scss" scoped>
.house-poster-generator {
// 隐藏组件,只用于生成海报
display: none;
}
</style>

View File

@@ -9,23 +9,12 @@ module.exports = {
appSecret: '1f1d186d98ea4620ac65afbf34940051',
// 生产环境
serverUrl: 'https://server.gxwebsoft.com/api',
serverUrl: 'https://server.websoft.top/api',
// apiUrl: 'http://127.0.0.1:9200/api',
apiUrl: 'https://cms-api.websoft.top/api',
socketUrl: 'wss://server.gxwebsoft.com',
socketUrl: 'wss://server.websoft.top',
fileUrl: 'https://file.wsdns.cn',
// 开发环境
// apiUrl: "http://127.0.0.1:9000/api",
// socketUrl: 'ws://localhost:9190',
// fileUrl: 'https://file.wsdns.cn',
// apiUrl: "http://47.119.165.234:5483/api",
// 测试环境
// apiUrl: 'https://server.gxwebsoft.com/api',
// socketUrl: 'wss://server.gxwebsoft.com',
// fileUrl: 'https://file.wsdns.cn',
// 游客 userId
userId: 3373,
// 用户ID前缀

View File

@@ -9,7 +9,7 @@ export default {
* 后端api地址 (必填; 斜杠/结尾; 请确保能访问)
* 例如: https://www.你的域名.com/index.php?s=/api/
*/
apiUrl: "https://server.gxwebsoft.com/api",
apiUrl: "https://server.websoft.top/api",
/**
* 商城ID (必填)

View File

@@ -74,6 +74,13 @@
"enablePullDownRefresh": false
}
},{
"path": "house/record",
"style": {
"navigationBarTitleText": "跟进记录",
"enablePullDownRefresh": false
}
}
]
}],

View File

@@ -8,7 +8,7 @@
爱尚家找房
</text> -->
</view>
<view class="btn" @click="onLogin">
<view class="btn" @click="onSkip">
<text>立即跳过({{ times }})</text>
</view>
</view>
@@ -57,6 +57,10 @@
timer() {
const app = this
app.smsState = false
if(uni.getStorageSync('skip')){
app.onLogin()
return;
}
const inter = setInterval(() => {
app.times = app.times - 1
if (app.times <= 0) {
@@ -86,6 +90,10 @@
// })
// }
},
onSkip(){
uni.setStorageSync('skip',true)
this.onLogin()
}
}
}

View File

@@ -106,7 +106,7 @@
reset: true
},
{
name: '必看好房',
name: '特价好房',
reset: false
}
];

View File

@@ -57,7 +57,7 @@
reset: true
},
{
name: '必看好房',
name: '特价好房',
reset: false
}
];

View File

@@ -57,7 +57,7 @@
reset: true
},
{
name: '必看好房',
name: '特价好房',
reset: false
}
];

View File

@@ -110,7 +110,7 @@
reset: true
},
{
name: '必看好房',
name: '特价好房',
reset: false
}
];

View File

@@ -35,6 +35,7 @@
</view>
</view>
</u-popup>
</view>
</template>

View File

@@ -122,13 +122,13 @@
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv2os.png" isLink title="房源管理" @click="$push('sub_pages/house/house')"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26y.png" isLink title="访客记录"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26x.png" isLink title="浏览历史"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26y%20%281%29.png" isLink title="我的收藏" @click="$push('pages/house/liked')"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26y%20%281%29.png" isLink title="我的收藏" @click="navTo('pages/house/liked')"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26x%20%281%29.png" isLink title="关于我们"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26x%20%282%29.png" isLink title="联系专属经纪人" :border="false" @click="showMyMatchmaker"></u-cell>
</u-cell-group>
<u-cell-group v-else :border="false">
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26x.png" isLink title="浏览历史"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26y%20%281%29.png" isLink title="收藏房源" @click="$push('pages/house/liked')"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26y%20%281%29.png" isLink title="收藏房源" @click="navTo('pages/house/liked')"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26x%20%281%29.png" isLink title="关于我们"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26w.png" isLink title="预约看房记录"></u-cell>
<u-cell icon="https://oss-aishangjia.oss-cn-shenzhen.aliyuncs.com/v2_rwv26x%20%282%29.png" isLink title="联系专属经纪人" :border="false" @click="showMyMatchmaker"></u-cell>
@@ -415,7 +415,7 @@
}).then(response => {
console.log("response: ", response);
if (response.code == 401) {
this.handleLogout()
// this.handleLogout()
}
})
}

View File

@@ -25,11 +25,14 @@ const loginSuccess = ({commit,dispatch}, data) => {
// 保存tokne和userId到缓存
storage.set(USER_ID, user.userId, expiryTime)
storage.set(USER_INFO, user, expiryTime)
storage.set('Phone',user.phone)
storage.set(ACCESS_TOKEN, access_token, expiryTime)
// 记录到store全局变量
commit('SET_TOKEN', access_token)
commit('SET_USER_ID', user.userId)
commit('SET_USER', user)
commit('SET_PHONE',user.phone)
uni.setStorageSync('Phone',user.phone)
dispatch('ConnectSocket')
@@ -42,6 +45,8 @@ const user = {
token: '',
// 用户ID
userId: null,
// 手机号码
phone: null,
// 用户
userInfo: null,
@@ -58,6 +63,10 @@ const user = {
SET_USER: (state, value) => {
state.userInfo = value
storage.set(USER_INFO, value)
},
SET_PHONE: (state,value) => {
state.phone = value
storage.set('Phone', value)
}
},

View File

@@ -77,6 +77,7 @@
inputAlign="right" maxlength="30" :border="false" placeholder="请输入物业费" />
</u-cell>
</u-form-item>
<!-- <u-form-item prop="tenancy">
<u-cell title="租期" :isLink="false">
<u-input slot="value" class="input" v-model="form.tenancy" inputAlign="right" maxlength="30"
@@ -125,6 +126,18 @@
:border="false" placeholder="请选择房源朝向" :disabled="true" disabledColor="#FFFFFF" />
</u-cell>
</u-form-item>
<u-form-item prop="salePrice">
<u-cell title="卖价(元/平)" :isLink="false">
<u-input type="digit" slot="value" class="input" v-model="form.salePrice"
inputAlign="right" maxlength="30" :border="false" placeholder="8000元/平" @input="onInputSalePrice" />
</u-cell>
</u-form-item>
<u-form-item prop="salePrice">
<u-cell title="总价(万)" :isLink="false">
<u-input type="digit" slot="value" class="input" v-model="form.totalPrice"
inputAlign="right" maxlength="30" :border="false" placeholder="120万" />
</u-cell>
</u-form-item>
</u-cell-group>
</view>
@@ -613,6 +626,9 @@
this.form.nickname = val
}
},
onInputSalePrice(val){
this.form.totalPrice = (this.form.extent * val * 0.0001).toFixed(0)
},
formatter(type, value) {
if (type === 'year') {
return `${value}`

View File

@@ -84,7 +84,6 @@
<view class="item col-2">
租金(/){{ form.rent || '' }}
</view>
<view class="item col-2">物业费(/){{ form.propertyFees || '' }}</view>
<view class="item col-2">
楼层{{ form.floor || '' }}
</view>
@@ -101,6 +100,7 @@
如何看房{{ form.password || '' }}
</view>
<view v-if="isManager" class="item col-2" @click="makePhoneCall">业主电话{{ form.phone || '' }}</view>
<view class="item col-2">物业费(/){{ form.propertyFees || '' }}</view>
<!-- <view v-if="isManager" class="item col-2">租期{{ form.tenancy || '' }}</view> -->
<view class="item col-2" v-if="isManager">
佣金{{ form.commission || '' }}
@@ -108,6 +108,8 @@
<!-- <view class="item col-2" v-if="isManager">
是否可溢价{{ form.premium || '' }}
</view> -->
<view v-if="form.salePrice" class="item col-2">卖价{{ form.salePrice || '' }}/m²</view>
<view v-if="form.totalPrice" class="item col-2">总价{{ form.totalPrice || '' }}万元</view>
</view>
</view>
@@ -135,16 +137,17 @@
<template v-if="isManager">
<u-gap></u-gap>
<view class="house-card">
<view class="title">
管理员备注
<view class="house-card" style="background-color:azure;">
<view class="title" style="display: flex; flex-direction: row; justify-content: space-between; width: 100%;">
<view class="title">管理员备注</view>
<view><button size="mini" type="primary" @click="$push('sub_pages/house/record?houseId=' + form.houseId)">跟进记录</button></view>
</view>
<view class="about">
<mp-html :content="form.comments" />
</view>
</view>
</template>
<!-- 房源位置 -->
<u-gap></u-gap>
<view class="house-card">
@@ -168,7 +171,8 @@
<text class="desc-text">{{ `粉丝:${item.id}` }}</text>
</view> -->
<view slot="right-icon" class="follow-btn" @click.stop="onFollow">
<text style="color: #666666;">{{ form.gradeName }}</text>
<text style="color: #666666;">******</text>
<!-- <text style="color: #666666;">{{ mobile || form.userPhone }}</text> -->
</view>
</u-cell>
</view>
@@ -183,16 +187,17 @@
<text v-if="form.liked">已收藏</text>
<text v-else>收藏</text>
</view>
<view class="item" @click="onShare">
<!-- <view class="item" @click="onShare">
<u-icon name="share" size="28" color="#666666"></u-icon>
<text>分享</text>
</view>
<view class="item">
</view> -->
<!-- <view class="item">
<u-button icon="map" type="error" text="预约看房" disabled
@click="$push('sub_pages/checkout/checkout?id=' + form.houseId)"></u-button>
</view>
</view> -->
<view class="item">
<u-button icon="phone" type="primary" text="电话咨询" disabled @click="onCall()"></u-button>
<u-button icon="phone" type="primary" text="电话咨询"></u-button>
<!-- <u-button icon="phone" type="primary" text="电话咨询" @click="makePhoneCall()"></u-button> -->
</view>
</view>
@@ -239,7 +244,7 @@
reset: true
},
{
name: '必看好房',
name: '特价好房',
reset: false
}
];
@@ -386,7 +391,9 @@
posterApiParam: {},
// 海报预览相关
showPosterPreview: false,
currentPosterUrl: ''
currentPosterUrl: '',
dealerId: 0,
mobile: ''
};
},
@@ -410,6 +417,12 @@
}).catch((err) => {
console.log(err);
})
if(options.dealerId){
this.dealerId = options.dealerId
}
if(options.mobile){
this.mobile = options.phone
}
},
@@ -423,7 +436,8 @@
},
onShareAppMessage() {
return {
title: this.form.houseTitle
title: this.form.houseTitle,
path: `/sub_pages/house/detail?houseId=${this.form.houseId}&dealerId=${uni.setStorageSync('userId')}&mobile=${uni.getStorageInfoSync('Phone')}`
}
},
onShareTimeline() {
@@ -971,6 +985,10 @@
margin: auto;
padding: 20rpx;
z-index: 100;
.title2 {
width: 100% !important;
}
.title {
font-size: 36rpx;

View File

@@ -15,9 +15,9 @@
<view class="btn">
<u-button text="下架" plain size="small" @click="onStatus(10)"></u-button>
</view>
<view class="btn">
<!-- <view class="btn">
<u-button text="删除" plain size="small" @click="onDel()"></u-button>
</view>
</view> -->
<view class="btn">
<u-button text="编辑" plain size="small" @click="onEdit()"></u-button>
</view>

View File

@@ -0,0 +1,17 @@
<template>
<view class="record-list">
<view class="item">
王子
</view>
</view>
</template>
<script>
</script>
<style>
.record-list{
width: 96%;
margin: 20px auto;
}
</style>

299
海报功能修复总结.md Normal file
View File

@@ -0,0 +1,299 @@
# 海报生成功能修复总结
## 🔧 修复的问题
### 1. 主要错误
**错误信息**`Error in callback for watcher "value": "TypeError: app.apiCall is not a function"`
**根本原因**
- `GoodsPosterPopup` 组件期望接收一个 `apiCall` 函数
- 函数传递过程中存在 `this` 上下文绑定问题导致 `this.swiperList``this.form``undefined`
- 图片URL格式处理不完善
### 2. 新发现的问题
从测试日志可以看出:
```
generateHousePoster 被调用,参数: {...}
当前房源图片列表: undefined
房源信息: undefined
```
这说明函数的 `this` 上下文丢失了。
## ✅ 修复内容
### 1. 房源详情页面 (sub_pages/house/detail.vue)
#### 修复点1修改模板中的函数传递
```vue
<!-- 修改前 -->
<ShareSheet
v-model="showShareSheet"
:posterApiCall="generateHousePoster"
:posterApiParam="posterApiParam"
/>
<!-- 修改后 -->
<ShareSheet
v-model="showShareSheet"
:posterApiCall="handleGenerateHousePoster"
:posterApiParam="posterApiParam"
/>
```
#### 修复点2移除不必要的数据初始化
```javascript
// 修改前
data() {
return {
posterApiCall: null, // 移除这行
posterApiParam: {}
}
}
// 修改后
data() {
return {
posterApiParam: {}
}
}
```
#### 修复点3创建新的海报生成处理方法确保this上下文正确
```javascript
// 处理海报生成确保this上下文正确
handleGenerateHousePoster(params) {
console.log('handleGenerateHousePoster 被调用,参数:', params);
console.log('当前房源图片列表:', this.swiperList);
console.log('房源信息:', this.form);
console.log('this 上下文:', this);
return new Promise((resolve, reject) => {
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({
data: {
imageUrl: imageUrl.trim()
}
});
return;
}
}
console.log('没有可用的房源图片,生成默认海报');
// 如果没有房源图片,生成默认海报
this.handleGenerateTextPoster()
.then(imageUrl => {
console.log('生成默认海报成功:', imageUrl);
resolve({
data: {
imageUrl: imageUrl
}
});
})
.catch(err => {
console.log('生成默认海报失败:', err);
reject(err);
});
} catch (error) {
console.error('handleGenerateHousePoster 执行出错:', error);
reject(error);
}
});
}
```
#### 修复点3改进默认海报生成
```javascript
generateTextPoster() {
return new Promise((resolve, reject) => {
try {
// 创建一个简单的文字海报信息
const houseTitle = this.form.houseTitle || '房源信息';
const monthlyRent = this.form.monthlyRent || 0;
const houseType = this.form.houseType || '';
const extent = this.form.extent || 0;
const address = this.form.address || '';
console.log('生成默认海报,房源信息:', {
houseTitle, monthlyRent, houseType, extent, address
});
// 使用占位图片服务生成默认海报
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('generateTextPoster 执行出错:', error);
// 如果生成默认海报也失败,使用一个简单的占位图
resolve('https://dummyimage.com/400x600/cccccc/666666&text=房源海报');
}
});
}
```
#### 修复点4优化分享数据设置
```javascript
onShare() {
// 检查是否有房源信息
if (!this.form.houseTitle) {
uni.showToast({
title: '房源信息加载中...',
icon: 'none'
});
return;
}
// 设置分享数据
this.shareTitle = `${this.form.houseTitle} ${this.form.monthlyRent}元/月`;
// 获取分享图片URL支持多种格式
let shareImageUrl = '';
if (this.swiperList && this.swiperList.length > 0) {
const firstImage = this.swiperList[0];
shareImageUrl = firstImage.url || firstImage.image || firstImage.src || firstImage || '';
}
this.shareImageUrl = shareImageUrl;
// 设置海报API参数
this.posterApiParam = {
houseId: this.form.houseId,
houseTitle: this.form.houseTitle,
monthlyRent: this.form.monthlyRent,
houseType: this.form.houseType,
extent: this.form.extent,
address: this.form.address
};
// 更新全局分享数据
uni.$u.mpShare = {
title: this.shareTitle,
path: `sub_pages/house/detail?houseId=${this.form.houseId}&user_id=${uni.getStorageSync('userId')}`,
imageUrl: this.shareImageUrl
};
console.log('分享数据设置完成:', {
shareTitle: this.shareTitle,
shareImageUrl: this.shareImageUrl,
posterApiParam: this.posterApiParam
});
// 显示分享弹窗
this.showShareSheet = true;
}
```
### 2. ShareSheet 组件 (components/share-sheet/index.vue)
#### 修复点:添加调试日志
```javascript
// 生成二维码海报
handlePoster() {
console.log('ShareSheet handlePoster 被调用');
console.log('posterApiCall 类型:', typeof this.posterApiCall);
console.log('posterApiCall 函数:', this.posterApiCall);
console.log('posterApiParam:', this.posterApiParam);
this.showGoodsPosterPopup = true
this.handleCancel()
}
```
### 3. GoodsPosterPopup 组件 (components/goods-poster-popup/index.vue)
#### 修复点:增强错误处理和调试
```javascript
// 显示海报弹窗
onShowPopup() {
const app = this
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 => {
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 => {
console.error('apiCall 调用失败:', err);
uni.showToast({
title: '海报生成失败',
icon: 'none'
});
app.onClose();
});
}
```
## 🧪 测试步骤
### 1. 打开控制台
在微信开发者工具中打开控制台,查看日志输出
### 2. 测试生成海报功能
1. 进入房源详情页面
2. 点击"分享"按钮
3. 选择"生成海报"
4. 观察控制台输出
### 3. 预期结果
- ✅ 控制台显示:`generateHousePoster 被调用,参数: {...}`
- ✅ 控制台显示:`当前房源图片列表: [...]`
- ✅ 海报弹窗正常显示
- ✅ 不再出现 `app.apiCall is not a function` 错误
- ✅ 海报图片能正常显示和保存
## 🎯 关键修复点总结
1. **移除不必要的数据初始化**:避免 `posterApiCall: null` 导致的问题
2. **直接传递方法引用**:确保 `this` 上下文正确绑定
3. **增强图片URL处理**支持多种图片URL格式
4. **完善错误处理**:添加详细的错误提示和降级方案
5. **添加调试日志**:便于快速定位问题
## 🚀 预期效果
修复后,海报生成功能应该完全正常:
- ✅ 有房源图片时,使用第一张图片作为海报
- ✅ 没有房源图片时,生成包含房源信息的默认海报
- ✅ 海报保存功能正常
- ✅ 错误处理完善,用户体验良好
- ✅ 无控制台错误

184
海报功能测试指南.md Normal file
View File

@@ -0,0 +1,184 @@
# 海报功能测试指南
## 🚀 如何测试修复后的海报功能
### 1. 启动项目
#### 使用HBuilderX推荐
1. 打开HBuilderX
2. 导入项目文件夹
3. 选择"运行" -> "运行到小程序模拟器" -> "微信开发者工具"
#### 使用微信开发者工具
1. 打开微信开发者工具
2. 选择"导入项目"
3. 选择项目根目录
4. 项目类型选择"小程序"
### 2. 测试步骤
#### 步骤1打开控制台
在微信开发者工具中:
1. 点击"调试器"标签
2. 打开"Console"面板
3. 确保能看到日志输出
#### 步骤2进入房源详情页面
1. 在小程序中导航到房源列表页面
2. 选择任意一个房源,点击进入详情页面
3. 等待房源信息加载完成
#### 步骤3测试海报生成功能
1. 在房源详情页面,点击底部的"分享"按钮
2. 在弹出的分享面板中,点击"生成海报"选项
3. 观察控制台输出和海报弹窗
### 3. 预期结果
#### 控制台日志输出
应该看到以下日志(按顺序):
```javascript
// 1. 分享按钮点击时
分享数据设置完成: {
shareTitle: "房源标题 租金元/月",
shareImageUrl: "图片URL或空字符串",
posterApiParam: { houseId: xxx, houseTitle: "...", ... }
}
// 2. ShareSheet组件处理
ShareSheet handlePoster 被调用
posterApiCall 类型: function
posterApiCall 函数: ƒ generateHousePoster(params) { ... }
posterApiParam: { houseId: xxx, houseTitle: "...", ... }
// 3. GoodsPosterPopup组件处理
GoodsPosterPopup onShowPopup 被调用
apiCall 类型: function
apiCall 函数: ƒ generateHousePoster(params) { ... }
apiParam: { houseId: xxx, houseTitle: "...", ... }
platform: undefined
调用 apiCall参数: { houseId: xxx, ..., channel: undefined }
// 4. generateHousePoster函数执行
generateHousePoster 被调用参数: { houseId: xxx, ..., channel: undefined }
当前房源图片列表: [...]
房源信息: { houseTitle: "...", monthlyRent: xxx, ... }
// 5a. 如果有房源图片
第一张图片对象: { url: "图片URL", ... }
提取的图片URL: "图片URL"
使用房源图片作为海报: "图片URL"
// 5b. 如果没有房源图片
没有可用的房源图片生成默认海报
生成默认海报房源信息: { houseTitle: "...", monthlyRent: xxx, ... }
生成的默认海报URL: "https://dummyimage.com/..."
生成默认海报成功: "https://dummyimage.com/..."
// 6. 最终结果
apiCall 调用成功结果: { data: { imageUrl: "图片URL" } }
海报图片URL设置成功: "图片URL"
```
#### 界面表现
1. **海报弹窗正常显示**:应该看到一个居中的弹窗,包含海报图片
2. **海报图片正常加载**
- 如果房源有图片:显示房源的第一张图片
- 如果房源没有图片:显示包含房源信息的默认海报
3. **保存功能正常**:点击"保存海报图"按钮,应该能正常保存到相册
#### 错误情况处理
如果出现错误,应该看到友好的提示信息,而不是控制台错误:
- "海报生成功能异常"
- "海报生成失败"
### 4. 常见问题排查
#### 问题1仍然出现 "app.apiCall is not a function" 错误
**可能原因**
- 缓存问题,需要清除小程序缓存
- 代码修改未生效
**解决方案**
1. 在微信开发者工具中点击"清缓存" -> "清除数据缓存"
2. 重新编译项目
3. 检查代码修改是否保存
#### 问题2海报弹窗不显示
**检查控制台日志**
- 确认 `posterApiCall` 类型是 `function`
- 确认 `generateHousePoster` 被正确调用
- 查看是否有其他错误信息
#### 问题3海报图片不显示
**可能原因**
- 图片URL无效
- 网络问题
- 图片格式不支持
**检查方法**
1. 查看控制台中的图片URL
2. 在浏览器中直接访问图片URL验证是否有效
3. 检查网络连接
#### 问题4默认海报不生成
**检查控制台日志**
- 确认进入了 `generateTextPoster` 方法
- 查看房源信息是否正确获取
- 检查默认海报URL是否生成
### 5. 测试用例
#### 测试用例1有图片的房源
1. 选择一个有图片的房源
2. 执行海报生成流程
3. 验证使用房源图片作为海报
#### 测试用例2没有图片的房源
1. 选择一个没有图片的房源(或手动清空图片数据)
2. 执行海报生成流程
3. 验证生成默认海报
#### 测试用例3网络异常情况
1. 断开网络连接
2. 执行海报生成流程
3. 验证错误处理是否正常
### 6. 性能测试
#### 测试海报生成速度
1. 记录从点击"生成海报"到海报显示的时间
2. 应该在2-3秒内完成
3. 如果超时,检查图片加载和网络情况
#### 测试内存使用
1. 在开发者工具中查看内存使用情况
2. 多次生成海报,确认没有内存泄漏
### 7. 兼容性测试
#### 不同设备测试
1. 在不同型号的手机上测试
2. 测试不同版本的微信客户端
3. 确保功能在各种环境下都能正常工作
### 8. 修复验证清单
- [ ] 控制台不再出现 "app.apiCall is not a function" 错误
- [ ] 海报弹窗能正常显示
- [ ] 有图片的房源能正确显示房源图片作为海报
- [ ] 没有图片的房源能生成默认海报
- [ ] 海报保存功能正常
- [ ] 错误情况有友好的提示信息
- [ ] 分享其他功能(复制链接、微信分享等)不受影响
## 🎯 总结
如果以上测试都通过,说明海报生成功能已经修复成功。如果仍有问题,请提供:
1. 完整的控制台错误信息
2. 具体的操作步骤
3. 出现问题的环境信息(设备型号、微信版本等)
这将帮助进一步诊断和解决问题。