feat:完善小程序
parent
075054a6c1
commit
1941fd60e5
20
App.vue
20
App.vue
|
|
@ -4,10 +4,26 @@
|
||||||
export default {
|
export default {
|
||||||
globalData: {
|
globalData: {
|
||||||
// 用于从首页跳转到服务页面时传递需要高亮的分类
|
// 用于从首页跳转到服务页面时传递需要高亮的分类
|
||||||
serviceCategory: null
|
serviceCategory: null,
|
||||||
|
// 扫码进入时携带的邀请码(小程序码 scene),格式:邀请类型-用户id,如 '0-123'
|
||||||
|
inviteCode: null
|
||||||
},
|
},
|
||||||
onLaunch: function() {
|
onLaunch: function(options) {
|
||||||
console.log('App Launch')
|
console.log('App Launch')
|
||||||
|
// 扫码进入:小程序码的 scene 即邀请码(格式 邀请类型-用户id,如 0-123、1-123)
|
||||||
|
const scene = (options && options.query && options.query.scene) || (options && options.scene)
|
||||||
|
const sceneStr = scene != null ? String(scene) : ''
|
||||||
|
if (sceneStr && /^\d+-\d+$/.test(sceneStr)) {
|
||||||
|
this.globalData.inviteCode = sceneStr
|
||||||
|
uni.setStorageSync('inviteCode', sceneStr)
|
||||||
|
// 未登录时直接进入登录注册页,便于邀请流程
|
||||||
|
if (!uni.getStorageSync('token')) {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.reLaunch({ url: '/pages/login/login' })
|
||||||
|
}, 50)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
// 检查登录状态
|
// 检查登录状态
|
||||||
this.checkLoginStatus()
|
this.checkLoginStatus()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export function login(data) {
|
||||||
* @param {String} data.phoneCode 手机 code, 小程序通过 wx.getPhoneNumber 方法获得
|
* @param {String} data.phoneCode 手机 code, 小程序通过 wx.getPhoneNumber 方法获得
|
||||||
* @param {String} data.loginCode 登录 code, 小程序通过 wx.login 方法获得
|
* @param {String} data.loginCode 登录 code, 小程序通过 wx.login 方法获得
|
||||||
* @param {String} data.state state 参数,必填,用于回调的随机值
|
* @param {String} data.state state 参数,必填,用于回调的随机值
|
||||||
* @param {String} data.inviteCode 邀请码,可选(第一位是类型第二位是用户id,例如:1-1, 0-1)
|
* @param {String} data.inviteCode 邀请码,可选。格式 "邀请类型-用户id":0=会员邀请会员,1=系统用户邀请会员。例如 0-123、1-123
|
||||||
* @returns {Promise} 返回登录结果(包含token等)
|
* @returns {Promise} 返回登录结果(包含token等)
|
||||||
*/
|
*/
|
||||||
export function loginByPhone(data) {
|
export function loginByPhone(data) {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,17 @@ export function createRealNameInfo(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得实名信息
|
||||||
|
export function getRealNameInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: '/app-api/member/user-real-name/get',
|
||||||
|
method: 'GET',
|
||||||
|
data: data,
|
||||||
|
showLoading: false,
|
||||||
|
needAuth: true // 需要token认证
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 我的收藏
|
// 我的收藏
|
||||||
export function getMyCollect(data) {
|
export function getMyCollect(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -39,6 +50,17 @@ export function getMyCollect(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 业务数据获取,主要是分享,收藏等根据业务id 和类型获取数据
|
||||||
|
export function getBusinessData(data) {
|
||||||
|
return request({
|
||||||
|
url: '/app-api/member/lu-buiness/get',
|
||||||
|
method: 'GET',
|
||||||
|
data: data,
|
||||||
|
showLoading: false,
|
||||||
|
needAuth: true // 需要token认证
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 创建会员建议
|
// 创建会员建议
|
||||||
export function createLaborUnionSuggest(data) {
|
export function createLaborUnionSuggest(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -50,6 +72,17 @@ export function createLaborUnionSuggest(data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得会员建议分页
|
||||||
|
export function getLaborUnionSuggestPage(data) {
|
||||||
|
return request({
|
||||||
|
url: '/app-api/member/labor-union-suggest/page',
|
||||||
|
method: 'GET',
|
||||||
|
data: data,
|
||||||
|
showLoading: false,
|
||||||
|
needAuth: true // 需要token认证
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 发布消息
|
// 发布消息
|
||||||
export function createLaborUnionMessage(data) {
|
export function createLaborUnionMessage(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -60,3 +93,43 @@ export function createLaborUnionMessage(data) {
|
||||||
needAuth: true // 需要token认证
|
needAuth: true // 需要token认证
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取邀请小程序码图片。入参格式与后端约定一致,支持返回 URL 或 Base64。
|
||||||
|
* @param {String} inviteCode 邀请码,格式 邀请类型-用户id,如 '0-123',会作为 scene 传给后端(若后端需数字可传 userId)
|
||||||
|
* @returns {Promise<String>} 小程序码图片 URL 或 Data URL(Base64),失败则抛出
|
||||||
|
*/
|
||||||
|
export function getInviteQRCode(inviteCode) {
|
||||||
|
// 入参示例:scene、path、width、autoColor、checkPath、hyaline
|
||||||
|
const scene = inviteCode || ''
|
||||||
|
const path = 'pages/login/login'
|
||||||
|
return request({
|
||||||
|
url: '/app-api/member/social-user/wxa-qrcode',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
scene,
|
||||||
|
path,
|
||||||
|
width: 430,
|
||||||
|
autoColor: true,
|
||||||
|
checkPath: true,
|
||||||
|
hyaline: true
|
||||||
|
},
|
||||||
|
showLoading: false,
|
||||||
|
needAuth: true
|
||||||
|
}).then(res => {
|
||||||
|
if (res == null) return null
|
||||||
|
// 接口可能直接返回 base64 字符串(request 里 resolve 的 data 即为该字符串)
|
||||||
|
if (typeof res === 'string') {
|
||||||
|
const raw = res.trim()
|
||||||
|
if (raw.startsWith('data:image')) return raw
|
||||||
|
if (raw.length > 0) return 'data:image/png;base64,' + raw
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const url = res.url || (res.data && res.data.url)
|
||||||
|
if (url) return url
|
||||||
|
const base64 = res.data && typeof res.data === 'string' ? res.data : (res.data && res.data.data)
|
||||||
|
if (typeof base64 === 'string' && base64.length > 0)
|
||||||
|
return base64.startsWith('data:image') ? base64 : 'data:image/png;base64,' + base64
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,13 @@
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
<view class="action-item" @click="handleLike">
|
<view class="action-item" @click="handleLike">
|
||||||
<image class="action-icon" src="/static/service/like_icon.png" mode="aspectFill"></image>
|
<image
|
||||||
<text class="action-text">点赞</text>
|
class="action-icon"
|
||||||
|
:class="{ active: isLiked }"
|
||||||
|
:src="isLiked ? '/static/service/like_icon_active.png' : '/static/service/like_icon.png'"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
<text class="action-text" :class="{ active: isLiked }">点赞</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<button class="action-item share-button" open-type="share" @click="handleShareClick">
|
<button class="action-item share-button" open-type="share" @click="handleShareClick">
|
||||||
|
|
@ -16,8 +21,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<view class="action-item" @click="handleCollect">
|
<view class="action-item" @click="handleCollect">
|
||||||
<image class="action-icon" src="/static/service/collect_icon.png" mode="aspectFill"></image>
|
<image
|
||||||
<text class="action-text">收藏</text>
|
class="action-icon"
|
||||||
|
:class="{ active: isCollected }"
|
||||||
|
:src="isCollected ? '/static/service/collect_icon_active.png' : '/static/service/collect_icon.png'"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
<text class="action-text" :class="{ active: isCollected }">收藏</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -255,11 +265,6 @@ export default {
|
||||||
&.active {
|
&.active {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-text {
|
|
||||||
font-size: 48rpx;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-text {
|
.action-text {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="complaints-page">
|
<view class="complaints-page">
|
||||||
<!-- 头部区域 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="投诉建议" />
|
<NavHeader title="投诉建议" />
|
||||||
|
</view>
|
||||||
<!-- 表单内容区 -->
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view class="form-content" scroll-y="true">
|
<scroll-view
|
||||||
|
class="form-content"
|
||||||
|
scroll-y="true"
|
||||||
|
:refresher-enabled="true"
|
||||||
|
:refresher-triggered="listRefreshing"
|
||||||
|
@refresherrefresh="onListRefresh"
|
||||||
|
@scrolltolower="onListLoadMore"
|
||||||
|
:lower-threshold="80"
|
||||||
|
>
|
||||||
<!-- 投诉建议表单 -->
|
<!-- 投诉建议表单 -->
|
||||||
<view class="form-section">
|
<view class="form-section">
|
||||||
<view class="section-title">填写投诉建议</view>
|
<view class="section-title">填写投诉建议</view>
|
||||||
|
|
@ -52,12 +60,41 @@
|
||||||
{{ loading ? '提交中...' : '提交' }}
|
{{ loading ? '提交中...' : '提交' }}
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 我的投诉建议列表 -->
|
||||||
|
<view class="list-section">
|
||||||
|
<view class="section-title">我的投诉建议</view>
|
||||||
|
<view class="empty-list" v-if="!listLoading && suggestList.length === 0">
|
||||||
|
<text class="empty-text">暂无投诉建议记录</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="suggest-card"
|
||||||
|
v-for="(item, index) in suggestList"
|
||||||
|
:key="item.id || index"
|
||||||
|
>
|
||||||
|
<view class="card-header">
|
||||||
|
<text class="card-title">{{ item.title || '无标题' }}</text>
|
||||||
|
<text class="card-time">{{ formatTime(item.createTime) }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="card-content" v-if="item.content">{{ item.content }}</text>
|
||||||
|
<view class="card-reply" v-if="getReplyText(item)">
|
||||||
|
<text class="reply-label">{{ item.replyName }}回复:</text>
|
||||||
|
<text class="reply-text">{{ getReplyText(item) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="list-footer" v-if="suggestList.length > 0">
|
||||||
|
<text v-if="listLoadMore" class="footer-text">加载中...</text>
|
||||||
|
<text v-else-if="!listHasMore" class="footer-text">没有更多了</text>
|
||||||
|
<text v-else class="footer-text">上拉加载更多</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { createLaborUnionSuggest } from '@/api/profile.js'
|
import { createLaborUnionSuggest,getLaborUnionSuggestPage } from '@/api/profile.js'
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -66,22 +103,91 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
formData: {
|
formData: {
|
||||||
title: '',
|
title: '',
|
||||||
content: ''
|
content: ''
|
||||||
}
|
},
|
||||||
|
suggestList: [],
|
||||||
|
listPageNo: 1,
|
||||||
|
listPageSize: 10,
|
||||||
|
listHasMore: true,
|
||||||
|
listLoading: false,
|
||||||
|
listLoadMore: false,
|
||||||
|
listRefreshing: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44
|
||||||
|
},
|
||||||
// 是否可以提交
|
// 是否可以提交
|
||||||
canSubmit() {
|
canSubmit() {
|
||||||
return this.formData.title && this.formData.title.trim().length >= 1
|
return this.formData.title && this.formData.title.trim().length >= 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0
|
||||||
|
this.loadSuggestList()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
formatTime(val) {
|
||||||
|
if (val == null || val === '') return ''
|
||||||
|
let ts = Number(val)
|
||||||
|
if (!Number.isFinite(ts)) return val
|
||||||
|
if (String(ts).length <= 10) ts *= 1000
|
||||||
|
const d = new Date(ts)
|
||||||
|
const y = d.getFullYear()
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
const h = String(d.getHours()).padStart(2, '0')
|
||||||
|
const min = String(d.getMinutes()).padStart(2, '0')
|
||||||
|
return `${y}-${m}-${day} ${h}:${min}`
|
||||||
|
},
|
||||||
|
getReplyText(item) {
|
||||||
|
const text = item.replyContent
|
||||||
|
return text ? String(text).trim() : ''
|
||||||
|
},
|
||||||
|
getReplyTime(item) {
|
||||||
|
const t = item.replyTime
|
||||||
|
return t != null && t !== '' ? this.formatTime(t) : ''
|
||||||
|
},
|
||||||
|
async loadSuggestList(append = false) {
|
||||||
|
if (this.listLoading) return
|
||||||
|
this.listLoading = true
|
||||||
|
try {
|
||||||
|
const res = await getLaborUnionSuggestPage({
|
||||||
|
pageNo: this.listPageNo,
|
||||||
|
pageSize: this.listPageSize
|
||||||
|
})
|
||||||
|
const raw = res.list || res.records || (res.data && (res.data.list || res.data.records)) || []
|
||||||
|
const list = Array.isArray(raw) ? raw : []
|
||||||
|
this.suggestList = append ? this.suggestList.concat(list) : list
|
||||||
|
this.listHasMore = list.length >= this.listPageSize
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载投诉建议列表失败:', e)
|
||||||
|
if (!append) this.suggestList = []
|
||||||
|
uni.showToast({ title: '加载列表失败', icon: 'none' })
|
||||||
|
} finally {
|
||||||
|
this.listLoading = false
|
||||||
|
this.listLoadMore = false
|
||||||
|
this.listRefreshing = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onListRefresh() {
|
||||||
|
this.listRefreshing = true
|
||||||
|
this.listPageNo = 1
|
||||||
|
this.listHasMore = true
|
||||||
|
this.loadSuggestList()
|
||||||
|
},
|
||||||
|
onListLoadMore() {
|
||||||
|
if (!this.listHasMore || this.listLoadMore || this.listLoading) return
|
||||||
|
this.listLoadMore = true
|
||||||
|
this.listPageNo += 1
|
||||||
|
this.loadSuggestList(true)
|
||||||
|
},
|
||||||
// 提交表单
|
// 提交表单
|
||||||
async handleSubmit() {
|
async handleSubmit() {
|
||||||
// 验证标题
|
// 验证标题
|
||||||
|
|
@ -110,10 +216,11 @@ export default {
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 延迟返回上一页
|
this.formData.title = ''
|
||||||
setTimeout(() => {
|
this.formData.content = ''
|
||||||
uni.navigateBack()
|
this.listPageNo = 1
|
||||||
}, 1500)
|
this.listHasMore = true
|
||||||
|
this.loadSuggestList()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('提交投诉建议失败:', error)
|
console.error('提交投诉建议失败:', error)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|
@ -136,6 +243,22 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.form-content {
|
.form-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
@ -235,7 +358,7 @@ export default {
|
||||||
|
|
||||||
.submit-section {
|
.submit-section {
|
||||||
margin-top: 40rpx;
|
margin-top: 40rpx;
|
||||||
padding-bottom: 40rpx;
|
padding-bottom: 24rpx;
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -260,4 +383,99 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-section {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
padding-bottom: 40rpx;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-left: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-list {
|
||||||
|
padding: 60rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggest-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1819;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-time {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999999;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
display: block;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-reply {
|
||||||
|
background: #f0f6ff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 16rpx;
|
||||||
|
border-left: 4rpx solid #004294;
|
||||||
|
|
||||||
|
.reply-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #004294;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-time {
|
||||||
|
display: block;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999999;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-footer {
|
||||||
|
padding: 24rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
.footer-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="activities-list-page">
|
<view class="activities-list-page">
|
||||||
<!-- 顶部导航栏 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="工会活动" :show-placeholder="true" />
|
<NavHeader title="工会活动" :show-placeholder="true" />
|
||||||
|
</view>
|
||||||
<!-- 活动列表 -->
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="activities-scroll"
|
class="activities-scroll"
|
||||||
scroll-y="true"
|
scroll-y="true"
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -75,6 +76,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
activitiesList: [],
|
activitiesList: [],
|
||||||
// 分页相关
|
// 分页相关
|
||||||
page: 1,
|
page: 1,
|
||||||
|
|
@ -86,7 +88,14 @@ export default {
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
this.loadActivities();
|
this.loadActivities();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -185,7 +194,22 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 活动列表 */
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.activities-scroll {
|
.activities-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="my-collect-page">
|
<view class="my-collect-page">
|
||||||
<!-- 顶部导航栏 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="我的收藏" />
|
<NavHeader title="我的收藏" />
|
||||||
|
</view>
|
||||||
<!-- 收藏列表 -->
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="collect-scroll"
|
class="collect-scroll"
|
||||||
scroll-y="true"
|
scroll-y="true"
|
||||||
|
|
@ -57,11 +57,12 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getMyCollect } from "@/api/profile.js";
|
import { getMyCollect,getBusinessData } from "@/api/profile.js";
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -70,6 +71,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
collectList: [],
|
collectList: [],
|
||||||
// 分页相关
|
// 分页相关
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
|
|
@ -81,7 +83,14 @@ export default {
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
this.loadCollectList();
|
this.loadCollectList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -160,11 +169,17 @@ export default {
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 收藏项点击
|
// 收藏项点击:跳转详情,详情页通过 getBusinessData(id: collectId, type: collectType) 拉取数据
|
||||||
handleCollectClick(item) {
|
handleCollectClick(item) {
|
||||||
// uni.navigateTo({
|
const collectId = item.collectId ?? item.id;
|
||||||
// url: `/pages/detail/richTextDetail?id=${item.collectId}`,
|
const collectType = item.collectType ?? item.type ?? "";
|
||||||
// });
|
if (collectId == null || collectId === "") {
|
||||||
|
uni.showToast({ title: "数据异常", icon: "none" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/detail/richTextDetail?collectId=${encodeURIComponent(collectId)}&collectType=${encodeURIComponent(collectType)}&hideBar=1`,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -179,7 +194,22 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 收藏列表 */
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.collect-scroll {
|
.collect-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="post-message-page">
|
<view class="post-message-page">
|
||||||
<!-- 头部区域 -->
|
<!-- 头部区域(固定,滑动时不动) -->
|
||||||
<NavHeader title="发布消息" />
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
|
<NavHeader title="发布消息" />
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 表单内容区 -->
|
<!-- 表单内容区(预留头部高度) -->
|
||||||
|
<view class="form-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view class="form-content" scroll-y="true">
|
<scroll-view class="form-content" scroll-y="true">
|
||||||
<!-- 基本信息 -->
|
<!-- 基本信息 -->
|
||||||
<view class="form-section">
|
<view class="form-section">
|
||||||
|
|
@ -191,6 +194,7 @@
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -204,12 +208,14 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
messageTypeIndex: -1,
|
messageTypeIndex: -1,
|
||||||
messageTypeOptions: [
|
messageTypeOptions: [
|
||||||
{ label: '类型1', value: 1 },
|
{ label: '租房', value: 3 },
|
||||||
{ label: '类型2', value: 2 },
|
{ label: '保险', value: 4 },
|
||||||
{ label: '类型3', value: 3 }
|
{ label: '健康', value: 5 },
|
||||||
|
{ label: '安全知识', value: 6 },
|
||||||
],
|
],
|
||||||
gradeIndex: -1,
|
gradeIndex: -1,
|
||||||
gradeOptions: [
|
gradeOptions: [
|
||||||
|
|
@ -230,6 +236,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// 头部总高度(状态栏 + 88rpx 内容区 ≈ 44px)
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44
|
||||||
|
},
|
||||||
// 开始时间显示(从 ISO 格式提取日期部分)
|
// 开始时间显示(从 ISO 格式提取日期部分)
|
||||||
startTimeDisplay() {
|
startTimeDisplay() {
|
||||||
if (!this.formData.startTime) return ''
|
if (!this.formData.startTime) return ''
|
||||||
|
|
@ -242,6 +252,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 选择封面图片
|
// 选择封面图片
|
||||||
|
|
@ -415,6 +427,22 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.form-content {
|
.form-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="map-detail-page">
|
<view class="map-detail-page">
|
||||||
<!-- 顶部导航栏 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="店铺位置" />
|
<NavHeader title="店铺位置" />
|
||||||
|
</view>
|
||||||
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<!-- 地图区域 -->
|
<!-- 地图区域 -->
|
||||||
<view class="map-container" v-if="mapInitialized">
|
<view class="map-container" v-if="mapInitialized">
|
||||||
<map
|
<map
|
||||||
|
|
@ -85,6 +86,7 @@
|
||||||
<view class="loading-mask" v-if="loading">
|
<view class="loading-mask" v-if="loading">
|
||||||
<text class="loading-text">加载中...</text>
|
<text class="loading-text">加载中...</text>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -99,6 +101,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
shopId: null,
|
shopId: null,
|
||||||
storeInfo: {},
|
storeInfo: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
@ -118,6 +121,9 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
// // 计算距离文本
|
// // 计算距离文本
|
||||||
// distanceText() {
|
// distanceText() {
|
||||||
// if (
|
// if (
|
||||||
|
|
@ -162,6 +168,8 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
this.shopId = options.id;
|
this.shopId = options.id;
|
||||||
if (this.shopId) {
|
if (this.shopId) {
|
||||||
// 从存储中读取用户位置信息
|
// 从存储中读取用户位置信息
|
||||||
|
|
@ -421,6 +429,22 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 地图容器 */
|
/* 地图容器 */
|
||||||
.map-container {
|
.map-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="rich-text-detail-page">
|
<view class="rich-text-detail-page" :class="{ 'no-bottom-bar': hideBottomBar }">
|
||||||
<!-- 顶部导航栏 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="详情" />
|
<NavHeader title="详情" />
|
||||||
|
</view>
|
||||||
<!-- 内容区域 -->
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view class="content-scroll" scroll-y="true">
|
<scroll-view class="content-scroll" scroll-y="true">
|
||||||
<!-- 加载中 -->
|
<!-- 加载中 -->
|
||||||
<view class="loading-state" v-if="loading">
|
<view class="loading-state" v-if="loading">
|
||||||
|
|
@ -45,20 +45,22 @@
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 底部操作栏 -->
|
<!-- 底部操作栏(从收藏进入时隐藏) -->
|
||||||
<DetailActionBar
|
<DetailActionBar
|
||||||
v-if="detailData"
|
v-if="detailData && !hideBottomBar"
|
||||||
:liked="isLiked"
|
:liked="isLiked"
|
||||||
:collected="isCollected"
|
:collected="isCollected"
|
||||||
:id="detailId"
|
:id="detailId"
|
||||||
:zanType="noticeType"
|
:zanType="noticeType"
|
||||||
:type="noticeType"
|
:type="noticeType"
|
||||||
/>
|
/>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getGuildDetail } from "@/api/home.js";
|
import { getGuildDetail } from "@/api/home.js";
|
||||||
|
import { getBusinessData } from "@/api/profile.js";
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
import DetailActionBar from "@/components/DetailActionBar/DetailActionBar.vue";
|
import DetailActionBar from "@/components/DetailActionBar/DetailActionBar.vue";
|
||||||
|
|
||||||
|
|
@ -69,6 +71,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
detailId: null, // 详情ID(用于通过接口获取数据)
|
detailId: null, // 详情ID(用于通过接口获取数据)
|
||||||
title: "",
|
title: "",
|
||||||
content: "",
|
content: "",
|
||||||
|
|
@ -78,13 +81,28 @@ export default {
|
||||||
isCollected: false, // 是否已收藏
|
isCollected: false, // 是否已收藏
|
||||||
detailData: null, // 详情数据(通过接口获取时使用)
|
detailData: null, // 详情数据(通过接口获取时使用)
|
||||||
noticeType: null, // 内容类型
|
noticeType: null, // 内容类型
|
||||||
|
hideBottomBar: false, // 为 true 时不显示底部操作栏(如从收藏进入)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
// 支持两种模式:
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
// 1. 通过 id 调用接口获取详情(原 activitiesDetail 的用法)
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
// 2. 直接传入 title 和 content(原 richTextDetail 的用法)
|
// 支持三种模式:
|
||||||
if (options.id) {
|
// 1. 从收藏进入:collectId + collectType,用 getBusinessData 拉取详情
|
||||||
|
// 2. 通过 id 调用接口获取详情(原 activitiesDetail 的用法)
|
||||||
|
// 3. 直接传入 title 和 content(原 richTextDetail 的用法)
|
||||||
|
const collectId = options.collectId != null ? decodeURIComponent(String(options.collectId)) : null;
|
||||||
|
const collectType = options.collectType != null ? decodeURIComponent(String(options.collectType)) : null;
|
||||||
|
if (collectId != null && collectId !== "") {
|
||||||
|
this.detailId = collectId;
|
||||||
|
this.hideBottomBar = !!(options.hideBar || options.hideBar === "1");
|
||||||
|
this.loadDetailByBusinessData(collectId, collectType || "");
|
||||||
|
} else if (options.id) {
|
||||||
this.detailId = options.id;
|
this.detailId = options.id;
|
||||||
this.loadDetailById();
|
this.loadDetailById();
|
||||||
} else if (options.content) {
|
} else if (options.content) {
|
||||||
|
|
@ -148,6 +166,35 @@ export default {
|
||||||
},
|
},
|
||||||
// #endif
|
// #endif
|
||||||
methods: {
|
methods: {
|
||||||
|
// 判断 VO 是否有数据(有数据即已点赞/已收藏)
|
||||||
|
hasRespData(vo) {
|
||||||
|
if (vo == null) return false;
|
||||||
|
if (Array.isArray(vo)) return vo.length > 0;
|
||||||
|
if (typeof vo === "object") return Object.keys(vo).length > 0;
|
||||||
|
return !!vo;
|
||||||
|
},
|
||||||
|
// 通过收藏进入:getBusinessData(id: collectId, type: collectType) 拉取详情
|
||||||
|
async loadDetailByBusinessData(collectId, collectType) {
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
const res = await getBusinessData({ id: collectId, type: collectType });
|
||||||
|
if (res) {
|
||||||
|
this.detailData = res;
|
||||||
|
this.noticeType = res.noticeType ?? res.messageType ?? res.type ?? collectType;
|
||||||
|
this.title = res.title || "详情";
|
||||||
|
if (res.content) {
|
||||||
|
this.parsedContent = this.parseHtmlContent(res.content);
|
||||||
|
}
|
||||||
|
this.isLiked = this.hasRespData(res.zanRespVO);
|
||||||
|
this.isCollected = this.hasRespData(res.collectRespVO);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载详情失败:", error);
|
||||||
|
uni.showToast({ title: "加载失败,请重试", icon: "none" });
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
// 通过 ID 加载详情(原 activitiesDetail 的功能)
|
// 通过 ID 加载详情(原 activitiesDetail 的功能)
|
||||||
async loadDetailById() {
|
async loadDetailById() {
|
||||||
if (!this.detailId) {
|
if (!this.detailId) {
|
||||||
|
|
@ -165,13 +212,10 @@ export default {
|
||||||
if (res.content) {
|
if (res.content) {
|
||||||
this.parsedContent = this.parseHtmlContent(res.content);
|
this.parsedContent = this.parseHtmlContent(res.content);
|
||||||
}
|
}
|
||||||
// 设置点赞和收藏状态(如果接口返回了这些字段)
|
// 点赞状态:zanRespVO 有数据表示已点赞
|
||||||
if (res.isLiked !== undefined) {
|
this.isLiked = this.hasRespData(res.zanRespVO);
|
||||||
this.isLiked = res.isLiked;
|
// 收藏状态:collectRespVO 有数据表示已收藏
|
||||||
}
|
this.isCollected = this.hasRespData(res.collectRespVO);
|
||||||
if (res.isCollected !== undefined) {
|
|
||||||
this.isCollected = res.isCollected;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载详情失败:", error);
|
console.error("加载详情失败:", error);
|
||||||
|
|
@ -254,9 +298,28 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-bottom: 120rpx; // 为底部操作栏留出空间
|
padding-bottom: 120rpx; // 为底部操作栏留出空间
|
||||||
|
|
||||||
|
&.no-bottom-bar {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 内容区域 */
|
|
||||||
.content-scroll {
|
.content-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="store-page">
|
<view class="store-page">
|
||||||
<!-- 顶部导航栏 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="店铺详情" />
|
<NavHeader title="店铺详情" />
|
||||||
|
</view>
|
||||||
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<!-- 顶部店铺信息(点击进入地图页) -->
|
<!-- 顶部店铺信息(点击进入地图页) -->
|
||||||
<view class="store-header" @click="handleGoMap">
|
<view class="store-header" @click="handleGoMap">
|
||||||
<view class="store-brand">
|
<view class="store-brand">
|
||||||
|
|
@ -112,6 +113,7 @@
|
||||||
去结算
|
去结算
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -130,6 +132,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
storeInfo: {},
|
storeInfo: {},
|
||||||
menuList: [],
|
menuList: [],
|
||||||
categoryLabel: "",
|
categoryLabel: "",
|
||||||
|
|
@ -140,6 +143,9 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
// 计算总金额(只计算选中且数量大于0的商品)
|
// 计算总金额(只计算选中且数量大于0的商品)
|
||||||
totalAmount() {
|
totalAmount() {
|
||||||
return this.menuList.reduce((total, item) => {
|
return this.menuList.reduce((total, item) => {
|
||||||
|
|
@ -164,7 +170,8 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
console.log(options, 111110);
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
// 从路由参数获取店铺ID和分类标签
|
// 从路由参数获取店铺ID和分类标签
|
||||||
this.shopId = options.id;
|
this.shopId = options.id;
|
||||||
this.categoryLabel = options.categoryLabel;
|
this.categoryLabel = options.categoryLabel;
|
||||||
|
|
@ -445,6 +452,22 @@ export default {
|
||||||
padding-bottom: 120rpx; // 为底部结算栏留出空间
|
padding-bottom: 120rpx; // 为底部结算栏留出空间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 顶部店铺信息 */
|
/* 顶部店铺信息 */
|
||||||
.store-header {
|
.store-header {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="home-page">
|
<view class="home-page">
|
||||||
<!-- 应用头部 -->
|
<!-- 应用头部(固定,滑动时不动) -->
|
||||||
<view class="header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<view
|
||||||
|
class="header header-fixed"
|
||||||
|
:style="{ paddingTop: statusBarHeight + 'px' }"
|
||||||
|
>
|
||||||
<image class="logo" src="/static/home/logo.png" mode="aspectFit"></image>
|
<image class="logo" src="/static/home/logo.png" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 主体内容(预留头部高度,避免被遮挡) -->
|
||||||
|
<view class="main-content" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<!-- 招募宣传横幅 -->
|
<!-- 招募宣传横幅 -->
|
||||||
<swiper
|
<swiper
|
||||||
class="recruit-banner"
|
class="recruit-banner"
|
||||||
|
|
@ -101,6 +106,7 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -121,6 +127,8 @@ export default {
|
||||||
statusBarHeight: 0,
|
statusBarHeight: 0,
|
||||||
// 导航栏高度(状态栏 + 导航栏内容)
|
// 导航栏高度(状态栏 + 导航栏内容)
|
||||||
navBarHeight: 0,
|
navBarHeight: 0,
|
||||||
|
// 头部总高度(用于主体 padding-top,约 72rpx ≈ 36px)
|
||||||
|
headerContentHeight: 44,
|
||||||
bannerList: [],
|
bannerList: [],
|
||||||
// 公会福利列表
|
// 公会福利列表
|
||||||
benefitsList: [
|
benefitsList: [
|
||||||
|
|
@ -153,6 +161,11 @@ export default {
|
||||||
activitiesList: [],
|
activitiesList: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + this.headerContentHeight;
|
||||||
|
},
|
||||||
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.getSystemInfo();
|
this.getSystemInfo();
|
||||||
this.loadHomeData();
|
this.loadHomeData();
|
||||||
|
|
@ -384,6 +397,15 @@ export default {
|
||||||
width: 306rpx;
|
width: 306rpx;
|
||||||
height: 72rpx;
|
height: 72rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.header-fixed {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background-color: #e2e8f1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 招募宣传横幅 */
|
/* 招募宣传横幅 */
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="login-page">
|
<view class="login-page">
|
||||||
<!-- 状态栏占位 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<!-- <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view> -->
|
<NavHeader title="登录" />
|
||||||
|
</view>
|
||||||
<NavHeader title="登录" />
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
|
|
||||||
<!-- 登录内容区 -->
|
|
||||||
<view class="login-content">
|
<view class="login-content">
|
||||||
<!-- Logo区域 -->
|
<!-- Logo区域 -->
|
||||||
<view class="logo-section">
|
<view class="logo-section">
|
||||||
|
|
@ -93,6 +91,7 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -118,6 +117,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44
|
||||||
|
},
|
||||||
// 判断是否可以登录
|
// 判断是否可以登录
|
||||||
canLogin() {
|
canLogin() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -142,13 +144,17 @@ export default {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存页面参数(state 和 inviteCode)
|
// 保存页面参数(state、inviteCode;扫码进入时邀请码在 scene 中)
|
||||||
if (options.state) {
|
if (options.state) {
|
||||||
this.state = options.state
|
this.state = options.state
|
||||||
}
|
}
|
||||||
if (options.inviteCode) {
|
if (options.inviteCode) {
|
||||||
this.inviteCode = options.inviteCode
|
this.inviteCode = options.inviteCode
|
||||||
}
|
}
|
||||||
|
if (options.scene && /^\d+-\d+$/.test(String(options.scene))) {
|
||||||
|
this.inviteCode = String(options.scene)
|
||||||
|
uni.setStorageSync('inviteCode', this.inviteCode)
|
||||||
|
}
|
||||||
|
|
||||||
// 判断是否显示返回按钮(从其他页面跳转过来时显示)
|
// 判断是否显示返回按钮(从其他页面跳转过来时显示)
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
|
|
@ -492,6 +498,22 @@ export default {
|
||||||
background: linear-gradient(180deg, #e2e8f1 0%, #ffffff 100%);
|
background: linear-gradient(180deg, #e2e8f1 0%, #ffffff 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: linear-gradient(180deg, #e2e8f1 0%, #ffffff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.status-bar {
|
.status-bar {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="profile-page">
|
<view class="profile-page">
|
||||||
<view class="header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
<!-- 头部固定,滑动时不动 -->
|
||||||
|
<view
|
||||||
|
class="header header-fixed"
|
||||||
|
:style="{ paddingTop: statusBarHeight + 'px' }"
|
||||||
|
>
|
||||||
<text class="header-text">个人中心</text>
|
<text class="header-text">个人中心</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 主体内容(预留头部高度) -->
|
||||||
|
<view class="profile-main" :style="{ paddingTop: navBarHeight + 'px' }">
|
||||||
<!-- 用户资料卡片 -->
|
<!-- 用户资料卡片 -->
|
||||||
<view
|
<view
|
||||||
class="user-card"
|
class="user-card"
|
||||||
|
|
@ -57,6 +63,9 @@
|
||||||
<text class="user-id">NO.{{ userInfo.id }}</text>
|
<text class="user-id">NO.{{ userInfo.id }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="invite-btn" @click="goToInvite">
|
||||||
|
<image src="https://resource2.ctshenglong.cn/20260309/yaoqingma_btn_1773020094853.png" mode="aspectFill"></image>
|
||||||
|
</view>
|
||||||
<view class="member-benefits-btn" @click="goToMemberBenefits">
|
<view class="member-benefits-btn" @click="goToMemberBenefits">
|
||||||
<image :src="memberLevelIcon" mode="aspectFill"></image>
|
<image :src="memberLevelIcon" mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -117,11 +126,40 @@
|
||||||
<view class="logout-section">
|
<view class="logout-section">
|
||||||
<button class="logout-btn" @click="handleLogout">退出登录</button>
|
<button class="logout-btn" @click="handleLogout">退出登录</button>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 邀请海报弹窗(图5样式) -->
|
||||||
|
<view class="invite-modal-mask" v-if="inviteModalVisible" @click="closeInviteModal"></view>
|
||||||
|
<view class="invite-modal-wrap" v-if="inviteModalVisible" @click.stop>
|
||||||
|
<view class="invite-poster" id="invite-poster">
|
||||||
|
<view class="invite-poster-close" @click="closeInviteModal">×</view>
|
||||||
|
<view class="poster-inner">
|
||||||
|
<view class="poster-tag">邀请好友</view>
|
||||||
|
<view class="poster-qr-wrap" v-if="inviteQRCodeUrl">
|
||||||
|
<view class="poster-qr-outer">
|
||||||
|
<image class="poster-qr-image" :src="inviteQRCodeUrl" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<text class="poster-qr-tip">长按识别图中二维码</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="poster-actions">
|
||||||
|
<view class="poster-btn poster-btn-cancel" @click="closeInviteModal">取消</view>
|
||||||
|
<view class="poster-btn poster-btn-save" @click="savePosterImage">保存图片</view>
|
||||||
|
</view>
|
||||||
|
<!-- 用于导出海报的 canvas(隐藏) -->
|
||||||
|
<canvas
|
||||||
|
class="poster-canvas"
|
||||||
|
type="2d"
|
||||||
|
id="poster-canvas"
|
||||||
|
:style="{ width: posterCanvasWidth + 'px', height: posterCanvasHeight + 'px' }"
|
||||||
|
></canvas>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getUserInfo } from "@/api/profile.js";
|
import { getUserInfo, getInviteQRCode } from "@/api/profile.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -134,6 +172,12 @@ export default {
|
||||||
},
|
},
|
||||||
noticeNum: 3,
|
noticeNum: 3,
|
||||||
uploading: false, // 是否正在上传头像
|
uploading: false, // 是否正在上传头像
|
||||||
|
inviteModalVisible: false,
|
||||||
|
inviteQRCodeUrl: "",
|
||||||
|
inviteCodeStr: "", // 邀请码,格式:邀请类型-用户id,如 0-123
|
||||||
|
posterCanvasWidth: 680,
|
||||||
|
posterCanvasHeight: 860,
|
||||||
|
posterSaving: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -234,6 +278,185 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 邀请好友:弹出邀请二维码。邀请码格式 "邀请类型-用户id",扫码后打开小程序登录注册页,登录接口 /app-api/member/auth/weixin-mini-app-login 使用 inviteCode
|
||||||
|
// 邀请类型:0=会员邀请会员,1=系统用户邀请会员。当前为会员邀请,故用 0-用户id
|
||||||
|
async goToInvite() {
|
||||||
|
const uid = this.userInfo && this.userInfo.id;
|
||||||
|
if (!uid) {
|
||||||
|
uni.showToast({ title: "请先登录", icon: "none" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const inviteCodeStr = `0-${uid}`;
|
||||||
|
this.inviteCodeStr = inviteCodeStr;
|
||||||
|
this.inviteModalVisible = true;
|
||||||
|
this.inviteQRCodeUrl = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = await getInviteQRCode(inviteCodeStr);
|
||||||
|
if (url) {
|
||||||
|
this.inviteQRCodeUrl = url;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("获取邀请二维码失败:", e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closeInviteModal() {
|
||||||
|
this.inviteModalVisible = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存海报图片到相册(按图5样式绘制 canvas 后导出)
|
||||||
|
async savePosterImage() {
|
||||||
|
if (this.posterSaving || !this.inviteQRCodeUrl) return;
|
||||||
|
this.posterSaving = true;
|
||||||
|
uni.showLoading({ title: "生成中...", mask: true });
|
||||||
|
const dpr = uni.getSystemInfoSync().pixelRatio || 2;
|
||||||
|
const w = this.posterCanvasWidth;
|
||||||
|
const h = this.posterCanvasHeight;
|
||||||
|
const padding = 48;
|
||||||
|
const qrSize = 260;
|
||||||
|
const qrTop = 220;
|
||||||
|
const avatarSize = 68;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query = uni.createSelectorQuery().in(this);
|
||||||
|
query
|
||||||
|
.select("#poster-canvas")
|
||||||
|
.fields({ node: true, size: true })
|
||||||
|
.exec(async (res) => {
|
||||||
|
if (!res || !res[0] || !res[0].node) {
|
||||||
|
uni.hideLoading();
|
||||||
|
this.posterSaving = false;
|
||||||
|
uni.showToast({ title: "生成失败,请重试", icon: "none" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const canvas = res[0].node;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
canvas.width = w * dpr;
|
||||||
|
canvas.height = h * dpr;
|
||||||
|
ctx.scale(dpr, dpr);
|
||||||
|
|
||||||
|
// 白底圆角
|
||||||
|
ctx.fillStyle = "#ffffff";
|
||||||
|
const r = 16;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(padding + r, padding);
|
||||||
|
ctx.lineTo(w - padding - r, padding);
|
||||||
|
ctx.quadraticCurveTo(w - padding, padding, w - padding, padding + r);
|
||||||
|
ctx.lineTo(w - padding, h - padding - r);
|
||||||
|
ctx.quadraticCurveTo(w - padding, h - padding, w - padding - r, h - padding);
|
||||||
|
ctx.lineTo(padding + r, h - padding);
|
||||||
|
ctx.quadraticCurveTo(padding, h - padding, padding, h - padding - r);
|
||||||
|
ctx.lineTo(padding, padding + r);
|
||||||
|
ctx.quadraticCurveTo(padding, padding, padding + r, padding);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// 文案
|
||||||
|
ctx.fillStyle = "#333333";
|
||||||
|
ctx.font = "bold 14px PingFang SC, sans-serif";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
const nickname = (this.userInfo.nickname || "微信用户").slice(0, 20);
|
||||||
|
ctx.font = "bold 16px PingFang SC, sans-serif";
|
||||||
|
ctx.fillText(nickname, w / 2, 96);
|
||||||
|
ctx.font = "13px PingFang SC, sans-serif";
|
||||||
|
ctx.fillStyle = "#666666";
|
||||||
|
ctx.fillText("长按识别图中二维码", w / 2, qrTop + qrSize + 44);
|
||||||
|
|
||||||
|
const drawImage = (src) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = canvas.createImage();
|
||||||
|
img.onload = () => resolve(img);
|
||||||
|
img.onerror = () => reject(new Error("图片加载失败"));
|
||||||
|
img.src = src;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const qrImg = await drawImage(this.inviteQRCodeUrl);
|
||||||
|
const qrX = (w - qrSize) / 2;
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(w / 2, qrTop + qrSize / 2, qrSize / 2, 0, Math.PI * 2);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.clip();
|
||||||
|
ctx.drawImage(qrImg, qrX, qrTop, qrSize, qrSize);
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
const avatarSrc = this.avatarSrc;
|
||||||
|
const avatarImg = await drawImage(avatarSrc);
|
||||||
|
const ax = (w - avatarSize) / 2;
|
||||||
|
const ay = qrTop + (qrSize - avatarSize) / 2;
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(w / 2, qrTop + qrSize / 2, avatarSize / 2, 0, Math.PI * 2);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.clip();
|
||||||
|
ctx.drawImage(avatarImg, ax, ay, avatarSize, avatarSize);
|
||||||
|
ctx.restore();
|
||||||
|
} catch (imgErr) {
|
||||||
|
console.warn("海报图片绘制跳过:", imgErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.createSelectorQuery()
|
||||||
|
.in(this)
|
||||||
|
.select("#poster-canvas")
|
||||||
|
.fields({ node: true, size: true })
|
||||||
|
.exec((r2) => {
|
||||||
|
if (!r2 || !r2[0] || !r2[0].node) {
|
||||||
|
uni.hideLoading();
|
||||||
|
this.posterSaving = false;
|
||||||
|
uni.showToast({ title: "导出失败", icon: "none" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const node = r2[0].node;
|
||||||
|
uni.canvasToTempFilePath({
|
||||||
|
canvas: node,
|
||||||
|
fileType: "png",
|
||||||
|
quality: 1,
|
||||||
|
success: (pathRes) => {
|
||||||
|
uni.saveImageToPhotosAlbum({
|
||||||
|
filePath: pathRes.tempFilePath,
|
||||||
|
success: () => {
|
||||||
|
uni.hideLoading();
|
||||||
|
this.posterSaving = false;
|
||||||
|
uni.showToast({ title: "已保存到相册", icon: "success" });
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
uni.hideLoading();
|
||||||
|
this.posterSaving = false;
|
||||||
|
if (err.errMsg && err.errMsg.indexOf("auth") !== -1) {
|
||||||
|
uni.showModal({
|
||||||
|
title: "提示",
|
||||||
|
content: "需要您授权保存图片到相册",
|
||||||
|
confirmText: "去设置",
|
||||||
|
success: (s) => {
|
||||||
|
if (s.confirm) uni.openSetting();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: "保存失败", icon: "none" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.hideLoading();
|
||||||
|
this.posterSaving = false;
|
||||||
|
uni.showToast({ title: "导出失败", icon: "none" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 400);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("保存海报失败:", e);
|
||||||
|
uni.hideLoading();
|
||||||
|
this.posterSaving = false;
|
||||||
|
uni.showToast({ title: "生成失败,请重试", icon: "none" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 会员权益
|
// 会员权益
|
||||||
goToMemberBenefits() {
|
goToMemberBenefits() {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
|
|
@ -458,6 +681,15 @@ export default {
|
||||||
height: 88rpx;
|
height: 88rpx;
|
||||||
line-height: 88rpx;
|
line-height: 88rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.header-fixed {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 用户资料卡片 */
|
/* 用户资料卡片 */
|
||||||
|
|
@ -565,6 +797,18 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invite-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 255rpx;
|
||||||
|
bottom: 24rpx;
|
||||||
|
width: 175rpx;
|
||||||
|
height: 42rpx;
|
||||||
|
image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.member-benefits-btn {
|
.member-benefits-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 50rpx;
|
right: 50rpx;
|
||||||
|
|
@ -702,5 +946,153 @@ export default {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 邀请海报弹窗(图5样式) */
|
||||||
|
.invite-modal-mask {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 900;
|
||||||
|
}
|
||||||
|
.invite-modal-wrap {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 901;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.invite-poster {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 660rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
overflow: visible;
|
||||||
|
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.invite-poster-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 20rpx;
|
||||||
|
top: 20rpx;
|
||||||
|
width: 64rpx;
|
||||||
|
height: 64rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 44rpx;
|
||||||
|
color: #999999;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.poster-inner {
|
||||||
|
padding: 48rpx 48rpx 44rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.poster-tag {
|
||||||
|
position: absolute;
|
||||||
|
top: 28rpx;
|
||||||
|
right: 96rpx;
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666666;
|
||||||
|
font-family: PingFang-SC, PingFang-SC;
|
||||||
|
}
|
||||||
|
.poster-nickname {
|
||||||
|
font-size: 38rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: PingFang-SC, PingFang-SC;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
.poster-qr-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.poster-qr-outer {
|
||||||
|
width: 380rpx;
|
||||||
|
height: 380rpx;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
.poster-qr-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.poster-qr-avatar-wrap {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
padding: 8rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.poster-qr-avatar {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.poster-qr-tip {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #999999;
|
||||||
|
margin-top: 28rpx;
|
||||||
|
font-family: PingFang-SC, PingFang-SC;
|
||||||
|
}
|
||||||
|
.poster-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 28rpx;
|
||||||
|
margin-top: 32rpx;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 660rpx;
|
||||||
|
}
|
||||||
|
.poster-btn {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 280rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
border-radius: 44rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-family: PingFang-SC, PingFang-SC;
|
||||||
|
}
|
||||||
|
.poster-btn-cancel {
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
.poster-btn-save {
|
||||||
|
background: #004294;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.poster-canvas {
|
||||||
|
position: fixed;
|
||||||
|
left: -9999px;
|
||||||
|
top: 0;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="rules-page">
|
<view class="rules-page">
|
||||||
<NavHeader title="积分会员制度" />
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
|
<NavHeader title="积分会员制度" />
|
||||||
|
</view>
|
||||||
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view class="content-scroll" scroll-y="true">
|
<scroll-view class="content-scroll" scroll-y="true">
|
||||||
<!-- 一、会员等级与积分门槛 -->
|
<!-- 一、会员等级与积分门槛 -->
|
||||||
<view class="section">
|
<view class="section">
|
||||||
|
|
@ -67,6 +69,7 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -77,6 +80,7 @@ export default {
|
||||||
components: { NavHeader },
|
components: { NavHeader },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
levelRows: [
|
levelRows: [
|
||||||
{ level: "初级", points: "0", name: "筑巢会员" },
|
{ level: "初级", points: "0", name: "筑巢会员" },
|
||||||
{ level: "中级", points: "1000", name: "安居会员" },
|
{ level: "中级", points: "1000", name: "安居会员" },
|
||||||
|
|
@ -112,24 +116,49 @@ export default {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.rules-page {
|
.rules-page {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: #ffffff;
|
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.content-scroll {
|
.content-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
padding: 20rpx 24rpx 40rpx;
|
padding: 20rpx 24rpx 40rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: #ffffff;
|
background: radial-gradient(0% 0% at 0% 0%, #ffffff 0%, #e2e8f1 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="privacy-page">
|
<view class="privacy-page">
|
||||||
<NavHeader title="隐私政策" />
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
|
<NavHeader title="隐私政策" />
|
||||||
|
</view>
|
||||||
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view class="content" scroll-y="true">
|
<scroll-view class="content" scroll-y="true">
|
||||||
<view class="privacy-content">
|
<view class="privacy-content">
|
||||||
<text class="privacy-text">{{ privacyText }}</text>
|
<text class="privacy-text">{{ privacyText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -18,6 +22,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
privacyText: `隐私政策
|
privacyText: `隐私政策
|
||||||
|
|
||||||
本隐私政策(以下简称"本政策")由广厦千万间司机公会(以下简称"我们")制定,旨在说明我们通过运营的微信小程序(以下简称"小程序")向您(以下简称"用户")提供餐饮、维修、保险、健康等服务过程中,对用户个人信息的收集、使用、存储、传输、共享、披露及保护等相关事宜。您在使用我们的服务时,即视为您已充分阅读、理解并同意本政策的全部内容,包括我们对本政策的后续更新。如您不同意本政策,请勿使用我们的服务。
|
本隐私政策(以下简称"本政策")由广厦千万间司机公会(以下简称"我们")制定,旨在说明我们通过运营的微信小程序(以下简称"小程序")向您(以下简称"用户")提供餐饮、维修、保险、健康等服务过程中,对用户个人信息的收集、使用、存储、传输、共享、披露及保护等相关事宜。您在使用我们的服务时,即视为您已充分阅读、理解并同意本政策的全部内容,包括我们对本政策的后续更新。如您不同意本政策,请勿使用我们的服务。
|
||||||
|
|
@ -102,7 +107,16 @@ export default {
|
||||||
|
|
||||||
广厦千万间司机公会`
|
广厦千万间司机公会`
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -114,6 +128,22 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="real-name-auth-page">
|
<view class="real-name-auth-page">
|
||||||
<!-- 头部区域 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="实名认证" />
|
<NavHeader title="实名认证" />
|
||||||
|
</view>
|
||||||
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
|
<!-- 加载中 -->
|
||||||
|
<view class="loading-wrap" v-if="realNameLoading">
|
||||||
|
<text class="loading-text">加载实名信息中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 表单内容区 -->
|
<!-- 表单内容区 -->
|
||||||
<scroll-view class="form-content" scroll-y="true">
|
<scroll-view class="form-content" scroll-y="true" v-else>
|
||||||
<!-- 基本信息 -->
|
<!-- 基本信息 -->
|
||||||
<view class="form-section">
|
<view class="form-section">
|
||||||
<view class="section-title">基本信息</view>
|
<view class="section-title">基本信息</view>
|
||||||
|
|
||||||
|
<!-- 实名状态提示(不可编辑时显示) -->
|
||||||
|
<view class="status-tip" v-if="!formEditable && realNameStatus != null">
|
||||||
|
<text class="status-tip-text">当前状态:{{ realNameStatusText }},不可修改</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 真实姓名 -->
|
<!-- 真实姓名 -->
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
|
|
@ -19,6 +30,7 @@
|
||||||
v-model="formData.realName"
|
v-model="formData.realName"
|
||||||
placeholder="请输入真实姓名"
|
placeholder="请输入真实姓名"
|
||||||
maxlength="20"
|
maxlength="20"
|
||||||
|
:disabled="!formEditable"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -28,6 +40,7 @@
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
<text class="input-label">证件类型</text>
|
<text class="input-label">证件类型</text>
|
||||||
<picker
|
<picker
|
||||||
|
v-if="formEditable"
|
||||||
mode="selector"
|
mode="selector"
|
||||||
:range="idTypeOptions"
|
:range="idTypeOptions"
|
||||||
range-key="label"
|
range-key="label"
|
||||||
|
|
@ -40,6 +53,7 @@
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
|
<text v-else class="picker-text readonly">{{ idTypeLabel }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
@ -53,6 +67,7 @@
|
||||||
v-model="formData.idNumber"
|
v-model="formData.idNumber"
|
||||||
placeholder="请输入证件号码"
|
placeholder="请输入证件号码"
|
||||||
maxlength="30"
|
maxlength="30"
|
||||||
|
:disabled="!formEditable"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -66,7 +81,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">身份证正面</text>
|
<text class="upload-label">身份证正面</text>
|
||||||
<view class="upload-box" @click="chooseImage('idFrontImg')">
|
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('idFrontImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.idFrontImg"
|
v-if="formData.idFrontImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -85,7 +100,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">身份证背面</text>
|
<text class="upload-label">身份证背面</text>
|
||||||
<view class="upload-box" @click="chooseImage('idBackImg')">
|
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('idBackImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.idBackImg"
|
v-if="formData.idBackImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -109,7 +124,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">驾驶证正面</text>
|
<text class="upload-label">驾驶证正面</text>
|
||||||
<view class="upload-box" @click="chooseImage('driverLicenseFrontImg')">
|
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('driverLicenseFrontImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.driverLicenseFrontImg"
|
v-if="formData.driverLicenseFrontImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -128,7 +143,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">驾驶证背面</text>
|
<text class="upload-label">驾驶证背面</text>
|
||||||
<view class="upload-box" @click="chooseImage('driverLicenseBackImg')">
|
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('driverLicenseBackImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.driverLicenseBackImg"
|
v-if="formData.driverLicenseBackImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -152,7 +167,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">行驶证正面</text>
|
<text class="upload-label">行驶证正面</text>
|
||||||
<view class="upload-box" @click="chooseImage('vehicleLicenseFrontImg')">
|
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('vehicleLicenseFrontImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.vehicleLicenseFrontImg"
|
v-if="formData.vehicleLicenseFrontImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -171,7 +186,7 @@
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<view class="upload-wrapper">
|
<view class="upload-wrapper">
|
||||||
<text class="upload-label">行驶证背面</text>
|
<text class="upload-label">行驶证背面</text>
|
||||||
<view class="upload-box" @click="chooseImage('vehicleLicenseBackImg')">
|
<view class="upload-box" :class="{ disabled: !formEditable }" @click="formEditable && chooseImage('vehicleLicenseBackImg')">
|
||||||
<image
|
<image
|
||||||
v-if="formData.vehicleLicenseBackImg"
|
v-if="formData.vehicleLicenseBackImg"
|
||||||
class="uploaded-image"
|
class="uploaded-image"
|
||||||
|
|
@ -199,13 +214,14 @@
|
||||||
placeholder="请输入备注信息(选填)"
|
placeholder="请输入备注信息(选填)"
|
||||||
maxlength="200"
|
maxlength="200"
|
||||||
:auto-height="true"
|
:auto-height="true"
|
||||||
|
:disabled="!formEditable"
|
||||||
></textarea>
|
></textarea>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 协议同意区域 -->
|
<!-- 协议同意区域(仅可编辑时显示) -->
|
||||||
<view class="agreement-section">
|
<view class="agreement-section" v-if="formEditable">
|
||||||
<view class="agreement-checkbox" @click="toggleAgreement">
|
<view class="agreement-checkbox" @click="toggleAgreement">
|
||||||
<view class="checkbox-icon" :class="{ checked: agreedToTerms }">
|
<view class="checkbox-icon" :class="{ checked: agreedToTerms }">
|
||||||
<text v-if="agreedToTerms" class="checkmark">✓</text>
|
<text v-if="agreedToTerms" class="checkmark">✓</text>
|
||||||
|
|
@ -219,8 +235,8 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
<!-- 提交按钮(仅可编辑时显示) -->
|
||||||
<view class="submit-section">
|
<view class="submit-section" v-if="formEditable">
|
||||||
<button
|
<button
|
||||||
class="submit-btn"
|
class="submit-btn"
|
||||||
:class="{ disabled: loading || !agreedToTerms }"
|
:class="{ disabled: loading || !agreedToTerms }"
|
||||||
|
|
@ -231,11 +247,12 @@
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { createRealNameInfo } from '@/api/profile.js'
|
import { createRealNameInfo, getRealNameInfo } from '@/api/profile.js'
|
||||||
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -244,8 +261,11 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
loading: false,
|
loading: false,
|
||||||
agreedToTerms: false, // 是否同意协议
|
realNameLoading: true, // 实名信息加载中
|
||||||
|
realNameStatus: null, // 实名状态:0 未认证/没有 1 待审核 2 已通过 3 已退回 4 已过期
|
||||||
|
agreedToTerms: false, // 是否同意协议
|
||||||
idTypeIndex: 0,
|
idTypeIndex: 0,
|
||||||
idTypeOptions: [
|
idTypeOptions: [
|
||||||
{ label: '身份证', value: 1 },
|
{ label: '身份证', value: 1 },
|
||||||
|
|
@ -265,9 +285,79 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44
|
||||||
|
},
|
||||||
|
// 仅 没有(无数据/0)、退回(3)、过期(4) 可编辑;待审核(1)、已通过(2) 不可编辑
|
||||||
|
formEditable() {
|
||||||
|
const s = this.realNameStatus
|
||||||
|
if (s == null) return true
|
||||||
|
return s === 0 || s === 3 || s === 4
|
||||||
|
},
|
||||||
|
realNameStatusText() {
|
||||||
|
const map = { 0: '未认证', 1: '待审核', 2: '已通过', 3: '已退回', 4: '已过期' }
|
||||||
|
return map[this.realNameStatus] ?? '未知'
|
||||||
|
},
|
||||||
|
idTypeLabel() {
|
||||||
|
if (this.formData.idType == null) return '请选择证件类型'
|
||||||
|
const opt = this.idTypeOptions.find(item => item.value === this.formData.idType)
|
||||||
|
return opt ? opt.label : '请选择证件类型'
|
||||||
|
}
|
||||||
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync()
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0
|
||||||
|
this.loadRealNameInfo()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async loadRealNameInfo() {
|
||||||
|
this.realNameLoading = true
|
||||||
|
try {
|
||||||
|
const userInfo = uni.getStorageSync('userInfo')
|
||||||
|
if (!userInfo || !userInfo.id) {
|
||||||
|
this.realNameStatus = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await getRealNameInfo({ id: userInfo.id })
|
||||||
|
this.fillFormFromApi(res)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取实名信息失败:', e)
|
||||||
|
this.realNameStatus = 0
|
||||||
|
} finally {
|
||||||
|
this.realNameLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fillFormFromApi(data) {
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
this.realNameStatus = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const s = data.status != null ? Number(data.status) : (data.id ? 1 : 0)
|
||||||
|
this.realNameStatus = s
|
||||||
|
this.formData.realName = data.realName ?? ''
|
||||||
|
this.formData.idType = data.idType != null ? data.idType : null
|
||||||
|
this.formData.idNumber = data.idNumber ?? ''
|
||||||
|
this.formData.idFrontImg = data.idFrontImg ?? ''
|
||||||
|
this.formData.idBackImg = data.idBackImg ?? ''
|
||||||
|
this.formData.driverLicenseFrontImg = data.driverLicenseFrontImg ?? ''
|
||||||
|
this.formData.driverLicenseBackImg = data.driverLicenseBackImg ?? ''
|
||||||
|
this.formData.vehicleLicenseFrontImg = data.vehicleLicenseFrontImg ?? ''
|
||||||
|
this.formData.vehicleLicenseBackImg = data.vehicleLicenseBackImg ?? ''
|
||||||
|
this.formData.imgVerifyRemark = data.imgVerifyRemark ?? ''
|
||||||
|
const idx = this.idTypeOptions.findIndex(item => item.value === this.formData.idType)
|
||||||
|
this.idTypeIndex = idx >= 0 ? idx : 0
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleAgreement() {
|
||||||
|
this.agreedToTerms = !this.agreedToTerms
|
||||||
|
},
|
||||||
|
goToUserAgreement() {
|
||||||
|
uni.navigateTo({ url: '/pages/profileSub/userAgreement' })
|
||||||
|
},
|
||||||
|
goToPrivacyPolicy() {
|
||||||
|
uni.navigateTo({ url: '/pages/profileSub/privacyPolicy' })
|
||||||
|
},
|
||||||
|
|
||||||
// 证件类型选择
|
// 证件类型选择
|
||||||
handleIdTypeChange(e) {
|
handleIdTypeChange(e) {
|
||||||
|
|
@ -430,6 +520,34 @@ export default {
|
||||||
background: #e2e8f1;
|
background: #e2e8f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 80rpx 0;
|
||||||
|
.loading-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-content {
|
.form-content {
|
||||||
height: calc(100vh - 120rpx);
|
height: calc(100vh - 120rpx);
|
||||||
padding: 0 30rpx 40rpx;
|
padding: 0 30rpx 40rpx;
|
||||||
|
|
@ -446,6 +564,17 @@ export default {
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
padding-left: 10rpx;
|
padding-left: 10rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-tip {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding: 16rpx 20rpx;
|
||||||
|
background: rgba(255, 152, 0, 0.1);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
.status-tip-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #e65100;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-item {
|
.form-item {
|
||||||
|
|
@ -487,6 +616,10 @@ export default {
|
||||||
&.placeholder {
|
&.placeholder {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.readonly {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-arrow {
|
.picker-arrow {
|
||||||
|
|
@ -546,6 +679,11 @@ export default {
|
||||||
color: #999999;
|
color: #999999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="service-records-page">
|
<view class="service-records-page">
|
||||||
<!-- 头部区域 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="服务记录" />
|
<NavHeader title="服务记录" />
|
||||||
|
</view>
|
||||||
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<!-- Tab 切换 -->
|
<!-- Tab 切换 -->
|
||||||
<view class="tab-bar">
|
<view class="tab-bar">
|
||||||
<view
|
<view
|
||||||
|
|
@ -119,6 +120,7 @@
|
||||||
src="/static/home/qr_code_icon.png"
|
src="/static/home/qr_code_icon.png"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
class="qr-code-icon"
|
class="qr-code-icon"
|
||||||
|
:class="{ gray: item.status === 4 }"
|
||||||
></image>
|
></image>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -217,6 +219,7 @@
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -230,6 +233,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
currentTab: "pending_payment", // 当前选中的 tab
|
currentTab: "pending_payment", // 当前选中的 tab
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
@ -252,6 +256,9 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
currentList() {
|
currentList() {
|
||||||
return this.recordsMap[this.currentTab] || [];
|
return this.recordsMap[this.currentTab] || [];
|
||||||
},
|
},
|
||||||
|
|
@ -262,7 +269,8 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
// 如果通过参数传入 tab,优先使用传入的 tab 值
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
if (options && options.tab) {
|
if (options && options.tab) {
|
||||||
this.currentTab = options.tab;
|
this.currentTab = options.tab;
|
||||||
}
|
}
|
||||||
|
|
@ -577,6 +585,22 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #e2e8f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tab 切换栏 */
|
/* Tab 切换栏 */
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -843,6 +867,11 @@ export default {
|
||||||
.qr-code-icon {
|
.qr-code-icon {
|
||||||
width: 44rpx;
|
width: 44rpx;
|
||||||
height: 44rpx;
|
height: 44rpx;
|
||||||
|
|
||||||
|
&.gray {
|
||||||
|
filter: grayscale(100%);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="agreement-page">
|
<view class="agreement-page">
|
||||||
<NavHeader title="用户服务协议" />
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
|
<NavHeader title="用户服务协议" />
|
||||||
|
</view>
|
||||||
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<scroll-view class="content" scroll-y="true">
|
<scroll-view class="content" scroll-y="true">
|
||||||
<view class="agreement-content">
|
<view class="agreement-content">
|
||||||
<text class="agreement-text">{{ agreementText }}</text>
|
<text class="agreement-text">{{ agreementText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -18,6 +22,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
agreementText: `用户服务协议
|
agreementText: `用户服务协议
|
||||||
|
|
||||||
鉴于甲方运营微信小程序(以下简称"小程序"),为乙方提供餐饮、维修、保险、健康等专属收费服务;乙方自愿使用甲方提供的服务,为明确双方权利义务,根据《中华人民共和国民法典》《中华人民共和国个人信息保护法》等相关法律法规,甲乙双方本着平等、自愿、公平、诚实信用的原则,达成如下协议,以资共同遵守。
|
鉴于甲方运营微信小程序(以下简称"小程序"),为乙方提供餐饮、维修、保险、健康等专属收费服务;乙方自愿使用甲方提供的服务,为明确双方权利义务,根据《中华人民共和国民法典》《中华人民共和国个人信息保护法》等相关法律法规,甲乙双方本着平等、自愿、公平、诚实信用的原则,达成如下协议,以资共同遵守。
|
||||||
|
|
@ -98,7 +103,16 @@ export default {
|
||||||
10.2 甲方通过小程序公告、短信发送的通知、公示,自发布或送达之日起生效,视为已履行告知义务,乙方应及时关注。
|
10.2 甲方通过小程序公告、短信发送的通知、公示,自发布或送达之日起生效,视为已履行告知义务,乙方应及时关注。
|
||||||
10.3 本协议条款被认定为无效或不可执行的,不影响其他条款的效力。`
|
10.3 本协议条款被认定为无效或不可执行的,不影响其他条款的效力。`
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad() {
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -110,6 +124,22 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="webview-page">
|
<view class="webview-page">
|
||||||
<!-- 头部导航栏 -->
|
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
|
||||||
<NavHeader title="网页" />
|
<NavHeader title="网页" />
|
||||||
|
</view>
|
||||||
<!-- WebView 容器 -->
|
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
|
||||||
<view class="webview-container">
|
<view class="webview-container">
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<web-view :src="webviewUrl"></web-view>
|
<web-view :src="webviewUrl"></web-view>
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
</view>
|
</view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -27,10 +28,18 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
statusBarHeight: 0,
|
||||||
webviewUrl: ''
|
webviewUrl: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
headerHeight() {
|
||||||
|
return this.statusBarHeight + 44;
|
||||||
|
},
|
||||||
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
|
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||||
// 从路由参数获取要加载的 URL
|
// 从路由参数获取要加载的 URL
|
||||||
if (options.url) {
|
if (options.url) {
|
||||||
this.webviewUrl = decodeURIComponent(options.url);
|
this.webviewUrl = decodeURIComponent(options.url);
|
||||||
|
|
@ -56,6 +65,22 @@ export default {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-fixed-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-wrap {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.webview-container {
|
.webview-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0; // 配合 flex: 1 使用
|
height: 0; // 配合 flex: 1 使用
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Loading…
Reference in New Issue