wangfukang 3 months ago
parent
commit
51886cd247
  1. 6
      package1/buyFood/buyFood.vue
  2. 128
      package1/components/common-loading/common-loading.vue
  3. 211
      package1/components/ie-auth-dialog/ie-auth-dialog.vue
  4. 155
      package1/components/ie-bottom-tab/ie-bottom-tab.vue
  5. 126
      package1/ieBrowser/chat.vue
  6. 217
      package1/ieBrowser/index.vue
  7. 2
      package1/ieBrowser/messages.vue
  8. 6
      package1/ieBrowser/mySpace.vue
  9. 2
      package1/ieBrowser/universe.vue
  10. 111
      package1/order/orderDetail.vue
  11. 163
      package1/planet/index.vue
  12. 27
      package1/planet/pkHall.vue

6
package1/buyFood/buyFood.vue

@ -908,8 +908,10 @@
return arr.join(',');
},
submitPay() {
if (this.currentOrderId && this.backendTotalAmount) {
this.$refs.payPopup.open('bottom');
if (this.currentOrderId) {
uni.redirectTo({
url: '/package1/order/orderDetail?id=' + this.currentOrderId
});
return;
}
if (this.isCreatingOrder) return;

128
package1/components/common-loading/common-loading.vue

@ -0,0 +1,128 @@
<template>
<view
v-if="visible"
class="common-loading"
:class="{'common-loading--mask': mask}"
@touchmove.stop.prevent="noop"
@tap.stop="noop"
>
<view class="common-loading__box">
<view class="common-loading__halo"></view>
<image class="common-loading__gif" src="/static/images/img/loading.gif" mode="aspectFit"></image>
<view v-if="title" class="common-loading__text">{{ title }}</view>
<view class="common-loading__hint">请稍候正在为你处理</view>
</view>
</view>
</template>
<script>
import { LOADING_HIDE_EVENT, LOADING_SHOW_EVENT, getLoadingState } from '@/utils/loading.js'
export default {
name: 'CommonLoading',
data() {
return {
visible: false,
title: '加载中...',
mask: true
}
},
created() {
const state = getLoadingState()
this.visible = state.visible
this.title = state.title
this.mask = state.mask
uni.$on(LOADING_SHOW_EVENT, this.show)
uni.$on(LOADING_HIDE_EVENT, this.hide)
},
beforeDestroy() {
uni.$off(LOADING_SHOW_EVENT, this.show)
uni.$off(LOADING_HIDE_EVENT, this.hide)
},
methods: {
show(options = {}) {
this.visible = true
this.title = options.title || '加载中...'
this.mask = options.mask !== false
},
hide() {
this.visible = false
},
noop() {}
}
}
</script>
<style lang="scss" scoped>
.common-loading {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 99999;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
background: rgba(13, 18, 34, 0.46);
backdrop-filter: blur(16rpx);
}
.common-loading--mask {
pointer-events: auto;
}
.common-loading__box {
position: relative;
min-width: 280rpx;
min-height: 242rpx;
overflow: hidden;
padding: 44rpx 44rpx 36rpx;
border-radius: 40rpx;
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(245,249,255,.96));
border: 1rpx solid rgba(255,255,255,.86);
box-shadow: 0 28rpx 82rpx rgba(28, 35, 74, 0.28);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.common-loading__halo {
position: absolute;
top: -82rpx;
right: -74rpx;
width: 220rpx;
height: 220rpx;
border-radius: 50%;
background: rgba(169, 255, 231, .72);
}
.common-loading__gif {
position: relative;
width: 92rpx;
height: 92rpx;
}
.common-loading__text {
position: relative;
margin-top: 18rpx;
max-width: 380rpx;
font-size: 28rpx;
line-height: 40rpx;
color: #11162a;
font-weight: 900;
white-space: pre-line;
text-align: center;
}
.common-loading__hint {
position: relative;
margin-top: 10rpx;
color: rgba(17, 22, 42, .48);
font-size: 22rpx;
line-height: 32rpx;
text-align: center;
}
</style>

211
package1/components/ie-auth-dialog/ie-auth-dialog.vue

@ -0,0 +1,211 @@
<template>
<view v-if="visible" class="auth-mask" @touchmove.stop.prevent="noop" @tap.stop="cancel">
<view class="auth-card" @tap.stop>
<view class="auth-glow auth-glow-a"></view>
<view class="auth-glow auth-glow-b"></view>
<view class="auth-badge">{{ badge }}</view>
<view class="auth-title">{{ title }}</view>
<view class="auth-content">{{ content }}</view>
<view class="auth-steps" v-if="steps && steps.length">
<view class="auth-step" v-for="(step, index) in steps" :key="step">
<view class="step-dot">{{ index + 1 }}</view>
<view>{{ step }}</view>
</view>
</view>
<view class="auth-actions">
<view class="auth-btn ghost" @tap="cancel">{{ cancelText }}</view>
<view class="auth-btn primary" @tap="confirm">{{ confirmText }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'IeAuthDialog',
data() {
return {
visible: false,
badge: 'i/e 安全认证',
title: '',
content: '',
confirmText: '继续',
cancelText: '暂不继续',
steps: [],
resolver: null
}
},
created() {
uni.$on('ie-auth-dialog:show', this.show)
},
beforeDestroy() {
uni.$off('ie-auth-dialog:show', this.show)
},
methods: {
show(options = {}) {
if (typeof options.handled === 'function') {
options.handled()
}
this.badge = options.badge || 'i/e 安全认证'
this.title = options.title || '进阶实名认证'
this.content = options.content || ''
this.confirmText = options.confirmText || '继续'
this.cancelText = options.cancelText || '暂不继续'
this.steps = options.steps || []
this.resolver = options.resolve || null
this.visible = true
},
confirm() {
this.close(true)
},
cancel() {
this.close(false)
},
close(value) {
this.visible = false
if (this.resolver) {
this.resolver(value)
}
this.resolver = null
},
noop() {}
}
}
</script>
<style lang="scss" scoped>
.auth-mask {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 99998;
display: flex;
align-items: center;
justify-content: center;
padding: 48rpx;
background: rgba(13, 18, 34, 0.48);
backdrop-filter: blur(18rpx);
}
.auth-card {
position: relative;
width: 100%;
overflow: hidden;
padding: 46rpx 38rpx 34rpx;
border-radius: 46rpx;
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(245,249,255,.96));
border: 1rpx solid rgba(255,255,255,.88);
box-shadow: 0 34rpx 90rpx rgba(28, 35, 74, .28);
}
.auth-glow {
position: absolute;
border-radius: 999rpx;
filter: blur(4rpx);
opacity: .72;
}
.auth-glow-a {
right: -80rpx;
top: -90rpx;
width: 230rpx;
height: 230rpx;
background: rgba(169,255,231,.78);
}
.auth-glow-b {
left: -70rpx;
bottom: 120rpx;
width: 190rpx;
height: 190rpx;
background: rgba(255,126,179,.22);
}
.auth-badge {
position: relative;
display: inline-flex;
padding: 10rpx 18rpx;
border-radius: 999rpx;
color: #6c55d8;
background: rgba(122,77,255,.09);
font-size: 22rpx;
font-weight: 800;
}
.auth-title {
position: relative;
margin-top: 22rpx;
color: #11162a;
font-size: 42rpx;
line-height: 54rpx;
font-weight: 900;
}
.auth-content {
position: relative;
margin-top: 16rpx;
color: rgba(17,22,42,.62);
font-size: 26rpx;
line-height: 42rpx;
}
.auth-steps {
position: relative;
margin-top: 26rpx;
padding: 22rpx;
border-radius: 28rpx;
background: rgba(241,247,255,.82);
}
.auth-step {
display: flex;
align-items: center;
gap: 16rpx;
min-height: 48rpx;
color: rgba(17,22,42,.72);
font-size: 24rpx;
font-weight: 700;
}
.step-dot {
width: 34rpx;
height: 34rpx;
line-height: 34rpx;
text-align: center;
border-radius: 50%;
color: #11162a;
background: #a9ffe7;
font-size: 20rpx;
font-weight: 900;
}
.auth-actions {
position: relative;
display: flex;
gap: 18rpx;
margin-top: 34rpx;
}
.auth-btn {
flex: 1;
height: 84rpx;
line-height: 84rpx;
text-align: center;
border-radius: 999rpx;
font-size: 27rpx;
font-weight: 900;
}
.auth-btn.ghost {
color: rgba(17,22,42,.56);
background: rgba(17,22,42,.06);
}
.auth-btn.primary {
color: #11162a;
background: linear-gradient(135deg, #a9ffe7, #ffcf7b);
box-shadow: 0 16rpx 32rpx rgba(76, 209, 184, .22);
}
</style>

155
package1/components/ie-bottom-tab/ie-bottom-tab.vue

@ -0,0 +1,155 @@
<template>
<view>
<view class="safe-tip" v-if="showTip">请勿发送涉黄涉暴反动侮辱等内容违规会被封禁</view>
<view class="bottom-actions">
<view class="bottom-item" :class="{ active: active === 'index' }" @tap="goTab('index')">
<text class="tab-icon">🪐</text>
<text>此刻</text>
</view>
<view class="bottom-item message-tab" :class="{ active: active === 'messages' }" @tap="goTab('messages')">
<text class="tab-icon">💬</text>
<text>消息</text>
<text class="unread-badge" v-if="displayUnreadCount > 0">{{ unreadText }}</text>
</view>
<view class="bottom-item" :class="{ active: active === 'universe' }" @tap="goTab('universe')">
<text class="tab-icon">🌙</text>
<text>我的</text>
</view>
</view>
</view>
</template>
<script>
const TAB_PATHS = {
index: '/package1/ieBrowser/index',
messages: '/package1/ieBrowser/messages',
universe: '/package1/ieBrowser/universe'
}
export default {
name: 'IeBottomTab',
props: {
active: {
type: String,
default: 'index'
},
unreadCount: {
type: Number,
default: 0
},
showTip: {
type: Boolean,
default: true
}
},
computed: {
displayUnreadCount() {
return Number(this.unreadCount) || 0
},
unreadText() {
return this.displayUnreadCount > 99 ? '99+' : String(this.displayUnreadCount)
}
},
methods: {
goTab(tab) {
if (tab === this.active) return
const path = TAB_PATHS[tab]
if (!path) return
uni.redirectTo({
url: path + '?unreadCount=' + encodeURIComponent(String(this.displayUnreadCount))
})
}
}
}
</script>
<style lang="scss" scoped>
.safe-tip {
position: fixed;
left: 36rpx;
right: 36rpx;
bottom: 142rpx;
z-index: 10;
padding: 12rpx 18rpx;
border-radius: 22rpx;
text-align: center;
color: rgba(22, 27, 46, .5);
background: rgba(255, 255, 255, .74);
border: 1rpx solid rgba(255, 255, 255, .9);
backdrop-filter: blur(18rpx);
box-shadow: 0 14rpx 38rpx rgba(96, 112, 160, .1);
font-size: 20rpx;
line-height: 28rpx;
font-weight: 700;
}
.bottom-actions {
position: fixed;
left: 30rpx;
right: 30rpx;
bottom: 30rpx;
z-index: 10;
display: flex;
padding: 12rpx;
border: 1rpx solid rgba(255, 255, 255, .92);
border-radius: 999rpx;
overflow: hidden;
background: rgba(255, 255, 255, .92);
backdrop-filter: blur(24rpx);
box-shadow: 0 24rpx 80rpx rgba(96, 112, 160, .18);
}
.bottom-item {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 80rpx;
border-radius: 999rpx;
color: rgba(22, 27, 46, .4);
font-size: 21rpx;
font-weight: 700;
transition: transform .16s ease, background .24s ease;
}
.bottom-item:active {
transform: scale(.94);
}
.bottom-item.active {
color: #161b2e;
background: linear-gradient(135deg, #eafff8, #a9ffe7);
box-shadow: 0 12rpx 30rpx rgba(169, 255, 231, .34), inset 0 1rpx 0 rgba(255, 255, 255, .95);
}
.tab-icon {
margin-bottom: 4rpx;
font-size: 30rpx;
filter: grayscale(1);
opacity: .55;
}
.bottom-item.active .tab-icon {
filter: none;
opacity: 1;
}
.unread-badge {
position: absolute;
right: 16rpx;
top: 2rpx;
min-width: 30rpx;
height: 30rpx;
line-height: 30rpx;
padding: 0 8rpx;
border-radius: 999rpx;
text-align: center;
color: #fff;
background: linear-gradient(135deg, #ff8ba0, #e85d75);
box-shadow: 0 8rpx 20rpx rgba(232, 93, 117, .28);
font-size: 18rpx;
font-weight: 900;
}
</style>

126
package1/ieBrowser/chat.vue

@ -982,43 +982,117 @@
}
this.closeEmoji()
if (this.uploadingImage) return
if (!uni.chooseMedia) {
this.chooseImageCompat()
uni.showActionSheet({
itemList: ['拍照', '从相册选图', '拍视频', '从相册选视频'],
success: (res) => {
if (res.tapIndex === 0) this.chooseHighQualityImage(['camera'])
if (res.tapIndex === 1) this.chooseHighQualityImage(['album'])
if (res.tapIndex === 2) this.chooseHighQualityVideo(['camera'])
if (res.tapIndex === 3) this.chooseHighQualityVideo(['album'])
}
})
},
compressCameraImage(filePath) {
return new Promise(resolve => {
if (!uni.compressImage || !filePath) {
resolve(filePath)
return
}
uni.compressImage({
src: filePath,
quality: 88,
success: res => resolve(res.tempFilePath || filePath),
fail: () => resolve(filePath)
})
})
},
chooseHighQualityImage(sourceType = ['album', 'camera']) {
const checkAndSend = async (filePath, fileSize) => {
if (!filePath) return
if (fileSize && fileSize > 20 * 1024 * 1024) {
uni.showToast({ title: '图片不能超过20MB,请换一张', icon: 'none' })
return
}
const isCamera = sourceType.length === 1 && sourceType[0] === 'camera'
const finalPath = isCamera ? await this.compressCameraImage(filePath) : filePath
await this.uploadAndSendImage(finalPath, fileSize)
}
const isCamera = sourceType.length === 1 && sourceType[0] === 'camera'
if (uni.chooseMedia) {
uni.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType,
camera: 'back',
sizeType: isCamera ? ['compressed'] : ['original', 'compressed'],
success: async (res) => {
const file = res.tempFiles && res.tempFiles[0]
await checkAndSend(file && file.tempFilePath, file && file.size)
}
})
return
}
uni.chooseMedia({
uni.chooseImage({
count: 1,
mediaType: ['image', 'video'],
sourceType: ['album', 'camera'],
camera: 'back',
maxDuration: 60,
sourceType,
sizeType: isCamera ? ['compressed'] : ['original', 'compressed'],
success: async (res) => {
const file = res.tempFiles && res.tempFiles[0]
if (!file || !file.tempFilePath) return
if (res.type === 'video' || file.fileType === 'video') {
if (file.size && file.size > 100 * 1024 * 1024) {
uni.showToast({ title: '视频不能超过100MB,请先剪辑', icon: 'none' })
return
}
if (file.duration && file.duration > 61) {
uni.showToast({ title: '视频时长不能超过60秒', icon: 'none' })
return
}
await this.uploadAndSendVideo(file)
return
}
if (file.size && file.size > 10 * 1024 * 1024) {
uni.showToast({ title: '图片不能超过10MB', icon: 'none' })
return
}
await this.uploadAndSendImage(file.tempFilePath, file.size)
const filePath = (file && file.path) || (res.tempFilePaths && res.tempFilePaths[0])
await checkAndSend(filePath, file && file.size)
}
})
},
chooseHighQualityVideo(sourceType = ['album', 'camera']) {
const checkAndSend = async (file) => {
if (!file || !file.tempFilePath) return
if (file.size && file.size > 150 * 1024 * 1024) {
uni.showToast({ title: '视频不能超过150MB,请先剪辑', icon: 'none' })
return
}
if (file.duration && file.duration > 61) {
uni.showToast({ title: '视频时长不能超过60秒', icon: 'none' })
return
}
await this.uploadAndSendVideo(file)
}
if (uni.chooseMedia) {
uni.chooseMedia({
count: 1,
mediaType: ['video'],
sourceType,
camera: 'back',
maxDuration: 60,
sizeType: ['original', 'compressed'],
success: async (res) => {
const file = res.tempFiles && res.tempFiles[0]
await checkAndSend(file)
}
})
return
}
if (uni.chooseVideo) {
uni.chooseVideo({
sourceType,
camera: 'back',
maxDuration: 60,
compressed: false,
success: async (res) => {
await checkAndSend({
tempFilePath: res.tempFilePath,
thumbTempFilePath: res.thumbTempFilePath || '',
duration: res.duration,
size: res.size
})
}
})
}
},
chooseImageCompat() {
uni.chooseImage({
count: 1,
sourceType: ['album', 'camera'],
sizeType: ['original', 'compressed'],
success: async (res) => {
const filePath = res.tempFilePaths && res.tempFilePaths[0]
if (!filePath) return
@ -1118,6 +1192,7 @@
const local = this.messages.find(item => item.clientMsgId === clientMsgId)
if (local) {
local.localState = 'sending'
local.poster = posterUrl || local.poster || ''
this.saveLocalMessages()
}
this.persistMessage({
@ -1128,6 +1203,7 @@
mediaDuration,
mediaSize: file.size,
mediaFormat: 'video',
poster: posterUrl,
mediaCheckUrl: posterUrl,
mediaCheckType: posterUrl ? 2 : undefined
})

217
package1/ieBrowser/index.vue

@ -213,23 +213,30 @@
<view class="companion-orb" v-else :class="matchedPerson.mode || currentMode">{{ matchedPerson.avatar }}</view>
</view>
<view class="match-name">{{ matchedPerson.name }}</view>
<view class="match-online" v-if="matchedPerson.lastActiveText">
<view class="online-dot" :class="{ on: matchedOnline }"></view>
<text>{{ matchedPerson.lastActiveText }}</text>
<view class="match-status-row">
<view class="match-online" v-if="matchedPerson.lastActiveText">
<view class="online-dot" :class="{ on: matchedOnline }"></view>
<text>{{ matchedPerson.lastActiveText }}</text>
</view>
<view class="match-intent" v-if="currentMatch && currentMatch.intentMatched">
也在找{{ intentLabel(currentMatch.intent) }}
</view>
<view class="match-mood" v-if="matchedPerson.moodText">
<text class="match-mood-icon">{{ matchedPerson.moodIcon }}</text>
<text>{{ matchedPerson.moodText }}</text>
</view>
</view>
<view class="match-intent" v-if="currentMatch && currentMatch.intentMatched">
TA 也在找{{ intentLabel(currentMatch.intent) }}
<view class="match-profile-card">
<view class="match-profile-row">
<view class="match-profile-chip match-mode-chip">{{ modeText(matchedPerson.mode) }}</view>
<view class="match-profile-chip">{{ genderText(matchedPerson.gender) }}</view>
<view class="match-profile-chip match-region" v-if="matchedPerson.regionName">📍 {{ matchedPerson.regionName }}</view>
</view>
<view class="match-tags" v-if="matchedPerson.tags && matchedPerson.tags.length">
<text v-for="tag in matchedPerson.tags" :key="tag"># {{ tag }}</text>
</view>
<view class="match-state-text">{{ matchedPerson.state }}</view>
</view>
<view class="match-mood" v-if="matchedPerson.moodText">
<text class="match-mood-icon">{{ matchedPerson.moodIcon }}</text>
<text>{{ matchedPerson.moodText }}</text>
</view>
<view class="match-state">{{ modeText(matchedPerson.mode) }} · {{ genderText(matchedPerson.gender) }}</view>
<view class="match-region" v-if="matchedPerson.regionName">📍 {{ matchedPerson.regionName }}</view>
<view class="match-tags" v-if="matchedPerson.tags && matchedPerson.tags.length">
<text v-for="tag in matchedPerson.tags" :key="tag"># {{ tag }}</text>
</view>
<view class="match-state-text">{{ matchedPerson.state }}</view>
<view class="match-quote">{{ matchedPerson.quote }}</view>
<view class="match-persona" v-if="matchedPerson.personaImages && matchedPerson.personaImages.length">
<view class="match-section-title">人格卡片</view>
@ -271,16 +278,20 @@
<view class="creating-progress"><view></view></view>
</view>
</view>
<ie-auth-dialog />
<common-loading />
</view>
</template>
<script>
import { ieHome, getIeUnreadCount, updateIeStatus, startIeMatch, matchIeProfile, getIeDailyQuestion, saveIeProfile } from '@/common/ieApi.js'
import { ensureIeVerifiedBeforeAction } from '@/common/ieRealNameAuth.js'
import IeBottomTab from '@/components/ie-bottom-tab/ie-bottom-tab.vue'
import IeBottomTab from '@/package1/components/ie-bottom-tab/ie-bottom-tab.vue'
import IeAuthDialog from '@/package1/components/ie-auth-dialog/ie-auth-dialog.vue'
import CommonLoading from '@/package1/components/common-loading/common-loading.vue'
export default {
components: { IeBottomTab },
components: { IeBottomTab, IeAuthDialog, CommonLoading },
data() {
return {
menuButtonInfo: { top: 44 },
@ -616,9 +627,9 @@
const home = await ieHome()
if (home && home.banned) {
this.redirectBannedIe(home.message)
return
return null
}
if (!home) return
if (!home) return null
this.profile = home.profile || {}
this.customCompanionIntent = this.profile.companionIntent || this.customCompanionIntent
this.editCompanionIntent = this.customCompanionIntent
@ -635,6 +646,7 @@
this.driftMessages = this.buildDriftMessages(home)
this.animateAwakeCount(this.awakeCount)
this.syncStatus()
return this.profile
},
buildDriftMessages(home = {}) {
const realItems = (home.companionIntents || []).filter(item => item && item.text).slice(0, 50).map((item, index) => ({
@ -2146,7 +2158,7 @@
.match-panel {
width: 100%;
max-height: 80vh;
max-height: 84vh;
border: 1rpx solid rgba(255, 255, 255, .9);
border-radius: 46rpx;
background: #ffffff;
@ -2157,7 +2169,7 @@
}
.match-scroll {
max-height: 80vh;
max-height: 84vh;
box-sizing: border-box;
background: linear-gradient(180deg, #ffffff 0%, #f7f9ff 100%);
}
@ -2203,7 +2215,7 @@
.match-body {
position: relative;
z-index: 2;
padding: 0 34rpx 34rpx;
padding: 0 34rpx 38rpx;
background: linear-gradient(180deg, #ffffff 0%, #f7f9ff 100%);
}
@ -2244,41 +2256,44 @@
}
.match-name {
margin-top: 18rpx;
margin-top: 14rpx;
text-align: center;
font-size: 36rpx;
line-height: 48rpx;
font-weight: 900;
color: #161b2e;
}
.match-intent {
margin: 12rpx auto 0;
width: fit-content;
padding: 8rpx 24rpx;
border-radius: 999rpx;
font-size: 23rpx;
font-weight: 800;
color: #ff5f3c;
background: rgba(255, 138, 84, .12);
border: 1rpx solid rgba(255, 138, 84, .32);
.match-status-row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12rpx;
margin: 16rpx auto 0;
padding: 0 10rpx;
}
.match-mood {
margin: 12rpx auto 0;
.match-intent,
.match-mood,
.match-online {
min-height: 46rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
width: fit-content;
max-width: 520rpx;
padding: 8rpx 22rpx;
box-sizing: border-box;
border-radius: 999rpx;
color: #5a55c8;
background: rgba(139, 124, 255, .1);
border: 1rpx solid rgba(139, 124, 255, .18);
font-size: 22rpx;
font-size: 21rpx;
line-height: 28rpx;
font-weight: 800;
}
.match-mood {
gap: 8rpx;
padding: 8rpx 20rpx;
color: #5d55d8;
background: linear-gradient(135deg, rgba(239, 242, 255, .96), rgba(232, 248, 255, .88));
border: 1rpx solid rgba(139, 124, 255, .22);
}
.match-mood-icon {
font-size: 24rpx;
line-height: 1;
@ -2296,17 +2311,18 @@
}
.match-online {
margin: 10rpx auto 0;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
width: fit-content;
padding: 6rpx 20rpx;
border-radius: 999rpx;
background: rgba(22, 27, 46, .05);
font-size: 22rpx;
color: rgba(22, 27, 46, .55);
padding: 8rpx 20rpx;
color: #526172;
background: rgba(247, 249, 255, .96);
border: 1rpx solid rgba(203, 212, 230, .8);
}
.match-intent {
padding: 8rpx 20rpx;
color: #b85a1d;
background: linear-gradient(135deg, rgba(255, 244, 224, .98), rgba(255, 230, 200, .88));
border: 1rpx solid rgba(255, 170, 92, .38);
}
.online-dot {
@ -2321,63 +2337,90 @@
box-shadow: 0 0 8rpx rgba(61, 220, 151, .8);
}
.match-state {
margin-top: 8rpx;
text-align: center;
color: rgba(22, 27, 46, .48);
font-size: 23rpx;
.match-profile-card {
margin: 22rpx 10rpx 0;
padding: 22rpx 22rpx 24rpx;
border-radius: 30rpx;
background:
radial-gradient(circle at 12% 0%, rgba(169, 255, 231, .28), transparent 180rpx),
linear-gradient(180deg, rgba(255, 255, 255, .96), rgba(247, 249, 255, .9));
border: 1rpx solid rgba(226, 232, 248, .92);
box-shadow: inset 0 1rpx 0 rgba(255, 255, 255, .96), 0 12rpx 30rpx rgba(96, 112, 160, .08);
}
.match-region {
width: fit-content;
max-width: 520rpx;
margin: 14rpx auto 0;
padding: 8rpx 20rpx;
.match-profile-row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12rpx;
}
.match-profile-chip {
min-height: 44rpx;
display: flex;
align-items: center;
padding: 6rpx 18rpx;
border-radius: 999rpx;
color: #6c69d8;
box-sizing: border-box;
color: #4d5a72;
background: rgba(238, 244, 255, .88);
font-size: 21rpx;
font-weight: 800;
}
.match-mode-chip {
color: #0e6a55;
background: rgba(169, 255, 231, .54);
}
.match-region {
max-width: 100%;
color: #5f58cc;
background: rgba(139, 124, 255, .1);
font-size: 22rpx;
font-weight: 700;
}
.match-tags {
margin-top: 16rpx;
padding: 0 34rpx;
margin-top: 18rpx;
padding: 0;
text-align: center;
}
.match-tags text {
display: inline-block;
height: 46rpx;
line-height: 46rpx;
padding: 0 18rpx;
margin: 0 8rpx 10rpx;
min-height: 42rpx;
line-height: 42rpx;
padding: 0 16rpx;
margin: 0 6rpx 10rpx;
border-radius: 999rpx;
color: #6c69d8;
background: rgba(139, 124, 255, .1);
font-size: 21rpx;
font-weight: 700;
color: #6f56c9;
background: rgba(139, 124, 255, .09);
font-size: 20rpx;
font-weight: 800;
}
.match-state-text {
margin-top: 8rpx;
text-align: center;
color: rgba(22, 27, 46, .54);
color: rgba(22, 27, 46, .58);
font-size: 23rpx;
line-height: 36rpx;
font-weight: 700;
}
.match-quote {
margin: 26rpx 34rpx 0;
padding: 26rpx;
border-radius: 8rpx 28rpx 28rpx 28rpx;
color: rgba(22, 27, 46, .72);
background: rgba(139, 124, 255, .08);
font-size: 26rpx;
line-height: 42rpx;
margin: 22rpx 10rpx 0;
padding: 24rpx 26rpx;
border-radius: 28rpx;
color: #454b63;
background: linear-gradient(135deg, rgba(244, 248, 255, .96), rgba(240, 255, 250, .88));
border: 1rpx solid rgba(226, 232, 248, .9);
font-size: 25rpx;
line-height: 40rpx;
font-weight: 700;
}
.match-persona {
margin: 22rpx 34rpx 0;
margin: 22rpx 10rpx 0;
}
.match-section-title {
@ -2404,7 +2447,7 @@
.match-actions {
display: flex;
gap: 18rpx;
margin: 30rpx 34rpx 34rpx;
margin: 30rpx 10rpx 8rpx;
}
.ghost-btn,

2
package1/ieBrowser/messages.vue

@ -50,7 +50,7 @@
<script>
import { getIeUnreadCount, pageIeRecords, deleteIeRecord } from '@/common/ieApi.js'
import IeBottomTab from '@/components/ie-bottom-tab/ie-bottom-tab.vue'
import IeBottomTab from '@/package1/components/ie-bottom-tab/ie-bottom-tab.vue'
export default {
components: { IeBottomTab },

6
package1/ieBrowser/mySpace.vue

@ -67,6 +67,8 @@
<view class="composer-submit" :class="{ disabled: publishing }" @tap="submitMoment">发布</view>
</view>
</view>
<ie-auth-dialog />
<common-loading />
</view>
</template>
@ -74,8 +76,11 @@
import { ieHome, getIeProfile, pageIeMoments, publishIeMoment, deleteIeMoment } from '@/common/ieApi.js'
import { ensureIeVerifiedBeforeAction } from '@/common/ieRealNameAuth.js'
import tui from '@/common/httpRequest.js'
import IeAuthDialog from '@/package1/components/ie-auth-dialog/ie-auth-dialog.vue'
import CommonLoading from '@/package1/components/common-loading/common-loading.vue'
export default {
components: { IeAuthDialog, CommonLoading },
data() {
return {
menuButtonInfo: { top: 44 },
@ -124,6 +129,7 @@
const home = await ieHome().catch(() => null)
const profile = home && home.profile ? home.profile : await getIeProfile()
if (profile) this.profile = profile
return this.profile
},
async loadMoments(reset, force = false) {
if (this.loadingMoments && !force) return

2
package1/ieBrowser/universe.vue

@ -181,7 +181,7 @@
<script>
import { getIeProfile, saveIeProfile, getIeUnreadCount } from '@/common/ieApi.js'
import IeBottomTab from '@/components/ie-bottom-tab/ie-bottom-tab.vue'
import IeBottomTab from '@/package1/components/ie-bottom-tab/ie-bottom-tab.vue'
import tui from '@/common/httpRequest.js'
export default {

111
package1/order/orderDetail.vue

@ -194,6 +194,9 @@
</view>
</view>
<view class="btn-box">
<view class="btn" v-if="orderDetail.status == 0" @tap="openPayPopup">
立即支付
</view>
<view class="btn"
v-if="orderDetail.status == 3 && orderDetail.deliveryType == 2 && orderDetail.userRequireMake == null && orderDetail.otherOrder == null"
@tap="openCode">
@ -717,6 +720,44 @@
</view>
</uni-popup>
<!-- 待支付订单支付弹出层 -->
<uni-popup ref="payPopup" background-color="#fff">
<view class="pay-popup" style="height: 580rpx;background: #fff;border-radius: 40rpx 40rpx 0 0;">
<view class="content" style="height: 100%;margin-top:0;top:0">
<view class="box1">
<view style="height: 70rpx;line-height: 70rpx;text-align: center;">
微信支付
</view>
<view
style="height: 90rpx;line-height: 90rpx;text-align: center;font-weight: 700;font-size: 30rpx;">
<text style="font-size: 60rpx;">{{payAmountText}}</text>
</view>
</view>
<view class="btn"
style="background: linear-gradient(90deg, rgba(227, 255, 150, 1), rgba(166, 255, 234, 1));width: 90%;height: 100rpx;border-radius: 100rpx;text-align: center;font-size: 28rpx;font-weight: 700;line-height: 100rpx;margin: 40rpx auto;"
@tap="wxPayment">
确认付款
</view>
<view class="box1" style="display: flex;padding: 40rpx;">
<view style="flex: 1;">
<view style="height: 42rpx;line-height: 42rpx;display: flex;">
<img src="https://jewel-shop.oss-cn-beijing.aliyuncs.com/4c8e0cc311db4d38ab43e019673c4b8c.png"
alt="" style="width: 42rpx;height: 42rpx;margin-right: 20rpx;" />
<text style="font-size: 30rpx;font-weight: 700;">微信支付</text>
</view>
<view style="text-align: right;margin-left: 60rpx;color: #777;width: 146rpx;">
使用微信支付
</view>
</view>
<view style="width: 36rpx;padding-top: 20rpx;">
<img src="https://jewel-shop.oss-cn-beijing.aliyuncs.com/02bff7edc4e04caaa1868955ff684f1f.png"
alt="" style="width: 36rpx;height: 36rpx;" />
</view>
</view>
</view>
</view>
</uni-popup>
<uni-popup ref="returnPopupBuy" background-color="#fff" style="height: 1600rpx !important;">
<view class="guize-list" style="height: 600rpx;width:300rpx;padding: 20rpx;background: #fff;">
@ -825,6 +866,12 @@
isRefreshing: false
}
},
computed: {
payAmountText() {
let amount = Number(this.orderDetail.totalAmount || 0);
return amount ? amount.toFixed(2) : '0.00';
}
},
components: {
},
@ -1407,6 +1454,70 @@
this.additionalFee = '';
this.$refs.addFeePopup.open('center');
},
openPayPopup() {
if (!this.orderDetail.id || !this.orderDetail.totalAmount) {
this.tui.toast('订单金额异常,无法支付');
return;
}
this.$refs.payPopup.open('bottom');
},
wxPayment() {
let that = this;
if (!this.orderDetail.id || !this.orderDetail.totalAmount) return;
let amountInCents = Math.round(Number(this.orderDetail.totalAmount) * 100);
this.tui.request("/api/wechat/pay/unified-order", "POST", {
openid: uni.getStorageSync('miniProgramOpenid') || 'test-openid',
amount: amountInCents,
description: '商城订单',
outTradeNo: this.orderDetail.id
}, false, false).then((res) => {
if (res.code == 200) {
uni.requestPayment({
provider: 'wxpay',
timeStamp: res.timeStamp,
nonceStr: res.nonceStr,
package: res.package,
signType: res.signType,
paySign: res.paySign,
success: function(res2) {
that.handlePaymentSuccess();
},
fail: function(err) {
that.tui.toast("支付失败或取消");
}
});
} else {
if (res.code == 404 || res.code == 500 || res.code == 400 || !res.code) {
let workerId = that.orderDetail.deliveryInfo && that.orderDetail.deliveryInfo.workerId ? that.orderDetail.deliveryInfo.workerId : '';
that.tui.request(
`/hiver/order/payMallOrderSuccess?orderId=${that.orderDetail.id}&workerId=${workerId}`,
"POST", {}, false, false).then(res2 => {
that.$refs.payPopup.close();
uni.showToast({
title: '支付成功(模拟)',
icon: 'none'
});
setTimeout(() => {
that.handlePaymentSuccess();
}, 1500);
}).catch(e => {
uni.showToast({
title: '请求失败',
icon: 'none'
})
});
} else {
that.tui.toast(res.message);
}
}
})
},
handlePaymentSuccess() {
uni.redirectTo({
url: '/package1/order/orderConfirm?id=' + this.orderDetail.id + '&amount=' + this.orderDetail.totalAmount
});
},
formatMoneyInput(value) {
value = String(value || '');
value = value.replace(/[^\d.]/g, '');

163
package1/planet/index.vue

@ -68,8 +68,8 @@
id="guide-tasks"
class="flow-card"
:class="{'guide-focus-target': guideFocus.key === 'tasks'}">
<view class="flow-title">玩法很简单</view>
<view class="flow-sub">先赚券再参与玩法最后等开奖</view>
<view class="flow-title">点这里完成每日收券任务</view>
<view class="flow-sub">点击下方按钮先收券再投券开奖</view>
<view class="flow-steps">
<view
class="flow-step"
@ -81,7 +81,7 @@
<view class="flow-name">{{item.name}}</view>
<view class="flow-desc">{{item.desc}}</view>
</view>
<view class="flow-go">{{item.action}}</view>
<view class="flow-go">点这里{{item.action}}</view>
<view v-if="i < flowButtons.length - 1" class="flow-arrow"></view>
</view>
</view>
@ -216,6 +216,17 @@
</view>
</view>
</view>
<view class="drawer-play-title">更多收券玩法</view>
<view class="drawer-play-list">
<view class="drawer-play-item" v-for="entry in drawerPlayEntries" :key="entry.key" @tap="goDrawerEntry(entry)">
<view class="drawer-play-icon">{{entry.icon}}</view>
<view class="drawer-play-copy">
<view class="drawer-play-name">{{entry.name}}</view>
<view class="drawer-play-desc">{{entry.desc}}</view>
</view>
<view class="drawer-play-go">去玩</view>
</view>
</view>
</view>
</view>
@ -390,7 +401,7 @@
{
key: 'tasks',
name: '获得星球券',
desc: '打开日任务',
desc: '打开日任务',
action: '收券'
},
{
@ -401,6 +412,28 @@
}
]
},
drawerPlayEntries() {
return [
{
key: 'adventure',
icon: '赛',
name: '学院排位赛赢现金',
desc: '为学院冲榜,个人排名也有奖励'
},
{
key: 'pk',
icon: 'PK',
name: '好友PK赛赢星球券',
desc: '开擂台邀请好友,同关卡拼手速'
},
{
key: 'more',
icon: '图',
name: '星球补给地图',
desc: '下单得券、补给成长、每日顺手经营'
}
]
},
gloryPrizes() {
return [
{ rank: 1, amount: 5 },
@ -778,6 +811,21 @@
this.goRank()
}
},
goDrawerEntry(entry) {
this.taskDrawerVisible = false
if (!entry) return
if (entry.key === 'adventure') {
this.goAdventure()
return
}
if (entry.key === 'pk') {
this.goArena()
return
}
if (entry.key === 'more') {
this.goMore()
}
},
startGuideSteps() {
if (!this.guideTasks.length) return
const record = uni.getStorageSync(this.guideTaskKey())
@ -1692,6 +1740,29 @@
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.1);
}
.flow-card {
position: relative;
background:
radial-gradient(circle at 92% 8%, rgba(255,184,77,0.24), transparent 34%),
linear-gradient(155deg, rgba(255,255,255,0.96), rgba(239,255,250,0.9));
border-color: rgba(53,214,166,0.28);
box-shadow: 0 22rpx 54rpx rgba(53,214,166,0.16);
}
.flow-card:after {
content: '点按钮完成任务';
position: absolute;
right: 22rpx;
top: 24rpx;
padding: 8rpx 16rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #FFB84D, #35D6A6);
color: #fff;
font-size: 20rpx;
font-weight: 900;
box-shadow: 0 10rpx 22rpx rgba(255,184,77,0.22);
}
.flow-title {
color: #12342F;
font-size: 30rpx;
@ -1730,6 +1801,12 @@
overflow: visible;
}
.flow-step:active,
.task-item:active,
.drawer-play-item:active {
transform: scale(0.97);
}
.flow-dot {
position: absolute;
left: 16rpx;
@ -1767,13 +1844,13 @@
.flow-go {
margin-top: 10rpx;
align-self: flex-end;
padding: 6rpx 14rpx;
padding: 10rpx 18rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
background: linear-gradient(135deg, #FFB84D, #35D6A6);
color: #FFFFFF;
font-size: 19rpx;
font-size: 20rpx;
font-weight: 900;
box-shadow: 0 8rpx 18rpx rgba(53,214,166,0.16);
box-shadow: 0 10rpx 22rpx rgba(255,184,77,0.22);
}
.flow-arrow {
@ -1939,6 +2016,76 @@
color: #8EA4A0;
}
.drawer-play-title {
margin: 26rpx 2rpx 14rpx;
color: #12342F;
font-size: 28rpx;
font-weight: 900;
}
.drawer-play-list {
display: flex;
flex-direction: column;
gap: 14rpx;
}
.drawer-play-item {
display: flex;
align-items: center;
gap: 16rpx;
padding: 18rpx;
border-radius: 28rpx;
background:
radial-gradient(circle at 88% 12%, rgba(255,184,77,0.18), transparent 30%),
linear-gradient(135deg, rgba(255,255,255,0.94), rgba(235,255,249,0.82));
border: 2rpx solid rgba(53,214,166,0.18);
box-shadow: 0 12rpx 28rpx rgba(53,214,166,0.1);
transition: transform .16s ease;
}
.drawer-play-icon {
width: 68rpx;
height: 68rpx;
line-height: 68rpx;
text-align: center;
border-radius: 24rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #fff;
font-size: 24rpx;
font-weight: 900;
flex-shrink: 0;
}
.drawer-play-copy {
flex: 1;
min-width: 0;
}
.drawer-play-name {
color: #12342F;
font-size: 26rpx;
font-weight: 900;
}
.drawer-play-desc {
margin-top: 6rpx;
color: #7E9691;
font-size: 21rpx;
font-weight: 800;
line-height: 1.35;
}
.drawer-play-go {
flex-shrink: 0;
padding: 12rpx 18rpx;
border-radius: 999rpx;
color: #fff;
background: linear-gradient(135deg, #FFB84D, #35D6A6);
font-size: 22rpx;
font-weight: 900;
box-shadow: 0 10rpx 22rpx rgba(255,184,77,0.18);
}
.play-card {
background: rgba(255,255,255,0.62);
box-shadow: 0 14rpx 30rpx rgba(18,52,47,0.06);

27
package1/planet/pkHall.vue

@ -80,7 +80,7 @@
<view class="modal-card">
<view class="modal-title">创建星球擂台</view>
<input class="field" v-model="form.roomName" maxlength="20" placeholder="房间名称" />
<input class="field" v-model.number="form.maxPlayers" type="number" min="2" max="8" placeholder="人数 2-8" />
<input class="field" v-model.number="form.maxPlayers" type="number" min="2" max="8" placeholder="人数 2-8" @blur="normalizeMaxPlayers" />
<input class="field" v-model.number="form.tickets" type="number" min="1" placeholder="入场券数量" @blur="normalizeTickets" />
<input class="field" v-model="form.password" placeholder="私密房密码,不填则公开" />
<view class="modal-actions">
@ -334,6 +334,12 @@
this.shareRoom = room
},
createRoom() {
const rawMaxPlayers = parseInt(this.form.maxPlayers || 0, 10)
if (Number.isNaN(rawMaxPlayers) || rawMaxPlayers < 2 || rawMaxPlayers > 8) {
this.tui.toast('限制人数必须在2-8人之间')
return
}
const maxPlayers = this.normalizeMaxPlayers()
const tickets = this.normalizeTickets()
if (this.myTicketCount === null) {
this.loadTicketCount().then(() => {
@ -352,7 +358,7 @@
avatar: this.avatar,
college: this.college,
roomName: this.form.roomName,
maxPlayers: this.form.maxPlayers,
maxPlayers,
publicFlag: this.form.password ? 0 : 1,
password: this.form.password,
tickets: tickets
@ -370,6 +376,23 @@
this.joinRoom(res.result)
})
},
normalizeMaxPlayers() {
const maxPlayers = parseInt(this.form.maxPlayers || 2, 10)
if (Number.isNaN(maxPlayers)) {
this.form.maxPlayers = 2
return 2
}
if (maxPlayers < 2) {
this.form.maxPlayers = 2
return 2
}
if (maxPlayers > 8) {
this.form.maxPlayers = 8
return 8
}
this.form.maxPlayers = maxPlayers
return maxPlayers
},
normalizeTickets() {
const tickets = parseInt(this.form.tickets || 1, 10)
this.form.tickets = Number.isNaN(tickets) || tickets < 1 ? 1 : tickets

Loading…
Cancel
Save