修改登录页样式、车牌号输入问题、定位加载问题、联系物业权限问题

This commit is contained in:
zhouyanli
2026-05-23 16:44:29 +08:00
parent 6b520fb70e
commit c4d91787b5
14 changed files with 278 additions and 81 deletions

View File

@@ -1,9 +1,9 @@
{ {
"name" : "uniapp-project-test", "name" : "花开物业用户端app",
"appid" : "", "appid" : "__UNI__29C3D60",
"description" : "", "description" : "物业用户端app",
"versionName" : "1.0.0", "versionName" : "1.0.1",
"versionCode" : "100", "versionCode" : "101",
"transformPx" : false, "transformPx" : false,
/* 5+App */ /* 5+App */
"app-plus" : { "app-plus" : {
@@ -17,7 +17,10 @@
"delay" : 0 "delay" : 0
}, },
/* */ /* */
"modules" : {}, "modules" : {
"Geolocation" : {},
"Maps" : {}
},
/* */ /* */
"distribute" : { "distribute" : {
/* android */ /* android */
@@ -37,13 +40,31 @@
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>", "<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>"
] ]
}, },
/* ios */ /* ios */
"ios" : {}, "ios" : {
"dSYMs" : false
},
/* SDK */ /* SDK */
"sdkConfigs" : {} "sdkConfigs" : {
"geolocation" : {
"system" : {
"__platform__" : [ "ios", "android" ]
}
},
"maps" : {
"amap" : {
"name" : "玉米汁穿过三峡人家",
"appkey_ios" : "39a4c28a01c3ec15684b21d4093aad3d",
"appkey_android" : "044055aed47cb305e86419fd82e4c5f3"
}
}
}
} }
}, },
/* */ /* */
@@ -54,16 +75,13 @@
"setting" : { "setting" : {
"urlCheck" : false "urlCheck" : false
}, },
"requiredPrivateInfos": [ "requiredPrivateInfos" : [ "chooseLocation", "getLocation" ],
"chooseLocation", "permission" : {
"getLocation" "scope.camera" : {
], "desc" : "用于扫码"
"permission": {
"scope.camera": {
"desc": "用于扫码"
}, },
"scope.userLocation": { "scope.userLocation" : {
"desc": "你的位置信息将用于定位选择" "desc" : "你的位置信息将用于定位选择"
} }
}, },
"usingComponents" : true "usingComponents" : true

View File

@@ -65,7 +65,7 @@
<up-row justify="space-between" customStyle="margin-bottom: 10px"> <up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4"> <up-col span="4">
<view class="info-image-left"> <view class="info-image-left">
<image :src="activity.coverImage" style="width: 100%;height: 100%;border-radius: 16rpx;"></image> <image :src="activity.coverImage" mode="aspectFit" style="width: 100%;height: 100%;border-radius: 16rpx;"></image>
</view> </view>
</up-col> </up-col>
<up-col span="8"> <up-col span="8">

View File

@@ -58,6 +58,9 @@ import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { addActivityLive, signinActivityLive, uploadFile } from '../api/api.js'; import { addActivityLive, signinActivityLive, uploadFile } from '../api/api.js';
// 高德地图 Web API Key用于逆地址解析需要去 https://lbs.amap.com 申请免费 Key
const AMAP_KEY = 'f362f595be12ab7d84bd502b48b30d2a';
// 活动ID // 活动ID
const activityId = ref(''); const activityId = ref('');
// 实况描述内容 // 实况描述内容
@@ -82,6 +85,29 @@ const handleTextInput = (e) => {
currentLength.value = e.detail.value.length; currentLength.value = e.detail.value.length;
}; };
// 逆地址解析(坐标转地址)
const reverseGeocode = async (lng, lat) => {
if (!AMAP_KEY) return null;
try {
const [err, res] = await uni.request({
url: 'https://restapi.amap.com/v3/geocode/regeo',
data: {
key: AMAP_KEY,
location: `${lng},${lat}`,
extensions: 'base'
}
});
if (err) throw err;
if (res.data?.status === '1' && res.data.regeocode?.formatted_address) {
return res.data.regeocode.formatted_address;
}
return null;
} catch (e) {
console.error('逆地址解析失败:', e);
return null;
}
};
// 选择图片 // 选择图片
const chooseImg = () => { const chooseImg = () => {
uni.chooseImage({ uni.chooseImage({
@@ -103,43 +129,92 @@ const chooseImg = () => {
imgList.value.splice(index, 1); 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 = () => { const reLocate = () => {
// 调用微信原生选择位置接口(自带地址解析,无调用上限) // App 端先检查权限,无权限直接引导设置
uni.chooseLocation({ // #ifdef APP-PLUS
if (!checkAppLocationPermission()) {
uni.showModal({
title: '需要定位权限',
content: '请前往系统设置中允许本应用使用定位权限',
confirmText: '去设置',
success: (res) => { success: (res) => {
// res.address = 具体地址XX市XX区XX街道XX小区 if (res.confirm) openAppSettings();
// res.name = 地点名称XX小区 }
});
return;
}
// #endif
uni.showLoading({ title: '正在定位...', mask: true });
uni.getLocation({
type: 'gcj02',
isHighAccuracy: true,
success: async (res) => {
longitude.value = res.longitude; longitude.value = res.longitude;
latitude.value = res.latitude; latitude.value = res.latitude;
location.value = res.address; // 显示具体地址 // 逆地址解析
uni.showToast({ title: `定位成功:${res.name}`, icon: 'success' }); const address = await reverseGeocode(res.longitude, res.latitude);
location.value = address || `${res.longitude.toFixed(6)}, ${res.latitude.toFixed(6)}`;
uni.hideLoading();
}, },
fail: (err) => { fail: (err) => {
// 处理权限拒绝/定位失败 uni.hideLoading();
if (err.errMsg.includes('auth deny')) { const errMsgStr = err?.errMsg || err?.message || JSON.stringify(err);
console.error('定位失败:', err);
if (err?.code === -22 || /权限|auth|deny|permission/i.test(errMsgStr)) {
uni.showModal({ uni.showModal({
title: '需要定位权限', title: '需要定位权限',
content: '请允许获取位置信息以显示具体地点', content: '请允许获取位置信息以显示具体地点',
confirmText: '去设置', confirmText: '去设置',
success: (modalRes) => { success: (res) => { if (res.confirm) openAppSettings(); }
if (modalRes.confirm) {
uni.openSetting({
success: (settingRes) => {
// 用户开启权限后重新定位
if (settingRes.authSetting['scope.userLocation']) {
reLocate();
}
}
});
}
}
}); });
} else { } else {
uni.hideLoading(); uni.showToast({ title: `定位失败:${errMsgStr}`, icon: 'none', duration: 3000 });
uni.showToast({ title: '定位失败,请重试', icon: 'none' });
// console.error('定位失败:', err);
} }
} }
}); });

View File

@@ -60,7 +60,7 @@ export default function request(url, data = {}, method = "GET") {
}); });
} }
export const get = (url, data) => request(url, data, 'GET'); export const get = (url, data = {}) => request(url, data, 'GET');
export const post = (url, data) => request(url, data, 'POST'); export const post = (url, data) => request(url, data, 'POST');
export const deleteMethod = (url, data) => request(url, data, 'DELETE'); export const deleteMethod = (url, data) => request(url, data, 'DELETE');
export const putMethod = (url, data) => request(url, data, 'PUT'); export const putMethod = (url, data) => request(url, data, 'PUT');

View File

@@ -157,6 +157,21 @@ const handleConfirm = () => {
height: 100%; height: 100%;
} }
/* 深度覆盖 uni-easyinput 内部默认样式,解决高度偏移 */
.input-content :deep(.uni-easyinput__content) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
background-color: transparent !important;
}
.input-content :deep(.uni-easyinput__content-input) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
line-height: 88rpx;
}
/* 确定按钮 */ /* 确定按钮 */
.confirm-btn { .confirm-btn {
width: 100%; width: 100%;

View File

@@ -187,6 +187,22 @@ const goBack = () => {
color: #333; color: #333;
height: 100%; height: 100%;
} }
/* 深度覆盖 uni-easyinput 内部默认样式,解决高度偏移 */
.input-content :deep(.uni-easyinput__content) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
background-color: transparent !important;
}
.input-content :deep(.uni-easyinput__content-input) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
line-height: 88rpx;
}
.verify-input { .verify-input {
padding-right: 20rpx; padding-right: 20rpx;
} }
@@ -231,7 +247,7 @@ const goBack = () => {
color: #fff; color: #fff;
} }
/* 禁用按钮边框 */ /* 禁用按钮边框 */
button:after{ .forget-btn:after{
border-radius: 8px; border-radius: 8px;
} }

View File

@@ -302,6 +302,21 @@ onUnmounted(() => {
height: 100%; height: 100%;
} }
/* 深度覆盖 uni-easyinput 内部默认样式,解决高度偏移 */
.input-content :deep(.uni-easyinput__content) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
background-color: transparent !important;
}
.input-content :deep(.uni-easyinput__content-input) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
line-height: 88rpx;
}
/* 验证码输入框 */ /* 验证码输入框 */
.verify-input { .verify-input {
padding-right: 20rpx; padding-right: 20rpx;

View File

@@ -160,6 +160,7 @@ const handleLogin = () => {
} }
}).catch(err => { }).catch(err => {
// console.log(err)
uni.showToast({ uni.showToast({
title: err.msg || '网络异常', title: err.msg || '网络异常',
icon: 'error' icon: 'error'
@@ -212,6 +213,21 @@ const handleLogin = () => {
height: 100%; height: 100%;
} }
/* 深度覆盖 uni-easyinput 内部默认样式,解决高度偏移 */
.input-content :deep(.uni-easyinput__content) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
background-color: transparent !important;
}
.input-content :deep(.uni-easyinput__content-input) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
line-height: 88rpx;
}
.agreement-wrap { .agreement-wrap {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@@ -358,6 +358,21 @@
height: 100%; height: 100%;
} }
/* 深度覆盖 uni-easyinput 内部默认样式,解决高度偏移 */
.input-content :deep(.uni-easyinput__content) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
background-color: transparent !important;
}
.input-content :deep(.uni-easyinput__content-input) {
height: 100% !important;
min-height: unset !important;
padding: 0 !important;
line-height: 88rpx;
}
.verify-input { .verify-input {
padding-right: 20rpx; padding-right: 20rpx;
} }

View File

@@ -8,14 +8,14 @@
class="search-input" class="search-input"
placeholder="请输入关键字进行搜索" placeholder="请输入关键字进行搜索"
placeholder-class="placeholder" placeholder-class="placeholder"
v-model="searchText" :value="searchText"
@input="handleSearchInput" @input="handleSearchInput"
/> />
<view v-if="searchText" class="clear-icon" @tap="clearSearch"> <view v-if="searchText" class="clear-icon" @tap="clearSearch">
<uni-icons type="clear" size="18" color="#999"></uni-icons> <uni-icons type="clear" size="18" color="#999"></uni-icons>
</view> </view>
</view> </view>
<button class="search-btn" @tap="handleSearch">搜索</button> <!-- <button class="search-btn" @tap="handleSearch">搜索</button> -->
</view> </view>
<!-- 小区列表 --> <!-- 小区列表 -->
@@ -103,7 +103,7 @@ const currentVillageIdFromHome = ref('') // 首页传入的当前选中小区ID
// 分页相关 // 分页相关
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = 5 const pageSize = 10
const totalCount = ref(0) const totalCount = ref(0)
const displayedCommunities = ref([]) const displayedCommunities = ref([])
const hasMoreData = ref(true) const hasMoreData = ref(true)
@@ -119,6 +119,7 @@ const getCommunityList = async (pageNum = 1, keyword = '') => {
isLoading.value = true isLoading.value = true
try { try {
keyword = decodeURIComponent(keyword)
const params = { const params = {
pageNum, pageNum,
pageSize, pageSize,
@@ -188,22 +189,19 @@ const loadMoreData = () => {
} }
// 处理搜索输入(防抖) // 处理搜索输入(防抖)
const handleSearchInput = () => { const handleSearchInput = (e) => {
let val = e.detail.value || ''
searchText.value = val
if (searchTimeout) { if (searchTimeout) {
clearTimeout(searchTimeout) clearTimeout(searchTimeout)
} }
searchTimeout = setTimeout(() => { searchTimeout = setTimeout(() => {
handleSearch() currentPage.value = 1
isSearching.value = !!val.trim()
getCommunityList(1, val.trim())
}, 300) }, 300)
} }
// 处理搜索
const handleSearch = () => {
const keyword = searchText.value.trim()
isSearching.value = !!keyword
currentPage.value = 1
getCommunityList(1, keyword)
}
// 清空搜索 // 清空搜索
const clearSearch = () => { const clearSearch = () => {
@@ -274,13 +272,6 @@ const handleConfirm = async () => {
} }
} }
// 监听搜索文本变化
watch(searchText, (newVal) => {
if (!newVal.trim()) {
clearSearch()
}
})
// 初始化 // 初始化
onMounted(() => { onMounted(() => {
if (displayedCommunities.value.length === 0 && !isLoading.value) { if (displayedCommunities.value.length === 0 && !isLoading.value) {

View File

@@ -8,7 +8,7 @@
<!-- 表单区域 --> <!-- 表单区域 -->
<scroll-view class="page" scroll-y="true"> <scroll-view class="page" scroll-y="true">
<view v-if="dataList.length === 0" class="empty-state"> <view v-if="dataList.length === 0" class="empty-state">
<uni-icons type="info" size="60" color="#ccc"></uni-icons> <uni-icons type="info" size="30" color="#ccc"></uni-icons>
<text class="empty-text"> <text class="empty-text">
{{ '暂无数据'}} {{ '暂无数据'}}
</text> </text>
@@ -28,10 +28,9 @@
</view> </view>
</view> </view>
<!-- 值班电话 --> <!-- 值班电话 -->
<view class="phone-item" v-for="phoneItems,phoneIndex in item.phoneList" :key="phoneIndex" @click="callPhone(phoneItems.phoneNum)"> <view class="phone-item" v-for="phoneItems,phoneIndex in item.phoneList" :key="phoneIndex" @click="callPhone(phoneItems.phoneNum)" @longpress="copyPhone(phoneItems.phoneNum)">
<text class="phone-label">值班电话</text> <text class="phone-label">值班电话</text>
<text class="phone-num">{{phoneItems.phoneNum}}</text> <text class="phone-num">{{phoneItems.phoneNum}}</text>
<!-- <text class="phone-icon"></text> -->
<uni-icons type="phone" size="16" color="#2F77FD"></uni-icons> <uni-icons type="phone" size="16" color="#2F77FD"></uni-icons>
</view> </view>
</view> </view>
@@ -52,13 +51,39 @@ import { getManagePhoneData } from '../api/apiSub.js'
// ====方法===== // ====方法=====
function callPhone(phone) { function callPhone(phone) {
if (!phone || !phone.trim()) {
uni.showToast({ title: '电话号码为空', icon: 'none' });
return;
}
const cleanPhone = phone.replace(/-/g, '').trim();
if (!/^1[3-9]\d{9}$/.test(cleanPhone) && !/^0\d{2,3}-?\d{7,8}$/.test(cleanPhone)) {
uni.showToast({ title: '电话号码格式有误', icon: 'none' });
return;
}
uni.makePhoneCall({ uni.makePhoneCall({
phoneNumber: phone.replace(/-/g, ""), phoneNumber: cleanPhone,
fail: (err) => { fail: (err) => {
console.log('用户取消拨号', err) console.log('拨号失败或用户取消', err);
uni.showToast({ title: '拨号失败,请长按复制号码', icon: 'none' });
} }
}); });
} }
function copyPhone(phone) {
if (!phone || !phone.trim()) {
uni.showToast({ title: '电话号码为空', icon: 'none' });
return;
}
uni.setClipboardData({
data: phone.replace(/-/g, '').trim(),
success: () => {
uni.showToast({ title: '号码已复制', icon: 'none' });
},
fail: () => {
uni.showToast({ title: '复制失败', icon: 'none' });
}
});
}
function goBack() { function goBack() {
uni.switchTab({ uni.switchTab({
url: '/pages/serviceIndex/serviceIndex', url: '/pages/serviceIndex/serviceIndex',
@@ -117,7 +142,7 @@ import { getManagePhoneData } from '../api/apiSub.js'
} }
.empty-text { .empty-text {
font-size: 32rpx; font-size: 28rpx;
color: #999; color: #999;
margin-top: 20rpx; margin-top: 20rpx;
margin-bottom: 10rpx; margin-bottom: 10rpx;

View File

@@ -12,7 +12,7 @@
<scroll-view class="page" scroll-y> <scroll-view class="page" scroll-y>
<view class="list-container"> <view class="list-container">
<view v-if="currentList.length === 0" class="empty-state"> <view v-if="currentList.length === 0" class="empty-state">
<uni-icons type="info" size="60" color="#ccc"></uni-icons> <uni-icons type="info" size="30" color="#ccc"></uni-icons>
<text class="empty-text">暂无数据</text> <text class="empty-text">暂无数据</text>
</view> </view>
@@ -256,7 +256,7 @@
} }
.empty-text { .empty-text {
font-size: 32rpx; font-size: 28rpx;
color: #999; color: #999;
margin-top: 20rpx; margin-top: 20rpx;
margin-bottom: 10rpx; margin-bottom: 10rpx;

View File

@@ -58,6 +58,8 @@
<view class="car-number-wrap"> <view class="car-number-wrap">
<input class="car-input" v-for="(item, index) in 8" :key="index" <input class="car-input" v-for="(item, index) in 8" :key="index"
:value="form.carNumber[index]" @input="(e) => onCarInput(index, e)" :value="form.carNumber[index]" @input="(e) => onCarInput(index, e)"
:focus="carFocusIndex === index"
@focus="carFocusIndex = index"
type="text" /> type="text" />
</view> </view>
</view> </view>
@@ -170,6 +172,9 @@
const addressModalRef = ref(null); const addressModalRef = ref(null);
const genderModalRef = ref(null); const genderModalRef = ref(null);
// 车牌号输入框聚焦索引
const carFocusIndex = ref(0);
// =========方法=========== // =========方法===========
// 获取房屋列表 // 获取房屋列表
const fetchHouseList = async () => { const fetchHouseList = async () => {
@@ -228,7 +233,13 @@
// 车牌号单格输入 // 车牌号单格输入
const onCarInput = (index, e) => { const onCarInput = (index, e) => {
const val = (e.detail && e.detail.value !== undefined) ? e.detail.value : (e.target ? e.target.value : ''); const val = (e.detail && e.detail.value !== undefined) ? e.detail.value : (e.target ? e.target.value : '');
form.carNumber[index] = (val || '').toUpperCase().slice(0, 1); const upperVal = (val || '').toUpperCase().slice(0, 1);
form.carNumber[index] = upperVal;
// 输入一位后自动聚焦到下一个
if (upperVal && index < 7) {
carFocusIndex.value = index + 1;
}
}; };
// 时间校验 // 时间校验

View File

@@ -131,7 +131,7 @@
<up-row justify="space-between" customStyle="margin-bottom: 10px"> <up-row justify="space-between" customStyle="margin-bottom: 10px">
<up-col span="4"> <up-col span="4">
<view class="demo-layout bg-purple-light activity-img"> <view class="demo-layout bg-purple-light activity-img">
<image :src="activity.coverImage" style="width: 100%;height: 100%;"></image> <image :src="activity.coverImage" style="width: 100%;height: 100%;" mode="aspectFit"></image>
</view> </view>
</up-col> </up-col>
<up-col span="8"> <up-col span="8">