Files
property-resident-side/property-uniapp-project/pageSubPack/activity-list/live-add.vue
2026-06-29 15:39:26 +08:00

434 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="add-status-page">
<!-- 主体内容 -->
<scroll-view class="content" scroll-y>
<!-- 实况描述输入框 -->
<view class="form-item">
<text class="label">实况描述</text>
<textarea
class="textarea"
placeholder="请输入活动实况内容,也可打卡签到"
v-model="statusDesc"
maxlength="500"
@input="handleTextInput"
></textarea>
<text class="count">{{ currentLength }}/500</text>
</view>
<!-- 活动照片上传 -->
<view class="form-item">
<text class="label">活动照片</text>
<view class="upload-area">
<!-- 已上传图片 -->
<view class="uploaded-img" v-for="(img, index) in imgList" :key="index">
<image :src="img" mode="aspectFill" class="upload-img"></image>
<image src="http://47.104.199.163:9090/images/imgs/delete.png" class="upload-delete" @click="deleteImage(index)"></image>
<!-- <text class="del-img" @click="deleteImg(index)">×</text> -->
</view>
<!-- 上传按钮最多3张 -->
<view class="upload-btn" @click="chooseImg" v-if="imgList.length < 3">
<text class="plus-icon">+</text>
<text class="tips">最多3张图片</text>
</view>
</view>
</view>
<!-- 签到地点 -->
<view class="form-item">
<text class="label">签到地点</text>
<view class="location-area">
<text class="location-text">{{ location }}</text>
<view class="re-locate" @click="reLocate">
<uni-icons type="refreshempty" size="14" color="#2196f3" class="re-icon"></uni-icons>
重新定位
</view>
</view>
</view>
</scroll-view>
<!-- 底部提交按钮 -->
<view class="submit-btn-area">
<button class="submit-btn" @click="submitStatus">提交</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { addActivityLive, signinActivityLive, uploadFile } from '../api/api.js';
// 天地图 Web API Key用于逆地址解析
const TIANDITU_KEY = 'b0201708e26cb56e514d74d1686f6f91';
// 活动ID
const activityId = ref('');
// 实况描述内容
const statusDesc = ref('');
// 输入字符长度
const currentLength = ref(0);
// 图片列表(本地临时路径)
const imgList = ref([]);
// 定位信息
const location = ref('');
// 经纬度
const longitude = ref('');
const latitude = ref('');
// 接收活动ID
onLoad((option) => {
activityId.value = option?.id || '';
});
// 监听输入框字数变化
const handleTextInput = (e) => {
currentLength.value = e.detail.value.length;
};
// 逆地址解析(坐标转地址)- 天地图 API
const reverseGeocode = async (lng, lat) => {
if (!TIANDITU_KEY) return null;
try {
const res = await uni.request({
url: 'http://api.tianditu.gov.cn/geocoder',
data: {
type: 'rgc',
postStr: JSON.stringify({ lon: lng, lat: lat, ver: 1 }),
tk: TIANDITU_KEY
}
});
if (res.data?.status === '0' && res.data.result?.formatted_address) {
return res.data.result.formatted_address;
}
return null;
} catch (e) {
console.error('逆地址解析失败:', e);
return null;
}
};
// 选择图片
const chooseImg = () => {
uni.chooseImage({
count: 3 - imgList.value.length,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
imgList.value = [...imgList.value, ...res.tempFilePaths];
},
fail: (err) => {
uni.showToast({ title: '选择图片失败', icon: 'none' });
console.error('选择图片失败:', err);
}
});
};
// 删除图片
const deleteImage = (index) => {
imgList.value.splice(index, 1);
};
// 检查 App 端定位权限Android/iOS
const checkAppLocationPermission = () => {
// #ifdef APP-PLUS
try {
const platform = uni.getSystemInfoSync().platform;
if (platform === 'android') {
const main = plus.android.runtimeMainActivity();
const PackageManager = plus.android.importClass('android.content.pm.PackageManager');
return main.checkSelfPermission('android.permission.ACCESS_FINE_LOCATION') === PackageManager.PERMISSION_GRANTED;
}
if (platform === 'ios') {
const CLAuthorizationStatus = plus.ios.importClass('CLAuthorizationStatus');
const CLLocationManager = plus.ios.importClass('CLLocationManager');
return CLLocationManager.authorizationStatus() !== 2; // 2 = denied
}
} catch (e) {
console.error('检查权限失败', e);
}
// #endif
return true; // 非 App 端默认通过
};
// 跳转系统设置App 端)
const openAppSettings = () => {
// #ifdef APP-PLUS
const platform = uni.getSystemInfoSync().platform;
if (platform === 'android') {
const Intent = plus.android.importClass('android.content.Intent');
const Settings = plus.android.importClass('android.provider.Settings');
const Uri = plus.android.importClass('android.net.Uri');
const intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse('package:' + plus.runtime.packageName));
plus.android.runtimeMainActivity().startActivity(intent);
} else if (platform === 'ios') {
const UIApplication = plus.ios.importClass('UIApplication');
const app = UIApplication.sharedApplication();
const NSURL = plus.ios.importClass('NSURL');
const url = NSURL.URLWithString('app-settings:');
app.openURL(url);
}
// #endif
};
// 重新定位(获取当前位置 + 逆地址解析)
const reLocate = () => {
// App 端先检查权限,无权限直接引导设置
// #ifdef APP-PLUS
if (!checkAppLocationPermission()) {
uni.showModal({
title: '需要定位权限',
content: '请前往系统设置中允许本应用使用定位权限',
confirmText: '去设置',
success: (res) => {
if (res.confirm) openAppSettings();
}
});
return;
}
// #endif
uni.showLoading({ title: '正在定位...', mask: true });
uni.getLocation({
type: 'gcj02',
isHighAccuracy: true,
success: async (res) => {
longitude.value = res.longitude;
latitude.value = res.latitude;
// 逆地址解析
const address = await reverseGeocode(res.longitude, res.latitude);
location.value = address || `${res.longitude.toFixed(6)}, ${res.latitude.toFixed(6)}`;
uni.hideLoading();
},
fail: (err) => {
uni.hideLoading();
const errMsgStr = err?.errMsg || err?.message || JSON.stringify(err);
console.error('定位失败:', err);
if (err?.code === -22 || /权限|auth|deny|permission/i.test(errMsgStr)) {
uni.showModal({
title: '需要定位权限',
content: '请允许获取位置信息以显示具体地点',
confirmText: '去设置',
success: (res) => { if (res.confirm) openAppSettings(); }
});
} else {
uni.showToast({ title: `定位失败:${errMsgStr}`, icon: 'none', duration: 3000 });
}
}
});
};
// 上传所有图片
const uploadAllImages = async () => {
if (imgList.value.length === 0) return [];
const uploadPromises = imgList.value.map(img => uploadFile(img));
const results = await Promise.all(uploadPromises);
return results.map(res => res.url);
};
// 提交实况
const submitStatus = async () => {
if (!statusDesc.value.trim()) {
uni.showToast({ title: '请输入实况描述', icon: 'none' });
return;
}
// if (!location.value) {
// uni.showToast({ title: '请先签到', icon: 'none' });
// return;
// }
uni.showLoading({ title: '提交中...', mask: true });
try {
// 先上传图片
const imageUrls = await uploadAllImages();
// 同时签到
await signinActivityLive(activityId.value, {
location: location.value,
longitude: longitude.value,
latitude: latitude.value,
villageId:uni.getStorageSync('currentVillageId')
});
// 提交实况
const liveData = {
content: statusDesc.value.trim(),
images: imageUrls,
location: location.value,
longitude: longitude.value,
latitude: latitude.value,
villageId:uni.getStorageSync('currentVillageId')
};
console.log('ssasss',uni.getStorageSync('currentVillageId'))
console.log("xxx",activityId.value)
await addActivityLive(activityId.value, liveData);
uni.hideLoading();
uni.showToast({ title: '添加成功', icon: 'success' });
uni.$emit('refreshLiveList');
setTimeout(() => {
uni.navigateBack();
}, 1500);
} catch (err) {
uni.hideLoading();
uni.showToast({ title: err.msg || '提交失败', icon: 'none' });
}
};
</script>
<style scoped>
/* 页面整体样式 */
.add-status-page {
width: 100%;
height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
/* 主体内容 */
.content {
max-height: calc(100vh - 120px);
padding: 10px 0px;
}
/* 表单项通用样式 */
.form-item {
background-color: #fff;
padding: 20px 15px;
border-radius: 8px;
margin-bottom: 8px;
}
.label {
font-size: 16px;
font-weight: 500;
margin-bottom: 6px;
display: block;
}
/* 输入框样式 */
.textarea {
width: 100%;
height: 180px;
padding: 10px;
border: 1px solid #eee;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.count {
text-align: right;
font-size: 12px;
color: #999;
margin-top: 4px;
display: block;
}
/* 图片上传区域 */
.upload-area {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.uploaded-img {
width: 80px;
height: 80px;
position: relative;
}
.uploaded-img .upload-img{
width: 100%;
height: 100%;
border-radius: 4px;
}
.uploaded-img .upload-delete{
position: absolute;
top: -5px;
right: -5px;
width: 32rpx;
height: 32rpx;
border-radius: 3rpx;
padding: 2px;
}
.del-img {
position: absolute;
top: -5px;
right: -5px;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
background-color: #ff4444;
color: #fff;
border-radius: 50%;
font-size: 16px;
}
.upload-btn {
width: 80px;
height: 80px;
/* border: 1px dashed #ddd; */
background-color: #F3F3F3;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.plus-icon {
font-size: 24px;
color: #999;
margin-bottom: 5px;
}
.tips {
font-size: 12px;
color: #999;
}
/* 定位区域 */
.location-area {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
}
.location-text {
font-size: 14px;
color: #333;
flex: 1;
word-break: break-all;
margin-right: 10px;
}
.re-locate {
font-size: 14px;
color: #007aff;
display: flex;
align-items: center;
flex-shrink: 0;
}
.re-icon{
color: #007aff;
}
/* .re-locate::before {
content: '';
width: 16px;
height: 16px;
background: url(/static/logo.png) no-repeat center;
background-size: 100%;
margin-right: 5px;
} */
/* 提交按钮 */
.submit-btn-area {
padding: 15px;
}
.submit-btn {
width: 100%;
height: 48px;
line-height: 48px;
background-color: #007aff;
color: #fff;
border: none;
border-radius: 8px;
font-size: 16px;
}
</style>