feat(pwuniapp): 在HBuilderX原生工程中实现电竞暗夜流光全套静态UI页面及4位门票核销
这个提交包含在:
9
pwuniapp/.editorconfig
普通文件
9
pwuniapp/.editorconfig
普通文件
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
3
pwuniapp/.gitignore
vendored
普通文件
3
pwuniapp/.gitignore
vendored
普通文件
@@ -0,0 +1,3 @@
|
||||
unpackage/
|
||||
.DS_Store
|
||||
.hbuilderx/
|
||||
43
pwuniapp/App.vue
普通文件
43
pwuniapp/App.vue
普通文件
@@ -0,0 +1,43 @@
|
||||
<script>
|
||||
export default {
|
||||
onLaunch: function () {
|
||||
console.log('App Launch: 电竞陪玩小程序初始化');
|
||||
},
|
||||
onShow: function () {
|
||||
console.log('App Show');
|
||||
},
|
||||
onHide: function () {
|
||||
console.log('App Hide');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* 全局暗黑电竞基础重置 */
|
||||
page {
|
||||
background-color: #0A0F1D;
|
||||
color: #F8FAFC;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'PingFang SC', sans-serif;
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
view, text, image, input, button {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 触控反馈标准类 (满足 UI/UX Pro Max 规范) */
|
||||
.btn-active:active, .card-active:active {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.9;
|
||||
transition: transform 100ms ease-out, opacity 100ms ease-out;
|
||||
}
|
||||
|
||||
/* 隐藏移动端滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
223
pwuniapp/common/mock.js
普通文件
223
pwuniapp/common/mock.js
普通文件
@@ -0,0 +1,223 @@
|
||||
export const GAMES_LIST = [
|
||||
{ id: 'all', name: '全部', icon: '🎮', hot: true },
|
||||
{ id: 'wzry', name: '王者荣耀', icon: '👑', hot: true },
|
||||
{ id: 'hpjy', name: '和平精英', icon: '🎯', hot: true },
|
||||
{ id: 'lol', name: '英雄联盟', icon: '⚔️', hot: true },
|
||||
{ id: 'val', name: '无畏契约', icon: '🔫', hot: true },
|
||||
{ id: 'yjjw', name: '永劫无间', icon: '🗡️', hot: false },
|
||||
{ id: 'pubg', name: '绝地求生', icon: '🏆', hot: false },
|
||||
];
|
||||
|
||||
export const BANNERS = [
|
||||
{ id: '1', title: '周末车队狂欢 | AA制五排冲分', tag: '热门活动', bg: 'linear-gradient(135deg, #4F46E5 0%, #7C3AED 100%)' },
|
||||
{ id: '2', title: '新用户专享 | 首单立减20元', tag: '新人福利', bg: 'linear-gradient(135deg, #059669 0%, #10B981 100%)' },
|
||||
{ id: '3', title: '全国水友杯内战 | 项目制万元奖池', tag: '巅峰赛事', bg: 'linear-gradient(135deg, #D97706 0%, #F59E0B 100%)' },
|
||||
];
|
||||
|
||||
export const EXPERTS_LIST = [
|
||||
{
|
||||
id: 'exp_101',
|
||||
nickname: '月色浅浅',
|
||||
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=200&h=200&fit=crop&crop=faces',
|
||||
coverImage: 'https://images.unsplash.com/photo-1542751371-adc38448a05e?w=800&h=400&fit=crop',
|
||||
gender: 'female',
|
||||
age: 21,
|
||||
game: '王者荣耀',
|
||||
rankTag: '百星王者 · 国服瑶妹',
|
||||
levelTag: '金牌导师',
|
||||
price: 35,
|
||||
rating: 4.9,
|
||||
orderCount: 1280,
|
||||
isOnline: true,
|
||||
voiceDuration: 12,
|
||||
bio: '全天在线可连麦,心态超好,软妹音/御姐音切换,带你轻松摘星~',
|
||||
photos: [
|
||||
'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=500&fit=crop',
|
||||
'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=500&fit=crop',
|
||||
],
|
||||
services: [
|
||||
{ id: 'srv_1', title: '王者荣耀 · 排位包胜率', price: 35, unit: '小时', desc: '不包赢不退,高效稳定冲星' },
|
||||
{ id: 'srv_2', title: '王者荣耀 · 娱乐连麦陪练', price: 28, unit: '小时', desc: '温柔互动,欢乐开黑' },
|
||||
],
|
||||
reviews: [
|
||||
{ id: 'r1', userNickname: '电竞刺客', userAvatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=100', rating: 5, tags: ['声音甜美', '技术过硬'], content: '小姐姐声音太好听了,操作也很稳,连赢5把!', time: '2小时前' },
|
||||
{ id: 'r2', userNickname: '峡谷追梦人', userAvatar: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?w=100', rating: 5, tags: ['准时上号', '耐心教学'], content: '很耐心教辅助意识,体验绝佳。', time: '昨天' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'exp_102',
|
||||
nickname: '夜鹰狙神',
|
||||
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&h=200&fit=crop&crop=faces',
|
||||
coverImage: 'https://images.unsplash.com/photo-1511512578047-dfb367046420?w=800&h=400&fit=crop',
|
||||
gender: 'male',
|
||||
age: 23,
|
||||
game: '和平精英',
|
||||
rankTag: '无敌战神 · 15KD',
|
||||
levelTag: '顶流大神',
|
||||
price: 45,
|
||||
rating: 5.0,
|
||||
orderCount: 2360,
|
||||
isOnline: true,
|
||||
voiceDuration: 18,
|
||||
bio: '前职业青训选手,意识拉满,带粉乱杀,绝不白给!',
|
||||
photos: [
|
||||
'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=500&fit=crop',
|
||||
'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=500&fit=crop',
|
||||
],
|
||||
services: [
|
||||
{ id: 'srv_3', title: '和平精英 · 战神四排车队', price: 45, unit: '小时', desc: '战神指引,全程高能护航' },
|
||||
{ id: 'srv_4', title: '吃鸡指导 · 枪法与身法特训', price: 60, unit: '小时', desc: '1对1定制身法特训' },
|
||||
],
|
||||
reviews: [
|
||||
{ id: 'r3', userNickname: 'AWM战狂', userAvatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=100', rating: 5, tags: ['枪法如神', '落地包稳'], content: '真的太猛了,连吃三把鸡,KD直接飙升!', time: '前天' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'exp_103',
|
||||
nickname: '软软喵',
|
||||
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=200&h=200&fit=crop&crop=faces',
|
||||
coverImage: 'https://images.unsplash.com/photo-1538481199705-c710c4e965fc?w=800&h=400&fit=crop',
|
||||
gender: 'female',
|
||||
age: 20,
|
||||
game: '英雄联盟',
|
||||
rankTag: '傲世宗师 · 中单法王',
|
||||
levelTag: '新星大神',
|
||||
price: 30,
|
||||
rating: 4.8,
|
||||
orderCount: 650,
|
||||
isOnline: false,
|
||||
voiceDuration: 9,
|
||||
bio: '峡谷高分妹子,阿狸/辛德拉本命,能C能辅,幽默开心果~',
|
||||
photos: ['https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=500&fit=crop'],
|
||||
services: [
|
||||
{ id: 'srv_5', title: 'LOL峡谷排位双排', price: 30, unit: '小时', desc: '双排上分,稳定Carry' },
|
||||
],
|
||||
reviews: [],
|
||||
},
|
||||
{
|
||||
id: 'exp_104',
|
||||
nickname: '瞬移者雷克',
|
||||
avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=200&h=200&fit=crop&crop=faces',
|
||||
coverImage: 'https://images.unsplash.com/photo-1550745165-9bc0b252726f?w=800&h=400&fit=crop',
|
||||
gender: 'male',
|
||||
age: 22,
|
||||
game: '无畏契约',
|
||||
rankTag: '赋能战魂 · 芮娜绝活',
|
||||
levelTag: '优质陪练',
|
||||
price: 40,
|
||||
rating: 4.9,
|
||||
orderCount: 890,
|
||||
isOnline: true,
|
||||
voiceDuration: 15,
|
||||
bio: '瓦罗兰特高分车头,定位准身法快,报点清晰,带你上神话!',
|
||||
photos: ['https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=500&fit=crop'],
|
||||
services: [
|
||||
{ id: 'srv_6', title: '无畏契约冲分排位', price: 40, unit: '小时', desc: '带队突破,绝不拖泥带水' },
|
||||
],
|
||||
reviews: [],
|
||||
},
|
||||
];
|
||||
|
||||
export const ACTIVITIES_LIST = [
|
||||
{
|
||||
id: 'act_101',
|
||||
title: '【AA制五排】今晚8点王者百星车队冲分',
|
||||
game: '王者荣耀',
|
||||
payType: 'AA',
|
||||
totalFee: 120,
|
||||
aaFee: 30,
|
||||
maxParticipants: 4,
|
||||
currentParticipants: 3,
|
||||
startTime: '今晚 20:00',
|
||||
location: '开黑语音1号频道',
|
||||
creatorNickname: '月色浅浅',
|
||||
creatorAvatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100',
|
||||
status: 'RECRUITING',
|
||||
participants: [
|
||||
{ id: 'p1', nickname: '峡谷追梦', avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=100', verifyCode: '8392', checkedIn: false },
|
||||
{ id: 'p2', nickname: '无敌小刺客', avatar: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?w=100', verifyCode: '6147', checkedIn: true },
|
||||
{ id: 'p3', nickname: '快乐小风男', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100', verifyCode: '9025', checkedIn: false },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'act_102',
|
||||
title: '【项目制·粉丝福利】夜鹰带队水友吃鸡四排',
|
||||
game: '和平精英',
|
||||
payType: 'PROJECT',
|
||||
totalFee: 300,
|
||||
aaFee: 0,
|
||||
maxParticipants: 3,
|
||||
currentParticipants: 3,
|
||||
startTime: '周六 15:00',
|
||||
location: 'YY频道 668899',
|
||||
creatorNickname: '夜鹰狙神',
|
||||
creatorAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',
|
||||
status: 'RUNNING',
|
||||
participants: [
|
||||
{ id: 'p4', nickname: '绝地菜鸟', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100', verifyCode: '2481', checkedIn: true },
|
||||
{ id: 'p5', nickname: '伏地魔本魔', avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100', verifyCode: '7390', checkedIn: false },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'act_103',
|
||||
title: '【AA制】无畏契约五黑内战水友欢乐局',
|
||||
game: '无畏契约',
|
||||
payType: 'AA',
|
||||
totalFee: 160,
|
||||
aaFee: 20,
|
||||
maxParticipants: 8,
|
||||
currentParticipants: 5,
|
||||
startTime: '周日 19:30',
|
||||
location: '自定义内战房间',
|
||||
creatorNickname: '瞬移者雷克',
|
||||
creatorAvatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=100',
|
||||
status: 'RECRUITING',
|
||||
participants: [],
|
||||
},
|
||||
];
|
||||
|
||||
export const ORDERS_LIST = [
|
||||
{
|
||||
id: 'ord_1',
|
||||
orderSn: 'PW20260916001',
|
||||
expert: { id: 'exp_101', nickname: '月色浅浅', avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100', game: '王者荣耀' },
|
||||
serviceTitle: '王者荣耀 · 排位包胜率',
|
||||
quantity: 2,
|
||||
totalAmount: 70,
|
||||
status: 'IN_SERVICE',
|
||||
createdAt: '2026-09-16 15:30',
|
||||
},
|
||||
{
|
||||
id: 'ord_2',
|
||||
orderSn: 'PW20260915088',
|
||||
expert: { id: 'exp_102', nickname: '夜鹰狙神', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100', game: '和平精英' },
|
||||
serviceTitle: '和平精英 · 战神四排车队',
|
||||
quantity: 1,
|
||||
totalAmount: 45,
|
||||
status: 'COMPLETED',
|
||||
createdAt: '2026-09-15 19:00',
|
||||
},
|
||||
{
|
||||
id: 'ord_3',
|
||||
orderSn: 'PW20260914032',
|
||||
expert: { id: 'exp_103', nickname: '软软喵', avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100', game: '英雄联盟' },
|
||||
serviceTitle: 'LOL峡谷排位双排',
|
||||
quantity: 3,
|
||||
totalAmount: 90,
|
||||
status: 'PENDING_PAY',
|
||||
createdAt: '2026-09-14 21:10',
|
||||
},
|
||||
];
|
||||
|
||||
export const USER_PROFILE = {
|
||||
id: 'u_888',
|
||||
nickname: '超能电竞玩家',
|
||||
avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=200&h=200&fit=crop&crop=faces',
|
||||
gender: 'male',
|
||||
role: 'PLAYER',
|
||||
balance: 380.5,
|
||||
frozenBalance: 0,
|
||||
commission: 128.0,
|
||||
invitedCount: 16,
|
||||
isCertifiedExpert: false,
|
||||
};
|
||||
20
pwuniapp/index.html
普通文件
20
pwuniapp/index.html
普通文件
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
24
pwuniapp/main.js
普通文件
24
pwuniapp/main.js
普通文件
@@ -0,0 +1,24 @@
|
||||
import App from './App'
|
||||
|
||||
// #ifndef VUE3
|
||||
import Vue from 'vue'
|
||||
import './uni.promisify.adaptor'
|
||||
Vue.config.productionTip = false
|
||||
App.mpType = 'app'
|
||||
const app = new Vue({
|
||||
...App
|
||||
})
|
||||
app.$mount()
|
||||
// #endif
|
||||
|
||||
// #ifdef VUE3
|
||||
import {
|
||||
createSSRApp
|
||||
} from 'vue'
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
return {
|
||||
app
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
69
pwuniapp/manifest.json
普通文件
69
pwuniapp/manifest.json
普通文件
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name" : "pwuniapp",
|
||||
"appid" : "__UNI__02D4155",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules" : {},
|
||||
/* 应用发布信息 */
|
||||
"distribute" : {
|
||||
/* android打包配置 */
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios" : {},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs" : {}
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "",
|
||||
"setting" : {
|
||||
"urlCheck" : false
|
||||
},
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"vueVersion" : "3"
|
||||
}
|
||||
106
pwuniapp/pages.json
普通文件
106
pwuniapp/pages.json
普通文件
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "电竞陪玩大厅",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "组队活动大厅",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "活动详情",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/activity/my-ticket",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的活动门票",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/expert/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "陪玩大神主页",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/create",
|
||||
"style": {
|
||||
"navigationBarTitleText": "确认预约订单",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的订单",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人中心",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "电竞陪玩",
|
||||
"navigationBarBackgroundColor": "#0A0F1D",
|
||||
"backgroundColor": "#0A0F1D"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#94A3B8",
|
||||
"selectedColor": "#6366F1",
|
||||
"backgroundColor": "#151D30",
|
||||
"borderStyle": "black",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/activity/list",
|
||||
"text": "活动项目"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/order/list",
|
||||
"text": "订单中心"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/user/index",
|
||||
"text": "我的"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
474
pwuniapp/pages/activity/detail.vue
普通文件
474
pwuniapp/pages/activity/detail.vue
普通文件
@@ -0,0 +1,474 @@
|
||||
<template>
|
||||
<view class="detail-container">
|
||||
<!-- 头部活动基本信息 -->
|
||||
<view class="hero-card">
|
||||
<view class="badge-row">
|
||||
<text class="game-tag">{{ activity.game }}</text>
|
||||
<text class="mode-tag" :class="activity.payType === 'AA' ? 'aa-mode' : 'project-mode'">
|
||||
{{ activity.payType === 'AA' ? 'AA制平摊车队' : '项目制悬赏' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="act-title">{{ activity.title }}</view>
|
||||
|
||||
<!-- 费用看板 -->
|
||||
<view class="fee-board">
|
||||
<view class="fee-col">
|
||||
<text class="col-label">总费用</text>
|
||||
<text class="col-val">¥{{ activity.totalFee }}</text>
|
||||
</view>
|
||||
<view class="fee-divider"></view>
|
||||
<view class="fee-col">
|
||||
<text class="col-label">{{ activity.payType === 'AA' ? '人均分摊' : '奖金池' }}</text>
|
||||
<text class="col-val highlight">
|
||||
¥{{ activity.payType === 'AA' ? activity.aaFee : activity.totalFee }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="fee-divider"></view>
|
||||
<view class="fee-col">
|
||||
<text class="col-label">招募席位</text>
|
||||
<text class="col-val">{{ activity.currentParticipants }}/{{ activity.maxParticipants }}人</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 房主与开黑信息 -->
|
||||
<view class="section-card">
|
||||
<view class="sec-header">
|
||||
<text class="sec-title">开黑阵地与房主</text>
|
||||
</view>
|
||||
<view class="creator-box">
|
||||
<image class="creator-avatar" :src="activity.creatorAvatar" mode="aspectFill" />
|
||||
<view class="creator-info">
|
||||
<view class="creator-name-row">
|
||||
<text class="creator-name">{{ activity.creatorNickname }}</text>
|
||||
<text class="host-badge">活动房主</text>
|
||||
</view>
|
||||
<text class="creator-sub">前职业选手 · 胜率保障车头</text>
|
||||
</view>
|
||||
<button class="btn-chat btn-active" @click="chatWithHost">私聊</button>
|
||||
</view>
|
||||
|
||||
<view class="info-list">
|
||||
<view class="info-row">
|
||||
<text class="info-label">开始时间</text>
|
||||
<text class="info-val">{{ activity.startTime }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">语音频道</text>
|
||||
<view class="copy-val" @click="copyLocation">
|
||||
<text class="info-val">{{ activity.location }}</text>
|
||||
<text class="copy-btn">复制</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">核销机制</text>
|
||||
<text class="info-val green-text">入队出示4位动态门票码核销</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 已报名队友成员列表 -->
|
||||
<view class="section-card">
|
||||
<view class="sec-header">
|
||||
<text class="sec-title">已报名队友 ({{ activity.participants ? activity.participants.length : 0 }})</text>
|
||||
<text class="sec-sub">满员即锁车</text>
|
||||
</view>
|
||||
<view class="member-list" v-if="activity.participants && activity.participants.length > 0">
|
||||
<view v-for="(p, index) in activity.participants" :key="p.id" class="member-item">
|
||||
<image class="member-avatar" :src="p.avatar" mode="aspectFill" />
|
||||
<view class="member-info">
|
||||
<text class="member-name">{{ p.nickname }}</text>
|
||||
<text class="member-seat">#{{ index + 1 }}号位</text>
|
||||
</view>
|
||||
<view class="checkin-badge" :class="{ checked: p.checkedIn }">
|
||||
{{ p.checkedIn ? '已核销入队' : '已购票待验' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty-members">
|
||||
<text>暂无其他队员,快抢先上车!</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="ticket-link btn-active" @click="goToMyTicket">
|
||||
<text class="ticket-icon">🎟️</text>
|
||||
<text class="ticket-text">我的门票</text>
|
||||
</view>
|
||||
<button class="btn-join btn-active" @click="handleJoin">
|
||||
立即上车 (¥{{ activity.payType === 'AA' ? activity.aaFee : 0 }})
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ACTIVITIES_LIST } from '@/common/mock.js';
|
||||
|
||||
const activity = ref(ACTIVITIES_LIST[0]);
|
||||
|
||||
onLoad((query) => {
|
||||
if (query && query.id) {
|
||||
const found = ACTIVITIES_LIST.find(a => a.id === query.id);
|
||||
if (found) {
|
||||
activity.value = found;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const copyLocation = () => {
|
||||
uni.setClipboardData({
|
||||
data: activity.value.location,
|
||||
success: () => {
|
||||
uni.showToast({ title: '语音频道已复制', icon: 'success' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const chatWithHost = () => {
|
||||
uni.showToast({ title: '已建立与房主的加密通道', icon: 'none' });
|
||||
};
|
||||
|
||||
const goToMyTicket = () => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/activity/my-ticket?id=${activity.value.id}`
|
||||
});
|
||||
};
|
||||
|
||||
const handleJoin = () => {
|
||||
uni.showModal({
|
||||
title: '确认报名',
|
||||
content: `报名该【${activity.value.payType === 'AA' ? 'AA制' : '项目制'}】活动,需支付分摊款 ¥${activity.value.aaFee}。支付成功后自动生成4位入队核销码。`,
|
||||
confirmText: '微信支付',
|
||||
confirmColor: '#6366F1',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '报名成功!已生成门票', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
goToMyTicket();
|
||||
}, 1200);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding: 24rpx 32rpx 160rpx;
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
background: linear-gradient(135deg, #1E1B4B 0%, #151D30 100%);
|
||||
border: 1px solid rgba(99, 102, 241, 0.25);
|
||||
border-radius: 28rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.badge-row {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.game-tag {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #F8FAFC;
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.mode-tag {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 600;
|
||||
|
||||
&.aa-mode {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: #10B981;
|
||||
border: 1px solid rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
&.project-mode {
|
||||
background: rgba(245, 158, 11, 0.2);
|
||||
color: #F59E0B;
|
||||
border: 1px solid rgba(245, 158, 11, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.act-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.fee-board {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
background: rgba(10, 15, 29, 0.6);
|
||||
border-radius: 18rpx;
|
||||
padding: 20rpx 0;
|
||||
|
||||
.fee-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.col-label {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.col-val {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
|
||||
&.highlight {
|
||||
color: #10B981;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fee-divider {
|
||||
width: 1px;
|
||||
height: 40rpx;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.sec-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.sec-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.sec-sub {
|
||||
font-size: 22rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
|
||||
.creator-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 16rpx;
|
||||
padding: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.creator-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.creator-info {
|
||||
flex: 1;
|
||||
|
||||
.creator-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
.creator-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.host-badge {
|
||||
background: rgba(99, 102, 241, 0.2);
|
||||
color: #818CF8;
|
||||
font-size: 18rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.creator-sub {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-chat {
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
border: 1px solid rgba(99, 102, 241, 0.3);
|
||||
color: #818CF8;
|
||||
font-size: 22rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 52rpx;
|
||||
line-height: 50rpx;
|
||||
border-radius: 26rpx;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.info-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 24rpx;
|
||||
|
||||
.info-label {
|
||||
color: #64748B;
|
||||
}
|
||||
.info-val {
|
||||
color: #E2E8F0;
|
||||
font-weight: 500;
|
||||
|
||||
&.green-text {
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-val {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
|
||||
.copy-btn {
|
||||
color: #6366F1;
|
||||
font-size: 22rpx;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.member-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12rpx 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 32rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.member-info {
|
||||
flex: 1;
|
||||
|
||||
.member-name {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #F8FAFC;
|
||||
display: block;
|
||||
}
|
||||
.member-seat {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
|
||||
.checkin-badge {
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 12rpx;
|
||||
background: rgba(100, 116, 139, 0.2);
|
||||
color: #94A3B8;
|
||||
|
||||
&.checked {
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: #10B981;
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-members {
|
||||
text-align: center;
|
||||
padding: 30rpx 0;
|
||||
color: #64748B;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 120rpx;
|
||||
background-color: rgba(21, 29, 48, 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
gap: 24rpx;
|
||||
z-index: 100;
|
||||
|
||||
.ticket-link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 120rpx;
|
||||
|
||||
.ticket-icon {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
.ticket-text {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-join {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #10B981 0%, #059669 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
border-radius: 44rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(16, 185, 129, 0.35);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
406
pwuniapp/pages/activity/list.vue
普通文件
406
pwuniapp/pages/activity/list.vue
普通文件
@@ -0,0 +1,406 @@
|
||||
<template>
|
||||
<view class="activity-container">
|
||||
<!-- 顶部模式切换选项卡 -->
|
||||
<view class="tab-header">
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: currentTab === 'ALL' }"
|
||||
@click="switchTab('ALL')"
|
||||
>
|
||||
<text class="tab-text">全部活动</text>
|
||||
<view class="tab-indicator" v-if="currentTab === 'ALL'"></view>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: currentTab === 'AA' }"
|
||||
@click="switchTab('AA')"
|
||||
>
|
||||
<text class="tab-text">⚡ AA制车队</text>
|
||||
<view class="tab-indicator" v-if="currentTab === 'AA'"></view>
|
||||
</view>
|
||||
<view
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: currentTab === 'PROJECT' }"
|
||||
@click="switchTab('PROJECT')"
|
||||
>
|
||||
<text class="tab-text">🏆 项目制赛事</text>
|
||||
<view class="tab-indicator" v-if="currentTab === 'PROJECT'"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 顶部状态小提示 -->
|
||||
<view class="tips-bar">
|
||||
<text class="tips-icon">💡</text>
|
||||
<text class="tips-text">AA制活动人均AA平摊,项目制为房主全额悬赏或奖池分配</text>
|
||||
</view>
|
||||
|
||||
<!-- 活动列表 -->
|
||||
<view class="activity-list">
|
||||
<view
|
||||
v-for="act in filteredActivities"
|
||||
:key="act.id"
|
||||
class="activity-card card-active"
|
||||
@click="goToDetail(act.id)"
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="game-tag">{{ act.game }}</view>
|
||||
<view class="mode-tag" :class="act.payType === 'AA' ? 'aa-mode' : 'project-mode'">
|
||||
{{ act.payType === 'AA' ? 'AA制平摊' : '项目制悬赏' }}
|
||||
</view>
|
||||
<view class="status-pill" :class="act.status">
|
||||
{{ act.status === 'RECRUITING' ? '招募中' : '进行中' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片标题 -->
|
||||
<view class="activity-title">{{ act.title }}</view>
|
||||
|
||||
<!-- 详细信息条 -->
|
||||
<view class="meta-row">
|
||||
<view class="meta-item">
|
||||
<text class="meta-icon">⏰</text>
|
||||
<text class="meta-val">{{ act.startTime }}</text>
|
||||
</view>
|
||||
<view class="meta-item">
|
||||
<text class="meta-icon">🎙️</text>
|
||||
<text class="meta-val">{{ act.location }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 进度与头像 -->
|
||||
<view class="progress-section">
|
||||
<view class="progress-info">
|
||||
<text class="progress-label">组队进度</text>
|
||||
<text class="progress-count">{{ act.currentParticipants }} / {{ act.maxParticipants }}人</text>
|
||||
</view>
|
||||
<view class="progress-track">
|
||||
<view
|
||||
class="progress-fill"
|
||||
:style="{ width: (act.currentParticipants / act.maxParticipants * 100) + '%' }"
|
||||
></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部房主与价格 -->
|
||||
<view class="card-footer">
|
||||
<view class="creator-info">
|
||||
<image class="creator-avatar" :src="act.creatorAvatar" mode="aspectFill" />
|
||||
<text class="creator-name">房主:{{ act.creatorNickname }}</text>
|
||||
</view>
|
||||
|
||||
<view class="fee-info">
|
||||
<template v-if="act.payType === 'AA'">
|
||||
<text class="fee-label">人均预估</text>
|
||||
<text class="fee-amount">¥{{ act.aaFee }}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="fee-label">奖金池</text>
|
||||
<text class="fee-amount project-gold">¥{{ act.totalFee }}</text>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 悬浮发起活动按钮 -->
|
||||
<view class="fab-btn btn-active" @click="createActivity">
|
||||
<text class="fab-icon">+</text>
|
||||
<text class="fab-text">发起组队</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ACTIVITIES_LIST } from '@/common/mock.js';
|
||||
|
||||
const activities = ref(ACTIVITIES_LIST);
|
||||
const currentTab = ref('ALL');
|
||||
|
||||
const filteredActivities = computed(() => {
|
||||
if (currentTab.value === 'ALL') return activities.value;
|
||||
return activities.value.filter(a => a.payType === currentTab.value);
|
||||
});
|
||||
|
||||
const switchTab = (tab) => {
|
||||
currentTab.value = tab;
|
||||
};
|
||||
|
||||
const goToDetail = (id) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/activity/detail?id=${id}`
|
||||
});
|
||||
};
|
||||
|
||||
const createActivity = () => {
|
||||
uni.showToast({
|
||||
title: '发起组队模块已就绪(支持AA制与项目制)',
|
||||
icon: 'none',
|
||||
duration: 2500
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.activity-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding: 20rpx 32rpx 140rpx;
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
display: flex;
|
||||
background-color: #151D30;
|
||||
border-radius: 20rpx;
|
||||
padding: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 18rpx 0;
|
||||
position: relative;
|
||||
border-radius: 14rpx;
|
||||
|
||||
.tab-text {
|
||||
font-size: 26rpx;
|
||||
color: #94A3B8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(99, 102, 241, 0.18);
|
||||
|
||||
.tab-text {
|
||||
color: #FFFFFF;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tab-indicator {
|
||||
position: absolute;
|
||||
bottom: 4rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 32rpx;
|
||||
height: 6rpx;
|
||||
background: #6366F1;
|
||||
border-radius: 3rpx;
|
||||
box-shadow: 0 0 10rpx #6366F1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(99, 102, 241, 0.08);
|
||||
border: 1px solid rgba(99, 102, 241, 0.2);
|
||||
border-radius: 12rpx;
|
||||
padding: 12rpx 20rpx;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.tips-icon {
|
||||
font-size: 24rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
.tips-text {
|
||||
font-size: 22rpx;
|
||||
color: #818CF8;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
|
||||
.activity-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.3);
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.game-tag {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: #E2E8F0;
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.mode-tag {
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 600;
|
||||
|
||||
&.aa-mode {
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: #10B981;
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
&.project-mode {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: #F59E0B;
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.status-pill {
|
||||
margin-left: auto;
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&.RECRUITING {
|
||||
background: rgba(99, 102, 241, 0.2);
|
||||
color: #818CF8;
|
||||
}
|
||||
&.RUNNING {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.meta-row {
|
||||
display: flex;
|
||||
gap: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
|
||||
.meta-icon {
|
||||
font-size: 22rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress-section {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.progress-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10rpx;
|
||||
font-size: 22rpx;
|
||||
|
||||
.progress-label {
|
||||
color: #64748B;
|
||||
}
|
||||
.progress-count {
|
||||
color: #10B981;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-track {
|
||||
height: 10rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 5rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #10B981, #059669);
|
||||
border-radius: 5rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
padding-top: 18rpx;
|
||||
|
||||
.creator-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.creator-avatar {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 24rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
.creator-name {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.fee-info {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8rpx;
|
||||
|
||||
.fee-label {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
.fee-amount {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #10B981;
|
||||
|
||||
&.project-gold {
|
||||
color: #F59E0B;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fab-btn {
|
||||
position: fixed;
|
||||
right: 36rpx;
|
||||
bottom: 160rpx;
|
||||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||||
border-radius: 44rpx;
|
||||
padding: 18rpx 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 8rpx 28rpx rgba(99, 102, 241, 0.5);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
z-index: 100;
|
||||
|
||||
.fab-icon {
|
||||
font-size: 32rpx;
|
||||
color: #FFF;
|
||||
font-weight: 700;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.fab-text {
|
||||
font-size: 26rpx;
|
||||
color: #FFF;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,359 @@
|
||||
<template>
|
||||
<view class="ticket-container">
|
||||
<view class="ticket-card">
|
||||
<!-- 票据头部 -->
|
||||
<view class="ticket-header">
|
||||
<view class="game-badge">{{ ticketData.game }}</view>
|
||||
<view class="stamp-badge" :class="{ verified: isCheckedIn }">
|
||||
{{ isCheckedIn ? '已核销入队' : '待核销' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="ticket-title">{{ ticketData.title }}</view>
|
||||
|
||||
<view class="ticket-meta">
|
||||
<view class="meta-row">
|
||||
<text class="meta-k">开黑时间</text>
|
||||
<text class="meta-v">{{ ticketData.startTime }}</text>
|
||||
</view>
|
||||
<view class="meta-row">
|
||||
<text class="meta-k">语音频道</text>
|
||||
<text class="meta-v">{{ ticketData.location }}</text>
|
||||
</view>
|
||||
<view class="meta-row">
|
||||
<text class="meta-k">房主昵称</text>
|
||||
<text class="meta-v">{{ ticketData.creatorNickname }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 撕票齿轮分割虚线与两侧凹槽 -->
|
||||
<view class="ticket-punch-wrap">
|
||||
<view class="punch punch-left"></view>
|
||||
<view class="dash-line"></view>
|
||||
<view class="punch punch-right"></view>
|
||||
</view>
|
||||
|
||||
<!-- 核心亮点:4位数字核销码专属卡槽 -->
|
||||
<view class="code-section">
|
||||
<view class="code-tips">进房向房主出示 4 位入队核销码</view>
|
||||
<view class="code-slots">
|
||||
<view v-for="(char, idx) in verifyCodeDigits" :key="idx" class="digit-box">
|
||||
<text class="digit-text">{{ char }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 条形码与二维码模拟区域 -->
|
||||
<view class="qrcode-section">
|
||||
<view class="barcode-graphic">
|
||||
<view class="bar" v-for="i in 36" :key="i" :style="{ width: (i % 3 === 0 ? '6rpx' : '2rpx'), marginRight: (i % 2 === 0 ? '4rpx' : '2rpx') }"></view>
|
||||
</view>
|
||||
<text class="barcode-num">TICKET-2026-9025</text>
|
||||
</view>
|
||||
|
||||
<!-- 动态防伪时间与安全保障 -->
|
||||
<view class="security-footer">
|
||||
<view class="security-time">
|
||||
<text class="dot-green"></text>
|
||||
<text class="time-text">动态防伪时间:{{ currentTime }}</text>
|
||||
</view>
|
||||
<view class="anti-counterfeit-bar"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 演示操作按钮 -->
|
||||
<view class="action-wrap">
|
||||
<button class="btn-verify-demo btn-active" @click="toggleCheckIn">
|
||||
{{ isCheckedIn ? '重置为待核销状态' : '模拟房主核销此门票' }}
|
||||
</button>
|
||||
<button class="btn-back btn-active" @click="goBack">返回活动详情</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { ACTIVITIES_LIST } from '@/common/mock.js';
|
||||
|
||||
const ticketData = ref(ACTIVITIES_LIST[0]);
|
||||
const verifyCode = ref('8392');
|
||||
const isCheckedIn = ref(false);
|
||||
const currentTime = ref('');
|
||||
|
||||
const verifyCodeDigits = computed(() => {
|
||||
return verifyCode.value.split('');
|
||||
});
|
||||
|
||||
let timer = null;
|
||||
const updateTime = () => {
|
||||
const now = new Date();
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
const y = now.getFullYear();
|
||||
const m = pad(now.getMonth() + 1);
|
||||
const d = pad(now.getDate());
|
||||
const hh = pad(now.getHours());
|
||||
const mm = pad(now.getMinutes());
|
||||
const ss = pad(now.getSeconds());
|
||||
currentTime.value = `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
updateTime();
|
||||
timer = setInterval(updateTime, 1000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) clearInterval(timer);
|
||||
});
|
||||
|
||||
const toggleCheckIn = () => {
|
||||
isCheckedIn.value = !isCheckedIn.value;
|
||||
uni.showToast({
|
||||
title: isCheckedIn.value ? '房主已成功核销该4位门票码!' : '状态已重置为待核销',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
};
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ticket-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding: 40rpx 36rpx;
|
||||
}
|
||||
|
||||
.ticket-card {
|
||||
background: #151D30;
|
||||
border-radius: 32rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.5);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.ticket-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 32rpx 32rpx 16rpx;
|
||||
|
||||
.game-badge {
|
||||
background: rgba(99, 102, 241, 0.2);
|
||||
color: #818CF8;
|
||||
font-size: 22rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stamp-badge {
|
||||
font-size: 22rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: #F59E0B;
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
font-weight: 700;
|
||||
|
||||
&.verified {
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: #10B981;
|
||||
border-color: rgba(16, 185, 129, 0.4);
|
||||
transform: rotate(-4deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ticket-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
padding: 0 32rpx 24rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.ticket-meta {
|
||||
padding: 0 32rpx 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
|
||||
.meta-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 24rpx;
|
||||
|
||||
.meta-k {
|
||||
color: #64748B;
|
||||
}
|
||||
.meta-v {
|
||||
color: #CBD5E1;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ticket-punch-wrap {
|
||||
position: relative;
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.punch {
|
||||
position: absolute;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #0A0F1D;
|
||||
|
||||
&.punch-left {
|
||||
left: -24rpx;
|
||||
}
|
||||
&.punch-right {
|
||||
right: -24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.dash-line {
|
||||
width: 84%;
|
||||
height: 2rpx;
|
||||
border-bottom: 2rpx dashed rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.code-section {
|
||||
padding: 36rpx 32rpx 24rpx;
|
||||
text-align: center;
|
||||
|
||||
.code-tips {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.code-slots {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20rpx;
|
||||
|
||||
.digit-box {
|
||||
width: 110rpx;
|
||||
height: 130rpx;
|
||||
background: rgba(10, 15, 29, 0.8);
|
||||
border: 2rpx solid #10B981;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 0 20rpx rgba(16, 185, 129, 0.3);
|
||||
|
||||
.digit-text {
|
||||
font-size: 64rpx;
|
||||
font-weight: 800;
|
||||
color: #10B981;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
text-shadow: 0 0 12rpx rgba(16, 185, 129, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qrcode-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 16rpx 32rpx 28rpx;
|
||||
|
||||
.barcode-graphic {
|
||||
display: flex;
|
||||
height: 60rpx;
|
||||
align-items: center;
|
||||
opacity: 0.8;
|
||||
|
||||
.bar {
|
||||
height: 100%;
|
||||
background-color: #F8FAFC;
|
||||
}
|
||||
}
|
||||
|
||||
.barcode-num {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
letter-spacing: 4rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.security-footer {
|
||||
background: rgba(10, 15, 29, 0.5);
|
||||
padding: 20rpx 32rpx;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.04);
|
||||
|
||||
.security-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
|
||||
.dot-green {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #10B981;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
|
||||
.anti-counterfeit-bar {
|
||||
height: 4rpx;
|
||||
margin-top: 12rpx;
|
||||
background: linear-gradient(90deg, #6366F1, #10B981, #F59E0B, #6366F1);
|
||||
background-size: 200% 100%;
|
||||
border-radius: 2rpx;
|
||||
animation: gradientMove 3s infinite linear;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-wrap {
|
||||
margin-top: 48rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
|
||||
.btn-verify-demo {
|
||||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||||
color: #FFFFFF;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
}
|
||||
|
||||
.btn-back {
|
||||
background: #151D30;
|
||||
color: #94A3B8;
|
||||
font-size: 26rpx;
|
||||
height: 84rpx;
|
||||
line-height: 84rpx;
|
||||
border-radius: 44rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gradientMove {
|
||||
0% { background-position: 0% 50%; }
|
||||
100% { background-position: 200% 50%; }
|
||||
}
|
||||
</style>
|
||||
618
pwuniapp/pages/expert/detail.vue
普通文件
618
pwuniapp/pages/expert/detail.vue
普通文件
@@ -0,0 +1,618 @@
|
||||
<template>
|
||||
<view class="expert-detail-container">
|
||||
<!-- 顶部封面与基本信息 -->
|
||||
<view class="hero-header">
|
||||
<image class="cover-bg" :src="expert.coverImage || expert.avatar" mode="aspectFill" />
|
||||
<view class="cover-mask"></view>
|
||||
|
||||
<view class="profile-card">
|
||||
<view class="avatar-wrap">
|
||||
<image class="avatar-img" :src="expert.avatar" mode="aspectFill" />
|
||||
<view class="status-badge" :class="{ online: expert.isOnline }">
|
||||
<view class="pulse-dot" v-if="expert.isOnline"></view>
|
||||
<text>{{ expert.isOnline ? '在线' : '离线' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info-wrap">
|
||||
<view class="name-row">
|
||||
<text class="nickname">{{ expert.nickname }}</text>
|
||||
<text class="level-tag">{{ expert.levelTag }}</text>
|
||||
</view>
|
||||
|
||||
<view class="rank-row">
|
||||
<text class="game-tag">{{ expert.game }}</text>
|
||||
<text class="rank-tag">{{ expert.rankTag }}</text>
|
||||
</view>
|
||||
|
||||
<view class="data-row">
|
||||
<text class="rating-val">⭐ {{ expert.rating }}</text>
|
||||
<text class="order-val">已陪玩 {{ expert.orderCount }} 次</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 专属打招呼试听语音 -->
|
||||
<view class="voice-card btn-active" @click="toggleVoice">
|
||||
<view class="voice-icon-wrap">
|
||||
<text class="play-icon">{{ isPlayingVoice ? '⏸' : '▶' }}</text>
|
||||
</view>
|
||||
<view class="voice-info">
|
||||
<text class="voice-title">{{ isPlayingVoice ? '正在连麦试听语音...' : '点击试听打招呼语音' }}</text>
|
||||
<view class="voice-wave" v-if="isPlayingVoice">
|
||||
<view class="bar b1"></view>
|
||||
<view class="bar b2"></view>
|
||||
<view class="bar b3"></view>
|
||||
<view class="bar b4"></view>
|
||||
</view>
|
||||
<text class="voice-dur" v-else>{{ expert.voiceDuration }}s 真实原声</text>
|
||||
</view>
|
||||
<text class="listen-tag">试听</text>
|
||||
</view>
|
||||
|
||||
<!-- 个人简介 -->
|
||||
<view class="section-card">
|
||||
<view class="sec-title">个人介绍</view>
|
||||
<text class="bio-text">{{ expert.bio }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 服务项目选择 -->
|
||||
<view class="section-card">
|
||||
<view class="sec-title">服务项目</view>
|
||||
<view class="service-list">
|
||||
<view
|
||||
v-for="srv in expert.services"
|
||||
:key="srv.id"
|
||||
class="service-item btn-active"
|
||||
:class="{ selected: selectedServiceId === srv.id }"
|
||||
@click="selectService(srv)"
|
||||
>
|
||||
<view class="srv-left">
|
||||
<view class="srv-title">{{ srv.title }}</view>
|
||||
<view class="srv-desc">{{ srv.desc }}</view>
|
||||
</view>
|
||||
<view class="srv-right">
|
||||
<text class="price-sym">¥</text>
|
||||
<text class="price-num">{{ srv.price }}</text>
|
||||
<text class="price-unit">/{{ srv.unit }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 战绩与生活照片 -->
|
||||
<view class="section-card" v-if="expert.photos && expert.photos.length > 0">
|
||||
<view class="sec-title">战绩相册</view>
|
||||
<scroll-view scroll-x class="gallery-scroll" :show-scrollbar="false">
|
||||
<view class="gallery-list">
|
||||
<image
|
||||
v-for="(photo, index) in expert.photos"
|
||||
:key="index"
|
||||
class="gallery-item"
|
||||
:src="photo"
|
||||
mode="aspectFill"
|
||||
@click="previewImage(index)"
|
||||
/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 真实买家评价 -->
|
||||
<view class="section-card">
|
||||
<view class="sec-header">
|
||||
<text class="sec-title">服务好评 ({{ expert.reviews ? expert.reviews.length : 0 }})</text>
|
||||
<text class="rate-percent">好评率 99.8%</text>
|
||||
</view>
|
||||
|
||||
<view class="review-list" v-if="expert.reviews && expert.reviews.length > 0">
|
||||
<view v-for="rev in expert.reviews" :key="rev.id" class="review-item">
|
||||
<image class="rev-avatar" :src="rev.userAvatar" mode="aspectFill" />
|
||||
<view class="rev-body">
|
||||
<view class="rev-top">
|
||||
<text class="rev-name">{{ rev.userNickname }}</text>
|
||||
<text class="rev-time">{{ rev.time }}</text>
|
||||
</view>
|
||||
<view class="tag-row">
|
||||
<text class="rev-tag" v-for="(t, i) in rev.tags" :key="i">{{ t }}</text>
|
||||
</view>
|
||||
<text class="rev-content">{{ rev.content }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="empty-reviews">
|
||||
<text>近期新增服务,暂无评价记录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="icon-action btn-active" @click="chatOnline">
|
||||
<text class="action-icon">💬</text>
|
||||
<text class="action-text">私聊</text>
|
||||
</view>
|
||||
<view class="price-summary">
|
||||
<text class="summary-label">当前选定:</text>
|
||||
<text class="summary-price">¥{{ currentSelectedService ? currentSelectedService.price : expert.price }}</text>
|
||||
</view>
|
||||
<button class="btn-book btn-active" @click="goToOrder">立即预约</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { EXPERTS_LIST } from '@/common/mock.js';
|
||||
|
||||
const expert = ref(EXPERTS_LIST[0]);
|
||||
const selectedServiceId = ref(EXPERTS_LIST[0].services[0] ? EXPERTS_LIST[0].services[0].id : null);
|
||||
const isPlayingVoice = ref(false);
|
||||
|
||||
onLoad((query) => {
|
||||
if (query && query.id) {
|
||||
const found = EXPERTS_LIST.find(e => e.id === query.id);
|
||||
if (found) {
|
||||
expert.value = found;
|
||||
if (found.services && found.services.length > 0) {
|
||||
selectedServiceId.value = found.services[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const currentSelectedService = computed(() => {
|
||||
if (!expert.value.services) return null;
|
||||
return expert.value.services.find(s => s.id === selectedServiceId.value);
|
||||
});
|
||||
|
||||
const selectService = (srv) => {
|
||||
selectedServiceId.value = srv.id;
|
||||
};
|
||||
|
||||
const toggleVoice = () => {
|
||||
isPlayingVoice.value = !isPlayingVoice.value;
|
||||
if (isPlayingVoice.value) {
|
||||
uni.showToast({ title: '正在播放大神语音打招呼', icon: 'none' });
|
||||
setTimeout(() => {
|
||||
isPlayingVoice.value = false;
|
||||
}, 4000);
|
||||
}
|
||||
};
|
||||
|
||||
const chatOnline = () => {
|
||||
uni.showToast({ title: '已开启即时连麦通道', icon: 'none' });
|
||||
};
|
||||
|
||||
const previewImage = (index) => {
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls: expert.value.photos
|
||||
});
|
||||
};
|
||||
|
||||
const goToOrder = () => {
|
||||
const srv = currentSelectedService.value || expert.value.services[0];
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/create?expertId=${expert.value.id}&serviceId=${srv.id}`
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.expert-detail-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding-bottom: 140rpx;
|
||||
}
|
||||
|
||||
.hero-header {
|
||||
position: relative;
|
||||
height: 440rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.cover-bg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: blur(8px);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.cover-mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg, rgba(10, 15, 29, 0.4) 0%, #0A0F1D 100%);
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
position: absolute;
|
||||
bottom: 20rpx;
|
||||
left: 32rpx;
|
||||
right: 32rpx;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 24rpx;
|
||||
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 70rpx;
|
||||
border: 4rpx solid #6366F1;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: #64748B;
|
||||
color: #FFF;
|
||||
font-size: 18rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
|
||||
&.online {
|
||||
background: #10B981;
|
||||
}
|
||||
|
||||
.pulse-dot {
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
background: #FFF;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-wrap {
|
||||
flex: 1;
|
||||
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.nickname {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #FFF;
|
||||
}
|
||||
.level-tag {
|
||||
background: linear-gradient(135deg, #F59E0B, #D97706);
|
||||
color: #FFF;
|
||||
font-size: 18rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.rank-row {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.game-tag {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: #E2E8F0;
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.rank-tag {
|
||||
background: rgba(99, 102, 241, 0.2);
|
||||
color: #818CF8;
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.data-row {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
|
||||
.rating-val {
|
||||
color: #F59E0B;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.voice-card {
|
||||
margin: 16rpx 32rpx 24rpx;
|
||||
background: linear-gradient(135deg, rgba(99, 102, 241, 0.2) 0%, rgba(139, 92, 246, 0.1) 100%);
|
||||
border: 1px solid rgba(99, 102, 241, 0.35);
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.voice-icon-wrap {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
background: #6366F1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 18rpx;
|
||||
|
||||
.play-icon {
|
||||
color: #FFF;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.voice-info {
|
||||
flex: 1;
|
||||
|
||||
.voice-title {
|
||||
font-size: 24rpx;
|
||||
color: #F8FAFC;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.voice-dur {
|
||||
font-size: 20rpx;
|
||||
color: #818CF8;
|
||||
}
|
||||
|
||||
.voice-wave {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 6rpx;
|
||||
height: 24rpx;
|
||||
|
||||
.bar {
|
||||
width: 6rpx;
|
||||
background-color: #818CF8;
|
||||
border-radius: 3rpx;
|
||||
|
||||
&.b1 { height: 16rpx; }
|
||||
&.b2 { height: 24rpx; }
|
||||
&.b3 { height: 12rpx; }
|
||||
&.b4 { height: 20rpx; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.listen-tag {
|
||||
font-size: 20rpx;
|
||||
color: #818CF8;
|
||||
border: 1px solid rgba(129, 140, 248, 0.4);
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-card {
|
||||
margin: 0 32rpx 24rpx;
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.sec-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.rate-percent {
|
||||
font-size: 22rpx;
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
|
||||
.sec-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.bio-text {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.service-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
|
||||
.service-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
|
||||
&.selected {
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
border-color: #6366F1;
|
||||
}
|
||||
|
||||
.srv-left {
|
||||
.srv-title {
|
||||
font-size: 26rpx;
|
||||
color: #F8FAFC;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
.srv-desc {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
|
||||
.srv-right {
|
||||
color: #10B981;
|
||||
.price-sym { font-size: 22rpx; }
|
||||
.price-num { font-size: 34rpx; font-weight: 700; }
|
||||
.price-unit { font-size: 20rpx; color: #64748B; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gallery-scroll {
|
||||
white-space: nowrap;
|
||||
|
||||
.gallery-list {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
width: 220rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 16rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.review-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
|
||||
.review-item {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
|
||||
.rev-avatar {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 32rpx;
|
||||
}
|
||||
|
||||
.rev-body {
|
||||
flex: 1;
|
||||
|
||||
.rev-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
.rev-name {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #E2E8F0;
|
||||
}
|
||||
.rev-time {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.rev-tag {
|
||||
font-size: 18rpx;
|
||||
color: #818CF8;
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.rev-content {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-reviews {
|
||||
text-align: center;
|
||||
color: #64748B;
|
||||
font-size: 22rpx;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 120rpx;
|
||||
background-color: rgba(21, 29, 48, 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
gap: 20rpx;
|
||||
z-index: 100;
|
||||
|
||||
.icon-action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 80rpx;
|
||||
|
||||
.action-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
.action-text {
|
||||
font-size: 18rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.price-summary {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
.summary-label {
|
||||
font-size: 22rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
.summary-price {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #10B981;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-book {
|
||||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||||
color: #FFF;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
border-radius: 44rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
padding: 0 48rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(99, 102, 241, 0.4);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
560
pwuniapp/pages/index/index.vue
普通文件
560
pwuniapp/pages/index/index.vue
普通文件
@@ -0,0 +1,560 @@
|
||||
<template>
|
||||
<view class="index-container">
|
||||
<!-- 顶部搜索栏 -->
|
||||
<view class="header-search">
|
||||
<view class="search-box">
|
||||
<text class="search-icon">🔍</text>
|
||||
<input class="search-input" placeholder="搜索陪玩大神、游戏昵称或项目" placeholder-class="placeholder-text" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 游戏分类滑动栏 -->
|
||||
<scroll-view scroll-x class="category-scroll" :show-scrollbar="false">
|
||||
<view class="category-list">
|
||||
<view
|
||||
v-for="item in games"
|
||||
:key="item.id"
|
||||
class="category-item btn-active"
|
||||
:class="{ active: currentCategory === item.id }"
|
||||
@click="selectCategory(item.id)"
|
||||
>
|
||||
<text class="cat-icon">{{ item.icon }}</text>
|
||||
<text class="cat-name">{{ item.name }}</text>
|
||||
<view v-if="item.hot" class="cat-badge">HOT</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 精彩活动横幅轮播 -->
|
||||
<swiper class="banner-swiper" circular autoplay interval="4000" indicator-dots indicator-color="rgba(255,255,255,0.2)" indicator-active-color="#6366F1">
|
||||
<swiper-item v-for="banner in banners" :key="banner.id" class="banner-item" @click="goToActivity">
|
||||
<view class="banner-card" :style="{ background: banner.bg }">
|
||||
<view class="banner-tag">{{ banner.tag }}</view>
|
||||
<view class="banner-title">{{ banner.title }}</view>
|
||||
<view class="banner-sub">点击立即参与 ⚡</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 快捷功能金刚区 -->
|
||||
<view class="quick-grid">
|
||||
<view class="quick-item btn-active" @click="navTo('/pages/activity/list')">
|
||||
<view class="quick-icon-bg bg-purple">🔥</view>
|
||||
<text class="quick-title">组队开黑</text>
|
||||
<text class="quick-sub">AA制五排</text>
|
||||
</view>
|
||||
<view class="quick-item btn-active" @click="navTo('/pages/activity/my-ticket')">
|
||||
<view class="quick-icon-bg bg-green">🎟️</view>
|
||||
<text class="quick-title">我的门票</text>
|
||||
<text class="quick-sub">4位核销码</text>
|
||||
</view>
|
||||
<view class="quick-item btn-active" @click="navTo('/pages/order/list')">
|
||||
<view class="quick-icon-bg bg-blue">⚡</view>
|
||||
<text class="quick-title">服务订单</text>
|
||||
<text class="quick-sub">状态实时看</text>
|
||||
</view>
|
||||
<view class="quick-item btn-active" @click="navTo('/pages/user/index')">
|
||||
<view class="quick-icon-bg bg-orange">👑</view>
|
||||
<text class="quick-title">大神中心</text>
|
||||
<text class="quick-sub">双身份切换</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 陪玩大神瀑布流 -->
|
||||
<view class="section-title-wrap">
|
||||
<view class="section-title">
|
||||
<view class="title-line"></view>
|
||||
<text>精选在线大神</text>
|
||||
</view>
|
||||
<text class="section-more">实时在线</text>
|
||||
</view>
|
||||
|
||||
<view class="expert-grid">
|
||||
<view
|
||||
v-for="expert in filteredExperts"
|
||||
:key="expert.id"
|
||||
class="expert-card card-active"
|
||||
@click="goToExpert(expert.id)"
|
||||
>
|
||||
<!-- 封面图 -->
|
||||
<view class="card-cover-wrap">
|
||||
<image class="card-cover" :src="expert.avatar" mode="aspectFill" />
|
||||
<view class="status-tag" :class="expert.isOnline ? 'online' : 'offline'">
|
||||
<view class="pulse-dot" v-if="expert.isOnline"></view>
|
||||
<text>{{ expert.isOnline ? '在线' : '离线' }}</text>
|
||||
</view>
|
||||
<view class="game-badge">{{ expert.game }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 信息区 -->
|
||||
<view class="card-body">
|
||||
<view class="row-nickname">
|
||||
<text class="nickname">{{ expert.nickname }}</text>
|
||||
<text class="gender-tag" :class="expert.gender">{{ expert.gender === 'female' ? '♀' : '♂' }}</text>
|
||||
</view>
|
||||
<view class="rank-badge">{{ expert.rankTag }}</view>
|
||||
|
||||
<!-- 语音试听条 -->
|
||||
<view class="voice-bar btn-active" @click.stop="toggleVoice(expert.id)">
|
||||
<text class="voice-icon">{{ playingId === expert.id ? '🔊' : '▶' }}</text>
|
||||
<text class="voice-text">{{ playingId === expert.id ? '试听中...' : expert.voiceDuration + 's 语音' }}</text>
|
||||
<view class="wave-lines" v-if="playingId === expert.id">
|
||||
<view class="wave-item w1"></view>
|
||||
<view class="wave-item w2"></view>
|
||||
<view class="wave-item w3"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-footer">
|
||||
<view class="price-wrap">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-val">{{ expert.price }}</text>
|
||||
<text class="price-unit">/时</text>
|
||||
</view>
|
||||
<text class="order-count">{{ expert.orderCount }}单</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { GAMES_LIST, BANNERS, EXPERTS_LIST } from '@/common/mock.js';
|
||||
|
||||
const games = ref(GAMES_LIST);
|
||||
const banners = ref(BANNERS);
|
||||
const experts = ref(EXPERTS_LIST);
|
||||
const currentCategory = ref('all');
|
||||
const playingId = ref(null);
|
||||
|
||||
const filteredExperts = computed(() => {
|
||||
if (currentCategory.value === 'all') return experts.value;
|
||||
const gameMap = {
|
||||
wzry: '王者荣耀',
|
||||
hpjy: '和平精英',
|
||||
lol: '英雄联盟',
|
||||
val: '无畏契约'
|
||||
};
|
||||
const target = gameMap[currentCategory.value];
|
||||
return target ? experts.value.filter(e => e.game === target) : experts.value;
|
||||
});
|
||||
|
||||
const selectCategory = (id) => {
|
||||
currentCategory.value = id;
|
||||
};
|
||||
|
||||
const toggleVoice = (id) => {
|
||||
if (playingId.value === id) {
|
||||
playingId.value = null;
|
||||
} else {
|
||||
playingId.value = id;
|
||||
uni.showToast({ title: '正在试听打招呼语音', icon: 'none' });
|
||||
setTimeout(() => {
|
||||
if (playingId.value === id) playingId.value = null;
|
||||
}, 4000);
|
||||
}
|
||||
};
|
||||
|
||||
const goToExpert = (id) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/expert/detail?id=${id}`
|
||||
});
|
||||
};
|
||||
|
||||
const goToActivity = () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/activity/list'
|
||||
});
|
||||
};
|
||||
|
||||
const navTo = (url) => {
|
||||
if (url === '/pages/activity/list' || url === '/pages/order/list' || url === '/pages/user/index') {
|
||||
uni.switchTab({ url });
|
||||
} else {
|
||||
uni.navigateTo({ url });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.index-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding-bottom: 140rpx;
|
||||
}
|
||||
|
||||
.header-search {
|
||||
padding: 20rpx 32rpx;
|
||||
background-color: #0A0F1D;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #151D30;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
padding: 0 28rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
|
||||
.search-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.placeholder-text {
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category-scroll {
|
||||
white-space: nowrap;
|
||||
padding: 10rpx 32rpx 20rpx;
|
||||
|
||||
.category-list {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 14rpx 28rpx;
|
||||
background-color: #151D30;
|
||||
border-radius: 32rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
position: relative;
|
||||
|
||||
.cat-icon {
|
||||
font-size: 26rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.cat-name {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
font-weight: 500;
|
||||
}
|
||||
.cat-badge {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
background: #EF4444;
|
||||
color: #FFF;
|
||||
font-size: 16rpx;
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 12rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||||
border-color: #818CF8;
|
||||
box-shadow: 0 4rpx 16rpx rgba(99, 102, 241, 0.4);
|
||||
|
||||
.cat-name {
|
||||
color: #FFFFFF;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.banner-swiper {
|
||||
height: 200rpx;
|
||||
margin: 10rpx 32rpx 24rpx;
|
||||
|
||||
.banner-card {
|
||||
height: 100%;
|
||||
border-radius: 20rpx;
|
||||
padding: 28rpx 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.4);
|
||||
|
||||
.banner-tag {
|
||||
align-self: flex-start;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 20rpx;
|
||||
color: #FFF;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.banner-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #FFF;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.banner-sub {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quick-grid {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0 32rpx 32rpx;
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx 16rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.quick-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.quick-icon-bg {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 34rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
&.bg-purple { background: rgba(139, 92, 246, 0.2); }
|
||||
&.bg-green { background: rgba(16, 185, 129, 0.2); }
|
||||
&.bg-blue { background: rgba(99, 102, 241, 0.2); }
|
||||
&.bg-orange { background: rgba(245, 158, 11, 0.2); }
|
||||
}
|
||||
|
||||
.quick-title {
|
||||
font-size: 24rpx;
|
||||
color: #F8FAFC;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
.quick-sub {
|
||||
font-size: 18rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-title-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 32rpx 20rpx;
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
|
||||
.title-line {
|
||||
width: 8rpx;
|
||||
height: 28rpx;
|
||||
background: linear-gradient(180deg, #6366F1, #8B5CF6);
|
||||
border-radius: 4rpx;
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-more {
|
||||
font-size: 22rpx;
|
||||
color: #10B981;
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.expert-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
padding: 0 32rpx;
|
||||
|
||||
.expert-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.card-cover-wrap {
|
||||
position: relative;
|
||||
height: 320rpx;
|
||||
|
||||
.card-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
position: absolute;
|
||||
top: 14rpx;
|
||||
left: 14rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 18rpx;
|
||||
font-weight: 600;
|
||||
|
||||
&.online {
|
||||
background: rgba(16, 185, 129, 0.85);
|
||||
color: #FFF;
|
||||
}
|
||||
&.offline {
|
||||
background: rgba(100, 116, 139, 0.8);
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.pulse-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 50%;
|
||||
background: #FFF;
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.game-badge {
|
||||
position: absolute;
|
||||
bottom: 14rpx;
|
||||
right: 14rpx;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
backdrop-filter: blur(4px);
|
||||
font-size: 18rpx;
|
||||
color: #E2E8F0;
|
||||
padding: 4rpx 10rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 18rpx;
|
||||
|
||||
.row-nickname {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.nickname {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #F8FAFC;
|
||||
max-width: 200rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.gender-tag {
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
&.female {
|
||||
background: rgba(236, 72, 153, 0.2);
|
||||
color: #F472B6;
|
||||
}
|
||||
&.male {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
color: #60A5FA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rank-badge {
|
||||
font-size: 20rpx;
|
||||
color: #F59E0B;
|
||||
background: rgba(245, 158, 11, 0.12);
|
||||
padding: 4rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
display: inline-block;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.voice-bar {
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
border: 1px solid rgba(99, 102, 241, 0.3);
|
||||
height: 48rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.voice-icon {
|
||||
font-size: 20rpx;
|
||||
color: #818CF8;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.voice-text {
|
||||
font-size: 20rpx;
|
||||
color: #818CF8;
|
||||
font-weight: 600;
|
||||
}
|
||||
.wave-lines {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 4rpx;
|
||||
margin-left: 8rpx;
|
||||
|
||||
.wave-item {
|
||||
width: 4rpx;
|
||||
background: #818CF8;
|
||||
border-radius: 2rpx;
|
||||
|
||||
&.w1 { height: 14rpx; animation: bounce 0.6s infinite ease-in-out; }
|
||||
&.w2 { height: 22rpx; animation: bounce 0.6s infinite ease-in-out 0.2s; }
|
||||
&.w3 { height: 10rpx; animation: bounce 0.6s infinite ease-in-out 0.4s; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
|
||||
.price-wrap {
|
||||
color: #10B981;
|
||||
.price-symbol { font-size: 22rpx; font-weight: 600; }
|
||||
.price-val { font-size: 32rpx; font-weight: 700; margin: 0 2rpx; }
|
||||
.price-unit { font-size: 20rpx; color: #64748B; }
|
||||
}
|
||||
|
||||
.order-count {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7); }
|
||||
70% { transform: scale(1); box-shadow: 0 0 0 8rpx rgba(255, 255, 255, 0); }
|
||||
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% { transform: scaleY(0.4); }
|
||||
50% { transform: scaleY(1); }
|
||||
}
|
||||
</style>
|
||||
448
pwuniapp/pages/order/create.vue
普通文件
448
pwuniapp/pages/order/create.vue
普通文件
@@ -0,0 +1,448 @@
|
||||
<template>
|
||||
<view class="create-order-container">
|
||||
<!-- 服务大神与项目信息 -->
|
||||
<view class="order-card">
|
||||
<view class="expert-profile">
|
||||
<image class="expert-avatar" :src="expert.avatar" mode="aspectFill" />
|
||||
<view class="expert-meta">
|
||||
<view class="name-row">
|
||||
<text class="expert-name">{{ expert.nickname }}</text>
|
||||
<text class="game-tag">{{ expert.game }}</text>
|
||||
</view>
|
||||
<text class="service-title">{{ selectedService.title }}</text>
|
||||
</view>
|
||||
<view class="price-single">
|
||||
<text class="price-unit">¥{{ selectedService.price }}/{{ selectedService.unit }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 数量/时长步进器 -->
|
||||
<view class="order-card">
|
||||
<view class="stepper-row">
|
||||
<view class="stepper-label">
|
||||
<text class="label-main">购买时长</text>
|
||||
<text class="label-sub">以{{ selectedService.unit }}为计费单位</text>
|
||||
</view>
|
||||
<view class="stepper-box">
|
||||
<button class="step-btn btn-active" :disabled="hours <= 1" @click="changeHours(-1)">-</button>
|
||||
<text class="step-val">{{ hours }} {{ selectedService.unit }}</text>
|
||||
<button class="step-btn btn-active" :disabled="hours >= 10" @click="changeHours(1)">+</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 开黑时段选择 -->
|
||||
<view class="order-card">
|
||||
<view class="section-title">服务时间</view>
|
||||
<view class="time-tabs">
|
||||
<view
|
||||
class="time-tab btn-active"
|
||||
:class="{ active: timeType === 'NOW' }"
|
||||
@click="timeType = 'NOW'"
|
||||
>
|
||||
<text class="tab-t">⚡ 即刻出发</text>
|
||||
<text class="tab-d">付款后立即连麦开黑</text>
|
||||
</view>
|
||||
<view
|
||||
class="time-tab btn-active"
|
||||
:class="{ active: timeType === 'APPOINT' }"
|
||||
@click="timeType = 'APPOINT'"
|
||||
>
|
||||
<text class="tab-t">📅 预约时间</text>
|
||||
<text class="tab-d">今晚 20:30 开打</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 优惠抵扣与备注 -->
|
||||
<view class="order-card">
|
||||
<view class="coupon-row btn-active" @click="selectCoupon">
|
||||
<text class="cell-label">优惠券</text>
|
||||
<view class="cell-right">
|
||||
<text class="coupon-val">-¥{{ discount }} (新人首单立减)</text>
|
||||
<text class="arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="remark-row">
|
||||
<text class="cell-label">开黑要求/游戏ID</text>
|
||||
<input class="remark-input" v-model="remark" placeholder="如:QQ区/王者段位/包赢" placeholder-class="ph" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 费用明细清单 -->
|
||||
<view class="order-card">
|
||||
<view class="fee-row">
|
||||
<text class="fee-k">服务小计</text>
|
||||
<text class="fee-v">¥{{ rawTotal }}</text>
|
||||
</view>
|
||||
<view class="fee-row">
|
||||
<text class="fee-k">优惠券抵扣</text>
|
||||
<text class="fee-v green">-¥{{ discount }}</text>
|
||||
</view>
|
||||
<view class="fee-row total-row">
|
||||
<text class="fee-k">实付金额</text>
|
||||
<text class="fee-v pay-val">¥{{ finalTotal }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部固定结算条 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="amount-wrap">
|
||||
<text class="amount-label">实付:</text>
|
||||
<text class="amount-num">¥{{ finalTotal }}</text>
|
||||
</view>
|
||||
<button class="btn-pay btn-active" @click="handlePay">微信支付</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { EXPERTS_LIST } from '@/common/mock.js';
|
||||
|
||||
const expert = ref(EXPERTS_LIST[0]);
|
||||
const selectedService = ref(EXPERTS_LIST[0].services[0]);
|
||||
const hours = ref(2);
|
||||
const timeType = ref('NOW');
|
||||
const discount = ref(10);
|
||||
const remark = ref('');
|
||||
|
||||
onLoad((query) => {
|
||||
if (query && query.expertId) {
|
||||
const e = EXPERTS_LIST.find(item => item.id === query.expertId);
|
||||
if (e) {
|
||||
expert.value = e;
|
||||
if (query.serviceId && e.services) {
|
||||
const s = e.services.find(srv => srv.id === query.serviceId);
|
||||
if (s) selectedService.value = s;
|
||||
} else if (e.services && e.services.length > 0) {
|
||||
selectedService.value = e.services[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const rawTotal = computed(() => {
|
||||
return (selectedService.value ? selectedService.value.price : 35) * hours.value;
|
||||
});
|
||||
|
||||
const finalTotal = computed(() => {
|
||||
const total = rawTotal.value - discount.value;
|
||||
return total > 0 ? total : 0;
|
||||
});
|
||||
|
||||
const changeHours = (delta) => {
|
||||
hours.value = Math.max(1, Math.min(10, hours.value + delta));
|
||||
};
|
||||
|
||||
const selectCoupon = () => {
|
||||
uni.showToast({ title: '已自动匹配最高优惠券 -¥10', icon: 'none' });
|
||||
};
|
||||
|
||||
const handlePay = () => {
|
||||
uni.showLoading({ title: '正在拉起微信支付...' });
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '支付成功!服务已生成',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.switchTab({
|
||||
url: '/pages/order/list'
|
||||
});
|
||||
}, 1200);
|
||||
}, 1000);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.create-order-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding: 24rpx 32rpx 160rpx;
|
||||
}
|
||||
|
||||
.order-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.expert-profile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.expert-avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.expert-meta {
|
||||
flex: 1;
|
||||
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.expert-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.game-tag {
|
||||
font-size: 20rpx;
|
||||
background: rgba(99, 102, 241, 0.2);
|
||||
color: #818CF8;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.service-title {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.price-single {
|
||||
.price-unit {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stepper-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.stepper-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.label-main {
|
||||
font-size: 28rpx;
|
||||
color: #F8FAFC;
|
||||
font-weight: 600;
|
||||
}
|
||||
.label-sub {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.stepper-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-radius: 16rpx;
|
||||
padding: 6rpx;
|
||||
|
||||
.step-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
line-height: 56rpx;
|
||||
padding: 0;
|
||||
background: #1E2942;
|
||||
color: #FFF;
|
||||
font-size: 32rpx;
|
||||
border-radius: 12rpx;
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.step-val {
|
||||
font-size: 28rpx;
|
||||
color: #F8FAFC;
|
||||
font-weight: 600;
|
||||
margin: 0 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #F8FAFC;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.time-tabs {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
|
||||
.time-tab {
|
||||
flex: 1;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx;
|
||||
text-align: center;
|
||||
|
||||
&.active {
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
border-color: #6366F1;
|
||||
|
||||
.tab-t {
|
||||
color: #818CF8;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-t {
|
||||
font-size: 26rpx;
|
||||
color: #CBD5E1;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.tab-d {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
.cell-label {
|
||||
font-size: 26rpx;
|
||||
color: #CBD5E1;
|
||||
}
|
||||
|
||||
.cell-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
|
||||
.coupon-val {
|
||||
font-size: 24rpx;
|
||||
color: #10B981;
|
||||
font-weight: 600;
|
||||
}
|
||||
.arrow {
|
||||
font-size: 24rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.remark-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 20rpx;
|
||||
|
||||
.cell-label {
|
||||
font-size: 24rpx;
|
||||
color: #64748B;
|
||||
width: 220rpx;
|
||||
}
|
||||
|
||||
.remark-input {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.ph {
|
||||
color: #475569;
|
||||
}
|
||||
}
|
||||
|
||||
.fee-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.fee-k {
|
||||
color: #94A3B8;
|
||||
}
|
||||
.fee-v {
|
||||
color: #E2E8F0;
|
||||
font-weight: 500;
|
||||
|
||||
&.green {
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
|
||||
&.total-row {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
padding-top: 16rpx;
|
||||
margin-top: 16rpx;
|
||||
margin-bottom: 0;
|
||||
|
||||
.fee-k {
|
||||
font-size: 26rpx;
|
||||
color: #F8FAFC;
|
||||
font-weight: 600;
|
||||
}
|
||||
.pay-val {
|
||||
font-size: 34rpx;
|
||||
color: #10B981;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 120rpx;
|
||||
background-color: rgba(21, 29, 48, 0.95);
|
||||
backdrop-filter: blur(12px);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
z-index: 100;
|
||||
|
||||
.amount-wrap {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
.amount-label {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
.amount-num {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-pay {
|
||||
background: linear-gradient(135deg, #10B981 0%, #059669 100%);
|
||||
color: #FFF;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
border-radius: 44rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
padding: 0 60rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(16, 185, 129, 0.35);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
412
pwuniapp/pages/order/list.vue
普通文件
412
pwuniapp/pages/order/list.vue
普通文件
@@ -0,0 +1,412 @@
|
||||
<template>
|
||||
<view class="order-list-container">
|
||||
<!-- 顶部状态分类 Tab -->
|
||||
<view class="tab-header">
|
||||
<view
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
class="tab-item btn-active"
|
||||
:class="{ active: currentTab === tab.key }"
|
||||
@click="switchTab(tab.key)"
|
||||
>
|
||||
<text class="tab-text">{{ tab.name }}</text>
|
||||
<view class="tab-indicator" v-if="currentTab === tab.key"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<view class="orders-wrap">
|
||||
<view
|
||||
v-for="order in filteredOrders"
|
||||
:key="order.id"
|
||||
class="order-card"
|
||||
>
|
||||
<!-- 订单卡片顶部 -->
|
||||
<view class="order-head">
|
||||
<text class="order-sn">单号:{{ order.orderSn }}</text>
|
||||
<view class="status-pill" :class="order.status">
|
||||
{{ getStatusText(order.status) }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 陪玩与服务信息 -->
|
||||
<view class="expert-block">
|
||||
<image class="expert-avatar" :src="order.expert.avatar" mode="aspectFill" />
|
||||
<view class="expert-meta">
|
||||
<view class="name-row">
|
||||
<text class="expert-name">{{ order.expert.nickname }}</text>
|
||||
<text class="game-tag">{{ order.expert.game }}</text>
|
||||
</view>
|
||||
<text class="service-name">{{ order.serviceTitle }}</text>
|
||||
<text class="order-time">下单时间:{{ order.createdAt }}</text>
|
||||
</view>
|
||||
<view class="amount-box">
|
||||
<text class="amount-val">¥{{ order.totalAmount }}</text>
|
||||
<text class="quantity-text">共 {{ order.quantity }} 小时</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部业务操作按钮组 -->
|
||||
<view class="order-actions">
|
||||
<template v-if="order.status === 'PENDING_PAY'">
|
||||
<button class="action-btn cancel-btn btn-active" @click="cancelOrder(order.id)">取消</button>
|
||||
<button class="action-btn primary-btn btn-active" @click="payOrder(order.id)">立即付款</button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="order.status === 'IN_SERVICE'">
|
||||
<button class="action-btn chat-btn btn-active" @click="contactExpert(order.expert.nickname)">联系大神</button>
|
||||
<button class="action-btn refund-btn btn-active" @click="applyRefund(order.id)">申请退款</button>
|
||||
<button class="action-btn success-btn btn-active" @click="confirmComplete(order.id)">确认验收</button>
|
||||
</template>
|
||||
|
||||
<template v-else-if="order.status === 'COMPLETED'">
|
||||
<button class="action-btn chat-btn btn-active" @click="reBook(order.expert.id)">再次预约</button>
|
||||
<button class="action-btn rate-btn btn-active" @click="rateExpert(order.id)">评价大神</button>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="filteredOrders.length === 0" class="empty-state">
|
||||
<text class="empty-icon">📦</text>
|
||||
<text class="empty-text">暂无此类订单</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { ORDERS_LIST } from '@/common/mock.js';
|
||||
|
||||
const tabs = [
|
||||
{ key: 'ALL', name: '全部' },
|
||||
{ key: 'IN_SERVICE', name: '进行中' },
|
||||
{ key: 'PENDING_PAY', name: '待付款' },
|
||||
{ key: 'COMPLETED', name: '已完成' },
|
||||
];
|
||||
|
||||
const currentTab = ref('ALL');
|
||||
const orders = ref(ORDERS_LIST);
|
||||
|
||||
const filteredOrders = computed(() => {
|
||||
if (currentTab.value === 'ALL') return orders.value;
|
||||
return orders.value.filter(o => o.status === currentTab.value);
|
||||
});
|
||||
|
||||
const switchTab = (tabKey) => {
|
||||
currentTab.value = tabKey;
|
||||
};
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const map = {
|
||||
PENDING_PAY: '待付款',
|
||||
IN_SERVICE: '服务中',
|
||||
COMPLETED: '已完成',
|
||||
REFUNDING: '退款中',
|
||||
};
|
||||
return map[status] || status;
|
||||
};
|
||||
|
||||
const cancelOrder = (id) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消此订单吗?',
|
||||
confirmColor: '#EF4444',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
orders.value = orders.value.filter(o => o.id !== id);
|
||||
uni.showToast({ title: '订单已取消', icon: 'none' });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const payOrder = (id) => {
|
||||
uni.showToast({ title: '拉起微信支付成功', icon: 'success' });
|
||||
const o = orders.value.find(item => item.id === id);
|
||||
if (o) o.status = 'IN_SERVICE';
|
||||
};
|
||||
|
||||
const contactExpert = (name) => {
|
||||
uni.showToast({ title: `已呼叫大神【${name}】进入开黑语音`, icon: 'none' });
|
||||
};
|
||||
|
||||
const applyRefund = (id) => {
|
||||
uni.showModal({
|
||||
title: '申请退款/售后仲裁',
|
||||
content: '申请后客服及平台专家将审核对局录像与连麦记录,款项将原路退回。',
|
||||
confirmText: '确认申请',
|
||||
confirmColor: '#EF4444',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const o = orders.value.find(item => item.id === id);
|
||||
if (o) o.status = 'REFUNDING';
|
||||
uni.showToast({ title: '退款申请已提交,等待审核', icon: 'none' });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const confirmComplete = (id) => {
|
||||
uni.showModal({
|
||||
title: '确认验收',
|
||||
content: '确认大神已圆满完成陪玩时长?确认后款项将结算入大神钱包。',
|
||||
confirmText: '确认验收',
|
||||
confirmColor: '#10B981',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const o = orders.value.find(item => item.id === id);
|
||||
if (o) o.status = 'COMPLETED';
|
||||
uni.showToast({ title: '验收完成,去给大神好评吧!', icon: 'success' });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const reBook = (expertId) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/expert/detail?id=${expertId}`
|
||||
});
|
||||
};
|
||||
|
||||
const rateExpert = (id) => {
|
||||
uni.showToast({ title: '评价功能已就绪,感谢支持!', icon: 'success' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-list-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding: 20rpx 32rpx 140rpx;
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
display: flex;
|
||||
background-color: #151D30;
|
||||
border-radius: 20rpx;
|
||||
padding: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 18rpx 0;
|
||||
position: relative;
|
||||
border-radius: 14rpx;
|
||||
|
||||
.tab-text {
|
||||
font-size: 26rpx;
|
||||
color: #94A3B8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(99, 102, 241, 0.18);
|
||||
|
||||
.tab-text {
|
||||
color: #FFFFFF;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tab-indicator {
|
||||
position: absolute;
|
||||
bottom: 4rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 32rpx;
|
||||
height: 6rpx;
|
||||
background: #6366F1;
|
||||
border-radius: 3rpx;
|
||||
box-shadow: 0 0 10rpx #6366F1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.orders-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
|
||||
.order-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.3);
|
||||
|
||||
.order-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 16rpx;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
.order-sn {
|
||||
font-size: 22rpx;
|
||||
color: #64748B;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.status-pill {
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 12rpx;
|
||||
font-weight: 600;
|
||||
|
||||
&.PENDING_PAY {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: #F59E0B;
|
||||
}
|
||||
&.IN_SERVICE {
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: #10B981;
|
||||
}
|
||||
&.COMPLETED {
|
||||
background: rgba(148, 163, 184, 0.15);
|
||||
color: #94A3B8;
|
||||
}
|
||||
&.REFUNDING {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
color: #EF4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.expert-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.expert-avatar {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 44rpx;
|
||||
margin-right: 18rpx;
|
||||
}
|
||||
|
||||
.expert-meta {
|
||||
flex: 1;
|
||||
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
.expert-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.game-tag {
|
||||
font-size: 20rpx;
|
||||
color: #818CF8;
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
display: block;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.order-time {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
|
||||
.amount-box {
|
||||
text-align: right;
|
||||
|
||||
.amount-val {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #10B981;
|
||||
display: block;
|
||||
}
|
||||
.quantity-text {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
|
||||
.action-btn {
|
||||
height: 60rpx;
|
||||
line-height: 58rpx;
|
||||
padding: 0 26rpx;
|
||||
font-size: 22rpx;
|
||||
border-radius: 30rpx;
|
||||
margin: 0;
|
||||
|
||||
&.cancel-btn {
|
||||
background: transparent;
|
||||
color: #94A3B8;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
&.primary-btn {
|
||||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||||
color: #FFF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.chat-btn {
|
||||
background: rgba(99, 102, 241, 0.12);
|
||||
color: #818CF8;
|
||||
border: 1px solid rgba(99, 102, 241, 0.25);
|
||||
}
|
||||
|
||||
&.refund-btn {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: #EF4444;
|
||||
border: 1px solid rgba(239, 68, 68, 0.25);
|
||||
}
|
||||
|
||||
&.success-btn {
|
||||
background: linear-gradient(135deg, #10B981 0%, #059669 100%);
|
||||
color: #FFF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.rate-btn {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: #F59E0B;
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64rpx;
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.empty-text {
|
||||
color: #64748B;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
468
pwuniapp/pages/user/index.vue
普通文件
468
pwuniapp/pages/user/index.vue
普通文件
@@ -0,0 +1,468 @@
|
||||
<template>
|
||||
<view class="user-container">
|
||||
<!-- 用户头部个人资料卡 -->
|
||||
<view class="profile-card">
|
||||
<image class="user-avatar" :src="user.avatar" mode="aspectFill" />
|
||||
<view class="user-info">
|
||||
<view class="name-row">
|
||||
<text class="user-name">{{ user.nickname }}</text>
|
||||
<text class="vip-badge">VIP 黄金玩家</text>
|
||||
</view>
|
||||
<text class="user-uid">ID: {{ user.id }}</text>
|
||||
</view>
|
||||
<view class="edit-btn btn-active" @click="editProfile">
|
||||
<text class="edit-icon">⚙️</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 核心亮点:双身份流光切换卡 -->
|
||||
<view class="role-switch-card card-active" @click="toggleRole">
|
||||
<view class="role-glow-border"></view>
|
||||
<view class="role-content">
|
||||
<view class="role-left">
|
||||
<view class="role-title-row">
|
||||
<text class="current-role-tag">{{ currentRole === 'PLAYER' ? '🎮 当前身份:普通玩家' : '👑 当前身份:接单大神' }}</text>
|
||||
</view>
|
||||
<text class="role-sub">
|
||||
{{ currentRole === 'PLAYER' ? '一键切换至大神接单工作台,开始接单赚收益' : '一键切换回玩家大厅,自由寻找开黑大神与车队' }}
|
||||
</text>
|
||||
</view>
|
||||
<button class="btn-switch-role btn-active">
|
||||
{{ currentRole === 'PLAYER' ? '切换为大神 ⚡' : '切换回玩家 🔄' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 钱包与资产看板 -->
|
||||
<view class="wallet-card">
|
||||
<view class="wallet-head">
|
||||
<text class="wallet-title">我的资产</text>
|
||||
<text class="wallet-detail" @click="viewWalletLog">账单明细 ></text>
|
||||
</view>
|
||||
|
||||
<view class="asset-grid">
|
||||
<view class="asset-col">
|
||||
<text class="asset-label">可用余额 (元)</text>
|
||||
<text class="asset-val green-val">¥{{ user.balance }}</text>
|
||||
<button class="asset-btn recharge btn-active" @click="handleRecharge">充值</button>
|
||||
</view>
|
||||
<view class="asset-divider"></view>
|
||||
<view class="asset-col">
|
||||
<text class="asset-label">分销佣金收益 (元)</text>
|
||||
<text class="asset-val gold-val">¥{{ user.commission }}</text>
|
||||
<button class="asset-btn withdraw btn-active" @click="handleWithdraw">提现</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 核心功能金刚区 -->
|
||||
<view class="menu-grid-card">
|
||||
<view class="menu-item btn-active" @click="navTo('/pages/activity/my-ticket')">
|
||||
<view class="menu-icon bg-green">🎟️</view>
|
||||
<text class="menu-title">我的门票</text>
|
||||
<text class="menu-desc">4位核销码</text>
|
||||
</view>
|
||||
<view class="menu-item btn-active" @click="showInvitePoster">
|
||||
<view class="menu-icon bg-purple">🤝</view>
|
||||
<text class="menu-title">推广赚钱</text>
|
||||
<text class="menu-desc">二级返佣</text>
|
||||
</view>
|
||||
<view class="menu-item btn-active" @click="navTo('/pages/order/list')">
|
||||
<view class="menu-icon bg-blue">📋</view>
|
||||
<text class="menu-title">所有订单</text>
|
||||
<text class="menu-desc">对局与退款</text>
|
||||
</view>
|
||||
<view class="menu-item btn-active" @click="applyExpert">
|
||||
<view class="menu-icon bg-orange">✨</view>
|
||||
<text class="menu-title">大神认证</text>
|
||||
<text class="menu-desc">入驻接单</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表式功能菜单 -->
|
||||
<view class="list-menu-card">
|
||||
<view class="list-row btn-active" @click="contactService">
|
||||
<text class="row-icon">🎧</text>
|
||||
<text class="row-label">官方在线客服</text>
|
||||
<text class="row-arrow">></text>
|
||||
</view>
|
||||
<view class="list-row btn-active" @click="openNotice">
|
||||
<text class="row-icon">🔔</text>
|
||||
<text class="row-label">平台消息与通知</text>
|
||||
<text class="row-arrow">></text>
|
||||
</view>
|
||||
<view class="list-row btn-active" @click="openAbout">
|
||||
<text class="row-icon">🛡️</text>
|
||||
<text class="row-label">平台规范与服务协议</text>
|
||||
<text class="row-arrow">></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { USER_PROFILE } from '@/common/mock.js';
|
||||
|
||||
const user = ref(USER_PROFILE);
|
||||
const currentRole = ref('PLAYER');
|
||||
|
||||
const toggleRole = () => {
|
||||
if (currentRole.value === 'PLAYER') {
|
||||
currentRole.value = 'EXPERT';
|
||||
uni.showToast({
|
||||
title: '已无缝切换为【大神接单端】!',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
});
|
||||
} else {
|
||||
currentRole.value = 'PLAYER';
|
||||
uni.showToast({
|
||||
title: '已切换回【普通玩家端】!',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const editProfile = () => {
|
||||
uni.showToast({ title: '个人信息设置已就绪', icon: 'none' });
|
||||
};
|
||||
|
||||
const viewWalletLog = () => {
|
||||
uni.showToast({ title: '已加载近期收支明细', icon: 'none' });
|
||||
};
|
||||
|
||||
const handleRecharge = () => {
|
||||
uni.showToast({ title: '微信充值通道已拉起', icon: 'none' });
|
||||
};
|
||||
|
||||
const handleWithdraw = () => {
|
||||
uni.showModal({
|
||||
title: '佣金提现申请',
|
||||
content: `当前可提现佣金为 ¥${user.value.commission},提现将自动发起微信企业付款打款到微信零钱。`,
|
||||
confirmText: '确认提现',
|
||||
confirmColor: '#10B981',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '提现申请已提交,将在1个工作日内到账', icon: 'success' });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const showInvitePoster = () => {
|
||||
uni.showModal({
|
||||
title: '🎉 二级分销推广',
|
||||
content: `您的专属邀请码为:PW${user.value.id}\n已成功邀请 ${user.value.invitedCount} 位好友!\n好友下单您将实时享受最高 15% 自动分润提成。`,
|
||||
showCancel: false,
|
||||
confirmText: '保存太阳码'
|
||||
});
|
||||
};
|
||||
|
||||
const applyExpert = () => {
|
||||
uni.showToast({ title: '已提交大神实名技能审核', icon: 'none' });
|
||||
};
|
||||
|
||||
const contactService = () => {
|
||||
uni.showToast({ title: '正在连接 7x24h 官方电竞客服', icon: 'none' });
|
||||
};
|
||||
|
||||
const openNotice = () => {
|
||||
uni.showToast({ title: '暂无未读系统公告', icon: 'none' });
|
||||
};
|
||||
|
||||
const openAbout = () => {
|
||||
uni.showModal({
|
||||
title: '平台服务协议',
|
||||
content: '严禁发布任何违规言论及作弊挂机行为,平台全面保障用户资金安全与绿色电竞环境。',
|
||||
showCancel: false
|
||||
});
|
||||
};
|
||||
|
||||
const navTo = (url) => {
|
||||
if (url === '/pages/order/list' || url === '/pages/activity/list') {
|
||||
uni.switchTab({ url });
|
||||
} else {
|
||||
uni.navigateTo({ url });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-container {
|
||||
min-height: 100vh;
|
||||
background-color: #0A0F1D;
|
||||
padding: 24rpx 32rpx 140rpx;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 4rpx solid #6366F1;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
|
||||
.name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.vip-badge {
|
||||
font-size: 18rpx;
|
||||
background: linear-gradient(135deg, #F59E0B, #D97706);
|
||||
color: #FFF;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.user-uid {
|
||||
font-size: 22rpx;
|
||||
color: #64748B;
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
background: #151D30;
|
||||
border-radius: 34rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
|
||||
.edit-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.role-switch-card {
|
||||
position: relative;
|
||||
border-radius: 24rpx;
|
||||
background: linear-gradient(135deg, #1E1B4B 0%, #0F172A 100%);
|
||||
border: 2rpx solid #6366F1;
|
||||
box-shadow: 0 8rpx 28rpx rgba(99, 102, 241, 0.35);
|
||||
overflow: hidden;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.role-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.role-left {
|
||||
flex: 1;
|
||||
margin-right: 20rpx;
|
||||
|
||||
.current-role-tag {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.role-sub {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-switch-role {
|
||||
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||||
color: #FFF;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
border-radius: 32rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 64rpx;
|
||||
line-height: 64rpx;
|
||||
margin: 0;
|
||||
box-shadow: 0 4rpx 16rpx rgba(99, 102, 241, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.wallet-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.wallet-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
.wallet-detail {
|
||||
font-size: 22rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
|
||||
.asset-grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
.asset-col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.asset-label {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.asset-val {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
margin-bottom: 14rpx;
|
||||
|
||||
&.green-val {
|
||||
color: #10B981;
|
||||
}
|
||||
&.gold-val {
|
||||
color: #F59E0B;
|
||||
}
|
||||
}
|
||||
|
||||
.asset-btn {
|
||||
font-size: 20rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 48rpx;
|
||||
line-height: 46rpx;
|
||||
border-radius: 24rpx;
|
||||
margin: 0;
|
||||
|
||||
&.recharge {
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: #10B981;
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
&.withdraw {
|
||||
background: rgba(245, 158, 11, 0.15);
|
||||
color: #F59E0B;
|
||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.asset-divider {
|
||||
width: 1px;
|
||||
height: 80rpx;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-grid-card {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.menu-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.menu-icon {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 34rpx;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
&.bg-green { background: rgba(16, 185, 129, 0.2); }
|
||||
&.bg-purple { background: rgba(139, 92, 246, 0.2); }
|
||||
&.bg-blue { background: rgba(99, 102, 241, 0.2); }
|
||||
&.bg-orange { background: rgba(245, 158, 11, 0.2); }
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 24rpx;
|
||||
color: #F8FAFC;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
.menu-desc {
|
||||
font-size: 18rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-menu-card {
|
||||
background-color: #151D30;
|
||||
border-radius: 24rpx;
|
||||
padding: 8rpx 28rpx;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
|
||||
.list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 26rpx 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.row-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.row-label {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #E2E8F0;
|
||||
}
|
||||
|
||||
.row-arrow {
|
||||
font-size: 24rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
二进制
pwuniapp/static/logo.png
普通文件
二进制
pwuniapp/static/logo.png
普通文件
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 3.9 KiB |
@@ -0,0 +1,14 @@
|
||||
uni.addInterceptor({
|
||||
returnValue(res) {
|
||||
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then ===
|
||||
"function")) {
|
||||
return res;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
res.then((res) => {
|
||||
if (!res) return resolve(res)
|
||||
return res[0] ? reject(res[0]) : resolve(res[1])
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
44
pwuniapp/uni.scss
普通文件
44
pwuniapp/uni.scss
普通文件
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* uni-app 内置样式变量与电竞暗夜流光设计系统 (Cyber-Esports Dark Elegance)
|
||||
*/
|
||||
|
||||
/* 1. 全局背景与表面色阶 */
|
||||
$bg-primary: #0A0F1D; // 页面最底色(深邃夜空黑)
|
||||
$bg-surface: #151D30; // 卡片/模块容器底色
|
||||
$bg-surface-elevated: #1E2942; // 浮层/弹窗底色
|
||||
$bg-surface-glass: rgba(21, 29, 48, 0.85); // 拟态磨砂玻璃
|
||||
|
||||
/* 2. 品牌核心色与流光 */
|
||||
$color-brand: #6366F1; // 核心电竞靛蓝
|
||||
$color-brand-light: #818CF8; // 亮色高光
|
||||
$color-brand-purple: #8B5CF6; // 紫罗兰流光
|
||||
$color-brand-glow: rgba(99, 102, 241, 0.35);
|
||||
|
||||
/* 3. 业务强调与状态色 */
|
||||
$color-accent: #10B981; // 翠绿高光(活动项目、在线呼吸灯、核销码)
|
||||
$color-accent-glow: rgba(16, 185, 129, 0.3);
|
||||
$color-warning: #F59E0B; // 暖橙金(金牌段位、倒计时)
|
||||
$color-danger: #EF4444; // 警示红(退款、仲裁)
|
||||
|
||||
/* 4. 文本与排版无障碍色阶 (满足 WCAG AA 标准) */
|
||||
$text-primary: #F8FAFC; // 一级标题与主要文字 (纯白)
|
||||
$text-secondary: #94A3B8; // 二级描述文字 (冷灰蓝)
|
||||
$text-muted: #64748B; // 三级占位说明文字
|
||||
$border-subtle: #1E293B; // 微弱分割线与描边
|
||||
|
||||
/* 5. 圆角阶梯 */
|
||||
$radius-sm: 8rpx;
|
||||
$radius-md: 16rpx;
|
||||
$radius-lg: 24rpx;
|
||||
$radius-xl: 32rpx;
|
||||
$radius-full: 9999rpx;
|
||||
|
||||
/* 6. 统一阴影与电竞流光 */
|
||||
$shadow-card: 0 8rpx 24rpx rgba(0, 0, 0, 0.4);
|
||||
$shadow-brand: 0 8rpx 24rpx rgba(99, 102, 241, 0.35);
|
||||
$shadow-accent: 0 8rpx 24rpx rgba(16, 185, 129, 0.3);
|
||||
|
||||
/* 默认变量兼容器 */
|
||||
$uni-color-primary: #6366F1;
|
||||
$uni-bg-color: #0A0F1D;
|
||||
$uni-text-color: #F8FAFC;
|
||||
在新工单中引用
屏蔽一个用户