|
|
@@ -0,0 +1,375 @@
|
|
|
+<template>
|
|
|
+ <view>
|
|
|
+ <view class="menu-list">
|
|
|
+ <!-- ===== 个人 ===== -->
|
|
|
+ <view class="menu-group">
|
|
|
+ <view class="menu-group-title">── 个人 ──</view>
|
|
|
+ <view class="menu-item" v-if="role === 'parent'" @click="goToFamilyMembers">
|
|
|
+ <text>👨👩👧👦 家庭成员</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" v-if="role === 'parent'" @click="goToAppointments">
|
|
|
+ <text>📅 测评预约</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="goToDailyTasks">
|
|
|
+ <text>📋 每日任务</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" v-if="role === 'parent'" @click="goToOnboarding">
|
|
|
+ <text>🎯 新手任务</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="goToReportManagement">
|
|
|
+ <text>📋 报告管理</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" v-if="role === 'parent'" @click="goToServiceRoleApply">
|
|
|
+ <text>📋 服务角色申请</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ===== 服务 ===== -->
|
|
|
+ <view class="menu-group" v-if="showServices">
|
|
|
+ <view class="menu-group-title">── 服务 ──</view>
|
|
|
+ <view class="menu-item" @click="goToMembership">
|
|
|
+ <text>👑 会员中心</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" v-if="isVendor" @click="goToVendorCenter">
|
|
|
+ <text>🏪 服务商中心</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="goToOrders">
|
|
|
+ <text>🛒 订单管理</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="goToConsigneeList">
|
|
|
+ <text>📍 收货人管理</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="goToAfterSales">
|
|
|
+ <text>🔄 售后记录</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ===== 设置 ===== -->
|
|
|
+ <view class="menu-group">
|
|
|
+ <view class="menu-group-title">── 设置 ──</view>
|
|
|
+ <view class="menu-item" v-if="role === 'parent'" @click="showPasswordModal = true">
|
|
|
+ <text>🔐 {{ hasPassword ? '重置密码' : '设置密码' }}</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item menu-item-switch" v-if="role === 'parent'">
|
|
|
+ <text>👁 对家庭成员可见</text>
|
|
|
+ <switch :checked="showToFamily === 1" @change="onShowToFamilyChange" color="#5B9BD5" />
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="showInviteActionSheet">
|
|
|
+ <text>📨 邀请加入</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="logout">
|
|
|
+ <text>🚪 退出登录</text>
|
|
|
+ <text class="arrow">›</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 设置/重置密码弹窗 -->
|
|
|
+ <view class="modal-mask" v-if="showPasswordModal" @click="showPasswordModal = false">
|
|
|
+ <view class="modal" @click.stop>
|
|
|
+ <view class="modal-title">{{ hasPassword ? '重置密码' : '设置密码' }}</view>
|
|
|
+ <view class="form-item" v-if="hasPassword">
|
|
|
+ <input v-model="oldPassword" type="number" password placeholder="输入旧密码" maxlength="6" />
|
|
|
+ </view>
|
|
|
+ <view class="form-item">
|
|
|
+ <input v-model="newPassword" type="number" password placeholder="输入新密码" maxlength="6" />
|
|
|
+ </view>
|
|
|
+ <view class="form-item">
|
|
|
+ <input v-model="confirmPassword" type="number" password placeholder="确认新密码" maxlength="6" />
|
|
|
+ </view>
|
|
|
+ <view class="modal-btns">
|
|
|
+ <button @click="showPasswordModal = false">取消</button>
|
|
|
+ <button class="btn-primary" @click="savePassword">确定</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { setPassword, verifyPassword, updateMemberVisibility, getVisibleFamilyMembers, vendorStatus, ensureFamily } from '../utils/api.js'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'ProfileMenu',
|
|
|
+ props: {
|
|
|
+ role: {
|
|
|
+ type: String,
|
|
|
+ default: 'parent'
|
|
|
+ },
|
|
|
+ showServices: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ hasPassword: false,
|
|
|
+ showPasswordModal: false,
|
|
|
+ oldPassword: '',
|
|
|
+ newPassword: '',
|
|
|
+ confirmPassword: '',
|
|
|
+ showToFamily: 1,
|
|
|
+ isVendor: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onShow() {
|
|
|
+ if (!uni.getStorageSync('token')) return
|
|
|
+ this.hasPassword = !!uni.getStorageSync('passwordSet')
|
|
|
+ this.loadShowToFamily()
|
|
|
+ this.checkVendorStatus()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async loadShowToFamily() {
|
|
|
+ try {
|
|
|
+ const uid = this.$store.state.userId || uni.getStorageSync('userId')
|
|
|
+ const res = await getVisibleFamilyMembers()
|
|
|
+ const members = res.data || []
|
|
|
+ const self = members.find(m => m.id == uid)
|
|
|
+ if (self) {
|
|
|
+ this.showToFamily = self.showToFamily !== 0 ? 1 : 0
|
|
|
+ }
|
|
|
+ } catch (e) {}
|
|
|
+ },
|
|
|
+ goToOnboarding() {
|
|
|
+ uni.navigateTo({ url: '/pages/profile-extra/onboarding' })
|
|
|
+ },
|
|
|
+ goToReportManagement() {
|
|
|
+ uni.navigateTo({ url: '/pages/profile/report-management' })
|
|
|
+ },
|
|
|
+ goToServiceRoleApply() {
|
|
|
+ uni.navigateTo({ url: '/pages/profile/service-role-apply' })
|
|
|
+ },
|
|
|
+ goToFamilyMembers() {
|
|
|
+ uni.navigateTo({ url: '/pages/profile-extra/family-members' })
|
|
|
+ },
|
|
|
+ goToAppointments() {
|
|
|
+ uni.navigateTo({ url: '/pages/parent/appointments' })
|
|
|
+ },
|
|
|
+ goToDailyTasks() {
|
|
|
+ uni.navigateTo({ url: '/pages/tasks/daily-tasks' })
|
|
|
+ },
|
|
|
+ async onShowToFamilyChange(e) {
|
|
|
+ const val = e.detail.value ? 1 : 0
|
|
|
+ const uid = this.$store.state.userId || uni.getStorageSync('userId')
|
|
|
+ try {
|
|
|
+ await updateMemberVisibility(uid, 'parent', val)
|
|
|
+ this.showToFamily = val
|
|
|
+ uni.showToast({ title: val ? '已对家庭成员可见' : '已对家庭成员隐藏', icon: 'none' })
|
|
|
+ } catch (e) {
|
|
|
+ uni.showToast({ title: '操作失败', icon: 'none' })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ goToMembership() {
|
|
|
+ uni.navigateTo({ url: '/pages/membership/index' })
|
|
|
+ },
|
|
|
+ goToVendorCenter() {
|
|
|
+ uni.navigateTo({ url: '/pages/vendor/center' })
|
|
|
+ },
|
|
|
+ goToOrders() {
|
|
|
+ uni.navigateTo({ url: '/pages/shop/order-list/order-list' })
|
|
|
+ },
|
|
|
+ goToConsigneeList() {
|
|
|
+ uni.navigateTo({ url: '/pages/shop/consignee-list/consignee-list' })
|
|
|
+ },
|
|
|
+ goToAfterSales() {
|
|
|
+ uni.navigateTo({ url: '/pages/shop/after-sales/after-sales' })
|
|
|
+ },
|
|
|
+
|
|
|
+ showInviteActionSheet() {
|
|
|
+ var self = this
|
|
|
+ var ui = uni.getStorageSync('userInfo') || {}
|
|
|
+ if (!ui.familyId) {
|
|
|
+ ensureFamily().then(function(result) {
|
|
|
+ if (result && result.created) {
|
|
|
+ self.showInviteActionSheet()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const userInfo = ui
|
|
|
+ const items = ['👨👩👧👦 邀请家人', '👨🏫 邀请成长规划师']
|
|
|
+ const types = ['family', 'guide']
|
|
|
+ uni.showActionSheet({
|
|
|
+ itemList: items,
|
|
|
+ success: (res) => {
|
|
|
+ self.inviteGenerate(types[res.tapIndex], userInfo)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ inviteGenerate(type, userInfo) {
|
|
|
+ const inviteTypes = {
|
|
|
+ family: {
|
|
|
+ title: (userInfo && userInfo.nickname ? userInfo.nickname : '家人') + ' 邀请你加入家庭',
|
|
|
+ path: '/pages/login/login?invite_type=family'
|
|
|
+ },
|
|
|
+ guide: {
|
|
|
+ title: (userInfo && userInfo.nickname ? userInfo.nickname : '家长') + ' 邀请你成为家庭成长规划师',
|
|
|
+ path: '/pages/login/login?invite_type=guide'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const config = inviteTypes[type] || inviteTypes.family
|
|
|
+ this.$emit('invite-generate', {
|
|
|
+ title: config.title,
|
|
|
+ path: config.path + '&invite_code=',
|
|
|
+ imageUrl: '/static/invite-card.png'
|
|
|
+ })
|
|
|
+ uni.showToast({ title: '点击右上角转发给TA', icon: 'none' })
|
|
|
+ },
|
|
|
+ async savePassword() {
|
|
|
+ if (this.newPassword.length !== 6) {
|
|
|
+ uni.showToast({ title: '请输入6位密码', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (this.newPassword !== this.confirmPassword) {
|
|
|
+ uni.showToast({ title: '两次密码不一致', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (this.hasPassword && this.oldPassword.length !== 6) {
|
|
|
+ uni.showToast({ title: '请输入旧密码', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ if (this.hasPassword) {
|
|
|
+ const verifyRes = await verifyPassword(this.oldPassword)
|
|
|
+ if (verifyRes.code !== 200) {
|
|
|
+ uni.showToast({ title: '旧密码错误', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ await setPassword(this.newPassword)
|
|
|
+ uni.setStorageSync('passwordSet', true)
|
|
|
+ this.hasPassword = true
|
|
|
+ uni.showToast({ title: '密码设置成功', icon: 'success' })
|
|
|
+ this.showPasswordModal = false
|
|
|
+ this.oldPassword = ''
|
|
|
+ this.newPassword = ''
|
|
|
+ this.confirmPassword = ''
|
|
|
+ } catch (e) {
|
|
|
+ console.error('设置密码失败', e)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ logout() {
|
|
|
+ uni.showModal({
|
|
|
+ title: '确认退出',
|
|
|
+ content: '确定要退出登录吗?',
|
|
|
+ success: (res) => {
|
|
|
+ if (res.confirm) {
|
|
|
+ this.$store.commit('logout')
|
|
|
+ uni.reLaunch({ url: '/pages/index-home/index' })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ async checkVendorStatus() {
|
|
|
+ try {
|
|
|
+ const res = await vendorStatus()
|
|
|
+ this.isVendor = res.data && res.data.vendorStatus === 'approved'
|
|
|
+ } catch (e) {
|
|
|
+ this.isVendor = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.menu-list {
|
|
|
+ margin: 30rpx;
|
|
|
+}
|
|
|
+.menu-group {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+.menu-group-title {
|
|
|
+ padding: 20rpx 30rpx 10rpx;
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #999;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+.menu-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 28rpx 30rpx;
|
|
|
+ border-bottom: 1rpx solid #f5f5f5;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+.menu-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+.arrow {
|
|
|
+ color: #ccc;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+.menu-item-switch {
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+.menu-item-switch text {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.modal-mask {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: rgba(0,0,0,0.5);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ z-index: 999;
|
|
|
+}
|
|
|
+.modal {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ padding: 40rpx;
|
|
|
+ width: 80%;
|
|
|
+}
|
|
|
+.modal-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ text-align: center;
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+}
|
|
|
+.form-item {
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+.form-item input {
|
|
|
+ border: 1rpx solid #ddd;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+}
|
|
|
+.modal-btns {
|
|
|
+ display: flex;
|
|
|
+ gap: 20rpx;
|
|
|
+ margin-top: 30rpx;
|
|
|
+}
|
|
|
+.modal-btns button {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 28rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
+}
|
|
|
+.modal-btns .btn-primary {
|
|
|
+ background: #667eea;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+</style>
|