租房小程序前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

551 lines
16 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="container">
  3. <view class="header">
  4. <view class="title">其他农村资源录入</view>
  5. </view>
  6. <view class="form-container">
  7. <uv-form labelPosition="left" :model="form" :rules="rules" ref="form" labelWidth="80" labelStyle="font-size:28rpx;">
  8. <!-- 必填项 -->
  9. <view class="section-title">基本信息必填</view>
  10. <!-- 1. 出租/流转 -->
  11. <uv-form-item label="类型" prop="type" required>
  12. <uv-radio-group v-model="form.type" placement="row">
  13. <uv-radio name="rent" label="出租"></uv-radio>
  14. <uv-radio name="transfer" customStyle="margin-left:30rpx;" label="流转"></uv-radio>
  15. </uv-radio-group>
  16. </uv-form-item>
  17. <!-- 2. 地址 -->
  18. <uv-form-item label="地址" prop="address" required @click="handleAddressSelect()">
  19. <uv-input v-model="form.address" @click="handleAddressSelect()" disabled disabledColor="#ffffff" placeholder="请选择资源位置" border="none">
  20. </uv-input>
  21. <template v-slot:right>
  22. <uv-icon name="arrow-right"></uv-icon>
  23. </template>
  24. </uv-form-item>
  25. <!-- 3. 联系人和电话 -->
  26. <uv-form-item label="联系人" prop="contactName" required>
  27. <uv-input v-model="form.contactName" type="text" placeholder="请输入联系人姓名" customStyle="border-radius: 5px;margin-top:5px;background-color: #f5f5f5;padding:5px 10px;"></uv-input>
  28. </uv-form-item>
  29. <uv-form-item label="联系电话" prop="contactPhone" required>
  30. <uv-input v-model="form.contactPhone" type="number" placeholder="请输入联系电话" customStyle="border-radius: 5px;margin-top:5px;background-color: #f5f5f5;padding:5px 10px;"></uv-input>
  31. </uv-form-item>
  32. <!-- 4. 照片和视频 -->
  33. <uv-form-item label="资源照片" labelWidth="250" prop="images" labelPosition="top" required>
  34. <view class="upload-tip">请上传资源相关照片</view>
  35. <uv-upload customStyle="margin-top:20rpx;" :fileList="form.images" @afterRead="afterImageRead" @delete="deleteImagePic" name="1"
  36. multiple :maxCount="10"></uv-upload>
  37. </uv-form-item>
  38. <uv-form-item label="资源视频" labelWidth="250" prop="videos" labelPosition="top" required>
  39. <view class="upload-tip">请上传资源视频时长不超过1分钟最多上传3个视频</view>
  40. <uv-upload customStyle="margin-top:20rpx;" accept="video" :fileList="form.videos" @afterRead="afterVideoRead" @delete="deleteVideoPic" name="1"
  41. multiple :maxCount="3"></uv-upload>
  42. </uv-form-item>
  43. <!-- 5. 价格 -->
  44. <uv-form-item label="价格" prop="price" required>
  45. <view class="price-container">
  46. <uv-input v-model="form.price" type="digit" placeholder="请输入价格" customStyle="border-radius: 5px;margin-top:5px;background-color: #f5f5f5;padding:5px 10px;flex:1;"></uv-input>
  47. <text class="price-unit"></text>
  48. </view>
  49. <view class="input-tip">请输入数字支持小数点后两位</view>
  50. </uv-form-item>
  51. <!-- 6. 资源介绍 -->
  52. <uv-form-item label="资源介绍" prop="description" labelPosition="top" required>
  53. <uv-input
  54. v-model="form.description"
  55. type="textarea"
  56. placeholder="请详细介绍资源类型、用途、特色、优势等信息"
  57. customStyle="border-radius: 5px;margin-top:5px;background-color: #f5f5f5;padding:10px;"
  58. :autoHeight="true"
  59. :maxlength="1000"
  60. ></uv-input>
  61. <view class="char-count">{{form.description.length}}/1000</view>
  62. <view class="input-tip">请详细描述资源的类型用途特色规模优势等</view>
  63. </uv-form-item>
  64. <!-- 分类标识 -->
  65. <uv-form-item label="分类标识" prop="classId" @click="handleClass()">
  66. <uv-input v-model="form.className" @click="handleClass()" disabled disabledColor="#ffffff" placeholder="选择分类标识" border="none">
  67. </uv-input>
  68. <template v-slot:right>
  69. <uv-icon name="arrow-right"></uv-icon>
  70. </template>
  71. </uv-form-item>
  72. <!-- 代理协议 -->
  73. <view class="section-title">代理协议</view>
  74. <view class="agreement-container">
  75. <view class="agreement-content">
  76. <text class="agreement-title">代理协议</text>
  77. <text class="agreement-text">
  78. 本人同意授权我平台发布信息代理销售等本人承诺所提供的信息真实有效并同意平台进行相关推广和销售代理
  79. </text>
  80. </view>
  81. <uv-checkbox v-model="form.agreeProtocol"
  82. name="1"
  83. shape="circle" activeColor="#1EC77A"
  84. @change="onAgreementChange">
  85. <text class="agreement-label">我已阅读并同意代理协议</text>
  86. </uv-checkbox>
  87. </view>
  88. <uv-form-item>
  89. <uv-button type="primary" text="提交信息" customStyle="margin-top: 30px;background-color: #1EC77A;border-radius: 30px;" @click="submit"></uv-button>
  90. </uv-form-item>
  91. </uv-form>
  92. </view>
  93. <!-- 分类选择器 -->
  94. <uv-picker ref="picker" :columns="columns" @confirm="confirm"></uv-picker>
  95. </view>
  96. </template>
  97. <script>
  98. import { saveOrUpdateHouse, houseType } from "@/common/api.js"
  99. export default {
  100. data() {
  101. return {
  102. commonClass: '', // 所属分类ID
  103. form: {
  104. type: '', // 出租/流转
  105. address: '', // 地址
  106. contactName: '', // 联系人
  107. contactPhone: '', // 联系电话
  108. images: [], // 资源照片
  109. videos: [], // 资源视频
  110. price: '', // 价格(开放式)
  111. description: '', // 资源介绍
  112. agreeProtocol: false, // 同意代理协议
  113. longitude: '', // 经度
  114. latitude: '', // 纬度
  115. classId: '', // 分类标识
  116. className: '' // 分类名称
  117. },
  118. columns: [], // 分类选择器数据
  119. houseTypeList: [], // 分类列表
  120. rules: {
  121. type: [
  122. { required: true, message: '请选择出租或流转', trigger: ['blur', 'change'] }
  123. ],
  124. address: [
  125. { required: true, message: '请选择资源地址', trigger: ['blur', 'change'] }
  126. ],
  127. contactName: [
  128. { required: true, message: '请输入联系人姓名', trigger: ['blur', 'change'] }
  129. ],
  130. contactPhone: [
  131. { required: true, message: '请输入联系电话', trigger: ['blur', 'change'] },
  132. { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: ['blur', 'change'] }
  133. ],
  134. price: [
  135. { required: true, message: '请输入价格', trigger: ['blur', 'change'] },
  136. { pattern: /^\d+(\.\d{1,2})?$/, message: '请输入正确的价格格式', trigger: ['blur', 'change'] }
  137. ],
  138. description: [
  139. { required: true, message: '请输入资源介绍', trigger: ['blur', 'change'] },
  140. { min: 10, message: '资源介绍至少需要10个字符', trigger: ['blur', 'change'] }
  141. ],
  142. classId: [
  143. { required: true, message: '请选择分类标识', trigger: ['blur', 'change'] }
  144. ]
  145. }
  146. }
  147. },
  148. onReady() {
  149. this.$refs.form.setRules(this.rules)
  150. },
  151. onLoad(options) {
  152. // 设置页面标题
  153. uni.setNavigationBarTitle({
  154. title: '其他农村资源录入'
  155. })
  156. // 接收传递的commonClass参数
  157. if(options.commonClass) {
  158. this.commonClass = options.commonClass
  159. }
  160. // 如果是编辑模式,设置classId用于回显
  161. if(options.classId) {
  162. this.form.classId = options.classId
  163. }
  164. // 初始化分类数据
  165. this.onHouseType()
  166. },
  167. methods: {
  168. // 获取分类数据
  169. onHouseType(){
  170. let that = this
  171. houseType({}).then(response=>{
  172. let arr=[]
  173. that.houseTypeList = response.result
  174. response.result.forEach(items=>{
  175. arr.push(items.title)
  176. // 数据回显:如果当前classId匹配,设置className
  177. if(items.id == that.form.classId){
  178. that.form.className = items.title
  179. }
  180. })
  181. that.columns[0]=arr
  182. }).catch(error=>{
  183. })
  184. },
  185. // 地址选择
  186. handleAddressSelect() {
  187. const that = this;
  188. wx.chooseLocation({
  189. success: function (res) {
  190. console.log('选择的位置:', res);
  191. that.form.longitude = res.longitude
  192. that.form.latitude = res.latitude
  193. that.form.address = res.address
  194. }
  195. })
  196. },
  197. // 资源照片上传
  198. async afterImageRead(e) {
  199. let self = this
  200. e.file.forEach(file => {
  201. self.$Oss.ossUpload(file.url).then(url => {
  202. self.form.images.push({
  203. url
  204. })
  205. })
  206. })
  207. },
  208. deleteImagePic(event) {
  209. this.form.images.splice(event.index, 1)
  210. },
  211. // 视频上传
  212. async afterVideoRead(e) {
  213. let self = this
  214. for (let file of e.file) {
  215. try {
  216. // 检查视频时长
  217. const duration = await this.getVideoDuration(file.url)
  218. if (duration > 60) {
  219. uni.showToast({
  220. title: '视频时长不能超过1分钟',
  221. icon: 'none'
  222. })
  223. continue // 跳过这个视频
  224. }
  225. self.$Oss.ossUpload(file.url).then(url => {
  226. self.form.videos.push({
  227. url
  228. })
  229. })
  230. } catch (error) {
  231. console.error('视频处理失败:', error)
  232. // 如果获取时长失败,仍然允许上传
  233. self.$Oss.ossUpload(file.url).then(url => {
  234. self.form.videos.push({
  235. url
  236. })
  237. })
  238. }
  239. }
  240. },
  241. // 获取视频时长的辅助方法
  242. getVideoDuration(videoUrl) {
  243. return new Promise((resolve, reject) => {
  244. // #ifdef MP-WEIXIN
  245. uni.getVideoInfo({
  246. src: videoUrl,
  247. success: (res) => {
  248. resolve(res.duration)
  249. },
  250. fail: (err) => {
  251. reject(err)
  252. }
  253. })
  254. // #endif
  255. // #ifdef APP-PLUS
  256. plus.io.getVideoInfo({
  257. filePath: videoUrl,
  258. success: (res) => {
  259. resolve(res.duration)
  260. },
  261. fail: (err) => {
  262. reject(err)
  263. }
  264. })
  265. // #endif
  266. // #ifdef H5
  267. // H5环境暂不支持视频时长检测,直接通过
  268. resolve(0)
  269. // #endif
  270. })
  271. },
  272. deleteVideoPic(event) {
  273. this.form.videos.splice(event.index, 1)
  274. },
  275. // 分类选择
  276. handleClass() {
  277. this.$refs.picker.open();
  278. },
  279. // 代理协议状态变化
  280. onAgreementChange(e) {
  281. console.log('代理协议状态变化:', e);
  282. this.form.agreeProtocol = e;
  283. },
  284. confirm(e) {
  285. let that = this
  286. let {indexs,value,values} = e
  287. that.form.classId = that.houseTypeList[indexs[0]].id;
  288. that.form.className = that.houseTypeList[indexs[0]].title;
  289. },
  290. // 提交表单
  291. submit() {
  292. console.log(this.form.agreeProtocol);
  293. // 检查是否同意代理协议
  294. if (!this.form.agreeProtocol) {
  295. uni.showToast({
  296. title: '请先同意代理协议',
  297. icon: 'none'
  298. });
  299. return;
  300. }
  301. this.$refs.form.validate().then(res => {
  302. // 验证必填的图片和视频
  303. if (this.form.images.length === 0) {
  304. uni.showToast({
  305. title: '请上传资源照片',
  306. icon: 'none'
  307. });
  308. return;
  309. }
  310. if (this.form.videos.length === 0) {
  311. uni.showToast({
  312. title: '请上传资源视频',
  313. icon: 'none'
  314. });
  315. return;
  316. }
  317. // 构建与index.vue兼容的提交参数
  318. const params = {
  319. userId: uni.getStorageSync('userInfo')?.id || "",
  320. id: "", // 新增数据,无ID
  321. classId: this.form.classId, // 使用分类标识
  322. commonClass: this.commonClass, // 所属分类
  323. address: this.form.address, // 地址
  324. homeAge: "", // 户主年龄 - 其他资源无此字段
  325. homeAz: "", // 是否经过安置 - 默认否
  326. homeBian: "", // 房屋周边 - 其他资源无此字段
  327. homeBjsx: "", // 报建手续 - 其他资源无此字段
  328. homeCai: "", // 菜地 - 其他资源无此字段
  329. homeCat: "", // 停车 - 其他资源无此字段
  330. homeGz: "", // 房屋主体是否改造 - 默认否
  331. homeHb: "", // 房屋朝向及海拔 - 其他资源无此字段
  332. homeBz: this.form.description, // 备注 - 使用资源介绍
  333. homeJg: "其他农村资源", // 房屋结构 - 使用固定值
  334. homeJl: "", // 距离场镇距离 - 其他资源无此字段
  335. homeJt: "", // 交通 - 其他资源无此字段
  336. homeJtzy: "", // 户主家庭职业 - 其他资源无此字段
  337. homeMi: "", // 面积 - 其他资源无此字段
  338. homeMj: "", // 房屋面积 - 其他资源无此字段
  339. homeMoney: "", // 佣金 - 其他资源无此字段
  340. homeNo: "", // 房屋编号 - 其他资源无此字段
  341. homeNum: "", // 房间数量 - 其他资源无此字段
  342. homePay: this.form.price, // 付款方式及押金 - 使用价格信息
  343. homePj: "", // 邻居对房东评价 - 其他资源无此字段
  344. homeSd: "", // 水电气网 - 其他资源无此字段
  345. homeShjl: "", // 距离成都西三环 - 其他资源无此字段
  346. homeSw: "", // 非正常死亡 - 默认无
  347. homeTf: "", // 府市民云房屋信息档案查询 - 其他资源无此字段
  348. homeTime: "", // 租期 - 其他资源无此字段
  349. homeType: this.form.type === 'rent' ? '出租' : '流转', // 户型 - 使用类型
  350. homeYs: "", // 钥匙 - 默认无
  351. homeYzmj: "", // 院子总面积 - 其他资源无此字段
  352. homeZy: "", // 坟包及电塔 工厂噪音 - 其他资源无此字段
  353. iconName: "其他农村资源", // 热点名称
  354. iconTitle: "其他资源", // 标签
  355. num: "0", // 浏览量 - 默认0
  356. price: this.form.price, // 价格
  357. timeGo: "", // 年限 - 其他资源无此字段
  358. title: `${this.form.type === 'rent' ? '出租' : '流转'}-其他农村资源-${this.form.address.split('市')[1] || this.form.address}`, // 标题 - 自动生成
  359. unit: "", // 单位 - 其他资源价格单位灵活
  360. image: this.form.images.map(item => item.url).join(','), // 图片 - 使用资源照片
  361. iconImage: "", // 左上角图标
  362. homeImage: "", // 产权证照片 - 其他资源可能无产权证
  363. homeMp4: this.form.videos.map(item => item.url).join(','), // 视频
  364. latitude: this.form.latitude,
  365. longitude: this.form.longitude,
  366. // 新增字段用于标识其他资源
  367. category: 'other',
  368. contactName: this.form.contactName,
  369. contactPhone: this.form.contactPhone
  370. };
  371. console.log('其他资源提交参数:', params);
  372. uni.showLoading({
  373. title: '提交中...'
  374. });
  375. // 调用统一API
  376. saveOrUpdateHouse(params).then(response => {
  377. uni.hideLoading();
  378. uni.showToast({
  379. title: response.message || '提交成功',
  380. icon: 'success'
  381. });
  382. setTimeout(() => {
  383. uni.redirectTo({
  384. url: "/pages_subpack/successful-apply/index"
  385. });
  386. }, 2000);
  387. }).catch(error => {
  388. uni.hideLoading();
  389. uni.showToast({
  390. title: error.message || '提交失败',
  391. icon: 'none'
  392. });
  393. console.error('其他资源提交失败:', error);
  394. });
  395. }).catch(errors => {
  396. console.log('表单验证失败:', errors);
  397. uni.showToast({
  398. title: '请补全必填项',
  399. icon: 'none'
  400. });
  401. });
  402. }
  403. }
  404. }
  405. </script>
  406. <style scoped>
  407. .container {
  408. min-height: 100vh;
  409. background-color: #f5f5f5;
  410. }
  411. .header {
  412. background: linear-gradient(135deg, #1EC77A 0%, #4CAF50 100%);
  413. padding: 40rpx 40rpx 60rpx;
  414. color: white;
  415. }
  416. .title {
  417. font-size: 36rpx;
  418. font-weight: bold;
  419. text-align: center;
  420. }
  421. .form-container {
  422. background: white;
  423. margin: -30rpx 20rpx 20rpx;
  424. border-radius: 20rpx;
  425. padding: 40rpx;
  426. box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.1);
  427. }
  428. .section-title {
  429. font-size: 32rpx;
  430. font-weight: bold;
  431. color: #333;
  432. margin: 40rpx 0 30rpx;
  433. padding-left: 20rpx;
  434. border-left: 6rpx solid #1EC77A;
  435. }
  436. .section-title:first-child {
  437. margin-top: 0;
  438. }
  439. .input-tip {
  440. font-size: 22rpx;
  441. color: #999;
  442. margin-top: 10rpx;
  443. line-height: 1.4;
  444. }
  445. .upload-tip {
  446. font-size: 24rpx;
  447. color: #666;
  448. margin-bottom: 10rpx;
  449. line-height: 1.5;
  450. }
  451. .price-container {
  452. display: flex;
  453. align-items: center;
  454. gap: 10rpx;
  455. }
  456. .price-unit {
  457. font-size: 26rpx;
  458. color: #1EC77A;
  459. font-weight: bold;
  460. margin-left: 10rpx;
  461. }
  462. .char-count {
  463. font-size: 22rpx;
  464. color: #999;
  465. text-align: right;
  466. margin-top: 10rpx;
  467. }
  468. .agreement-container {
  469. margin: 30rpx 0;
  470. padding: 30rpx;
  471. background: #f8f9fa;
  472. border-radius: 15rpx;
  473. border: 2rpx solid #e9ecef;
  474. }
  475. .agreement-content {
  476. margin-bottom: 30rpx;
  477. }
  478. .agreement-title {
  479. display: block;
  480. font-size: 28rpx;
  481. font-weight: bold;
  482. color: #333;
  483. margin-bottom: 20rpx;
  484. }
  485. .agreement-text {
  486. display: block;
  487. font-size: 24rpx;
  488. color: #666;
  489. line-height: 1.6;
  490. }
  491. .agreement-label {
  492. font-size: 26rpx;
  493. color: #333;
  494. margin-left: 10rpx;
  495. }
  496. </style>