更新个人资料页实现评估会议记录,明确展示与编辑页字段一致性要求,补充技能字段的展示与编辑需求。优化小程序页面,增加联系方式完善弹窗,确保用户在使用找伙伴功能前填写手机号或微信号。调整相关文档以反映最新进展,提升用户体验与功能一致性。
This commit is contained in:
@@ -9,8 +9,8 @@ App({
|
||||
globalData: {
|
||||
// API基础地址 - 连接真实后端
|
||||
// baseUrl: 'https://soulapi.quwanzhi.com',
|
||||
baseUrl: 'https://souldev.quwanzhi.com',
|
||||
// baseUrl: 'http://localhost:8080',
|
||||
// baseUrl: 'https://souldev.quwanzhi.com',
|
||||
baseUrl: 'http://localhost:8080',
|
||||
|
||||
|
||||
// 小程序配置 - 真实AppID
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"pages/addresses/edit",
|
||||
"pages/withdraw-records/withdraw-records",
|
||||
"pages/vip/vip",
|
||||
"pages/member-detail/member-detail"
|
||||
"pages/member-detail/member-detail","pages/mentors/mentors","pages/mentor-detail/mentor-detail","pages/profile-show/profile-show","pages/profile-edit/profile-edit"
|
||||
],
|
||||
"window": {
|
||||
"backgroundTextStyle": "light",
|
||||
|
||||
@@ -212,6 +212,7 @@ Page({
|
||||
navBarHeight: app.globalData.navBarHeight
|
||||
})
|
||||
this.updateUserStatus()
|
||||
this.loadBookDataFromServer()
|
||||
this.loadDailyChapters()
|
||||
this.loadTotalFromServer()
|
||||
},
|
||||
@@ -219,12 +220,64 @@ Page({
|
||||
async loadTotalFromServer() {
|
||||
try {
|
||||
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
||||
if (res && res.total) {
|
||||
this.setData({ totalSections: res.total })
|
||||
if (res && (res.total || (res.data && res.data.length))) {
|
||||
this.setData({ totalSections: res.total || (res.data || []).length })
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// stitch_soul P0-8:从服务端加载目录,按 part 聚合,带 isNew、免费/¥1
|
||||
async loadBookDataFromServer() {
|
||||
try {
|
||||
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
||||
const rows = (res && res.data) || (res && res.chapters) || []
|
||||
if (rows.length === 0) return
|
||||
const partMap = new Map()
|
||||
const numbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二']
|
||||
rows.forEach((r, idx) => {
|
||||
const pid = r.partId || r.part_id || 'part-1'
|
||||
const cid = r.chapterId || r.chapter_id || 'chapter-1'
|
||||
if (!partMap.has(pid)) {
|
||||
const partIdx = partMap.size
|
||||
partMap.set(pid, {
|
||||
id: pid,
|
||||
number: numbers[partIdx] || String(partIdx + 1),
|
||||
title: r.partTitle || r.part_title || '未分类',
|
||||
subtitle: r.chapterTitle || r.chapter_title || '',
|
||||
chapters: new Map()
|
||||
})
|
||||
}
|
||||
const part = partMap.get(pid)
|
||||
if (!part.chapters.has(cid)) {
|
||||
part.chapters.set(cid, {
|
||||
id: cid,
|
||||
title: r.chapterTitle || r.chapter_title || '未分类',
|
||||
sections: []
|
||||
})
|
||||
}
|
||||
const ch = part.chapters.get(cid)
|
||||
ch.sections.push({
|
||||
id: r.id,
|
||||
mid: r.mid ?? r.MID ?? 0,
|
||||
title: r.sectionTitle || r.section_title || r.title || '',
|
||||
isFree: r.isFree === true || (r.price !== undefined && r.price === 0),
|
||||
price: r.price ?? 1,
|
||||
isNew: r.isNew === true || r.is_new === true
|
||||
})
|
||||
})
|
||||
const bookData = Array.from(partMap.values()).map(p => ({
|
||||
...p,
|
||||
chapters: Array.from(p.chapters.values())
|
||||
}))
|
||||
const firstPart = bookData[0] && bookData[0].id
|
||||
this.setData({
|
||||
bookData,
|
||||
totalSections: rows.length,
|
||||
expandedPart: firstPart || this.data.expandedPart
|
||||
})
|
||||
} catch (e) { console.log('[Chapters] 加载目录失败:', e) }
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 设置TabBar选中状态
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
|
||||
@@ -71,10 +71,11 @@
|
||||
<view class="chapter-header">{{chapter.title}}</view>
|
||||
<view class="section-list">
|
||||
<block wx:for="{{chapter.sections}}" wx:key="id" wx:for-item="section">
|
||||
<view class="section-item" bindtap="goToRead" data-id="{{section.id}}">
|
||||
<view class="section-item" bindtap="goToRead" data-id="{{section.id}}" data-mid="{{section.mid}}">
|
||||
<view class="section-left">
|
||||
<text class="section-lock {{section.isFree || hasFullBook || purchasedSections.indexOf(section.id) > -1 ? 'lock-open' : 'lock-closed'}}">{{section.isFree || hasFullBook || purchasedSections.indexOf(section.id) > -1 ? '○' : '●'}}</text>
|
||||
<text class="section-title {{section.isFree || hasFullBook || purchasedSections.indexOf(section.id) > -1 ? '' : 'text-muted'}}">{{section.id}} {{section.title}}</text>
|
||||
<text wx:if="{{section.isNew}}" class="tag tag-new">NEW</text>
|
||||
</view>
|
||||
<view class="section-right">
|
||||
<text wx:if="{{section.isFree}}" class="tag tag-free">免费</text>
|
||||
|
||||
@@ -224,6 +224,14 @@
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
.tag-new {
|
||||
background: rgba(0, 206, 209, 0.15);
|
||||
color: #00CED1;
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 8rpx;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.text-brand {
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
@@ -162,60 +162,89 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
// 从服务端获取精选推荐(加权算法:阅读量50% + 时效30% + 付款率20%)和最新更新
|
||||
// 从服务端获取精选推荐、最新更新(stitch_soul:book/recommended、book/latest-chapters)
|
||||
async loadFeaturedFromServer() {
|
||||
try {
|
||||
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
||||
const chapters = (res && res.data) ? res.data : (res && res.chapters) ? res.chapters : []
|
||||
let featured = (res && res.featuredSections) ? res.featuredSections : []
|
||||
// 服务端未返回精选时,从前端按更新时间取前3条有效章节作为回退
|
||||
if (featured.length === 0 && chapters.length > 0) {
|
||||
const valid = chapters.filter(c => {
|
||||
const id = (c.id || '').toLowerCase()
|
||||
const pt = (c.part_title || c.partTitle || '').toLowerCase()
|
||||
return !id.includes('preface') && !id.includes('epilogue') && !id.includes('appendix')
|
||||
&& !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
|
||||
})
|
||||
featured = valid
|
||||
.sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
|
||||
.slice(0, 5)
|
||||
}
|
||||
const tagMap = ['热门', '推荐', '精选']
|
||||
const tagClassMap = ['tag-hot', 'tag-rec', 'tag-rec']
|
||||
if (featured.length > 0) {
|
||||
this.setData({
|
||||
featuredSections: featured.slice(0, 3).map((s, i) => ({
|
||||
// 1. 精选推荐:优先用 book/recommended(按阅读量+算法,带 热门/推荐/精选 标签)
|
||||
let featured = []
|
||||
try {
|
||||
const recRes = await app.request({ url: '/api/miniprogram/book/recommended', silent: true })
|
||||
if (recRes && recRes.success && Array.isArray(recRes.data) && recRes.data.length > 0) {
|
||||
featured = recRes.data.map((s, i) => ({
|
||||
id: s.id || s.section_id,
|
||||
mid: s.mid ?? s.MID ?? 0,
|
||||
title: s.section_title || s.sectionTitle || s.title || s.chapterTitle || '',
|
||||
part: (s.cleanPartTitle || s.part_title || s.partTitle || '').replace(/[_||]/g, ' ').trim(),
|
||||
tag: tagMap[i] || '精选',
|
||||
tagClass: tagClassMap[i] || 'tag-rec'
|
||||
title: s.sectionTitle || s.section_title || s.title || s.chapterTitle || '',
|
||||
part: (s.partTitle || s.part_title || '').replace(/[_||]/g, ' ').trim(),
|
||||
tag: s.tag || ['热门', '推荐', '精选'][i] || '精选',
|
||||
tagClass: ['tag-hot', 'tag-rec', 'tag-rec'][i] || 'tag-rec'
|
||||
}))
|
||||
this.setData({ featuredSections: featured })
|
||||
}
|
||||
} catch (e) { console.log('[Index] book/recommended 失败:', e) }
|
||||
|
||||
// 兜底:无 recommended 时从 all-chapters 按更新时间取前3
|
||||
if (featured.length === 0) {
|
||||
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
||||
const chapters = (res && res.data) || (res && res.chapters) || []
|
||||
const valid = chapters.filter(c => {
|
||||
const pt = (c.part_title || c.partTitle || '').toLowerCase()
|
||||
return !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
|
||||
})
|
||||
if (valid.length > 0) {
|
||||
const tagMap = ['热门', '推荐', '精选']
|
||||
featured = valid
|
||||
.sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
|
||||
.slice(0, 3)
|
||||
.map((s, i) => ({
|
||||
id: s.id,
|
||||
mid: s.mid ?? s.MID ?? 0,
|
||||
title: s.section_title || s.sectionTitle || s.title || s.chapterTitle || '',
|
||||
part: (s.part_title || s.partTitle || '').replace(/[_||]/g, ' ').trim(),
|
||||
tag: tagMap[i] || '精选',
|
||||
tagClass: ['tag-hot', 'tag-rec', 'tag-rec'][i] || 'tag-rec'
|
||||
}))
|
||||
this.setData({ featuredSections: featured })
|
||||
}
|
||||
}
|
||||
|
||||
// 最新更新 = 按 updated_at 排序第1篇(排除序言/尾声/附录)
|
||||
const validChapters = chapters.filter(c => {
|
||||
const id = (c.id || '').toLowerCase()
|
||||
const pt = (c.part_title || c.partTitle || '').toLowerCase()
|
||||
return !id.includes('preface') && !id.includes('epilogue') && !id.includes('appendix')
|
||||
&& !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
|
||||
})
|
||||
if (validChapters.length > 0) {
|
||||
validChapters.sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
|
||||
const latest = validChapters[0]
|
||||
this.setData({
|
||||
latestSection: {
|
||||
id: latest.id || latest.section_id,
|
||||
mid: latest.mid ?? latest.MID ?? 0,
|
||||
title: latest.section_title || latest.sectionTitle || latest.title || latest.chapterTitle || '',
|
||||
part: latest.cleanPartTitle || latest.part_title || latest.partTitle || ''
|
||||
}
|
||||
// 2. 最新更新:用 book/latest-chapters 取第1条
|
||||
try {
|
||||
const latestRes = await app.request({ url: '/api/miniprogram/book/latest-chapters', silent: true })
|
||||
const latestList = (latestRes && latestRes.data) ? latestRes.data : []
|
||||
if (latestList.length > 0) {
|
||||
const l = latestList[0]
|
||||
this.setData({
|
||||
latestSection: {
|
||||
id: l.id,
|
||||
mid: l.mid ?? l.MID ?? 0,
|
||||
title: l.section_title || l.sectionTitle || l.title || l.chapterTitle || '',
|
||||
part: l.part_title || l.partTitle || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
// 兜底:从 all-chapters 取
|
||||
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
||||
const chapters = (res && res.data) || (res && res.chapters) || []
|
||||
const valid = chapters.filter(c => {
|
||||
const pt = (c.part_title || c.partTitle || '').toLowerCase()
|
||||
return !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
|
||||
})
|
||||
if (valid.length > 0) {
|
||||
valid.sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
|
||||
const latest = valid[0]
|
||||
this.setData({
|
||||
latestSection: {
|
||||
id: latest.id,
|
||||
mid: latest.mid ?? latest.MID ?? 0,
|
||||
title: latest.section_title || latest.sectionTitle || latest.title || latest.chapterTitle || '',
|
||||
part: latest.part_title || latest.partTitle || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('[Index] 从服务端加载推荐失败,使用默认:', e)
|
||||
console.log('[Index] 从服务端加载推荐失败:', e)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -286,16 +315,12 @@ Page({
|
||||
try {
|
||||
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
||||
const chapters = (res && res.data) || (res && res.chapters) || []
|
||||
const sortOrder = c => (c.sortOrder ?? c.sectionOrder ?? c.sort_order ?? 0)
|
||||
// 优先取 sort_order > 62 的「新增」章节;若无则取最近更新的前 10 章(排除序言/尾声/附录)
|
||||
let candidates = chapters.filter(c => sortOrder(c) > 62)
|
||||
const pt = (c) => (c.partTitle || c.part_title || '').toLowerCase()
|
||||
const exclude = c => !pt(c).includes('序言') && !pt(c).includes('尾声') && !pt(c).includes('附录')
|
||||
// stitch_soul:优先取 isNew 标记的章节;若无则取最近更新的前 10 章(排除序言/尾声/附录)
|
||||
let candidates = chapters.filter(c => (c.isNew || c.is_new) === true && exclude(c))
|
||||
if (candidates.length === 0) {
|
||||
const id = (c) => (c.id || '').toLowerCase()
|
||||
const pt = (c) => (c.partTitle || c.part_title || '').toLowerCase()
|
||||
candidates = chapters.filter(c =>
|
||||
!id(c).includes('preface') && !id(c).includes('epilogue') && !id(c).includes('appendix')
|
||||
&& !pt(c).includes('序言') && !pt(c).includes('尾声') && !pt(c).includes('附录')
|
||||
)
|
||||
candidates = chapters.filter(exclude)
|
||||
}
|
||||
const latest = candidates
|
||||
.sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
|
||||
|
||||
@@ -65,6 +65,12 @@ Page({
|
||||
|
||||
// 解锁弹窗
|
||||
showUnlockModal: false,
|
||||
|
||||
// 手机/微信号弹窗(stitch_soul)
|
||||
showContactModal: false,
|
||||
contactPhone: '',
|
||||
contactWechat: '',
|
||||
contactSaving: false,
|
||||
|
||||
// 匹配价格(可配置)
|
||||
matchPrice: 1,
|
||||
@@ -192,8 +198,86 @@ Page({
|
||||
},
|
||||
|
||||
// 点击匹配按钮
|
||||
handleMatchClick() {
|
||||
async handleMatchClick() {
|
||||
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
|
||||
|
||||
// stitch_soul:导师顾问 → 跳转选择导师页
|
||||
if (currentType && currentType.id === 'mentor') {
|
||||
wx.navigateTo({ url: '/pages/mentors/mentors' })
|
||||
return
|
||||
}
|
||||
|
||||
// 找伙伴/资源对接:需先完善联系方式
|
||||
if (this.data.isLoggedIn && currentType?.matchFromDB) {
|
||||
await this.ensureContactInfo(() => this._handleMatchClickInner(currentType))
|
||||
} else {
|
||||
this._handleMatchClickInner(currentType)
|
||||
}
|
||||
},
|
||||
|
||||
async ensureContactInfo(callback) {
|
||||
const userId = app.globalData.userInfo?.id
|
||||
if (!userId) { callback(); return }
|
||||
try {
|
||||
const res = await app.request({ url: `/api/miniprogram/user/profile?userId=${userId}`, silent: true })
|
||||
const phone = (res?.data?.phone || '').trim()
|
||||
const wechat = (res?.data?.wechatId || '').trim()
|
||||
if (phone || wechat) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
showContactModal: true,
|
||||
contactPhone: phone || '',
|
||||
contactWechat: wechat || '',
|
||||
})
|
||||
this._contactCallback = callback
|
||||
} catch (e) {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
|
||||
closeContactModal() {
|
||||
this.setData({ showContactModal: false })
|
||||
this._contactCallback = null
|
||||
},
|
||||
|
||||
onContactPhoneInput(e) { this.setData({ contactPhone: e.detail.value }) },
|
||||
onContactWechatInput(e) { this.setData({ contactWechat: e.detail.value }) },
|
||||
|
||||
async saveContactInfo() {
|
||||
const phone = (this.data.contactPhone || '').trim()
|
||||
const wechat = (this.data.contactWechat || '').trim()
|
||||
if (!phone && !wechat) {
|
||||
wx.showToast({ title: '请至少填写手机号或微信号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({ contactSaving: true })
|
||||
try {
|
||||
await app.request({
|
||||
url: '/api/miniprogram/user/profile',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: app.globalData.userInfo?.id,
|
||||
phone: phone || undefined,
|
||||
wechatId: wechat || undefined,
|
||||
},
|
||||
})
|
||||
if (phone) wx.setStorageSync('user_phone', phone)
|
||||
if (wechat) wx.setStorageSync('user_wechat', wechat)
|
||||
this.loadStoredContact()
|
||||
this.closeContactModal()
|
||||
wx.showToast({ title: '已保存', icon: 'success' })
|
||||
const cb = this._contactCallback
|
||||
this._contactCallback = null
|
||||
if (cb) cb()
|
||||
} catch (e) {
|
||||
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
|
||||
}
|
||||
this.setData({ contactSaving: false })
|
||||
},
|
||||
|
||||
_handleMatchClickInner(currentType) {
|
||||
|
||||
// 资源对接类型需要登录+购买章节才能使用
|
||||
if (currentType && currentType.id === 'investor') {
|
||||
|
||||
@@ -265,6 +265,32 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机/微信号弹窗(stitch_soul comprehensive_profile_editor_v1_2) -->
|
||||
<view class="modal-overlay contact-modal-overlay" wx:if="{{showContactModal}}" bindtap="closeContactModal">
|
||||
<view class="contact-modal" catchtap="preventBubble">
|
||||
<text class="contact-modal-title">请完善联系方式</text>
|
||||
<view class="contact-modal-hint">需完善手机号或微信号才能使用找伙伴功能</view>
|
||||
<view class="form-input-wrap">
|
||||
<text class="form-label">手机号</text>
|
||||
<view class="form-input-inner">
|
||||
<text class="form-icon">📱</text>
|
||||
<input class="form-input" type="tel" placeholder="请输入您的手机号" value="{{contactPhone}}" bindinput="onContactPhoneInput"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-input-wrap">
|
||||
<text class="form-label">微信号</text>
|
||||
<view class="form-input-inner">
|
||||
<text class="form-icon">💬</text>
|
||||
<input class="form-input" type="text" placeholder="请输入您的微信号" value="{{contactWechat}}" bindinput="onContactWechatInput"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="contact-modal-btn" bindtap="saveContactInfo" disabled="{{contactSaving}}">
|
||||
{{contactSaving ? '保存中...' : '保存'}}
|
||||
</view>
|
||||
<text class="contact-modal-cancel" bindtap="closeContactModal">取消</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 解锁弹窗 -->
|
||||
<view class="modal-overlay" wx:if="{{showUnlockModal}}" bindtap="closeUnlockModal">
|
||||
<view class="modal-content unlock-modal" catchtap="preventBubble">
|
||||
|
||||
@@ -1200,3 +1200,26 @@
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 手机/微信号弹窗 - comprehensive_profile_editor_v1_2 */
|
||||
.contact-modal-overlay { background: rgba(0,0,0,0.85); backdrop-filter: blur(8rpx); }
|
||||
.contact-modal {
|
||||
width: 90%; max-width: 600rpx; background: #1A1A1A; border-radius: 48rpx;
|
||||
padding: 48rpx 40rpx; border: 1rpx solid rgba(255,255,255,0.1);
|
||||
}
|
||||
.contact-modal-title { display: block; text-align: center; font-size: 40rpx; font-weight: bold; color: #fff; margin-bottom: 16rpx; }
|
||||
.contact-modal-hint { display: block; font-size: 24rpx; color: #9CA3AF; text-align: center; margin-bottom: 40rpx; }
|
||||
.contact-modal .form-input-wrap { margin-bottom: 32rpx; }
|
||||
.contact-modal .form-label { display: block; font-size: 24rpx; color: #9CA3AF; margin-bottom: 12rpx; margin-left: 8rpx; }
|
||||
.contact-modal .form-input-inner {
|
||||
display: flex; align-items: center; padding: 24rpx 32rpx; background: #262626; border-radius: 24rpx;
|
||||
}
|
||||
.contact-modal .form-input-inner .form-icon { font-size: 36rpx; margin-right: 16rpx; opacity: 0.7; }
|
||||
.contact-modal .form-input-inner .form-input { flex: 1; font-size: 28rpx; color: #fff; background: transparent; }
|
||||
.contact-modal-btn {
|
||||
width: 100%; height: 96rpx; line-height: 96rpx; text-align: center;
|
||||
background: #4FD1C5; color: #000; font-size: 32rpx; font-weight: bold; border-radius: 24rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
.contact-modal-btn[disabled] { opacity: 0.6; }
|
||||
.contact-modal-cancel { display: block; text-align: center; font-size: 28rpx; color: #9CA3AF; margin-top: 24rpx; padding: 16rpx; }
|
||||
|
||||
113
miniprogram/pages/mentor-detail/mentor-detail.js
Normal file
113
miniprogram/pages/mentor-detail/mentor-detail.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Soul创业派对 - 导师详情(stitch_soul)
|
||||
* 联系导师按钮 → 弹出 v2 弹窗(选择咨询项目)
|
||||
*/
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
mentor: null,
|
||||
loading: true,
|
||||
showConsultModal: false,
|
||||
consultOptions: [],
|
||||
selectedType: '',
|
||||
selectedAmount: 0,
|
||||
creating: false,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 })
|
||||
if (options.id) this.loadDetail(options.id)
|
||||
},
|
||||
|
||||
async loadDetail(id) {
|
||||
this.setData({ loading: true })
|
||||
try {
|
||||
const res = await app.request({ url: `/api/miniprogram/mentors/${id}`, silent: true })
|
||||
if (res?.success && res.data) {
|
||||
const d = res.data
|
||||
if (d.judgmentStyle && typeof d.judgmentStyle === 'string') {
|
||||
d.judgmentStyleArr = d.judgmentStyle.split(/[,,]/).map(s => s.trim()).filter(Boolean)
|
||||
} else {
|
||||
d.judgmentStyleArr = []
|
||||
}
|
||||
const fmt = v => v != null ? String(Math.floor(v)).replace(/\B(?=(\d{3})+(?!\d))/g, ',') : ''
|
||||
d.priceSingleFmt = fmt(d.priceSingle)
|
||||
d.priceHalfYearFmt = fmt(d.priceHalfYear)
|
||||
d.priceYearFmt = fmt(d.priceYear)
|
||||
const options = []
|
||||
if (d.priceSingle != null) options.push({ type: 'single', label: '单次咨询', desc: '1小时深度沟通', price: d.priceSingle, priceFmt: fmt(d.priceSingle), original: null })
|
||||
if (d.priceHalfYear != null) options.push({ type: 'half_year', label: '半年咨询', desc: '不限次数 · 关键节点陪伴', price: d.priceHalfYear, priceFmt: fmt(d.priceHalfYear), original: '98,000' })
|
||||
if (d.priceYear != null) options.push({ type: 'year', label: '年度咨询', desc: '全年度战略顾问', price: d.priceYear, priceFmt: fmt(d.priceYear), original: '196,000', recommend: true })
|
||||
this.setData({
|
||||
mentor: d,
|
||||
consultOptions: options,
|
||||
loading: false,
|
||||
})
|
||||
} else {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
onContactTap() {
|
||||
if (!this.data.mentor || this.data.consultOptions.length === 0) {
|
||||
wx.showToast({ title: '暂无咨询项目', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
showConsultModal: true,
|
||||
selectedType: this.data.consultOptions[0].type,
|
||||
selectedAmount: this.data.consultOptions[0].price,
|
||||
})
|
||||
},
|
||||
|
||||
closeConsultModal() {
|
||||
this.setData({ showConsultModal: false })
|
||||
},
|
||||
|
||||
onSelectOption(e) {
|
||||
const item = e.currentTarget.dataset.item
|
||||
this.setData({ selectedType: item.type, selectedAmount: item.price })
|
||||
},
|
||||
|
||||
async onConfirmConsult() {
|
||||
const { mentor, selectedType } = this.data
|
||||
const userId = app.globalData.userInfo?.id
|
||||
if (!userId) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
wx.navigateTo({ url: '/pages/my/my' })
|
||||
return
|
||||
}
|
||||
this.setData({ creating: true })
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: `/api/miniprogram/mentors/${mentor.id}/book`,
|
||||
method: 'POST',
|
||||
data: { userId, consultationType: selectedType },
|
||||
})
|
||||
if (res?.success && res.data) {
|
||||
this.setData({ showConsultModal: false, creating: false })
|
||||
wx.showToast({ title: '预约创建成功', icon: 'success' })
|
||||
// TODO: 调起支付 productType: mentor_consultation, productId: res.data.id
|
||||
wx.showModal({
|
||||
title: '预约成功',
|
||||
content: '请联系客服完成后续对接',
|
||||
showCancel: false,
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: res?.error || '创建失败', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '创建失败', icon: 'none' })
|
||||
}
|
||||
this.setData({ creating: false })
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack()
|
||||
},
|
||||
})
|
||||
1
miniprogram/pages/mentor-detail/mentor-detail.json
Normal file
1
miniprogram/pages/mentor-detail/mentor-detail.json
Normal file
@@ -0,0 +1 @@
|
||||
{"usingComponents":{},"navigationStyle":"custom"}
|
||||
118
miniprogram/pages/mentor-detail/mentor-detail.wxml
Normal file
118
miniprogram/pages/mentor-detail/mentor-detail.wxml
Normal file
@@ -0,0 +1,118 @@
|
||||
<!-- 导师详情 -->
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-back" bindtap="goBack"><text class="back-icon">‹</text></view>
|
||||
<text class="nav-title">导师详情</text>
|
||||
<view class="nav-placeholder-r"></view>
|
||||
</view>
|
||||
<view style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<view class="loading" wx:if="{{loading}}">加载中...</view>
|
||||
<block wx:else>
|
||||
<view class="mentor-header" wx:if="{{mentor}}">
|
||||
<view class="mentor-avatar-wrap">
|
||||
<image wx:if="{{mentor.avatar}}" class="mentor-avatar" src="{{mentor.avatar}}" mode="aspectFill"></image>
|
||||
<view wx:else class="mentor-avatar-placeholder">{{mentor.name ? mentor.name[0] : '?'}}</view>
|
||||
</view>
|
||||
<text class="mentor-name">{{mentor.name}}</text>
|
||||
<text class="mentor-intro">{{mentor.intro}}</text>
|
||||
<view class="mentor-quote" wx:if="{{mentor.quote}}">{{mentor.quote}}</view>
|
||||
</view>
|
||||
|
||||
<view class="block" wx:if="{{mentor.whyFind}}">
|
||||
<view class="block-header"><text class="block-num">01</text><text class="block-title">为什么找{{mentor.name}}?</text></view>
|
||||
<text class="block-text">{{mentor.whyFind}}</text>
|
||||
</view>
|
||||
|
||||
<view class="block" wx:if="{{mentor.offering}}">
|
||||
<view class="block-header"><text class="block-num">02</text><text class="block-title">提供什么?</text></view>
|
||||
<text class="block-text">{{mentor.offering}}</text>
|
||||
</view>
|
||||
|
||||
<view class="block" wx:if="{{mentor.priceSingle || mentor.priceHalfYear || mentor.priceYear}}">
|
||||
<view class="block-header"><text class="block-num">03</text><text class="block-title">收费标准</text></view>
|
||||
<view class="price-table">
|
||||
<view class="price-thead">
|
||||
<text class="p-col-2">咨询项目</text>
|
||||
<text class="p-col-center">时长</text>
|
||||
<text class="p-col-right">价格</text>
|
||||
</view>
|
||||
<view class="price-row" wx:if="{{mentor.priceSingle}}">
|
||||
<text class="p-col-2">单次咨询</text>
|
||||
<text class="p-col-center">1小时</text>
|
||||
<text class="p-col-right price-num">¥{{mentor.priceSingleFmt || mentor.priceSingle}}</text>
|
||||
</view>
|
||||
<view class="price-row price-row-alt" wx:if="{{mentor.priceHalfYear}}">
|
||||
<text class="p-col-2">半年咨询</text>
|
||||
<text class="p-col-center">-</text>
|
||||
<view class="p-col-right">
|
||||
<text class="price-original">98,000</text>
|
||||
<text class="price-num">¥{{mentor.priceHalfYearFmt || mentor.priceHalfYear}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="price-row" wx:if="{{mentor.priceYear}}">
|
||||
<text class="p-col-2">年度咨询</text>
|
||||
<text class="p-col-center">-</text>
|
||||
<view class="p-col-right">
|
||||
<text class="price-original">196,000</text>
|
||||
<text class="price-num">¥{{mentor.priceYearFmt || mentor.priceYear}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="block" wx:if="{{mentor.judgmentStyle}}">
|
||||
<view class="block-header"><text class="block-num">04</text><text class="block-title">判断风格</text></view>
|
||||
<view class="style-tags">
|
||||
<text class="style-tag" wx:for="{{mentor.judgmentStyleArr}}" wx:key="*this">{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-btn-area">
|
||||
<view class="contact-btn" bindtap="onContactTap">
|
||||
<text class="contact-icon">💬</text>
|
||||
<text>联系导师</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- v2 弹窗:选择咨询项目 -->
|
||||
<view class="modal-overlay" wx:if="{{showConsultModal}}" bindtap="closeConsultModal">
|
||||
<view class="modal-content" catchtap="">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">选择咨询项目</text>
|
||||
<text class="modal-close" bindtap="closeConsultModal">✕</text>
|
||||
</view>
|
||||
<view class="consult-options">
|
||||
<view
|
||||
wx:for="{{consultOptions}}"
|
||||
wx:key="type"
|
||||
class="consult-option {{selectedType === item.type ? 'option-selected' : ''}}"
|
||||
data-item="{{item}}"
|
||||
bindtap="onSelectOption"
|
||||
>
|
||||
<view class="option-row1">
|
||||
<text class="option-label">{{item.label}}</text>
|
||||
<text class="option-rec" wx:if="{{item.recommend}}">推荐</text>
|
||||
<view class="option-radio {{selectedType === item.type ? 'radio-selected' : ''}}"></view>
|
||||
</view>
|
||||
<view class="option-row2">
|
||||
<text class="option-desc">{{item.desc}}</text>
|
||||
<view class="option-price-wrap">
|
||||
<text class="option-price-old" wx:if="{{item.original}}">¥{{item.original}}</text>
|
||||
<text class="option-price">¥{{item.priceFmt || item.price}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<view class="confirm-btn" bindtap="onConfirmConsult" disabled="{{creating}}">
|
||||
{{creating ? '处理中...' : '确认选择 →'}}
|
||||
</view>
|
||||
<text class="footer-hint">点击确认即代表同意 <text class="footer-link">服务协议</text></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
70
miniprogram/pages/mentor-detail/mentor-detail.wxss
Normal file
70
miniprogram/pages/mentor-detail/mentor-detail.wxss
Normal file
@@ -0,0 +1,70 @@
|
||||
/* 按 mentor_detail_profile_1 + mentor_detail_profile_2 设计稿 */
|
||||
.page { background: #000; min-height: 100vh; color: #fff; }
|
||||
.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; height: 44px; padding: 0 24rpx; background: rgba(0,0,0,0.9); border-bottom: 1rpx solid #27272a; }
|
||||
.nav-back { width: 60rpx; }
|
||||
.back-icon { font-size: 44rpx; color: #fff; }
|
||||
.nav-title { font-size: 36rpx; font-weight: 500; }
|
||||
.nav-placeholder-r { width: 60rpx; }
|
||||
|
||||
.loading { padding: 96rpx; text-align: center; color: #71717a; }
|
||||
.mentor-header { display: flex; flex-direction: column; align-items: center; padding: 48rpx 24rpx; text-align: center; }
|
||||
.mentor-avatar-wrap { margin-bottom: 24rpx; }
|
||||
.mentor-avatar { width: 192rpx; height: 192rpx; border-radius: 50%; border: 4rpx solid #4FD1C5; display: block; background: rgba(255,255,255,0.1); }
|
||||
.mentor-avatar-placeholder { width: 192rpx; height: 192rpx; border-radius: 50%; border: 4rpx solid #4FD1C5; background: rgba(79,209,197,0.2); display: flex; align-items: center; justify-content: center; font-size: 72rpx; font-weight: bold; color: #4FD1C5; }
|
||||
.mentor-name { font-size: 48rpx; font-weight: bold; margin-bottom: 8rpx; }
|
||||
.mentor-intro { font-size: 28rpx; color: #4FD1C5; font-weight: 500; margin-bottom: 24rpx; }
|
||||
.mentor-quote { padding: 32rpx; background: #1E1E1E; border-left: 8rpx solid #4FD1C5; border-radius: 24rpx; font-size: 28rpx; color: #d4d4d8; text-align: left; width: 100%; max-width: 600rpx; box-sizing: border-box; }
|
||||
|
||||
.block { padding: 0 24rpx 64rpx; }
|
||||
.block-header { display: flex; align-items: center; margin-bottom: 24rpx; }
|
||||
.block-num { background: #4FD1C5; color: #000; font-size: 24rpx; font-weight: bold; padding: 8rpx 20rpx; border-radius: 999rpx; margin-right: 16rpx; }
|
||||
.block-title { font-size: 36rpx; font-weight: bold; }
|
||||
.block-text { font-size: 26rpx; color: #A0AEC0; line-height: 1.7; display: block; }
|
||||
|
||||
/* 03 收费标准 - 表头青色 */
|
||||
.price-table { background: #1E1E1E; border-radius: 24rpx; overflow: hidden; }
|
||||
.price-thead { display: flex; align-items: center; padding: 24rpx 32rpx; background: #4FD1C5; color: #000; font-size: 24rpx; font-weight: bold; }
|
||||
.p-col-2 { flex: 2; }
|
||||
.p-col-center { flex: 1; text-align: center; }
|
||||
.p-col-right { flex: 1; text-align: right; min-width: 160rpx; }
|
||||
.price-row { display: flex; align-items: center; padding: 32rpx; border-bottom: 1rpx solid #27272a; }
|
||||
.price-row:last-child { border-bottom: none; }
|
||||
.price-row-alt { background: rgba(255,255,255,0.02); }
|
||||
.price-row .p-col-2 { font-size: 28rpx; font-weight: 500; }
|
||||
.price-row .p-col-center { font-size: 24rpx; color: #71717a; }
|
||||
.price-num { font-size: 28rpx; font-weight: bold; color: #4FD1C5; }
|
||||
.price-original { font-size: 20rpx; color: #71717a; text-decoration: line-through; display: block; margin-bottom: 4rpx; }
|
||||
.price-row .p-col-right { display: flex; flex-direction: column; align-items: flex-end; }
|
||||
|
||||
.style-tags { display: flex; flex-wrap: wrap; gap: 24rpx; }
|
||||
.style-tag { padding: 16rpx 32rpx; background: #1E1E1E; border: 1rpx solid #3f3f46; border-radius: 999rpx; font-size: 28rpx; font-weight: 500; }
|
||||
|
||||
.bottom-btn-area { position: fixed; bottom: 0; left: 0; right: 0; padding: 24rpx 32rpx; padding-bottom: calc(24rpx + env(safe-area-inset-bottom)); background: rgba(0,0,0,0.95); border-top: 1rpx solid #27272a; z-index: 50; }
|
||||
.contact-btn { display: flex; align-items: center; justify-content: center; gap: 16rpx; width: 100%; height: 96rpx; background: #4FD1C5; color: #000; font-size: 32rpx; font-weight: bold; border-radius: 24rpx; box-shadow: 0 8rpx 32rpx rgba(79,209,197,0.25); }
|
||||
.contact-icon { font-size: 36rpx; }
|
||||
|
||||
/* v2 弹窗 - mentor_detail_profile_2 */
|
||||
.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); backdrop-filter: blur(4px); z-index: 200; display: flex; align-items: flex-end; justify-content: center; }
|
||||
.modal-content { width: 100%; max-height: 85vh; background: #121212; border-radius: 32rpx 32rpx 0 0; padding: 32rpx; padding-bottom: calc(48rpx + env(safe-area-inset-bottom)); border: 1rpx solid rgba(255,255,255,0.08); }
|
||||
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 32rpx; padding-bottom: 24rpx; border-bottom: 1rpx solid #27272a; }
|
||||
.modal-title { font-size: 40rpx; font-weight: bold; }
|
||||
.modal-close { font-size: 40rpx; color: #71717a; padding: 16rpx; }
|
||||
.consult-options { margin-bottom: 32rpx; }
|
||||
.consult-option { padding: 32rpx; margin-bottom: 24rpx; background: #1E1E1E; border-radius: 24rpx; border: 2rpx solid #3f3f46; }
|
||||
.option-selected { border-color: #4FD1C5; background: rgba(79,209,197,0.08); }
|
||||
.option-row1 { display: flex; align-items: center; margin-bottom: 16rpx; }
|
||||
.option-label { font-size: 32rpx; font-weight: bold; flex: 1; }
|
||||
.option-rec { font-size: 20rpx; padding: 6rpx 16rpx; background: #ED8936; color: #fff; border-radius: 8rpx; margin-right: 16rpx; }
|
||||
.option-radio { width: 40rpx; height: 40rpx; border-radius: 50%; border: 2rpx solid #71717a; flex-shrink: 0; }
|
||||
.radio-selected { border-color: #4FD1C5; background: #4FD1C5; }
|
||||
.option-row2 { display: flex; justify-content: space-between; align-items: flex-end; }
|
||||
.option-desc { font-size: 24rpx; color: #71717a; }
|
||||
.option-price-wrap { display: flex; flex-direction: column; align-items: flex-end; }
|
||||
.option-price-old { font-size: 22rpx; color: #71717a; text-decoration: line-through; margin-bottom: 4rpx; }
|
||||
.option-price { font-size: 36rpx; font-weight: bold; color: #4FD1C5; }
|
||||
.confirm-btn { width: 100%; height: 100rpx; line-height: 100rpx; text-align: center; background: #4FD1C5; color: #000; font-size: 32rpx; font-weight: bold; border-radius: 24rpx; box-shadow: 0 8rpx 32rpx rgba(79,209,197,0.25); }
|
||||
.confirm-btn[disabled] { opacity: 0.6; }
|
||||
.footer-hint { display: block; font-size: 22rpx; color: #71717a; text-align: center; margin-top: 24rpx; }
|
||||
.footer-link { color: #4FD1C5; }
|
||||
|
||||
.bottom-space { height: 180rpx; }
|
||||
65
miniprogram/pages/mentors/mentors.js
Normal file
65
miniprogram/pages/mentors/mentors.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Soul创业派对 - 选择导师(stitch_soul)
|
||||
*/
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
mentors: [],
|
||||
loading: true,
|
||||
searchKw: '',
|
||||
filterSkill: '',
|
||||
skills: ['全部', '项目结构判断', '产品架构', 'BP梳理', '职业转型'],
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 })
|
||||
this.loadMentors()
|
||||
},
|
||||
|
||||
onSearchInput(e) {
|
||||
this.setData({ searchKw: e.detail.value })
|
||||
this.loadMentors()
|
||||
},
|
||||
|
||||
onFilterTap(e) {
|
||||
const skill = e.currentTarget.dataset.skill
|
||||
this.setData({ filterSkill: skill === '全部' ? '' : skill })
|
||||
this.loadMentors()
|
||||
},
|
||||
|
||||
async loadMentors() {
|
||||
this.setData({ loading: true })
|
||||
try {
|
||||
let url = '/api/miniprogram/mentors'
|
||||
const params = []
|
||||
if (this.data.searchKw) params.push(`q=${encodeURIComponent(this.data.searchKw)}`)
|
||||
if (this.data.filterSkill) params.push(`skill=${encodeURIComponent(this.data.filterSkill)}`)
|
||||
if (params.length) url += '?' + params.join('&')
|
||||
|
||||
const res = await app.request({ url, silent: true })
|
||||
if (res?.success && res.data) {
|
||||
this.setData({ mentors: res.data })
|
||||
} else {
|
||||
this.setData({ mentors: [] })
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ mentors: [] })
|
||||
}
|
||||
this.setData({ loading: false })
|
||||
},
|
||||
|
||||
onRefresh() {
|
||||
this.loadMentors()
|
||||
},
|
||||
|
||||
goDetail(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.navigateTo({ url: `/pages/mentor-detail/mentor-detail?id=${id}` })
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack()
|
||||
},
|
||||
})
|
||||
1
miniprogram/pages/mentors/mentors.json
Normal file
1
miniprogram/pages/mentors/mentors.json
Normal file
@@ -0,0 +1 @@
|
||||
{"usingComponents":{},"navigationStyle":"custom"}
|
||||
70
miniprogram/pages/mentors/mentors.wxml
Normal file
70
miniprogram/pages/mentors/mentors.wxml
Normal file
@@ -0,0 +1,70 @@
|
||||
<!-- 选择导师 -->
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-back" bindtap="goBack"><text class="back-icon">‹</text></view>
|
||||
<text class="nav-title">选择导师</text>
|
||||
<view class="nav-placeholder-r"></view>
|
||||
</view>
|
||||
<view style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<view class="search-bar">
|
||||
<view class="search-input-wrap">
|
||||
<text class="search-icon">🔍</text>
|
||||
<input
|
||||
class="search-input"
|
||||
placeholder="搜索导师、技能或行业..."
|
||||
value="{{searchKw}}"
|
||||
bindinput="onSearchInput"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="filter-row">
|
||||
<view
|
||||
class="filter-tag {{!filterSkill ? 'filter-active' : ''}}"
|
||||
data-skill="全部"
|
||||
bindtap="onFilterTap"
|
||||
>全部</view>
|
||||
<view
|
||||
wx:for="{{skills}}"
|
||||
wx:key="*this"
|
||||
wx:if="{{item !== '全部'}}"
|
||||
class="filter-tag {{filterSkill === item ? 'filter-active' : ''}}"
|
||||
data-skill="{{item}}"
|
||||
bindtap="onFilterTap"
|
||||
>{{item}}</view>
|
||||
</view>
|
||||
|
||||
<view class="section-header">
|
||||
<text class="section-title">推荐导师</text>
|
||||
<text class="section-more" bindtap="loadMentors">查看全部 ›</text>
|
||||
</view>
|
||||
|
||||
<view class="loading" wx:if="{{loading}}">加载中...</view>
|
||||
<view class="mentor-list" wx:else>
|
||||
<view class="mentor-card" wx:for="{{mentors}}" wx:key="id">
|
||||
<view class="mentor-card-inner">
|
||||
<view class="mentor-avatar-wrap">
|
||||
<image wx:if="{{item.avatar}}" class="mentor-avatar" src="{{item.avatar}}" mode="aspectFill"></image>
|
||||
<view wx:else class="mentor-avatar-placeholder">{{item.name ? item.name[0] : '?'}}</view>
|
||||
</view>
|
||||
<view class="mentor-info">
|
||||
<text class="mentor-name">{{item.name}}</text>
|
||||
<text class="mentor-intro">{{item.intro || ''}}</text>
|
||||
<view class="mentor-tags" wx:if="{{item.tagsArr && item.tagsArr.length}}">
|
||||
<text class="mentor-tag" wx:for="{{item.tagsArr}}" wx:key="*this" wx:for-item="tag">{{tag}}</text>
|
||||
</view>
|
||||
<view class="mentor-price-row">
|
||||
<view class="mentor-price-wrap">
|
||||
<text class="mentor-price-num">¥{{item.priceSingle || 0}}</text>
|
||||
<text class="mentor-price-unit">起 / 单次咨询</text>
|
||||
</view>
|
||||
<view class="mentor-btn" data-id="{{item.id}}" bindtap="goDetail">预约</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty" wx:if="{{!loading && mentors.length === 0}}">暂无导师</view>
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
40
miniprogram/pages/mentors/mentors.wxss
Normal file
40
miniprogram/pages/mentors/mentors.wxss
Normal file
@@ -0,0 +1,40 @@
|
||||
/* 按 mentor_listing_screen 设计稿 */
|
||||
.page { background: #000; min-height: 100vh; color: #fff; }
|
||||
.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; height: 44px; padding: 0 24rpx; background: rgba(0,0,0,0.95); border-bottom: 1rpx solid #27272a; }
|
||||
.nav-back { width: 60rpx; }
|
||||
.back-icon { font-size: 44rpx; color: #fff; }
|
||||
.nav-title { font-size: 34rpx; font-weight: 500; }
|
||||
.nav-placeholder-r { width: 60rpx; }
|
||||
|
||||
.search-bar { padding: 24rpx 24rpx 16rpx; }
|
||||
.search-input-wrap { display: flex; align-items: center; background: #1C1C1E; border-radius: 24rpx; padding: 24rpx 32rpx; border: 1rpx solid #27272a; }
|
||||
.search-icon { font-size: 36rpx; color: #71717a; margin-right: 16rpx; flex-shrink: 0; }
|
||||
.search-input { flex: 1; font-size: 28rpx; color: #fff; background: transparent; }
|
||||
.filter-row { display: flex; gap: 24rpx; padding: 24rpx; overflow-x: auto; }
|
||||
.filter-tag { flex-shrink: 0; padding: 12rpx 32rpx; border-radius: 999rpx; font-size: 24rpx; background: #1C1C1E; border: 1rpx solid #27272a; color: #d4d4d8; }
|
||||
.filter-active { background: #4FD1C5; border-color: #4FD1C5; color: #000; font-weight: 600; }
|
||||
|
||||
.section-header { display: flex; justify-content: space-between; align-items: flex-end; padding: 0 24rpx 24rpx; }
|
||||
.section-title { font-size: 32rpx; font-weight: bold; color: #e4e4e7; }
|
||||
.section-more { font-size: 24rpx; color: #4FD1C5; font-weight: 500; }
|
||||
|
||||
.loading { padding: 48rpx; text-align: center; color: #71717a; }
|
||||
.mentor-list { padding: 0 24rpx 48rpx; }
|
||||
.mentor-card { margin-bottom: 24rpx; background: #1C1C1E; border-radius: 32rpx; padding: 32rpx; border: 1rpx solid #27272a; }
|
||||
.mentor-card-inner { display: flex; gap: 32rpx; }
|
||||
.mentor-avatar-wrap { width: 112rpx; height: 112rpx; flex-shrink: 0; }
|
||||
.mentor-avatar { width: 112rpx; height: 112rpx; border-radius: 50%; display: block; border: 1rpx solid #3f3f46; }
|
||||
.mentor-avatar-placeholder { width: 112rpx; height: 112rpx; border-radius: 50%; background: rgba(79,209,197,0.2); border: 1rpx solid #3f3f46; display: flex; align-items: center; justify-content: center; font-size: 40rpx; font-weight: bold; color: #4FD1C5; }
|
||||
.mentor-info { flex: 1; min-width: 0; }
|
||||
.mentor-name { display: block; font-size: 32rpx; font-weight: bold; color: #fff; margin-bottom: 8rpx; }
|
||||
.mentor-intro { display: block; font-size: 24rpx; color: #A1A1AA; margin-bottom: 16rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.mentor-tags { display: flex; flex-wrap: wrap; gap: 12rpx; margin-bottom: 24rpx; }
|
||||
.mentor-tag { font-size: 20rpx; padding: 8rpx 16rpx; background: #2C2C2E; color: #E5E7EB; border-radius: 12rpx; border: 1rpx solid rgba(63,63,70,0.5); }
|
||||
.mentor-price-row { display: flex; justify-content: space-between; align-items: center; padding-top: 24rpx; border-top: 1rpx solid #27272a; }
|
||||
.mentor-price-wrap { display: flex; align-items: baseline; gap: 8rpx; }
|
||||
.mentor-price-num { font-size: 36rpx; font-weight: bold; color: #4FD1C5; }
|
||||
.mentor-price-unit { font-size: 24rpx; color: #71717a; }
|
||||
.mentor-btn { padding: 12rpx 32rpx; background: #fff; color: #000; font-size: 24rpx; font-weight: bold; border-radius: 999rpx; }
|
||||
|
||||
.empty { padding: 96rpx; text-align: center; color: rgba(255,255,255,0.4); }
|
||||
.bottom-space { height: 80rpx; }
|
||||
@@ -56,7 +56,14 @@ Page({
|
||||
|
||||
// 修改昵称弹窗
|
||||
showNicknameModal: false,
|
||||
editingNickname: ''
|
||||
editingNickname: '',
|
||||
|
||||
// 手机/微信号弹窗(stitch_soul comprehensive_profile_editor_v1_2)
|
||||
showContactModal: false,
|
||||
contactPhone: '',
|
||||
contactWechat: '',
|
||||
contactSaving: false,
|
||||
pendingWithdraw: false,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
@@ -687,6 +694,12 @@ Page({
|
||||
wx.navigateTo({ url: '/pages/vip/vip' })
|
||||
},
|
||||
|
||||
// 进入个人资料编辑页(stitch_soul)
|
||||
goToProfileEdit() {
|
||||
if (!this.data.isLoggedIn) { this.showLogin(); return }
|
||||
wx.navigateTo({ url: '/pages/profile-edit/profile-edit' })
|
||||
},
|
||||
|
||||
async handleWithdraw() {
|
||||
if (!this.data.isLoggedIn) { this.showLogin(); return }
|
||||
const amount = parseFloat(this.data.pendingEarnings)
|
||||
@@ -694,6 +707,10 @@ Page({
|
||||
wx.showToast({ title: '暂无可提现金额', icon: 'none' })
|
||||
return
|
||||
}
|
||||
await this.ensureContactInfo(() => this.doWithdraw(amount))
|
||||
},
|
||||
|
||||
async doWithdraw(amount) {
|
||||
wx.showModal({
|
||||
title: '申请提现',
|
||||
content: `确认提现 ¥${amount.toFixed(2)} ?`,
|
||||
@@ -714,6 +731,69 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
// 提现/找伙伴前检查手机或微信号,未填则弹窗(stitch_soul)
|
||||
async ensureContactInfo(callback) {
|
||||
const userId = app.globalData.userInfo?.id
|
||||
if (!userId) { callback(); return }
|
||||
try {
|
||||
const res = await app.request({ url: `/api/miniprogram/user/profile?userId=${userId}`, silent: true })
|
||||
const phone = (res?.data?.phone || '').trim()
|
||||
const wechat = (res?.data?.wechatId || '').trim()
|
||||
if (phone || wechat) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
this.setData({
|
||||
showContactModal: true,
|
||||
contactPhone: phone || '',
|
||||
contactWechat: wechat || '',
|
||||
pendingWithdraw: true,
|
||||
})
|
||||
this._contactCallback = callback
|
||||
} catch (e) {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
|
||||
closeContactModal() {
|
||||
this.setData({ showContactModal: false, pendingWithdraw: false })
|
||||
this._contactCallback = null
|
||||
},
|
||||
|
||||
onContactPhoneInput(e) { this.setData({ contactPhone: e.detail.value }) },
|
||||
onContactWechatInput(e) { this.setData({ contactWechat: e.detail.value }) },
|
||||
|
||||
async saveContactInfo() {
|
||||
const phone = (this.data.contactPhone || '').trim()
|
||||
const wechat = (this.data.contactWechat || '').trim()
|
||||
if (!phone && !wechat) {
|
||||
wx.showToast({ title: '请至少填写手机号或微信号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({ contactSaving: true })
|
||||
try {
|
||||
await app.request({
|
||||
url: '/api/miniprogram/user/profile',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: app.globalData.userInfo?.id,
|
||||
phone: phone || undefined,
|
||||
wechatId: wechat || undefined,
|
||||
},
|
||||
})
|
||||
if (phone) wx.setStorageSync('user_phone', phone)
|
||||
if (wechat) wx.setStorageSync('user_wechat', wechat)
|
||||
this.closeContactModal()
|
||||
wx.showToast({ title: '已保存', icon: 'success' })
|
||||
const cb = this._contactCallback
|
||||
this._contactCallback = null
|
||||
if (cb) cb()
|
||||
} catch (e) {
|
||||
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
|
||||
}
|
||||
this.setData({ contactSaving: false })
|
||||
},
|
||||
|
||||
// 阻止冒泡
|
||||
stopPropagation() {},
|
||||
|
||||
|
||||
@@ -1,156 +1,108 @@
|
||||
<!--pages/my/my.wxml-->
|
||||
<!--Soul创业实验 - 我的页面 1:1还原Web版本-->
|
||||
<view class="page page-transition">
|
||||
<!-- 自定义导航栏 -->
|
||||
<!-- 我的页 - professional_profile_with_earnings_vip 1:1 还原 -->
|
||||
<view class="page">
|
||||
<!-- 顶部导航:仅标题(设置已移至用户区右侧,避让胶囊) -->
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-content">
|
||||
<text class="nav-title-left brand-color">我的</text>
|
||||
</view>
|
||||
<text class="nav-title">我的</text>
|
||||
</view>
|
||||
|
||||
<!-- 导航栏占位 -->
|
||||
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<!-- 用户卡片 - 未登录:假资料界面,名字旁点击登录打开弹窗 -->
|
||||
<view class="user-card card-gradient" wx:if="{{!isLoggedIn}}">
|
||||
<view class="user-header-row">
|
||||
<view class="avatar avatar-placeholder">
|
||||
<image class="avatar-img" wx:if="{{guestAvatar}}" src="{{guestAvatar}}" mode="aspectFill"/>
|
||||
<text class="avatar-text" wx:else>{{guestNickname[0] || '游'}}</text>
|
||||
</view>
|
||||
<view class="user-info-block">
|
||||
<view class="user-name-row">
|
||||
<text class="user-name">{{guestNickname}}</text>
|
||||
<view class="btn-login-inline" bindtap="showLogin">点击登录</view>
|
||||
</view>
|
||||
<view class="user-id-row">
|
||||
<text class="user-id user-id-guest">登录后查看完整信息</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-grid">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value brand-color">--</text>
|
||||
<text class="stat-label">已读章节</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value brand-color">--</text>
|
||||
<text class="stat-label">推荐好友</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value gold-color">--</text>
|
||||
<text class="stat-label">待领收益</text>
|
||||
</view>
|
||||
<!-- 未登录:引导登录 -->
|
||||
<view class="guest-block" wx:if="{{!isLoggedIn}}">
|
||||
<view class="guest-avatar">
|
||||
<image wx:if="{{guestAvatar}}" class="guest-avatar-img" src="{{guestAvatar}}" mode="aspectFill"/>
|
||||
<text wx:else class="guest-avatar-text">{{guestNickname[0] || '游'}}</text>
|
||||
</view>
|
||||
<text class="guest-name">{{guestNickname}}</text>
|
||||
<view class="guest-login-btn" bindtap="showLogin">点击登录</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户卡片 - 已登录状态 -->
|
||||
<view class="user-card card-gradient" wx:else>
|
||||
<view class="user-header-row">
|
||||
<!-- 头像 - 点击进VIP/设置头像 -->
|
||||
<!-- 已登录:用户区 -->
|
||||
<view class="header-block" wx:else>
|
||||
<view class="user-row">
|
||||
<view class="avatar-wrap" bindtap="onAvatarTap">
|
||||
<view class="avatar {{isVip ? 'avatar-vip' : ''}}">
|
||||
<image class="avatar-img" wx:if="{{userInfo.avatar}}" src="{{userInfo.avatar}}" mode="aspectFill"/>
|
||||
<text class="avatar-text" wx:else>{{userInfo.nickname[0] || '用'}}</text>
|
||||
<view class="avatar-inner {{isVip ? 'avatar-vip' : ''}}">
|
||||
<image wx:if="{{userInfo.avatar}}" class="avatar-img" src="{{userInfo.avatar}}" mode="aspectFill"/>
|
||||
<text wx:else class="avatar-text">{{userInfo.nickname ? userInfo.nickname[0] : '?'}}</text>
|
||||
</view>
|
||||
<view class="vip-badge" wx:if="{{isVip}}">VIP</view>
|
||||
<view class="vip-badge vip-badge-gray" wx:else bindtap="goToVip">VIP</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<view class="user-info-block">
|
||||
<view class="user-name-row">
|
||||
<text class="user-name" bindtap="editNickname">{{userInfo.nickname || '点击设置昵称'}}</text>
|
||||
<view class="vip-tags-row">
|
||||
<view class="vip-tag-mini {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">会员</view>
|
||||
<view class="vip-tag-mini {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">匹配</view>
|
||||
<view class="vip-tag-mini {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">排行</view>
|
||||
</view>
|
||||
<view class="become-vip-chip" wx:if="{{!isVip}}" bindtap="goToVip">
|
||||
<text class="chip-star">⭐</text><text class="chip-text">成为会员</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-id-row" bindtap="copyUserId">
|
||||
<text class="user-id">{{userWechat ? '微信: ' + userWechat : 'ID: ' + userIdShort}}</text>
|
||||
<view class="user-meta">
|
||||
<text class="user-name" bindtap="editNickname">{{userInfo.nickname || '点击设置昵称'}}</text>
|
||||
<view class="vip-tags">
|
||||
<text class="vip-tag {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">会员</text>
|
||||
<text class="vip-tag {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">匹配</text>
|
||||
<text class="vip-tag {{isVip ? 'vip-tag-active' : ''}}" bindtap="goToVip">排行</text>
|
||||
</view>
|
||||
<text class="user-id" bindtap="copyUserId">{{userWechat ? '微信: ' + userWechat : 'ID: ' + userIdShort}}</text>
|
||||
<text class="vip-expire" wx:if="{{isVip && vipExpireDate}}">会员到期时间:{{vipExpireDate}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stats-grid">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value brand-color">{{readCount}}</text>
|
||||
<text class="stat-label">已读章节</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value brand-color">{{referralCount}}</text>
|
||||
<text class="stat-label">推荐好友</text>
|
||||
</view>
|
||||
<view class="stat-item" bindtap="goToReferral">
|
||||
<text class="stat-value gold-color">{{pendingEarnings > 0 ? '¥' + pendingEarnings : '--'}}</text>
|
||||
<text class="stat-label">我的收益</text>
|
||||
<view class="user-actions">
|
||||
<view class="action-btn" bindtap="handleMenuTap" data-id="settings"><text class="action-icon">⚙️</text></view>
|
||||
<view class="action-btn" catchtap="goToProfileEdit"><text class="action-icon">✎</text></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 统一内容区 - 仅登录用户显示 -->
|
||||
<view class="tab-content" wx:if="{{isLoggedIn}}">
|
||||
<!-- 菜单:我的订单 + 设置 -->
|
||||
<view class="menu-card card">
|
||||
<view class="menu-item" bindtap="handleMenuTap" data-id="orders">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon icon-brand">📦</view>
|
||||
<text class="menu-title">我的订单</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">→</text>
|
||||
</view>
|
||||
<!-- 已登录:内容区 -->
|
||||
<view class="main-content" wx:if="{{isLoggedIn}}">
|
||||
<!-- 分享收益 -->
|
||||
<view class="card earnings-card">
|
||||
<view class="card-header">
|
||||
<text class="card-icon">💰</text>
|
||||
<text class="card-title">分享收益</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="handleMenuTap" data-id="settings">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon icon-gray">⚙️</view>
|
||||
<text class="menu-title">设置</text>
|
||||
<view class="earnings-grid">
|
||||
<view class="earnings-col">
|
||||
<text class="earnings-val primary">{{referralCount}}</text>
|
||||
<text class="earnings-label">推荐好友</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">→</text>
|
||||
<view class="earnings-col">
|
||||
<text class="earnings-val primary">{{earnings === '-' ? '--' : earnings}}</text>
|
||||
<text class="earnings-label">我的收益</text>
|
||||
</view>
|
||||
<view class="earnings-col" bindtap="handleWithdraw">
|
||||
<text class="earnings-val primary">{{pendingEarnings === '-' ? '--' : pendingEarnings}}</text>
|
||||
<text class="earnings-label">可提现金额</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 阅读统计 -->
|
||||
<view class="stats-card card">
|
||||
<view class="card-title">
|
||||
<text class="title-icon">👁️</text>
|
||||
<text>阅读统计</text>
|
||||
<view class="card stats-card">
|
||||
<view class="card-header">
|
||||
<text class="card-icon">👁️</text>
|
||||
<text class="card-title">阅读统计</text>
|
||||
</view>
|
||||
<view class="stats-row">
|
||||
<view class="stats-grid">
|
||||
<view class="stat-box">
|
||||
<text class="stat-icon brand-color">📖</text>
|
||||
<text class="stat-icon">📖</text>
|
||||
<text class="stat-num">{{readCount}}</text>
|
||||
<text class="stat-text">已读章节</text>
|
||||
<text class="stat-label">已读章节</text>
|
||||
</view>
|
||||
<view class="stat-box">
|
||||
<text class="stat-icon gold-color">⏱️</text>
|
||||
<text class="stat-icon">⏱</text>
|
||||
<text class="stat-num">{{totalReadTime}}</text>
|
||||
<text class="stat-text">阅读分钟</text>
|
||||
<text class="stat-label">阅读分钟</text>
|
||||
</view>
|
||||
<view class="stat-box" wx:if="{{matchEnabled}}">
|
||||
<text class="stat-icon pink-color">👥</text>
|
||||
<view class="stat-box">
|
||||
<text class="stat-icon">👥</text>
|
||||
<text class="stat-num">{{matchHistory}}</text>
|
||||
<text class="stat-text">匹配伙伴</text>
|
||||
<text class="stat-label">匹配伙伴</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 最近阅读 -->
|
||||
<view class="recent-card card">
|
||||
<view class="card-title">
|
||||
<text class="title-icon">📖</text>
|
||||
<text>最近阅读</text>
|
||||
<view class="card recent-card">
|
||||
<view class="card-header">
|
||||
<text class="card-icon">📖</text>
|
||||
<text class="card-title">最近阅读</text>
|
||||
</view>
|
||||
<view class="recent-list" wx:if="{{recentChapters.length > 0}}">
|
||||
<view
|
||||
class="recent-item"
|
||||
wx:for="{{recentChapters}}"
|
||||
<view
|
||||
class="recent-item"
|
||||
wx:for="{{recentChapters}}"
|
||||
wx:key="id"
|
||||
bindtap="goToRead"
|
||||
data-id="{{item.id}}"
|
||||
@@ -160,48 +112,46 @@
|
||||
<text class="recent-index">{{index + 1}}</text>
|
||||
<text class="recent-title">{{item.title}}</text>
|
||||
</view>
|
||||
<text class="recent-btn">继续阅读</text>
|
||||
<text class="recent-link">继续阅读</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty-state" wx:else>
|
||||
<text class="empty-icon">📖</text>
|
||||
<text class="empty-text">暂无阅读记录</text>
|
||||
<view class="empty-btn" bindtap="goToChapters">去阅读 →</view>
|
||||
<view class="recent-empty" wx:else>
|
||||
<text class="recent-empty-text">暂无阅读记录</text>
|
||||
<view class="recent-empty-btn" bindtap="goToChapters">去阅读 →</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 关于作者(最底部) -->
|
||||
<view class="menu-card card" style="margin-top: 16rpx;">
|
||||
<!-- 我的订单 + 关于作者 -->
|
||||
<view class="card menu-card">
|
||||
<view class="menu-item" bindtap="handleMenuTap" data-id="orders">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon-wrap icon-teal"><text class="menu-icon">📦</text></view>
|
||||
<text class="menu-text">我的订单</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="handleMenuTap" data-id="about">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon icon-brand">ℹ️</view>
|
||||
<text class="menu-title">关于作者</text>
|
||||
</view>
|
||||
<view class="menu-right">
|
||||
<text class="menu-arrow">→</text>
|
||||
<view class="menu-icon-wrap icon-blue"><text class="menu-icon">ℹ</text></view>
|
||||
<text class="menu-text">关于作者</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 登录弹窗:可取消,用户主动选择是否登录 -->
|
||||
<!-- 登录弹窗 -->
|
||||
<view class="modal-overlay" wx:if="{{showLoginModal}}" bindtap="closeLoginModal">
|
||||
<view class="modal-content login-modal-content" catchtap="stopPropagation">
|
||||
<view class="modal-close" bindtap="closeLoginModal">✕</view>
|
||||
<view class="login-icon">🔐</view>
|
||||
<text class="login-title">登录 Soul创业实验</text>
|
||||
<text class="login-title">登录 Soul创业派对</text>
|
||||
<text class="login-desc">登录后可购买章节、解锁更多内容</text>
|
||||
|
||||
<button
|
||||
class="btn-wechat {{agreeProtocol ? '' : 'btn-wechat-disabled'}}"
|
||||
bindtap="handleWechatLogin"
|
||||
disabled="{{isLoggingIn || !agreeProtocol}}"
|
||||
>
|
||||
<button class="btn-wechat {{agreeProtocol ? '' : 'btn-wechat-disabled'}}" bindtap="handleWechatLogin" disabled="{{isLoggingIn || !agreeProtocol}}">
|
||||
<text class="btn-wechat-icon">微</text>
|
||||
<text>{{isLoggingIn ? '登录中...' : '微信快捷登录'}}</text>
|
||||
</button>
|
||||
<view class="login-modal-cancel" bindtap="closeLoginModal">取消</view>
|
||||
|
||||
<view class="login-agree-row" catchtap="toggleAgree">
|
||||
<view class="agree-checkbox {{agreeProtocol ? 'agree-checked' : ''}}">{{agreeProtocol ? '✓' : ''}}</view>
|
||||
<text class="agree-text">我已阅读并同意</text>
|
||||
@@ -212,6 +162,30 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机/微信号弹窗 -->
|
||||
<view class="modal-overlay contact-modal-overlay" wx:if="{{showContactModal}}" bindtap="closeContactModal">
|
||||
<view class="contact-modal" catchtap="stopPropagation">
|
||||
<text class="contact-modal-title">请完善联系方式</text>
|
||||
<view class="contact-modal-hint">需完善手机号或微信号才能使用提现和找伙伴功能</view>
|
||||
<view class="form-input-wrap">
|
||||
<text class="form-label">手机号</text>
|
||||
<view class="form-input-inner">
|
||||
<text class="form-icon">📱</text>
|
||||
<input class="form-input" type="tel" placeholder="请输入您的手机号" value="{{contactPhone}}" bindinput="onContactPhoneInput"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-input-wrap">
|
||||
<text class="form-label">微信号</text>
|
||||
<view class="form-input-inner">
|
||||
<text class="form-icon">💬</text>
|
||||
<input class="form-input" type="text" placeholder="请输入您的微信号" value="{{contactWechat}}" bindinput="onContactWechatInput"/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="contact-modal-btn" bindtap="saveContactInfo" disabled="{{contactSaving}}">{{contactSaving ? '保存中...' : '保存'}}</view>
|
||||
<text class="contact-modal-cancel" bindtap="closeContactModal">取消</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 修改昵称弹窗 -->
|
||||
<view class="modal-overlay" wx:if="{{showNicknameModal}}" bindtap="closeNicknameModal">
|
||||
<view class="modal-content nickname-modal" catchtap="stopPropagation">
|
||||
@@ -220,21 +194,10 @@
|
||||
<text class="modal-icon">✏️</text>
|
||||
<text class="modal-title">修改昵称</text>
|
||||
</view>
|
||||
|
||||
<view class="nickname-input-wrap">
|
||||
<input
|
||||
class="nickname-input"
|
||||
type="nickname"
|
||||
value="{{editingNickname}}"
|
||||
placeholder="点击输入昵称"
|
||||
placeholder-class="nickname-placeholder"
|
||||
bindchange="onNicknameChange"
|
||||
bindinput="onNicknameInput"
|
||||
maxlength="20"
|
||||
/>
|
||||
<input class="nickname-input" type="nickname" value="{{editingNickname}}" placeholder="点击输入昵称" placeholder-class="nickname-placeholder" bindchange="onNicknameChange" bindinput="onNicknameInput" maxlength="20"/>
|
||||
<text class="input-tip">微信用户可点击自动填充昵称</text>
|
||||
</view>
|
||||
|
||||
<view class="modal-actions">
|
||||
<view class="modal-btn modal-btn-cancel" bindtap="closeNicknameModal">取消</view>
|
||||
<view class="modal-btn modal-btn-confirm" bindtap="confirmNickname">确定</view>
|
||||
@@ -242,6 +205,5 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部留白 -->
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
199
miniprogram/pages/profile-edit/profile-edit.js
Normal file
199
miniprogram/pages/profile-edit/profile-edit.js
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Soul创业派对 - 资料编辑完整版(comprehensive_profile_editor_v1_1)
|
||||
* 温馨提示、头像、基本信息、核心联系方式、个人故事、互助需求、项目介绍
|
||||
*/
|
||||
const app = getApp()
|
||||
|
||||
const MBTI_OPTIONS = ['INTJ', 'INFP', 'INTP', 'ENTP', 'ENFP', 'ENTJ', 'ENFJ', 'INFJ', 'ISTJ', 'ISFJ', 'ESTJ', 'ESFJ', 'ISTP', 'ISFP', 'ESTP', 'ESFP']
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
avatar: '',
|
||||
nickname: '',
|
||||
mbti: '',
|
||||
mbtiIndex: 0,
|
||||
region: '',
|
||||
industry: '',
|
||||
businessScale: '',
|
||||
position: '',
|
||||
skills: '',
|
||||
phone: '',
|
||||
wechatId: '',
|
||||
storyBestMonth: '',
|
||||
storyAchievement: '',
|
||||
storyTurning: '',
|
||||
helpOffer: '',
|
||||
helpNeed: '',
|
||||
projectIntro: '',
|
||||
mbtiOptions: MBTI_OPTIONS,
|
||||
showMbtiPicker: false,
|
||||
saving: false,
|
||||
loading: true,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 })
|
||||
this.loadProfile()
|
||||
},
|
||||
|
||||
async loadProfile() {
|
||||
const userInfo = app.globalData.userInfo
|
||||
if (!app.globalData.isLoggedIn || !userInfo?.id) {
|
||||
this.setData({ loading: false })
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
setTimeout(() => wx.navigateBack(), 1500)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await app.request({ url: `/api/miniprogram/user/profile?userId=${userInfo.id}`, silent: true })
|
||||
if (res?.success && res.data) {
|
||||
const d = res.data
|
||||
const mbtiIndex = MBTI_OPTIONS.indexOf(d.mbti || '') >= 0 ? MBTI_OPTIONS.indexOf(d.mbti) : 0
|
||||
this.setData({
|
||||
avatar: d.avatar || '',
|
||||
nickname: d.nickname || '',
|
||||
mbti: d.mbti || '',
|
||||
mbtiIndex,
|
||||
region: d.region || '',
|
||||
industry: d.industry || '',
|
||||
businessScale: d.businessScale || '',
|
||||
position: d.position || '',
|
||||
skills: d.skills || '',
|
||||
phone: d.phone || '',
|
||||
wechatId: d.wechatId || wx.getStorageSync('user_wechat') || '',
|
||||
storyBestMonth: d.storyBestMonth || '',
|
||||
storyAchievement: d.storyAchievement || '',
|
||||
storyTurning: d.storyTurning || '',
|
||||
helpOffer: d.helpOffer || '',
|
||||
helpNeed: d.helpNeed || '',
|
||||
projectIntro: d.projectIntro || '',
|
||||
loading: false,
|
||||
})
|
||||
} else {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
goBack() { wx.navigateBack() },
|
||||
|
||||
onNicknameInput(e) { this.setData({ nickname: e.detail.value }) },
|
||||
onRegionInput(e) { this.setData({ region: e.detail.value }) },
|
||||
onIndustryInput(e) { this.setData({ industry: e.detail.value }) },
|
||||
onBusinessScaleInput(e) { this.setData({ businessScale: e.detail.value }) },
|
||||
onPositionInput(e) { this.setData({ position: e.detail.value }) },
|
||||
onSkillsInput(e) { this.setData({ skills: e.detail.value }) },
|
||||
onPhoneInput(e) { this.setData({ phone: e.detail.value }) },
|
||||
onWechatInput(e) { this.setData({ wechatId: e.detail.value }) },
|
||||
onStoryBestMonthInput(e) { this.setData({ storyBestMonth: e.detail.value }) },
|
||||
onStoryAchievementInput(e) { this.setData({ storyAchievement: e.detail.value }) },
|
||||
onStoryTurningInput(e) { this.setData({ storyTurning: e.detail.value }) },
|
||||
onHelpOfferInput(e) { this.setData({ helpOffer: e.detail.value }) },
|
||||
onHelpNeedInput(e) { this.setData({ helpNeed: e.detail.value }) },
|
||||
onProjectIntroInput(e) { this.setData({ projectIntro: e.detail.value }) },
|
||||
|
||||
onMbtiPickerChange(e) {
|
||||
const i = parseInt(e.detail.value, 10)
|
||||
this.setData({ mbtiIndex: i, mbti: MBTI_OPTIONS[i] })
|
||||
},
|
||||
|
||||
chooseAvatar() {
|
||||
wx.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: async (res) => {
|
||||
const tempPath = res.tempFiles[0].tempFilePath
|
||||
wx.showLoading({ title: '上传中...', mask: true })
|
||||
try {
|
||||
const uploadRes = await new Promise((resolve, reject) => {
|
||||
wx.uploadFile({
|
||||
url: app.globalData.baseUrl + '/api/miniprogram/upload',
|
||||
filePath: tempPath,
|
||||
name: 'file',
|
||||
formData: { folder: 'avatars' },
|
||||
success: (r) => {
|
||||
try {
|
||||
const data = JSON.parse(r.data)
|
||||
if (data.success) resolve(data)
|
||||
else reject(new Error(data.error || '上传失败'))
|
||||
} catch { reject(new Error('解析失败')) }
|
||||
},
|
||||
fail: reject,
|
||||
})
|
||||
})
|
||||
const avatarUrl = app.globalData.baseUrl + uploadRes.data.url
|
||||
this.setData({ avatar: avatarUrl })
|
||||
await app.request({
|
||||
url: '/api/miniprogram/user/profile',
|
||||
method: 'POST',
|
||||
data: { userId: app.globalData.userInfo?.id, avatar: avatarUrl },
|
||||
})
|
||||
if (app.globalData.userInfo) {
|
||||
app.globalData.userInfo.avatar = avatarUrl
|
||||
wx.setStorageSync('userInfo', app.globalData.userInfo)
|
||||
}
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '头像已更新', icon: 'success' })
|
||||
} catch (e) {
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: e.message || '上传失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
async saveProfile() {
|
||||
const userId = app.globalData.userInfo?.id
|
||||
if (!userId) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({ saving: true })
|
||||
try {
|
||||
const payload = {
|
||||
userId,
|
||||
nickname: this.data.nickname.trim() || undefined,
|
||||
mbti: this.data.mbti || undefined,
|
||||
region: this.data.region.trim() || undefined,
|
||||
industry: this.data.industry.trim() || undefined,
|
||||
businessScale: this.data.businessScale.trim() || undefined,
|
||||
position: this.data.position.trim() || undefined,
|
||||
skills: this.data.skills.trim() || undefined,
|
||||
phone: this.data.phone.trim() || undefined,
|
||||
wechatId: this.data.wechatId.trim() || undefined,
|
||||
storyBestMonth: this.data.storyBestMonth.trim() || undefined,
|
||||
storyAchievement: this.data.storyAchievement.trim() || undefined,
|
||||
storyTurning: this.data.storyTurning.trim() || undefined,
|
||||
helpOffer: this.data.helpOffer.trim() || undefined,
|
||||
helpNeed: this.data.helpNeed.trim() || undefined,
|
||||
projectIntro: this.data.projectIntro.trim() || undefined,
|
||||
}
|
||||
if (payload.wechatId) wx.setStorageSync('user_wechat', payload.wechatId)
|
||||
if (payload.phone) wx.setStorageSync('user_phone', payload.phone)
|
||||
const hasUpdate = Object.keys(payload).some(k => k !== 'userId' && payload[k] != null)
|
||||
if (!hasUpdate) {
|
||||
wx.showToast({ title: '无变更', icon: 'none' })
|
||||
this.setData({ saving: false })
|
||||
return
|
||||
}
|
||||
await app.request({
|
||||
url: '/api/miniprogram/user/profile',
|
||||
method: 'POST',
|
||||
data: payload,
|
||||
})
|
||||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||||
if (app.globalData.userInfo && payload.nickname) {
|
||||
app.globalData.userInfo.nickname = payload.nickname
|
||||
wx.setStorageSync('userInfo', app.globalData.userInfo)
|
||||
}
|
||||
setTimeout(() => wx.navigateBack(), 800)
|
||||
} catch (e) {
|
||||
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
|
||||
}
|
||||
this.setData({ saving: false })
|
||||
},
|
||||
})
|
||||
4
miniprogram/pages/profile-edit/profile-edit.json
Normal file
4
miniprogram/pages/profile-edit/profile-edit.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "编辑资料",
|
||||
"usingComponents": {}
|
||||
}
|
||||
135
miniprogram/pages/profile-edit/profile-edit.wxml
Normal file
135
miniprogram/pages/profile-edit/profile-edit.wxml
Normal file
@@ -0,0 +1,135 @@
|
||||
<!-- 资料编辑 - comprehensive_profile_editor_v1_1 | input/textarea 用 view 包裹,配色 enhanced -->
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-back" bindtap="goBack"><text class="back-icon">‹</text></view>
|
||||
<text class="nav-title">编辑资料</text>
|
||||
<view class="nav-placeholder"></view>
|
||||
</view>
|
||||
<view style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<view class="loading" wx:if="{{loading}}">加载中...</view>
|
||||
<scroll-view wx:else class="scroll-main" scroll-y>
|
||||
<!-- 温馨提示 -->
|
||||
<view class="tip-card">
|
||||
<text class="tip-icon">ℹ</text>
|
||||
<text class="tip-text">温馨提示:需完善手机号和微信号才能使用提现和找伙伴功能</text>
|
||||
</view>
|
||||
|
||||
<!-- 头像 -->
|
||||
<view class="avatar-section">
|
||||
<view class="avatar-wrap" bindtap="chooseAvatar">
|
||||
<image wx:if="{{avatar}}" class="avatar-img" src="{{avatar}}" mode="aspectFill"/>
|
||||
<view wx:else class="avatar-placeholder">{{nickname ? nickname[0] : '?'}}</view>
|
||||
<view class="avatar-camera">📷</view>
|
||||
</view>
|
||||
<text class="avatar-change">更换头像</text>
|
||||
</view>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<view class="section">
|
||||
<view class="form-row">
|
||||
<text class="form-label">昵称</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="请输入昵称" value="{{nickname}}" bindinput="onNicknameInput"/></view>
|
||||
</view>
|
||||
<view class="form-row form-row-2">
|
||||
<view class="form-item">
|
||||
<text class="form-label">MBTI</text>
|
||||
<picker mode="selector" range="{{mbtiOptions}}" value="{{mbtiIndex}}" bindchange="onMbtiPickerChange">
|
||||
<view class="form-input-wrap form-picker">{{mbti || '请选择'}}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">地区</text>
|
||||
<view class="form-input-wrap form-input-suffix">
|
||||
<input class="form-input-inner" placeholder="例如:杭州·余杭区" value="{{region}}" bindinput="onRegionInput"/>
|
||||
<text class="form-suffix">📍</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">行业</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="例如:新媒体/电商" value="{{industry}}" bindinput="onIndustryInput"/></view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">业务体量</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="例如:年GMV 5000万+" value="{{businessScale}}" bindinput="onBusinessScaleInput"/></view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">职位</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="例如:创始人/联合创始人" value="{{position}}" bindinput="onPositionInput"/></view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">我擅长</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="例如:短视频制作、IP打造、私域运营" value="{{skills}}" bindinput="onSkillsInput"/></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 核心联系方式 -->
|
||||
<view class="section">
|
||||
<view class="section-title">
|
||||
<text class="section-icon">📞</text>
|
||||
<text>核心联系方式</text>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">手机号</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" type="tel" placeholder="请输入手机号" value="{{phone}}" bindinput="onPhoneInput"/></view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">微信号</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="请输入微信号" value="{{wechatId}}" bindinput="onWechatInput"/></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 个人故事 -->
|
||||
<view class="section">
|
||||
<view class="section-title">
|
||||
<text class="section-icon">💡</text>
|
||||
<text>个人故事</text>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">你最赚钱的一个月做的是什么</text>
|
||||
<view class="form-textarea-wrap"><textarea class="form-textarea-inner" placeholder="例如:2021年主导电商大促,单月GMV突破500W..." value="{{storyBestMonth}}" bindinput="onStoryBestMonthInput"/></view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">最有成就感的一件事</text>
|
||||
<view class="form-textarea-wrap"><textarea class="form-textarea-inner" placeholder="例如:帮助3个素人打造个人IP,每月稳定变现5万+" value="{{storyAchievement}}" bindinput="onStoryAchievementInput"/></view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">人生的转折点</text>
|
||||
<view class="form-textarea-wrap"><textarea class="form-textarea-inner" placeholder="例如:辞去大厂工作开始做自媒体..." value="{{storyTurning}}" bindinput="onStoryTurningInput"/></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 互助需求 -->
|
||||
<view class="section">
|
||||
<view class="section-title">
|
||||
<text class="section-icon">🤝</text>
|
||||
<text>互助需求</text>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">我能帮助大家什么</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="例如:短视频脚本、账号冷启动、私域转化" value="{{helpOffer}}" bindinput="onHelpOfferInput"/></view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<text class="form-label">我需要什么帮助</text>
|
||||
<view class="form-input-wrap"><input class="form-input-inner" placeholder="例如:寻找供应链资源、线下活动合作" value="{{helpNeed}}" bindinput="onHelpNeedInput"/></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<view class="section">
|
||||
<view class="section-title">
|
||||
<text class="section-icon">🚀</text>
|
||||
<text>项目介绍</text>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<view class="form-textarea-wrap form-textarea-lg"><textarea class="form-textarea-inner" placeholder="详细介绍您的项目,让潜在伙伴更好地了解您..." value="{{projectIntro}}" bindinput="onProjectIntroInput"/></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="save-btn" bindtap="saveProfile" disabled="{{saving}}">
|
||||
{{saving ? '保存中...' : '保存'}}
|
||||
</view>
|
||||
<view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
99
miniprogram/pages/profile-edit/profile-edit.wxss
Normal file
99
miniprogram/pages/profile-edit/profile-edit.wxss
Normal file
@@ -0,0 +1,99 @@
|
||||
/* 资料编辑 - comprehensive_profile_editor_v1_1 | 配色 enhanced,input/textarea 用 view 包裹 */
|
||||
.page {
|
||||
background: #050B14; min-height: 100vh; color: #fff;
|
||||
width: 100%; box-sizing: border-box; overflow-x: hidden;
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
height: 44px; padding: 0 24rpx;
|
||||
background: rgba(5,11,20,0.9); backdrop-filter: blur(8rpx);
|
||||
border-bottom: 1rpx solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.nav-back { width: 60rpx; padding: 16rpx 0; }
|
||||
.back-icon { font-size: 44rpx; color: #5EEAD4; }
|
||||
.nav-title { font-size: 36rpx; font-weight: 600; }
|
||||
.nav-placeholder { width: 60rpx; }
|
||||
|
||||
.loading { padding: 96rpx; text-align: center; color: #94A3B8; }
|
||||
|
||||
.scroll-main { width: 100%; height: calc(100vh - 88rpx); padding: 24rpx; box-sizing: border-box; overflow-x: hidden; }
|
||||
|
||||
.tip-card {
|
||||
display: flex; align-items: flex-start; gap: 24rpx;
|
||||
padding: 32rpx;
|
||||
background: rgba(94,234,212,0.08); border: 1rpx solid rgba(94,234,212,0.25);
|
||||
border-radius: 24rpx; margin-bottom: 48rpx;
|
||||
}
|
||||
.tip-icon { font-size: 40rpx; color: #5EEAD4; flex-shrink: 0; }
|
||||
.tip-text { font-size: 26rpx; color: rgba(94,234,212,0.95); line-height: 1.6; }
|
||||
|
||||
.avatar-section { display: flex; flex-direction: column; align-items: center; margin-bottom: 48rpx; }
|
||||
.avatar-wrap {
|
||||
position: relative; width: 192rpx; height: 192rpx; border-radius: 50%; overflow: hidden;
|
||||
border: 4rpx solid #5EEAD4; box-shadow: 0 0 30rpx rgba(94,234,212,0.3);
|
||||
}
|
||||
.avatar-img { width: 100%; height: 100%; display: block; }
|
||||
.avatar-placeholder {
|
||||
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 72rpx; font-weight: bold; color: #5EEAD4; background: rgba(94,234,212,0.2);
|
||||
}
|
||||
.avatar-camera {
|
||||
position: absolute; bottom: 0; right: 0;
|
||||
width: 56rpx; height: 56rpx; background: #5EEAD4; color: #000;
|
||||
border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 28rpx;
|
||||
}
|
||||
.avatar-change { font-size: 28rpx; color: #5EEAD4; font-weight: 500; margin-top: 16rpx; }
|
||||
|
||||
.section {
|
||||
margin-bottom: 48rpx; padding-top: 32rpx;
|
||||
border-top: 1rpx solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.section-title { display: flex; align-items: center; gap: 16rpx; font-size: 32rpx; font-weight: 600; margin-bottom: 32rpx; }
|
||||
.section-icon { font-size: 40rpx; }
|
||||
|
||||
.form-row { margin-bottom: 32rpx; }
|
||||
.form-row:last-child { margin-bottom: 0; }
|
||||
.form-row-2 { display: flex; gap: 24rpx; }
|
||||
.form-row-2 .form-item { flex: 1; min-width: 0; }
|
||||
.form-label { display: block; font-size: 24rpx; color: #94A3B8; margin-bottom: 12rpx; margin-left: 8rpx; }
|
||||
|
||||
/* input/textarea 用 view 包裹,padding 写在 view 上 */
|
||||
.form-input-wrap {
|
||||
padding: 24rpx 32rpx;
|
||||
background: #17212F; border: 1rpx solid rgba(255,255,255,0.08);
|
||||
border-radius: 24rpx;
|
||||
box-sizing: border-box; min-width: 0; width: 100%;
|
||||
}
|
||||
.form-input-suffix { position: relative; padding-right: 64rpx; }
|
||||
.form-input-suffix .form-suffix {
|
||||
position: absolute; right: 24rpx; top: 50%; transform: translateY(-50%);
|
||||
font-size: 32rpx; color: #94A3B8;
|
||||
}
|
||||
.form-input-inner {
|
||||
width: 100%; max-width: 100%; font-size: 28rpx; color: #fff; background: transparent;
|
||||
box-sizing: border-box; display: block;
|
||||
}
|
||||
.form-picker { color: #fff; }
|
||||
|
||||
.form-textarea-wrap {
|
||||
padding: 24rpx 32rpx;
|
||||
background: #17212F; border: 1rpx solid rgba(255,255,255,0.08);
|
||||
border-radius: 24rpx; min-height: 160rpx;
|
||||
box-sizing: border-box; min-width: 0; width: 100%;
|
||||
}
|
||||
.form-textarea-wrap.form-textarea-lg { min-height: 240rpx; }
|
||||
.form-textarea-inner {
|
||||
width: 100%; max-width: 100%; min-height: 112rpx; font-size: 28rpx; color: #fff;
|
||||
background: transparent; line-height: 1.5; box-sizing: border-box; display: block;
|
||||
}
|
||||
.form-textarea-lg .form-textarea-inner { min-height: 192rpx; }
|
||||
|
||||
.save-btn {
|
||||
width: 100%; height: 96rpx; line-height: 96rpx; text-align: center;
|
||||
background: #5EEAD4; color: #050B14; font-size: 36rpx; font-weight: bold;
|
||||
border-radius: 24rpx; margin-top: 48rpx; margin-bottom: 48rpx;
|
||||
}
|
||||
.save-btn[disabled] { opacity: 0.6; }
|
||||
.bottom-space { height: 120rpx; }
|
||||
79
miniprogram/pages/profile-show/profile-show.js
Normal file
79
miniprogram/pages/profile-show/profile-show.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Soul创业派对 - 个人资料展示页(stitch_soul enhanced_professional_profile)
|
||||
* 从「我的」页编辑图标进入;展示基本信息、个人故事、互助需求、项目介绍
|
||||
*/
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
profile: null,
|
||||
loading: true,
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 })
|
||||
this.loadProfile()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (this.data.profile) this.loadProfile()
|
||||
},
|
||||
|
||||
async loadProfile() {
|
||||
const userInfo = app.globalData.userInfo
|
||||
if (!app.globalData.isLoggedIn || !userInfo?.id) {
|
||||
this.setData({ loading: false })
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
setTimeout(() => wx.navigateBack(), 1500)
|
||||
return
|
||||
}
|
||||
this.setData({ loading: true })
|
||||
try {
|
||||
const res = await app.request({ url: `/api/miniprogram/user/profile?userId=${userInfo.id}`, silent: true })
|
||||
if (res?.success && res.data) {
|
||||
const d = res.data
|
||||
const phone = d.phone || ''
|
||||
const wechat = d.wechatId || wx.getStorageSync('user_wechat') || ''
|
||||
this.setData({
|
||||
profile: {
|
||||
...d,
|
||||
phoneMask: phone ? phone.slice(0, 3) + '****' + phone.slice(-2) : '',
|
||||
wechatMask: wechat ? (wechat.length > 8 ? wechat.slice(0, 4) + '****' + wechat.slice(-3) : wechat) : '',
|
||||
phone,
|
||||
wechat,
|
||||
},
|
||||
loading: false,
|
||||
})
|
||||
} else {
|
||||
this.setData({ profile: null, loading: false })
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ profile: null, loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack()
|
||||
},
|
||||
|
||||
goToEdit() {
|
||||
wx.navigateTo({ url: '/pages/profile-edit/profile-edit' })
|
||||
},
|
||||
|
||||
copyPhone() {
|
||||
const p = this.data.profile?.phone
|
||||
if (!p) return
|
||||
wx.setClipboardData({ data: p, success: () => wx.showToast({ title: '已复制', icon: 'success' }) })
|
||||
},
|
||||
|
||||
copyWechat() {
|
||||
const w = this.data.profile?.wechat
|
||||
if (!w) return
|
||||
wx.setClipboardData({ data: w, success: () => wx.showToast({ title: '已复制', icon: 'success' }) })
|
||||
},
|
||||
|
||||
goToVip() {
|
||||
wx.navigateTo({ url: '/pages/vip/vip' })
|
||||
},
|
||||
})
|
||||
4
miniprogram/pages/profile-show/profile-show.json
Normal file
4
miniprogram/pages/profile-show/profile-show.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"navigationBarTitleText": "个人资料",
|
||||
"usingComponents": {}
|
||||
}
|
||||
135
miniprogram/pages/profile-show/profile-show.wxml
Normal file
135
miniprogram/pages/profile-show/profile-show.wxml
Normal file
@@ -0,0 +1,135 @@
|
||||
<!-- 个人资料展示页 - enhanced_professional_profile 1:1 重构 -->
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-back" bindtap="goBack"><text class="back-icon">‹</text></view>
|
||||
<text class="nav-title">个人资料</text>
|
||||
<view class="nav-right" bindtap="goToEdit"><text class="nav-more">⋯</text></view>
|
||||
</view>
|
||||
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<view class="loading" wx:if="{{loading}}">加载中...</view>
|
||||
<scroll-view wx:else class="scroll-main" scroll-y>
|
||||
<!-- 头像区卡片 -->
|
||||
<view class="hero-card" wx:if="{{profile}}">
|
||||
<view class="hero-gradient"></view>
|
||||
<view class="hero-content">
|
||||
<view class="hero-avatar">
|
||||
<image wx:if="{{profile.avatar}}" class="avatar-img" src="{{profile.avatar}}" mode="aspectFill"/>
|
||||
<view wx:else class="avatar-placeholder">{{profile.nickname ? profile.nickname[0] : '?'}}</view>
|
||||
</view>
|
||||
<text class="hero-name">{{profile.nickname || '未设置昵称'}}</text>
|
||||
<view class="hero-tags">
|
||||
<text class="tag tag-mbti" wx:if="{{profile.mbti}}">{{profile.mbti}}</text>
|
||||
<text class="tag tag-region" wx:if="{{profile.region}}">📍 {{profile.region}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<view class="section">
|
||||
<view class="section-head">
|
||||
<text class="section-icon">👤</text>
|
||||
<text class="section-title">基本信息</text>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view class="field" wx:if="{{profile.industry}}">
|
||||
<text class="field-label">行业</text>
|
||||
<text class="field-value">{{profile.industry}}</text>
|
||||
</view>
|
||||
<view class="field" wx:if="{{profile.position}}">
|
||||
<text class="field-label">职位</text>
|
||||
<text class="field-value">{{profile.position}}</text>
|
||||
</view>
|
||||
<view class="field" wx:if="{{profile.businessScale}}">
|
||||
<text class="field-label">业务体量</text>
|
||||
<text class="field-value">{{profile.businessScale}}</text>
|
||||
</view>
|
||||
<view class="field-divider" wx:if="{{profile.industry || profile.position || profile.businessScale}}"></view>
|
||||
<view class="field" wx:if="{{profile.skills}}">
|
||||
<text class="field-label">我擅长</text>
|
||||
<text class="field-value">{{profile.skills}}</text>
|
||||
</view>
|
||||
<view class="field" wx:if="{{profile.phoneMask || profile.phone}}">
|
||||
<text class="field-label">联系方式</text>
|
||||
<view class="field-value-row" bindtap="copyPhone">
|
||||
<text class="field-value mono">{{profile.phoneMask || profile.phone || '未填写'}}</text>
|
||||
<text class="field-hint" wx:if="{{profile.phone}}">复制</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="field" wx:if="{{profile.wechatMask || profile.wechat}}">
|
||||
<text class="field-label">微信号</text>
|
||||
<view class="field-value-row" bindtap="copyWechat">
|
||||
<text class="field-value mono">{{profile.wechatMask || profile.wechat || '未填写'}}</text>
|
||||
<text class="field-hint" wx:if="{{profile.wechat}}">复制</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="field-empty" wx:if="{{!profile.industry && !profile.position && !profile.phone && !profile.wechat && !profile.skills}}">
|
||||
点击右上角 ⋯ 编辑完善资料
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 个人故事 -->
|
||||
<view class="section" wx:if="{{profile.storyBestMonth || profile.storyAchievement || profile.storyTurning}}">
|
||||
<view class="section-head">
|
||||
<text class="section-icon section-icon-yellow">💡</text>
|
||||
<text class="section-title">个人故事</text>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view class="story-block" wx:if="{{profile.storyBestMonth}}">
|
||||
<view class="story-head"><text class="story-emoji">🏆</text><text class="story-label">最赚钱的一个月做的是什么</text></view>
|
||||
<text class="story-text">{{profile.storyBestMonth}}</text>
|
||||
</view>
|
||||
<view class="field-divider" wx:if="{{profile.storyBestMonth && (profile.storyAchievement || profile.storyTurning)}}"></view>
|
||||
<view class="story-block" wx:if="{{profile.storyAchievement}}">
|
||||
<view class="story-head"><text class="story-emoji">⭐</text><text class="story-label">最有成就感的一件事</text></view>
|
||||
<text class="story-text">{{profile.storyAchievement}}</text>
|
||||
</view>
|
||||
<view class="field-divider" wx:if="{{profile.storyAchievement && profile.storyTurning}}"></view>
|
||||
<view class="story-block" wx:if="{{profile.storyTurning}}">
|
||||
<view class="story-head"><text class="story-emoji">🔄</text><text class="story-label">人生的转折点</text></view>
|
||||
<text class="story-text">{{profile.storyTurning}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 互助需求 -->
|
||||
<view class="section" wx:if="{{profile.helpOffer || profile.helpNeed}}">
|
||||
<view class="section-head">
|
||||
<text class="section-icon">🤝</text>
|
||||
<text class="section-title">互助需求</text>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<view class="help-block" wx:if="{{profile.helpOffer}}">
|
||||
<text class="help-tag help-tag-accent">我能帮你</text>
|
||||
<text class="help-text">{{profile.helpOffer}}</text>
|
||||
</view>
|
||||
<view class="help-block" wx:if="{{profile.helpNeed}}">
|
||||
<text class="help-tag help-tag-orange">我需要帮助</text>
|
||||
<text class="help-text">{{profile.helpNeed}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 项目介绍 -->
|
||||
<view class="section" wx:if="{{profile.projectIntro}}">
|
||||
<view class="section-head">
|
||||
<text class="section-icon">🚀</text>
|
||||
<text class="section-title">项目介绍</text>
|
||||
</view>
|
||||
<view class="section-body">
|
||||
<text class="project-text">{{profile.projectIntro}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="bottom-spacer"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部按钮 - 设计稿为描边橙色 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="vip-btn-outline" bindtap="goToVip">
|
||||
<text>成为超级个体</text>
|
||||
<text class="vip-btn-arrow">→</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
114
miniprogram/pages/profile-show/profile-show.wxss
Normal file
114
miniprogram/pages/profile-show/profile-show.wxss
Normal file
@@ -0,0 +1,114 @@
|
||||
/* 个人资料展示页 - enhanced_professional_profile 1:1 重构 */
|
||||
.page { background: #050B14; min-height: 100vh; color: #fff; }
|
||||
|
||||
.nav-bar {
|
||||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
height: 44px; padding: 0 32rpx;
|
||||
background: rgba(5,11,20,0.9); backdrop-filter: blur(8rpx);
|
||||
border-bottom: 1rpx solid rgba(255,255,255,0.05);
|
||||
}
|
||||
.nav-back { padding: 16rpx; margin-left: -8rpx; }
|
||||
.back-icon { font-size: 40rpx; color: #5EEAD4; }
|
||||
.nav-title { font-size: 34rpx; font-weight: bold; }
|
||||
.nav-right { padding: 16rpx; }
|
||||
.nav-more { font-size: 48rpx; color: #fff; line-height: 1; }
|
||||
.nav-placeholder { width: 100%; }
|
||||
|
||||
.loading { padding: 96rpx; text-align: center; color: #94A3B8; }
|
||||
|
||||
.scroll-main { height: calc(100vh - 120rpx); padding: 0 32rpx 32rpx; }
|
||||
|
||||
/* 头像区卡片 */
|
||||
.hero-card {
|
||||
position: relative; overflow: hidden;
|
||||
background: #0F1720; border: 1rpx solid rgba(255,255,255,0.08);
|
||||
border-radius: 32rpx; margin-bottom: 32rpx;
|
||||
padding: 64rpx 32rpx; display: flex; flex-direction: column; align-items: center;
|
||||
}
|
||||
.hero-gradient {
|
||||
position: absolute; top: 0; left: 0; right: 0; height: 128rpx;
|
||||
background: linear-gradient(to bottom, rgba(30,58,69,0.3) 0%, transparent 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.hero-content { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; }
|
||||
.hero-avatar {
|
||||
width: 176rpx; height: 176rpx; border-radius: 50%;
|
||||
overflow: hidden; border: 2rpx solid rgba(255,255,255,0.1);
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
.avatar-img { width: 100%; height: 100%; display: block; }
|
||||
.avatar-placeholder {
|
||||
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 72rpx; font-weight: bold; color: #5EEAD4;
|
||||
background: rgba(94,234,212,0.2);
|
||||
}
|
||||
.hero-name { font-size: 40rpx; font-weight: bold; margin-bottom: 24rpx; }
|
||||
.hero-tags { display: flex; align-items: center; justify-content: center; gap: 24rpx; }
|
||||
.tag { padding: 8rpx 24rpx; border-radius: 999rpx; font-size: 24rpx; font-weight: 500; }
|
||||
.tag-mbti { background: #134E4A; color: #5EEAD4; border: 1rpx solid rgba(94,234,212,0.2); }
|
||||
.tag-region { background: #1F2937; color: #d1d5db; border: 1rpx solid rgba(255,255,255,0.1); }
|
||||
|
||||
/* 通用区块 */
|
||||
.section {
|
||||
background: #0F1720; border: 1rpx solid rgba(255,255,255,0.08);
|
||||
border-radius: 32rpx; margin-bottom: 32rpx;
|
||||
padding: 40rpx; box-shadow: 0 16rpx 32rpx rgba(0,0,0,0.2);
|
||||
}
|
||||
.section-head { display: flex; align-items: center; gap: 20rpx; margin-bottom: 40rpx; }
|
||||
.section-icon { font-size: 40rpx; }
|
||||
.section-icon-yellow { filter: brightness(1.2); }
|
||||
.section-title { font-size: 30rpx; font-weight: bold; }
|
||||
|
||||
.section-body { }
|
||||
.field { margin-bottom: 48rpx; }
|
||||
.field:last-child { margin-bottom: 0; }
|
||||
.field-label { display: block; font-size: 26rpx; color: #94A3B8; margin-bottom: 16rpx; }
|
||||
.field-value { font-size: 30rpx; font-weight: 500; color: #fff; line-height: 1.5; }
|
||||
.field-value.mono { font-family: monospace; letter-spacing: 0.02em; }
|
||||
.field-value-row { display: flex; align-items: center; gap: 16rpx; }
|
||||
.field-hint { font-size: 24rpx; color: #5EEAD4; }
|
||||
.field-divider { height: 1rpx; background: rgba(255,255,255,0.05); margin: 32rpx 0; }
|
||||
.field-empty { font-size: 26rpx; color: #64748b; }
|
||||
|
||||
/* 个人故事 */
|
||||
.story-block { margin-bottom: 48rpx; }
|
||||
.story-block:last-child { margin-bottom: 0; }
|
||||
.story-head { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
|
||||
.story-emoji { font-size: 32rpx; }
|
||||
.story-label { font-size: 26rpx; font-weight: 500; color: #94A3B8; }
|
||||
.story-text { font-size: 28rpx; color: #e5e7eb; line-height: 1.6; display: block; }
|
||||
|
||||
/* 互助需求 */
|
||||
.help-block {
|
||||
background: #17212F; border: 1rpx solid rgba(255,255,255,0.05);
|
||||
border-radius: 20rpx; padding: 32rpx; margin-bottom: 24rpx;
|
||||
}
|
||||
.help-block:last-child { margin-bottom: 0; }
|
||||
.help-tag {
|
||||
display: inline-block; font-size: 22rpx; font-weight: 500;
|
||||
padding: 8rpx 16rpx; border-radius: 8rpx; margin-bottom: 16rpx;
|
||||
}
|
||||
.help-tag-accent { background: #112D2A; color: #5EEAD4; }
|
||||
.help-tag-orange { background: #2D1F0D; color: #F59E0B; }
|
||||
.help-text { font-size: 26rpx; color: #fff; line-height: 1.6; display: block; }
|
||||
|
||||
.project-text { font-size: 28rpx; color: #e5e7eb; line-height: 1.6; }
|
||||
|
||||
.bottom-spacer { height: 180rpx; }
|
||||
|
||||
/* 底部按钮 - 设计稿:透明背景 + 橙色描边 */
|
||||
.bottom-bar {
|
||||
position: fixed; bottom: 0; left: 0; right: 0; z-index: 50;
|
||||
padding: 32rpx; padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
|
||||
background: rgba(5,11,20,0.95); backdrop-filter: blur(8rpx);
|
||||
border-top: 1rpx solid rgba(255,255,255,0.05);
|
||||
}
|
||||
.vip-btn-outline {
|
||||
display: flex; align-items: center; justify-content: center; gap: 16rpx;
|
||||
width: 100%; height: 96rpx;
|
||||
background: transparent; color: #F59E0B;
|
||||
border: 2rpx solid rgba(245,158,11,0.3);
|
||||
border-radius: 999rpx; font-size: 30rpx; font-weight: 500;
|
||||
}
|
||||
.vip-btn-arrow { font-size: 36rpx; }
|
||||
@@ -8,17 +8,18 @@ Page({
|
||||
expireDateStr: '',
|
||||
price: 1980,
|
||||
originalPrice: 6980,
|
||||
/* 按 premium_membership_landing_v1 设计稿 */
|
||||
contentRights: [
|
||||
{ title: '解锁全部章节', desc: '365天全部章节内容' },
|
||||
{ title: '案例库', desc: '30-100个创业项目案例' },
|
||||
{ title: '智能纪要', desc: '每天推送派对精华' },
|
||||
{ title: '会议纪要库', desc: '之前所有场次的会议纪要' }
|
||||
{ title: '解锁全部章节', desc: '365天全案精读', icon: '📖' },
|
||||
{ title: '案例库', desc: '100+创业实战案例', icon: '📚' },
|
||||
{ title: '智能纪要', desc: 'AI每日精华推送', icon: '💡' },
|
||||
{ title: '会议纪要库', desc: '往期完整沉淀', icon: '📁' }
|
||||
],
|
||||
socialRights: [
|
||||
{ title: '匹配创业伙伴', desc: '匹配所有创业伙伴' },
|
||||
{ title: '创业老板排行', desc: '排行榜展示您的项目' },
|
||||
{ title: '链接资源', desc: '进群聊天、链接资源的权利' },
|
||||
{ title: '专属VIP标识', desc: '头像金色VIP光圈' }
|
||||
{ title: '匹配创业伙伴', desc: '精准人脉匹配', icon: '👥' },
|
||||
{ title: '创业老板排行', desc: '项目曝光展示', icon: '📊' },
|
||||
{ title: '链接资源', desc: '深度私域资源池', icon: '🔗' },
|
||||
{ title: '专属VIP标识', desc: '金色尊享光圈', icon: '✓' }
|
||||
],
|
||||
profile: { vipName: '', vipProject: '', vipContact: '', vipAvatar: '', vipBio: '' },
|
||||
purchasing: false
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- VIP会员页 -->
|
||||
<!-- VIP会员页 - 按 premium_membership_landing_v1 设计稿 -->
|
||||
<view class="page">
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-back" bindtap="goBack">
|
||||
@@ -8,55 +8,53 @@
|
||||
<view class="nav-placeholder-r"></view>
|
||||
</view>
|
||||
<view style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
<!-- 会员状态 -->
|
||||
<!-- 会员宣传区 - 设计稿 premium 卡片 -->
|
||||
<view class="vip-hero {{isVip ? 'vip-hero-active' : ''}}">
|
||||
<text class="vip-hero-tag">卡若创业派对</text>
|
||||
<text class="vip-hero-tag">VIP PREMIUM</text>
|
||||
<text class="vip-hero-title">
|
||||
加入卡若的
|
||||
<text class="gold">创业派对</text>
|
||||
会员
|
||||
</text>
|
||||
<text class="vip-hero-sub" wx:if="{{isVip}}">有效期至 {{expireDateStr}}(剩余{{daysRemaining}}天)</text>
|
||||
<text class="vip-hero-sub" wx:else>专属会员尊享权益</text>
|
||||
<text class="vip-hero-sub" wx:else>一次加入 尊享终身陪伴与成长</text>
|
||||
</view>
|
||||
<!-- 内容权益 -->
|
||||
<view class="rights-card">
|
||||
<text class="rights-section-title">内容权益</text>
|
||||
<view class="rights-item" wx:for="{{contentRights}}" wx:key="title">
|
||||
<view class="rights-check-wrap">
|
||||
<text class="rights-check">✓</text>
|
||||
<!-- 双列权益:内容权益 + 社交权益 -->
|
||||
<view class="rights-grid">
|
||||
<view class="rights-col">
|
||||
<view class="rights-col-header">
|
||||
<text class="rights-dot rights-dot-teal"></text>
|
||||
<text class="rights-col-title">内容权益</text>
|
||||
</view>
|
||||
<view class="rights-info">
|
||||
<text class="rights-title">{{item.title}}</text>
|
||||
<text class="rights-desc">{{item.desc}}</text>
|
||||
<view class="benefit-card" wx:for="{{contentRights}}" wx:key="title">
|
||||
<text class="benefit-icon">{{item.icon || '✓'}}</text>
|
||||
<view class="benefit-info">
|
||||
<text class="benefit-title">{{item.title}}</text>
|
||||
<text class="benefit-desc">{{item.desc}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="rights-col">
|
||||
<view class="rights-col-header">
|
||||
<text class="rights-dot rights-dot-gold"></text>
|
||||
<text class="rights-col-title rights-col-title-gold">社交权益</text>
|
||||
</view>
|
||||
<view class="benefit-card" wx:for="{{socialRights}}" wx:key="title">
|
||||
<text class="benefit-icon benefit-icon-gold">{{item.icon || '✓'}}</text>
|
||||
<view class="benefit-info">
|
||||
<text class="benefit-title">{{item.title}}</text>
|
||||
<text class="benefit-desc">{{item.desc}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 社交权益 -->
|
||||
<view class="rights-card">
|
||||
<text class="rights-section-title">社交权益</text>
|
||||
<view class="rights-item" wx:for="{{socialRights}}" wx:key="title">
|
||||
<view class="rights-check-wrap">
|
||||
<text class="rights-check">✓</text>
|
||||
</view>
|
||||
<view class="rights-info">
|
||||
<text class="rights-title">{{item.title}}</text>
|
||||
<text class="rights-desc">{{item.desc}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 价格区 + 购买按钮 -->
|
||||
<view class="buy-area" wx:if="{{!isVip}}">
|
||||
<view class="price-row">
|
||||
<text class="price-original">¥{{originalPrice}}</text>
|
||||
<text class="price-current">¥{{price}}</text>
|
||||
<text class="price-unit">/年</text>
|
||||
</view>
|
||||
<button class="buy-btn" bindtap="handlePurchase" disabled="{{purchasing}}">
|
||||
{{purchasing ? '处理中...' : '¥' + price + ' 加入创业派对'}}
|
||||
<!-- 底部固定购买按钮(非 VIP 时显示) -->
|
||||
<view class="buy-footer" wx:if="{{!isVip}}">
|
||||
<button class="buy-btn-fixed" bindtap="handlePurchase" disabled="{{purchasing}}">
|
||||
{{purchasing ? "处理中..." : "¥" + price + "/年 加入创业派对"}}
|
||||
</button>
|
||||
<text class="buy-sub">加入卡若创业派对,获取创业资讯与优质人脉资源</text>
|
||||
</view>
|
||||
<view class="bottom-spacer" wx:if="{{!isVip}}"></view>
|
||||
<!-- VIP资料填写(仅VIP可见) -->
|
||||
<view class="profile-card" wx:if="{{isVip}}">
|
||||
<text class="profile-title">会员资料(展示在创业老板排行)</text>
|
||||
|
||||
@@ -12,26 +12,28 @@
|
||||
.gold { color: #FFD700; }
|
||||
.vip-hero-sub { display: block; font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 12rpx; }
|
||||
|
||||
.rights-card { margin: 24rpx; padding: 0 8rpx; }
|
||||
.rights-item { display: flex; align-items: flex-start; padding: 24rpx; margin-bottom: 16rpx; background: rgba(255,255,255,0.04); border: 1rpx solid rgba(255,255,255,0.06); border-radius: 16rpx; }
|
||||
.rights-item .rights-check-wrap { margin-right: 20rpx; }
|
||||
.rights-check-wrap { width: 44rpx; height: 44rpx; border-radius: 50%; background: rgba(0,206,209,0.15); display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 4rpx; }
|
||||
.rights-check { color: #00CED1; font-size: 24rpx; font-weight: bold; }
|
||||
.rights-info { display: flex; flex-direction: column; }
|
||||
.rights-title { font-size: 30rpx; font-weight: 600; color: rgba(255,255,255,0.95); }
|
||||
.rights-desc { font-size: 24rpx; color: rgba(255,255,255,0.45); margin-top: 6rpx; }
|
||||
/* 双列权益 - 按 premium_membership_landing_v1 */
|
||||
.rights-grid { display: flex; gap: 24rpx; margin: 24rpx; }
|
||||
.rights-col { flex: 1; min-width: 0; }
|
||||
.rights-col-header { display: flex; align-items: center; gap: 12rpx; margin-bottom: 16rpx; padding-left: 8rpx; }
|
||||
.rights-dot { width: 8rpx; height: 24rpx; border-radius: 4rpx; }
|
||||
.rights-dot-teal { background: #4FD1C5; }
|
||||
.rights-dot-gold { background: #FFBD2E; }
|
||||
.rights-col-title { font-size: 24rpx; font-weight: bold; color: #4FD1C5; letter-spacing: 2rpx; }
|
||||
.rights-col-title-gold { color: #FFBD2E; }
|
||||
.benefit-card { display: flex; flex-direction: column; gap: 16rpx; padding: 24rpx; margin-bottom: 16rpx; background: #141414; border: 1rpx solid rgba(255,255,255,0.05); border-radius: 24rpx; }
|
||||
.benefit-icon { font-size: 36rpx; color: #4FD1C5; }
|
||||
.benefit-icon-gold { color: #FFBD2E; }
|
||||
.benefit-info { display: flex; flex-direction: column; }
|
||||
.benefit-title { font-size: 26rpx; font-weight: bold; color: #fff; }
|
||||
.benefit-desc { font-size: 20rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; line-height: 1.4; }
|
||||
|
||||
.rights-section-title { display: block; font-size: 26rpx; color: #00CED1; font-weight: 600; margin-bottom: 16rpx; margin-left: 16rpx; padding-bottom: 12rpx; border-bottom: 1rpx solid rgba(0,206,209,0.15); }
|
||||
|
||||
.buy-area { margin: 24rpx; padding: 32rpx; text-align: center; background: rgba(255,255,255,0.03); border-radius: 20rpx; }
|
||||
.price-row { display: flex; align-items: baseline; justify-content: center; gap: 12rpx; margin-bottom: 24rpx; }
|
||||
.price-original { font-size: 28rpx; color: rgba(255,255,255,0.35); text-decoration: line-through; }
|
||||
.price-current { font-size: 64rpx; font-weight: bold; color: #FF4444; }
|
||||
.price-unit { font-size: 26rpx; color: rgba(255,255,255,0.5); }
|
||||
.buy-btn { width: 90%; height: 88rpx; padding: 0; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #FFD700, #FFA500); color: #000; font-size: 32rpx; font-weight: bold; border-radius: 44rpx; border: none; margin: 0 auto; }
|
||||
.buy-btn::after { border: none; }
|
||||
.buy-btn[disabled] { opacity: 0.5; }
|
||||
.buy-sub { display: block; font-size: 22rpx; color: rgba(255,255,255,0.4); margin-top: 16rpx; }
|
||||
/* 底部固定购买按钮 - 设计稿 */
|
||||
.buy-footer { position: fixed; bottom: 0; left: 0; right: 0; padding: 24rpx 32rpx; padding-bottom: calc(24rpx + env(safe-area-inset-bottom)); background: rgba(0,0,0,0.95); border-top: 1rpx solid rgba(255,255,255,0.05); z-index: 50; }
|
||||
.buy-btn-fixed { width: 100%; height: 96rpx; padding: 0; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #FFD700, #FFB000); color: #000; font-size: 32rpx; font-weight: bold; border-radius: 48rpx; border: none; box-shadow: 0 8rpx 32rpx rgba(255,188,46,0.2); }
|
||||
.buy-btn-fixed::after { border: none; }
|
||||
.buy-btn-fixed[disabled] { opacity: 0.6; }
|
||||
.bottom-spacer { height: 180rpx; }
|
||||
|
||||
.profile-card { margin: 24rpx; padding: 32rpx; background: rgba(255,255,255,0.04); border: 1rpx solid rgba(255,255,255,0.08); border-radius: 20rpx; }
|
||||
.profile-title { font-size: 30rpx; font-weight: 600; color: rgba(255,255,255,0.9); display: block; margin-bottom: 24rpx; }
|
||||
|
||||
@@ -24,50 +24,15 @@
|
||||
"miniprogram": {
|
||||
"list": [
|
||||
{
|
||||
"name": "vip资料填写",
|
||||
"pathName": "pages/vip/vip",
|
||||
"name": "个人资料",
|
||||
"pathName": "pages/profile-show/profile-show",
|
||||
"query": "",
|
||||
"scene": null,
|
||||
"launchMode": "default"
|
||||
},
|
||||
{
|
||||
"name": "pages/read/read",
|
||||
"pathName": "pages/read/read",
|
||||
"query": "id=1.1",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "pages/match/match",
|
||||
"pathName": "pages/match/match",
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "看书",
|
||||
"pathName": "pages/read/read",
|
||||
"query": "id=1.4",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "分销中心",
|
||||
"pathName": "pages/referral/referral",
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "阅读",
|
||||
"pathName": "pages/read/read",
|
||||
"query": "id=1.1",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "分销中心",
|
||||
"pathName": "pages/referral/referral",
|
||||
"name": "pages/mentors/mentors",
|
||||
"pathName": "pages/mentors/mentors",
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
@@ -78,13 +43,6 @@
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
},
|
||||
{
|
||||
"name": "新增地址",
|
||||
"pathName": "pages/addresses/edit",
|
||||
"query": "",
|
||||
"launchMode": "default",
|
||||
"scene": null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user