沐梦. 19 цаг өмнө
parent
commit
e75309fd40

+ 2 - 2
manifest.json

@@ -2,8 +2,8 @@
     "name" : "好萌友",
     "appid" : "__UNI__F19BBAD",
     "description" : "宠物服务商家端",
-    "versionName" : "1.2.3",
-    "versionCode" : 47,
+    "versionName" : "1.2.5",
+    "versionCode" : 49,
     "transformPx" : false,
     "app-plus" : {
         "privacy" : {

+ 225 - 0
pages/login/register.vue

@@ -107,6 +107,49 @@
 			</view>
 		</view>
 
+		<!-- 自定义权限弹窗 -->
+		<view class="permission-modal-mask" v-if="showPermissionModal" @touchmove.stop.prevent>
+			<view class="permission-modal-content">
+				<view class="permission-modal-header">
+					<view class="shield-icon-box">
+						<uni-icons type="info-filled" size="30" color="#ff9500"></uni-icons>
+					</view>
+					<text class="permission-modal-title">权限申请提示</text>
+				</view>
+				
+				<view class="permission-modal-body">
+					<text class="permission-desc-main">为了正常更换头像,我们需要获取您的以下权限:</text>
+					
+					<view class="permission-list-box">
+						<view class="permission-item-box">
+							<view class="icon-circle">
+								<uni-icons type="images-filled" size="20" color="#ff9500"></uni-icons>
+							</view>
+							<view class="item-text-box">
+								<text class="item-name-text">存储与相册权限</text>
+								<text class="item-desc-text">用于从相册中选择已有照片作为头像</text>
+							</view>
+						</view>
+						
+						<view class="permission-item-box">
+							<view class="icon-circle">
+								<uni-icons type="camera-filled" size="20" color="#ff9500"></uni-icons>
+							</view>
+							<view class="item-text-box">
+								<text class="item-name-text">相机拍照权限</text>
+								<text class="item-desc-text">用于拍摄新照片并上传作为头像</text>
+							</view>
+						</view>
+					</view>
+				</view>
+				
+				<view class="permission-modal-footer">
+					<button class="btn-decline" @click="declinePermission">暂不授权</button>
+					<button class="btn-grant" @click="confirmPermission">同意并授权</button>
+				</view>
+			</view>
+		</view>
+
 	</view>
 </template>
 
@@ -123,17 +166,40 @@ const email = ref('')
 const password = ref('')
 const confirmPassword = ref('')
 const checked = ref(false)
+const showPermissionModal = ref(false)
 
 
 const onChooseAvatar = () => {
+	const hasShown = uni.getStorageSync('has_shown_avatar_permission')
+	if (!hasShown) {
+		showPermissionModal.value = true
+	} else {
+		chooseAvatarImage()
+	}
+}
+
+const chooseAvatarImage = () => {
 	uni.chooseImage({
 		count: 1,
+		sizeType: ['compressed'],
+		sourceType: ['album', 'camera'],
 		success: (res) => {
 			avatar.value = res.tempFilePaths[0]
 		}
 	})
 }
 
+const confirmPermission = () => {
+	showPermissionModal.value = false
+	uni.setStorageSync('has_shown_avatar_permission', true)
+	chooseAvatarImage()
+}
+
+const declinePermission = () => {
+	showPermissionModal.value = false
+	uni.setStorageSync('has_shown_avatar_permission', true)
+}
+
 const onCheckChange = () => {
 	checked.value = !checked.value
 }
@@ -503,4 +569,163 @@ const onSubmit = async () => {
 	color: #999;
 	margin-top: 8rpx;
 }
+
+/* ==================== 权限弹窗样式 ==================== */
+.permission-modal-mask {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(0, 0, 0, 0.6);
+    backdrop-filter: blur(10rpx);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    z-index: 9999;
+    padding: 50rpx;
+}
+
+.permission-modal-content {
+    width: 100%;
+    background: #ffffff;
+    border-radius: 36rpx;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15);
+    animation: modalFadeIn 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+
+@keyframes modalFadeIn {
+    from {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+    to {
+        opacity: 1;
+        transform: scale(1);
+    }
+}
+
+.permission-modal-header {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 48rpx 40rpx 20rpx;
+    
+    .shield-icon-box {
+        width: 90rpx;
+        height: 90rpx;
+        border-radius: 45rpx;
+        background: #fff8eb;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-bottom: 16rpx;
+    }
+    
+    .permission-modal-title {
+        font-size: 32rpx;
+        font-weight: 800;
+        color: #1a1a1a;
+    }
+}
+
+.permission-modal-body {
+    padding: 0 40rpx 30rpx;
+    
+    .permission-desc-main {
+        font-size: 26rpx;
+        color: #666;
+        line-height: 1.5;
+        display: block;
+        text-align: center;
+        margin-bottom: 24rpx;
+    }
+}
+
+.permission-list-box {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.permission-item-box {
+    display: flex;
+    align-items: center;
+    background: #fdfdfd;
+    border: 2rpx solid #f6f7f9;
+    border-radius: 20rpx;
+    padding: 20rpx;
+    
+    .icon-circle {
+        width: 64rpx;
+        height: 64rpx;
+        border-radius: 32rpx;
+        background: #fff5e6;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-right: 16rpx;
+        flex-shrink: 0;
+    }
+    
+    .item-text-box {
+        flex: 1;
+        
+        .item-name-text {
+            font-size: 26rpx;
+            font-weight: 700;
+            color: #333;
+            display: block;
+            margin-bottom: 4rpx;
+            text-align: left;
+        }
+          
+        .item-desc-text {
+            font-size: 22rpx;
+            color: #999;
+            display: block;
+            text-align: left;
+        }
+    }
+}
+
+.permission-modal-footer {
+    padding: 24rpx 40rpx 40rpx;
+    display: flex;
+    gap: 20rpx;
+    border-top: 2rpx solid #f5f6f8;
+    background: #ffffff;
+}
+
+.btn-decline {
+    flex: 1;
+    height: 80rpx;
+    line-height: 80rpx;
+    font-size: 26rpx;
+    color: #666;
+    background: #f5f6f8;
+    border-radius: 40rpx;
+    border: none;
+    font-weight: 600;
+    text-align: center;
+    &::after { border: none; }
+}
+
+.btn-grant {
+    flex: 1.2;
+    height: 80rpx;
+    line-height: 80rpx;
+    font-size: 26rpx;
+    color: #fff;
+    background: linear-gradient(90deg, #ffd53f, #ff9500);
+    border-radius: 40rpx;
+    border: none;
+    font-weight: 700;
+    box-shadow: 0 6rpx 16rpx rgba(255, 149, 0, 0.2);
+    text-align: center;
+    &::after { border: none; }
+}
 </style>

+ 224 - 0
pages/my/settings/profile/index.vue

@@ -41,6 +41,49 @@
 				<button class="submit-btn" @click="submit">保存修改</button>
 			</view>
 		</view>
+
+		<!-- 自定义权限弹窗 -->
+		<view class="permission-modal-mask" v-if="showPermissionModal" @touchmove.stop.prevent>
+			<view class="permission-modal-content">
+				<view class="permission-modal-header">
+					<view class="shield-icon-box">
+						<uni-icons type="info-filled" size="30" color="#ff9500"></uni-icons>
+					</view>
+					<text class="permission-modal-title">权限申请提示</text>
+				</view>
+				
+				<view class="permission-modal-body">
+					<text class="permission-desc-main">为了正常更换头像,我们需要获取您的以下权限:</text>
+					
+					<view class="permission-list-box">
+						<view class="permission-item-box">
+							<view class="icon-circle">
+								<uni-icons type="images-filled" size="20" color="#ff9500"></uni-icons>
+							</view>
+							<view class="item-text-box">
+								<text class="item-name-text">存储与相册权限</text>
+								<text class="item-desc-text">用于从相册中选择已有照片作为头像</text>
+							</view>
+						</view>
+						
+						<view class="permission-item-box">
+							<view class="icon-circle">
+								<uni-icons type="camera-filled" size="20" color="#ff9500"></uni-icons>
+							</view>
+							<view class="item-text-box">
+								<text class="item-name-text">相机拍照权限</text>
+								<text class="item-desc-text">用于拍摄新照片并上传作为头像</text>
+							</view>
+						</view>
+					</view>
+				</view>
+				
+				<view class="permission-modal-footer">
+					<button class="btn-decline" @click="declinePermission">暂不授权</button>
+					<button class="btn-grant" @click="confirmPermission">同意并授权</button>
+				</view>
+			</view>
+		</view>
 	</view>
 </template>
 
@@ -52,6 +95,7 @@ import navBar from '@/components/nav-bar/index.vue'
 import { getInfo, updateUserProfile, uploadAvatar } from '@/api/system/user'
 
 const loading = ref(true)
+const showPermissionModal = ref(false)
 const formData = reactive({
 	avatar: '',
 	nickName: '',
@@ -95,6 +139,16 @@ const onSexChange = (e) => {
 
 // 选择并上传头像
 const handleChooseAvatar = () => {
+    const hasShown = uni.getStorageSync('has_shown_avatar_permission')
+    if (!hasShown) {
+        showPermissionModal.value = true
+    } else {
+        chooseAvatarImage()
+    }
+}
+
+// 实际执行图片选择与上传
+const chooseAvatarImage = () => {
     uni.chooseImage({
         count: 1,
         sizeType: ['compressed'],
@@ -116,6 +170,17 @@ const handleChooseAvatar = () => {
     });
 }
 
+const confirmPermission = () => {
+    showPermissionModal.value = false
+    uni.setStorageSync('has_shown_avatar_permission', true)
+    chooseAvatarImage()
+}
+
+const declinePermission = () => {
+    showPermissionModal.value = false
+    uni.setStorageSync('has_shown_avatar_permission', true)
+}
+
 const validate = () => {
 	if (!formData.nickName || !formData.nickName.trim()) {
 		uni.showToast({ title: '请输入昵称', icon: 'none' })
@@ -264,4 +329,163 @@ const submit = async () => {
 .picker-value.placeholder {
     color: #c0c4cc;
 }
+
+/* ==================== 权限弹窗样式 ==================== */
+.permission-modal-mask {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(0, 0, 0, 0.6);
+    backdrop-filter: blur(10rpx);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    z-index: 9999;
+    padding: 50rpx;
+}
+
+.permission-modal-content {
+    width: 100%;
+    background: #ffffff;
+    border-radius: 36rpx;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.15);
+    animation: modalFadeIn 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+
+@keyframes modalFadeIn {
+    from {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+    to {
+        opacity: 1;
+        transform: scale(1);
+    }
+}
+
+.permission-modal-header {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 48rpx 40rpx 20rpx;
+    
+    .shield-icon-box {
+        width: 90rpx;
+        height: 90rpx;
+        border-radius: 45rpx;
+        background: #fff8eb;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-bottom: 16rpx;
+    }
+    
+    .permission-modal-title {
+        font-size: 32rpx;
+        font-weight: 800;
+        color: #1a1a1a;
+    }
+}
+
+.permission-modal-body {
+    padding: 0 40rpx 30rpx;
+    
+    .permission-desc-main {
+        font-size: 26rpx;
+        color: #666;
+        line-height: 1.5;
+        display: block;
+        text-align: center;
+        margin-bottom: 24rpx;
+    }
+}
+
+.permission-list-box {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.permission-item-box {
+    display: flex;
+    align-items: center;
+    background: #fdfdfd;
+    border: 2rpx solid #f6f7f9;
+    border-radius: 20rpx;
+    padding: 20rpx;
+    
+    .icon-circle {
+        width: 64rpx;
+        height: 64rpx;
+        border-radius: 32rpx;
+        background: #fff5e6;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-right: 16rpx;
+        flex-shrink: 0;
+    }
+    
+    .item-text-box {
+        flex: 1;
+        
+        .item-name-text {
+            font-size: 26rpx;
+            font-weight: 700;
+            color: #333;
+            display: block;
+            margin-bottom: 4rpx;
+            text-align: left;
+        }
+          
+        .item-desc-text {
+            font-size: 22rpx;
+            color: #999;
+            display: block;
+            text-align: left;
+        }
+    }
+}
+
+.permission-modal-footer {
+    padding: 24rpx 40rpx 40rpx;
+    display: flex;
+    gap: 20rpx;
+    border-top: 2rpx solid #f5f6f8;
+    background: #ffffff;
+}
+
+.btn-decline {
+    flex: 1;
+    height: 80rpx;
+    line-height: 80rpx;
+    font-size: 26rpx;
+    color: #666;
+    background: #f5f6f8;
+    border-radius: 40rpx;
+    border: none;
+    font-weight: 600;
+    text-align: center;
+    &::after { border: none; }
+}
+
+.btn-grant {
+    flex: 1.2;
+    height: 80rpx;
+    line-height: 80rpx;
+    font-size: 26rpx;
+    color: #fff;
+    background: linear-gradient(90deg, #ffd53f, #ff9500);
+    border-radius: 40rpx;
+    border: none;
+    font-weight: 700;
+    box-shadow: 0 6rpx 16rpx rgba(255, 149, 0, 0.2);
+    text-align: center;
+    &::after { border: none; }
+}
 </style>