feat(项目初始化 by claude code)
This commit is contained in:
commit
c767d35d32
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# uni-app
|
||||
node_modules/
|
||||
unpackage/
|
||||
dist/
|
||||
.hbuilderx/
|
||||
|
||||
# npm
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
168
App.vue
Normal file
168
App.vue
Normal file
@ -0,0 +1,168 @@
|
||||
<script setup>
|
||||
import { onLaunch, onShow } from '@dcloudio/uni-app'
|
||||
|
||||
onLaunch(() => {
|
||||
console.log('App Launch - 电竞服务平台启动')
|
||||
|
||||
// 检查租户ID (从扫码参数获取)
|
||||
const tenantId = uni.getStorageSync('tenantId')
|
||||
if (!tenantId) {
|
||||
// 测试环境默认租户
|
||||
uni.setStorageSync('tenantId', 10001)
|
||||
}
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
console.log('App Show')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* 电竞赛博朋克风格 - 全局样式 */
|
||||
@import './uni.scss';
|
||||
|
||||
page {
|
||||
background: linear-gradient(180deg, #0a0e27 0%, #0f1229 50%, #1a1d3a 100%);
|
||||
min-height: 100vh;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB',
|
||||
'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
/* 通用容器 */
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
/* 赛博朋克卡片基础样式 */
|
||||
.cyber-card {
|
||||
background: linear-gradient(135deg, rgba(20, 25, 50, 0.9) 0%, rgba(10, 14, 39, 0.8) 100%);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 16rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 255, 255, 0.5) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
animation: scan 3s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
/* 扫描动画 */
|
||||
@keyframes scan {
|
||||
0%, 100% { opacity: 0; transform: translateY(0); }
|
||||
50% { opacity: 1; transform: translateY(100px); }
|
||||
}
|
||||
|
||||
/* 霓虹发光文字 */
|
||||
.neon-text {
|
||||
color: #00ffff;
|
||||
text-shadow:
|
||||
0 0 5px rgba(0, 255, 255, 0.5),
|
||||
0 0 10px rgba(0, 255, 255, 0.3),
|
||||
0 0 15px rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 主要按钮 - 赛博朋克风格 */
|
||||
.cyber-button {
|
||||
background: linear-gradient(135deg, #00ffff 0%, #0099ff 100%);
|
||||
color: #0a0e27;
|
||||
border: none;
|
||||
border-radius: 8rpx;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
&:active::after {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 次要按钮 */
|
||||
.cyber-button-outline {
|
||||
background: transparent;
|
||||
color: #00ffff;
|
||||
border: 1px solid #00ffff;
|
||||
border-radius: 8rpx;
|
||||
|
||||
&:active {
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 标签 */
|
||||
.cyber-tag {
|
||||
display: inline-block;
|
||||
padding: 4rpx 12rpx;
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 4rpx;
|
||||
color: #00ffff;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.cyber-divider {
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 255, 255, 0.3) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading-dot {
|
||||
display: inline-block;
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 50%;
|
||||
background: #00ffff;
|
||||
margin: 0 4rpx;
|
||||
animation: pulse 1.4s infinite ease-in-out;
|
||||
|
||||
&:nth-child(1) { animation-delay: -0.32s; }
|
||||
&:nth-child(2) { animation-delay: -0.16s; }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 80%, 100% {
|
||||
box-shadow: 0 0 0 0 rgba(0, 255, 255, 0.7);
|
||||
transform: scale(0.8);
|
||||
}
|
||||
40% {
|
||||
box-shadow: 0 0 10px 5px rgba(0, 255, 255, 0);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 渐变文字 */
|
||||
.gradient-text {
|
||||
background: linear-gradient(135deg, #00ffff 0%, #ff00ff 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
</style>
|
||||
230
README.md
Normal file
230
README.md
Normal file
@ -0,0 +1,230 @@
|
||||
# 🎮 游戏服务交易平台 - 电竞赛博朋克风格
|
||||
|
||||
> 基于 uni-app 开发的游戏服务交易平台微信小程序,采用电竞赛博朋克视觉风格,融合游戏元素、科技感和竞技感。
|
||||
|
||||
## ✨ 项目特色
|
||||
|
||||
### 🎨 视觉设计
|
||||
- **电竞赛博朋克风格**: 霓虹色、数字雨效果、科技光效
|
||||
- **游戏化UI**: 游戏主题图标、竞技感配色、等级系统
|
||||
- **动效丰富**: 扫描线动画、霓虹发光、粒子效果
|
||||
- **深色主题**: 深蓝黑背景,青色霓虹主色调
|
||||
|
||||
### 🛠️ 技术亮点
|
||||
- **Vue 3 Composition API**: 最新的Vue3语法
|
||||
- **静态数据Mock**: 完整的Mock数据支持,无需后端即可运行
|
||||
- **组件化开发**: 高度复用的公共组件
|
||||
- **响应式设计**: 适配各种屏幕尺寸
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
game-service-miniapp/
|
||||
├── pages/ # 页面
|
||||
│ ├── index/ # 首页
|
||||
│ │ └── index.vue # 游戏分类、热门推荐
|
||||
│ ├── category/ # 分类
|
||||
│ │ └── list.vue # 服务列表(筛选、排序)
|
||||
│ ├── service/ # 服务
|
||||
│ │ └── detail.vue # 服务详情
|
||||
│ ├── order/ # 订单
|
||||
│ │ ├── create.vue # 创建订单
|
||||
│ │ ├── list.vue # 订单列表
|
||||
│ │ └── detail.vue # 订单详情
|
||||
│ └── user/ # 用户
|
||||
│ └── index.vue # 个人中心
|
||||
│
|
||||
├── components/ # 公共组件
|
||||
│ ├── service-card/ # 服务卡片组件
|
||||
│ ├── order-card/ # 订单卡片组件
|
||||
│ └── game-category/ # 游戏分类组件
|
||||
│
|
||||
├── mock/ # Mock数据
|
||||
│ ├── categories.js # 游戏分类数据
|
||||
│ ├── services.js # 服务数据
|
||||
│ ├── orders.js # 订单数据
|
||||
│ ├── user.js # 用户数据
|
||||
│ ├── players.js # 选手数据
|
||||
│ ├── evaluations.js # 评价数据
|
||||
│ └── index.js # 统一导出
|
||||
│
|
||||
├── static/ # 静态资源
|
||||
│ ├── images/ # 图片
|
||||
│ └── icons/ # 图标
|
||||
│
|
||||
├── App.vue # 应用入口
|
||||
├── main.js # 主入口
|
||||
├── uni.scss # 全局样式变量
|
||||
├── pages.json # 页面配置
|
||||
├── manifest.json # 应用配置
|
||||
└── package.json # 项目配置
|
||||
```
|
||||
|
||||
## 🎯 核心功能
|
||||
|
||||
### 1. 首页
|
||||
- ✅ 搜索功能入口
|
||||
- ✅ 游戏分类横向滚动
|
||||
- ✅ 服务类型快捷入口
|
||||
- ✅ 热门服务推荐
|
||||
|
||||
### 2. 服务列表
|
||||
- ✅ 按分类筛选
|
||||
- ✅ 多种排序方式(价格/销量/评分)
|
||||
- ✅ 服务卡片展示
|
||||
|
||||
### 3. 服务详情
|
||||
- ✅ 服务信息展示
|
||||
- ✅ 商家信息
|
||||
- ✅ 用户评价
|
||||
- ✅ 一键下单
|
||||
|
||||
### 4. 订单管理
|
||||
- ✅ 创建订单(表单填写)
|
||||
- ✅ 订单列表(Tab分类)
|
||||
- ✅ 订单详情(状态流转)
|
||||
- ✅ 订单操作(支付/取消/确认/评价)
|
||||
|
||||
### 5. 个人中心
|
||||
- ✅ 用户信息展示
|
||||
- ✅ 资产信息(余额/积分/优惠券)
|
||||
- ✅ 订单统计
|
||||
- ✅ 功能菜单
|
||||
|
||||
## 🎨 设计系统
|
||||
|
||||
### 配色方案
|
||||
```scss
|
||||
// 主色调
|
||||
$neon-cyan: #00ffff // 青色霓虹(主色)
|
||||
$neon-magenta: #ff00ff // 洋红霓虹
|
||||
$neon-blue: #0099ff // 蓝色霓虹
|
||||
$neon-pink: #ff1493 // 粉色霓虹
|
||||
|
||||
// 背景色
|
||||
$bg-primary: #0a0e27 // 主背景
|
||||
$bg-secondary: #0f1229 // 次背景
|
||||
$bg-tertiary: #1a1d3a // 三级背景
|
||||
|
||||
// 文字颜色
|
||||
$text-primary: #ffffff // 主文字
|
||||
$text-secondary: #a0a4c4 // 次文字
|
||||
$text-tertiary: #7a7e9d // 三级文字
|
||||
```
|
||||
|
||||
### 视觉元素
|
||||
- **霓虹边框**: 青色发光边框
|
||||
- **渐变背景**: 深色渐变卡片背景
|
||||
- **扫描线**: 赛博朋克扫描动画
|
||||
- **光效**: 霓虹光晕效果
|
||||
- **粒子**: 数字雨背景(可选)
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境要求
|
||||
- Node.js >= 14.x
|
||||
- HBuilderX (推荐) 或 微信开发者工具
|
||||
|
||||
### 方式一:使用 HBuilderX (推荐)
|
||||
|
||||
1. **安装 HBuilderX**
|
||||
- 下载地址: https://www.dcloud.io/hbuilderx.html
|
||||
|
||||
2. **导入项目**
|
||||
```
|
||||
HBuilderX > 文件 > 导入 > 从本地目录导入
|
||||
选择 game-service-miniapp 文件夹
|
||||
```
|
||||
|
||||
3. **运行到微信开发者工具**
|
||||
```
|
||||
运行 > 运行到小程序模拟器 > 微信开发者工具
|
||||
```
|
||||
|
||||
### 方式二:使用命令行
|
||||
|
||||
1. **安装依赖**
|
||||
```bash
|
||||
cd game-service-miniapp
|
||||
npm install
|
||||
```
|
||||
|
||||
2. **运行开发环境**
|
||||
```bash
|
||||
npm run dev:mp-weixin
|
||||
```
|
||||
|
||||
3. **用微信开发者工具打开**
|
||||
```
|
||||
打开微信开发者工具
|
||||
导入项目 > 选择 dist/dev/mp-weixin 目录
|
||||
```
|
||||
|
||||
## 📱 功能演示
|
||||
|
||||
### 首页
|
||||
- 顶部搜索栏 + 通知按钮
|
||||
- 游戏分类滑动选择
|
||||
- 服务类型网格
|
||||
- 热门服务瀑布流
|
||||
|
||||
### 服务详情
|
||||
- 大图封面展示
|
||||
- 价格、评分、销量统计
|
||||
- 商家信息卡片
|
||||
- 用户评价列表
|
||||
- 底部下单按钮
|
||||
|
||||
### 订单流程
|
||||
```
|
||||
浏览服务 → 查看详情 → 填写订单 → 确认下单 →
|
||||
待支付 → 待接单 → 进行中 → 待确认 → 已完成 → 评价
|
||||
```
|
||||
|
||||
## 📊 Mock数据说明
|
||||
|
||||
项目包含完整的Mock数据,无需后端即可运行:
|
||||
|
||||
- **8种游戏分类**: 王者荣耀、英雄联盟、VALORANT等
|
||||
- **10+服务套餐**: 代练、陪玩、教学等
|
||||
- **5个订单示例**: 覆盖各种订单状态
|
||||
- **4位选手数据**: 包含头像、评分、接单数
|
||||
- **5条用户评价**: 展示评价系统
|
||||
|
||||
所有数据位于 `/mock` 目录,可根据需求自行修改。
|
||||
|
||||
## 🎯 下一步计划
|
||||
|
||||
- [ ] 接入真实后端API
|
||||
- [ ] 支付功能集成
|
||||
- [ ] IM聊天系统
|
||||
- [ ] 分享功能
|
||||
- [ ] 更多动效优化
|
||||
- [ ] 性能优化
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **图片资源**: 项目使用 picsum.photos 占位图,实际使用时需替换为真实图片
|
||||
2. **AppID配置**: manifest.json 中的 appid 需替换为你的小程序appid
|
||||
3. **API接口**: 当前使用Mock数据,接入后端时需修改 `/api` 目录
|
||||
4. **支付功能**: 支付相关功能为演示,实际使用需接入微信支付
|
||||
|
||||
## 💡 技术栈
|
||||
|
||||
- **框架**: uni-app (Vue 3)
|
||||
- **语言**: JavaScript ES6+
|
||||
- **样式**: SCSS
|
||||
- **状态管理**: Pinia (可选)
|
||||
- **构建工具**: Vue CLI
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT License
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
感谢 uni-app 团队提供的优秀跨端框架!
|
||||
|
||||
---
|
||||
|
||||
**🎮 Powered by Cyber Tech - 让游戏服务更智能**
|
||||
144
components/game-category/index.vue
Normal file
144
components/game-category/index.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<view class="game-category" @click="handleClick">
|
||||
<view class="category-icon" :style="{ background: getCategoryGradient(category.color) }">
|
||||
<text class="icon-text">{{ category.icon }}</text>
|
||||
|
||||
<!-- 霓虹光圈 -->
|
||||
<view class="glow-ring" :style="{ borderColor: category.color }"></view>
|
||||
</view>
|
||||
|
||||
<view class="category-name">{{ category.name }}</view>
|
||||
|
||||
<view v-if="category.hot" class="hot-badge">
|
||||
<text class="hot-text">HOT</text>
|
||||
</view>
|
||||
|
||||
<view v-if="showCount" class="service-count">
|
||||
{{ category.serviceCount }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
category: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
showCount: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click', props.category)
|
||||
}
|
||||
|
||||
const getCategoryGradient = (color) => {
|
||||
return `linear-gradient(135deg, ${color}40 0%, ${color}20 100%)`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.game-category {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 20rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
position: relative;
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
backdrop-filter: blur(10rpx);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.game-category:active .category-icon {
|
||||
border-color: rgba(0, 255, 255, 0.6);
|
||||
box-shadow:
|
||||
0 0 20rpx rgba(0, 255, 255, 0.4),
|
||||
0 0 40rpx rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
font-size: 56rpx;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
position: absolute;
|
||||
top: -4rpx;
|
||||
left: -4rpx;
|
||||
right: -4rpx;
|
||||
bottom: -4rpx;
|
||||
border: 2px solid;
|
||||
border-radius: 26rpx;
|
||||
opacity: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.game-category:active .glow-ring {
|
||||
opacity: 0.6;
|
||||
animation: glow 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.hot-badge {
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
right: 12rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
background: linear-gradient(135deg, rgba(255, 107, 107, 0.9) 0%, rgba(255, 71, 71, 0.9) 100%);
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.4);
|
||||
}
|
||||
|
||||
.hot-text {
|
||||
font-size: 20rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.service-count {
|
||||
font-size: 22rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
</style>
|
||||
327
components/order-card/index.vue
Normal file
327
components/order-card/index.vue
Normal file
@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<view class="order-card cyber-card" @click="handleClick">
|
||||
<!-- 订单头部 -->
|
||||
<view class="order-header">
|
||||
<view class="order-info">
|
||||
<text class="order-no">订单号: {{ order.orderNo }}</text>
|
||||
<view class="status-badge" :style="{ background: getStatusColor(order.status) }">
|
||||
{{ getStatusText(order.status) }}
|
||||
</view>
|
||||
</view>
|
||||
<text class="order-time">{{ formatTime(order.createTime) }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<view class="cyber-divider"></view>
|
||||
|
||||
<!-- 服务信息 -->
|
||||
<view class="service-info">
|
||||
<image :src="order.serviceCover" class="service-cover" mode="aspectFill" />
|
||||
|
||||
<view class="service-detail">
|
||||
<view class="service-name">{{ order.serviceName }}</view>
|
||||
<view class="category-tag">{{ order.categoryName }}</view>
|
||||
|
||||
<!-- 选手信息 (如果已接单) -->
|
||||
<view v-if="order.playerName" class="player-info">
|
||||
<image :src="order.playerAvatar" class="player-avatar" />
|
||||
<text class="player-name">{{ order.playerName }}</text>
|
||||
<text v-if="order.status === 3" class="online-dot">●</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="price-info">
|
||||
<view class="price-value">¥{{ order.actualPrice }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单操作按钮 -->
|
||||
<view class="order-actions">
|
||||
<!-- 待支付 -->
|
||||
<template v-if="order.status === 0">
|
||||
<button class="action-btn btn-cancel" @click.stop="handleCancel">取消订单</button>
|
||||
<button class="action-btn btn-primary" @click.stop="handlePay">立即支付</button>
|
||||
</template>
|
||||
|
||||
<!-- 待接单/已接单 -->
|
||||
<template v-else-if="order.status === 1 || order.status === 2">
|
||||
<button class="action-btn btn-outline" @click.stop="handleContact">联系商家</button>
|
||||
<button class="action-btn btn-cancel" @click.stop="handleCancel">取消订单</button>
|
||||
</template>
|
||||
|
||||
<!-- 进行中 -->
|
||||
<template v-else-if="order.status === 3">
|
||||
<button class="action-btn btn-outline" @click.stop="handleContact">联系选手</button>
|
||||
<button class="action-btn btn-primary" @click.stop="handleDetail">查看详情</button>
|
||||
</template>
|
||||
|
||||
<!-- 待确认 -->
|
||||
<template v-else-if="order.status === 4">
|
||||
<button class="action-btn btn-outline" @click.stop="handleRefund">申请退款</button>
|
||||
<button class="action-btn btn-primary" @click.stop="handleConfirm">确认完成</button>
|
||||
</template>
|
||||
|
||||
<!-- 已完成 -->
|
||||
<template v-else-if="order.status === 5">
|
||||
<button class="action-btn btn-outline" @click.stop="handleDetail">查看详情</button>
|
||||
<button class="action-btn btn-primary" @click.stop="handleEvaluate">去评价</button>
|
||||
</template>
|
||||
|
||||
<!-- 已评价 -->
|
||||
<template v-else-if="order.status === 6">
|
||||
<button class="action-btn btn-outline" @click.stop="handleDetail">查看评价</button>
|
||||
<button class="action-btn btn-primary" @click.stop="handleRebuy">再来一单</button>
|
||||
</template>
|
||||
|
||||
<!-- 已取消 -->
|
||||
<template v-else-if="order.status === 9">
|
||||
<button class="action-btn btn-outline" @click.stop="handleDelete">删除订单</button>
|
||||
<button class="action-btn btn-primary" @click.stop="handleRebuy">再来一单</button>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 霓虹发光效果 -->
|
||||
<view class="scan-line"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
import { OrderStatus } from '../../mock/orders'
|
||||
|
||||
const props = defineProps({
|
||||
order: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click', 'action'])
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click', props.order)
|
||||
}
|
||||
|
||||
const handleAction = (action) => {
|
||||
emit('action', { action, order: props.order })
|
||||
}
|
||||
|
||||
const handleCancel = () => handleAction('cancel')
|
||||
const handlePay = () => handleAction('pay')
|
||||
const handleContact = () => handleAction('contact')
|
||||
const handleDetail = () => handleAction('detail')
|
||||
const handleConfirm = () => handleAction('confirm')
|
||||
const handleEvaluate = () => handleAction('evaluate')
|
||||
const handleRefund = () => handleAction('refund')
|
||||
const handleRebuy = () => handleAction('rebuy')
|
||||
const handleDelete = () => handleAction('delete')
|
||||
|
||||
const getStatusColor = (status) => {
|
||||
const statusMap = {
|
||||
0: 'rgba(255, 170, 0, 0.2)',
|
||||
1: 'rgba(0, 255, 255, 0.2)',
|
||||
2: 'rgba(0, 255, 136, 0.2)',
|
||||
3: 'rgba(0, 153, 255, 0.2)',
|
||||
4: 'rgba(157, 0, 255, 0.2)',
|
||||
5: 'rgba(0, 255, 136, 0.2)',
|
||||
6: 'rgba(122, 126, 157, 0.2)',
|
||||
9: 'rgba(255, 51, 102, 0.2)'
|
||||
}
|
||||
return statusMap[status] || 'rgba(0, 255, 255, 0.2)'
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const statusObj = Object.values(OrderStatus).find(s => s.code === status)
|
||||
return statusObj ? statusObj.text : '未知'
|
||||
}
|
||||
|
||||
const formatTime = (time) => {
|
||||
return time.substring(0, 16)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-card {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 24rpx;
|
||||
background: linear-gradient(135deg, rgba(20, 25, 50, 0.95) 0%, rgba(10, 14, 39, 0.9) 100%);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 16rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.order-no {
|
||||
font-size: 26rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
backdrop-filter: blur(10rpx);
|
||||
}
|
||||
|
||||
.order-time {
|
||||
font-size: 24rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.service-info {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.service-cover {
|
||||
width: 160rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.service-detail {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.category-tag {
|
||||
display: inline-block;
|
||||
padding: 4rpx 12rpx;
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 6rpx;
|
||||
font-size: 22rpx;
|
||||
color: #00ffff;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.player-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.player-avatar {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-size: 24rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.online-dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
color: #00ff88;
|
||||
font-size: 20rpx;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.price-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.order-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 14rpx 32rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.btn-primary {
|
||||
background: linear-gradient(135deg, #00ffff 0%, #0099ff 100%);
|
||||
color: #0a0e27;
|
||||
}
|
||||
|
||||
&.btn-outline {
|
||||
background: transparent;
|
||||
border: 1px solid #00ffff;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
&.btn-cancel {
|
||||
background: rgba(122, 126, 157, 0.2);
|
||||
border: 1px solid rgba(122, 126, 157, 0.4);
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.scan-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(0, 255, 255, 0.6) 50%, transparent 100%);
|
||||
animation: scan 3s infinite;
|
||||
}
|
||||
|
||||
@keyframes scan {
|
||||
0%, 100% {
|
||||
opacity: 0;
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translateY(200rpx);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
304
components/service-card/index.vue
Normal file
304
components/service-card/index.vue
Normal file
@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<view class="service-card cyber-card" @click="handleClick">
|
||||
<!-- 封面图片 -->
|
||||
<view class="card-cover">
|
||||
<image :src="service.coverImage" mode="aspectFill" class="cover-image" />
|
||||
<!-- 渐变遮罩 -->
|
||||
<view class="cover-overlay"></view>
|
||||
|
||||
<!-- 热门标签 -->
|
||||
<view v-if="service.salesCount > 1000" class="hot-badge">
|
||||
<text class="hot-icon">🔥</text>
|
||||
<text class="hot-text">HOT</text>
|
||||
</view>
|
||||
|
||||
<!-- 分类标签 -->
|
||||
<view class="category-tag">
|
||||
{{ service.categoryName }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-content">
|
||||
<!-- 服务名称 -->
|
||||
<view class="service-name">{{ service.name }}</view>
|
||||
|
||||
<!-- 服务描述 -->
|
||||
<view class="service-desc">{{ service.description }}</view>
|
||||
|
||||
<!-- 标签 -->
|
||||
<view class="tags-container">
|
||||
<view v-for="tag in service.tags" :key="tag" class="tag-item">
|
||||
{{ tag }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家信息 -->
|
||||
<view class="merchant-info">
|
||||
<image :src="service.merchantAvatar" class="merchant-avatar" />
|
||||
<text class="merchant-name">{{ service.merchantName }}</text>
|
||||
<text class="player-count">{{ service.playerCount }}位选手</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<view class="card-footer">
|
||||
<view class="price-container">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">{{ service.price }}</text>
|
||||
<text v-if="service.originalPrice > service.price" class="original-price">
|
||||
¥{{ service.originalPrice }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="stats-container">
|
||||
<view class="stat-item">
|
||||
<text class="stat-icon">⭐</text>
|
||||
<text class="stat-text">{{ service.rating }}</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-icon">📦</text>
|
||||
<text class="stat-text">{{ formatSales(service.salesCount) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 霓虹边框效果 -->
|
||||
<view class="neon-border"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const handleClick = () => {
|
||||
emit('click', props.service)
|
||||
}
|
||||
|
||||
const formatSales = (count) => {
|
||||
if (count >= 10000) {
|
||||
return (count / 10000).toFixed(1) + 'w'
|
||||
} else if (count >= 1000) {
|
||||
return (count / 1000).toFixed(1) + 'k'
|
||||
}
|
||||
return count
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.service-card {
|
||||
margin-bottom: 24rpx;
|
||||
background: linear-gradient(135deg, rgba(20, 25, 50, 0.95) 0%, rgba(10, 14, 39, 0.9) 100%);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
border-color: rgba(0, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.card-cover {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 360rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cover-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 120rpx;
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(10, 14, 39, 0.9) 100%);
|
||||
}
|
||||
|
||||
.hot-badge {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 16rpx;
|
||||
background: linear-gradient(135deg, rgba(255, 107, 107, 0.9) 0%, rgba(255, 71, 71, 0.9) 100%);
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.4);
|
||||
}
|
||||
|
||||
.hot-icon {
|
||||
font-size: 24rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.hot-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.category-tag {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
left: 20rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
background: rgba(0, 255, 255, 0.2);
|
||||
border: 1px solid rgba(0, 255, 255, 0.4);
|
||||
border-radius: 8rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
font-size: 24rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 16rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
font-size: 26rpx;
|
||||
color: #a0a4c4;
|
||||
margin-bottom: 16rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
padding: 6rpx 16rpx;
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 6rpx;
|
||||
font-size: 22rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.merchant-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.merchant-avatar {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 12rpx;
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.merchant-name {
|
||||
font-size: 24rpx;
|
||||
color: #a0a4c4;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.player-count {
|
||||
font-size: 22rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.price-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 24rpx;
|
||||
color: #7a7e9d;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.stat-text {
|
||||
font-size: 24rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.neon-border {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 16rpx;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
box-shadow:
|
||||
0 0 20rpx rgba(0, 255, 255, 0.3),
|
||||
0 0 40rpx rgba(0, 255, 255, 0.2),
|
||||
inset 0 0 20rpx rgba(0, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.service-card:active .neon-border {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
16
index.html
Normal file
16
index.html
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<title>电竞服务平台</title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
15
main.js
Normal file
15
main.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { createSSRApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
app.use(pinia)
|
||||
|
||||
return {
|
||||
app,
|
||||
pinia
|
||||
}
|
||||
}
|
||||
27
manifest.json
Normal file
27
manifest.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "电竞服务平台",
|
||||
"appid": "__UNI__GAME_SERVICE",
|
||||
"description": "游戏服务交易平台 - 电竞赛博朋克风格",
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
"mp-weixin": {
|
||||
"appid": "",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true
|
||||
},
|
||||
"usingComponents": true,
|
||||
"permission": {
|
||||
"scope.userLocation": {
|
||||
"desc": "用于查找附近的游戏服务"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"vueVersion": "3"
|
||||
}
|
||||
79
mock/categories.js
Normal file
79
mock/categories.js
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 游戏分类数据
|
||||
*/
|
||||
export const gameCategories = [
|
||||
{
|
||||
id: 1,
|
||||
name: '王者荣耀',
|
||||
icon: '🎮',
|
||||
color: '#ff6b6b',
|
||||
hot: true,
|
||||
serviceCount: 156
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '英雄联盟',
|
||||
icon: '⚔️',
|
||||
color: '#4ecdc4',
|
||||
hot: true,
|
||||
serviceCount: 98
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'VALORANT',
|
||||
icon: '🔫',
|
||||
color: '#a8dadc',
|
||||
hot: false,
|
||||
serviceCount: 45
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'CSGO',
|
||||
icon: '🎯',
|
||||
color: '#f4a261',
|
||||
hot: true,
|
||||
serviceCount: 67
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'DOTA2',
|
||||
icon: '🛡️',
|
||||
color: '#e76f51',
|
||||
hot: false,
|
||||
serviceCount: 34
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '原神',
|
||||
icon: '✨',
|
||||
color: '#2a9d8f',
|
||||
hot: true,
|
||||
serviceCount: 89
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '和平精英',
|
||||
icon: '🔥',
|
||||
color: '#e63946',
|
||||
hot: false,
|
||||
serviceCount: 112
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '永劫无间',
|
||||
icon: '⚡',
|
||||
color: '#457b9d',
|
||||
hot: false,
|
||||
serviceCount: 23
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 服务类型
|
||||
*/
|
||||
export const serviceTypes = [
|
||||
{ id: 1, name: '上分代练', icon: '📈' },
|
||||
{ id: 2, name: '娱乐陪玩', icon: '🎵' },
|
||||
{ id: 3, name: '技术教学', icon: '📚' },
|
||||
{ id: 4, name: '账号租赁', icon: '🎭' }
|
||||
]
|
||||
144
mock/evaluations.js
Normal file
144
mock/evaluations.js
Normal file
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* 评价数据
|
||||
*/
|
||||
export const evaluations = [
|
||||
{
|
||||
id: 1,
|
||||
orderId: 100003,
|
||||
serviceId: 2002,
|
||||
serviceName: 'LOL 娱乐陪玩 御姐音',
|
||||
playerId: 30018,
|
||||
playerName: '冰霜女王',
|
||||
customerId: 20001,
|
||||
customerName: '电竞少年',
|
||||
customerAvatar: 'https://picsum.photos/100/100?random=301',
|
||||
rating: 5,
|
||||
content: '小姐姐声音真的超好听!技术也很好,打得很开心!五星好评!',
|
||||
images: [],
|
||||
isAnonymous: false,
|
||||
reply: '谢谢小哥哥的好评~下次继续约我哦💕',
|
||||
replyTime: '2025-01-26 21:00:00',
|
||||
createTime: '2025-01-26 20:35:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
orderId: 100006,
|
||||
serviceId: 1002,
|
||||
serviceName: '王者荣耀 娱乐陪玩 甜美女声',
|
||||
playerId: 30005,
|
||||
playerName: '甜心小鹿',
|
||||
customerId: 20002,
|
||||
customerName: '匿名用户',
|
||||
customerAvatar: 'https://picsum.photos/100/100?random=302',
|
||||
rating: 5,
|
||||
content: '甜心小姐姐真的太温柔了,声音超甜,还教我玩法师,超级耐心!强烈推荐!',
|
||||
images: [
|
||||
'https://picsum.photos/400/300?random=61',
|
||||
'https://picsum.photos/400/300?random=62'
|
||||
],
|
||||
isAnonymous: true,
|
||||
reply: '谢谢支持呀~很开心能帮到你🦌',
|
||||
replyTime: '2025-01-25 19:30:00',
|
||||
createTime: '2025-01-25 18:45:00'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
orderId: 100007,
|
||||
serviceId: 1003,
|
||||
serviceName: '王者荣耀 一对一教学 国服射手',
|
||||
playerId: 30012,
|
||||
playerName: '狙击之王',
|
||||
customerId: 20003,
|
||||
customerName: '游戏新手',
|
||||
customerAvatar: 'https://picsum.photos/100/100?random=303',
|
||||
rating: 5,
|
||||
content: '教练真的专业!我的后羿从青铜打法变成了钻石水平,讲解很细致,物超所值!',
|
||||
images: [
|
||||
'https://picsum.photos/400/300?random=63'
|
||||
],
|
||||
isAnonymous: false,
|
||||
reply: '加油!继续努力,下个赛季冲王者!',
|
||||
replyTime: '2025-01-24 16:20:00',
|
||||
createTime: '2025-01-24 15:55:00'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
orderId: 100008,
|
||||
serviceId: 6001,
|
||||
serviceName: '原神 深渊12层满星代打',
|
||||
playerId: 30025,
|
||||
playerName: '璃月大佬',
|
||||
customerId: 20004,
|
||||
customerName: '旅行者',
|
||||
customerAvatar: 'https://picsum.photos/100/100?random=304',
|
||||
rating: 4,
|
||||
content: '代打很快,半小时就满星了,就是没看到过程有点遗憾',
|
||||
images: [],
|
||||
isAnonymous: false,
|
||||
reply: '下次可以提前说要录屏哦~',
|
||||
replyTime: '2025-01-23 14:30:00',
|
||||
createTime: '2025-01-23 14:15:00'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
orderId: 100009,
|
||||
serviceId: 2002,
|
||||
serviceName: 'LOL 娱乐陪玩 御姐音',
|
||||
playerId: 30018,
|
||||
playerName: '冰霜女王',
|
||||
customerId: 20005,
|
||||
customerName: '召唤师',
|
||||
customerAvatar: 'https://picsum.photos/100/100?random=305',
|
||||
rating: 5,
|
||||
content: '御姐音太棒了!而且辅助玩得真好,这局躺赢!',
|
||||
images: [],
|
||||
isAnonymous: false,
|
||||
reply: '谢谢夸奖~继续努力💪',
|
||||
replyTime: '2025-01-22 21:45:00',
|
||||
createTime: '2025-01-22 21:30:00'
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 根据服务ID获取评价列表
|
||||
*/
|
||||
export function getEvaluationsByServiceId(serviceId) {
|
||||
return evaluations.filter(e => e.serviceId === serviceId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据选手ID获取评价列表
|
||||
*/
|
||||
export function getEvaluationsByPlayerId(playerId) {
|
||||
return evaluations.filter(e => e.playerId === playerId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 评价统计
|
||||
*/
|
||||
export function getEvaluationStats(serviceId) {
|
||||
const evals = getEvaluationsByServiceId(serviceId)
|
||||
const total = evals.length
|
||||
|
||||
if (total === 0) {
|
||||
return {
|
||||
total: 0,
|
||||
avgRating: 5.0,
|
||||
distribution: { 5: 0, 4: 0, 3: 0, 2: 0, 1: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
const distribution = { 5: 0, 4: 0, 3: 0, 2: 0, 1: 0 }
|
||||
let sum = 0
|
||||
|
||||
evals.forEach(e => {
|
||||
distribution[e.rating]++
|
||||
sum += e.rating
|
||||
})
|
||||
|
||||
return {
|
||||
total,
|
||||
avgRating: (sum / total).toFixed(2),
|
||||
distribution
|
||||
}
|
||||
}
|
||||
29
mock/index.js
Normal file
29
mock/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Mock数据统一导出
|
||||
*/
|
||||
|
||||
export * from './categories'
|
||||
export * from './services'
|
||||
export * from './orders'
|
||||
export * from './user'
|
||||
export * from './players'
|
||||
export * from './evaluations'
|
||||
|
||||
/**
|
||||
* 模拟API延迟
|
||||
*/
|
||||
export function mockDelay(ms = 500) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟API响应
|
||||
*/
|
||||
export async function mockApiResponse(data, delay = 500) {
|
||||
await mockDelay(delay)
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
data
|
||||
}
|
||||
}
|
||||
222
mock/orders.js
Normal file
222
mock/orders.js
Normal file
@ -0,0 +1,222 @@
|
||||
/**
|
||||
* 订单数据
|
||||
*/
|
||||
|
||||
// 订单状态枚举
|
||||
export const OrderStatus = {
|
||||
WAIT_PAY: { code: 0, text: '待支付', color: '#ffaa00' },
|
||||
WAIT_ACCEPT: { code: 1, text: '待接单', color: '#00ffff' },
|
||||
ACCEPTED: { code: 2, text: '已接单', color: '#00ff88' },
|
||||
IN_PROGRESS: { code: 3, text: '进行中', color: '#0099ff' },
|
||||
WAIT_CONFIRM: { code: 4, text: '待确认', color: '#9d00ff' },
|
||||
COMPLETED: { code: 5, text: '已完成', color: '#00ff88' },
|
||||
EVALUATED: { code: 6, text: '已评价', color: '#7a7e9d' },
|
||||
CANCELLED: { code: 9, text: '已取消', color: '#ff3366' }
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单列表
|
||||
*/
|
||||
export const orders = [
|
||||
{
|
||||
id: 100001,
|
||||
orderNo: 'ORD202501281234567',
|
||||
tenantId: 10001,
|
||||
customerId: 20001,
|
||||
serviceId: 1002,
|
||||
serviceName: '王者荣耀 娱乐陪玩 甜美女声',
|
||||
serviceCover: 'https://picsum.photos/400/300?random=2',
|
||||
categoryName: '王者荣耀',
|
||||
price: 35.00,
|
||||
actualPrice: 35.00,
|
||||
status: 3, // 进行中
|
||||
playerId: 30005,
|
||||
playerName: '甜心小鹿',
|
||||
playerAvatar: 'https://picsum.photos/100/100?random=201',
|
||||
merchantName: '星光陪玩',
|
||||
gameInfo: {
|
||||
gameId: 'Sweet_Deer_123',
|
||||
server: '微信区',
|
||||
rank: '钻石II'
|
||||
},
|
||||
contactInfo: {
|
||||
qq: '123456789',
|
||||
wechat: 'sweetdeer'
|
||||
},
|
||||
remark: '希望小姐姐温柔一点~',
|
||||
payType: 'wechat',
|
||||
payTime: '2025-01-28 14:30:00',
|
||||
acceptTime: '2025-01-28 14:32:00',
|
||||
startTime: '2025-01-28 14:35:00',
|
||||
createTime: '2025-01-28 14:28:00'
|
||||
},
|
||||
{
|
||||
id: 100002,
|
||||
orderNo: 'ORD202501271234568',
|
||||
tenantId: 10002,
|
||||
customerId: 20001,
|
||||
serviceId: 1003,
|
||||
serviceName: '王者荣耀 一对一教学 国服射手',
|
||||
serviceCover: 'https://picsum.photos/400/300?random=3',
|
||||
categoryName: '王者荣耀',
|
||||
price: 88.00,
|
||||
actualPrice: 88.00,
|
||||
status: 5, // 已完成
|
||||
playerId: 30012,
|
||||
playerName: '狙击之王',
|
||||
playerAvatar: 'https://picsum.photos/100/100?random=202',
|
||||
merchantName: '巅峰电竞学院',
|
||||
gameInfo: {
|
||||
gameId: 'Sniper_King_Pro',
|
||||
server: 'QQ区',
|
||||
rank: '荣耀王者'
|
||||
},
|
||||
remark: '想学后羿和公孙离',
|
||||
payType: 'wechat',
|
||||
payTime: '2025-01-27 10:15:00',
|
||||
acceptTime: '2025-01-27 10:20:00',
|
||||
startTime: '2025-01-27 10:30:00',
|
||||
finishTime: '2025-01-27 12:30:00',
|
||||
confirmTime: '2025-01-27 12:35:00',
|
||||
createTime: '2025-01-27 10:10:00'
|
||||
},
|
||||
{
|
||||
id: 100003,
|
||||
orderNo: 'ORD202501261234569',
|
||||
tenantId: 10003,
|
||||
customerId: 20001,
|
||||
serviceId: 2002,
|
||||
serviceName: 'LOL 娱乐陪玩 御姐音',
|
||||
serviceCover: 'https://picsum.photos/400/300?random=5',
|
||||
categoryName: '英雄联盟',
|
||||
price: 45.00,
|
||||
actualPrice: 45.00,
|
||||
status: 6, // 已评价
|
||||
playerId: 30018,
|
||||
playerName: '冰霜女王',
|
||||
playerAvatar: 'https://picsum.photos/100/100?random=203',
|
||||
merchantName: '梦幻陪玩',
|
||||
gameInfo: {
|
||||
gameId: 'FrostQueen',
|
||||
server: '艾欧尼亚',
|
||||
rank: '钻石I'
|
||||
},
|
||||
remark: '想玩辅助',
|
||||
evaluation: {
|
||||
rating: 5,
|
||||
content: '小姐姐声音真的超好听!技术也很好,打得很开心!',
|
||||
images: [],
|
||||
createTime: '2025-01-26 20:35:00'
|
||||
},
|
||||
payType: 'wechat',
|
||||
payTime: '2025-01-26 19:00:00',
|
||||
acceptTime: '2025-01-26 19:05:00',
|
||||
startTime: '2025-01-26 19:10:00',
|
||||
finishTime: '2025-01-26 20:10:00',
|
||||
confirmTime: '2025-01-26 20:15:00',
|
||||
createTime: '2025-01-26 18:55:00'
|
||||
},
|
||||
{
|
||||
id: 100004,
|
||||
orderNo: 'ORD202501251234570',
|
||||
tenantId: 10005,
|
||||
customerId: 20001,
|
||||
serviceId: 6001,
|
||||
serviceName: '原神 深渊12层满星代打',
|
||||
serviceCover: 'https://picsum.photos/400/300?random=8',
|
||||
categoryName: '原神',
|
||||
price: 68.00,
|
||||
actualPrice: 68.00,
|
||||
status: 4, // 待确认
|
||||
playerId: 30025,
|
||||
playerName: '璃月大佬',
|
||||
playerAvatar: 'https://picsum.photos/100/100?random=204',
|
||||
merchantName: '提瓦特工作室',
|
||||
gameInfo: {
|
||||
uid: '123456789',
|
||||
server: '天空岛',
|
||||
level: 60
|
||||
},
|
||||
serviceFiles: [
|
||||
'https://picsum.photos/400/300?random=51',
|
||||
'https://picsum.photos/400/300?random=52'
|
||||
],
|
||||
remark: '账号密码:test123',
|
||||
payType: 'wechat',
|
||||
payTime: '2025-01-25 15:20:00',
|
||||
acceptTime: '2025-01-25 15:25:00',
|
||||
startTime: '2025-01-25 15:30:00',
|
||||
createTime: '2025-01-25 15:18:00'
|
||||
},
|
||||
{
|
||||
id: 100005,
|
||||
orderNo: 'ORD202501241234571',
|
||||
tenantId: 10001,
|
||||
customerId: 20001,
|
||||
serviceId: 1001,
|
||||
serviceName: '王者荣耀 星耀→王者 上分',
|
||||
serviceCover: 'https://picsum.photos/400/300?random=1',
|
||||
categoryName: '王者荣耀',
|
||||
price: 198.00,
|
||||
actualPrice: 198.00,
|
||||
status: 1, // 待接单
|
||||
merchantName: '电竞之星工作室',
|
||||
gameInfo: {
|
||||
gameId: 'Player_Pro_888',
|
||||
server: '微信区',
|
||||
currentRank: '星耀II',
|
||||
targetRank: '王者'
|
||||
},
|
||||
remark: '希望3天内完成',
|
||||
payType: 'wechat',
|
||||
payTime: '2025-01-24 09:30:00',
|
||||
createTime: '2025-01-24 09:25:00'
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 根据状态获取订单数量
|
||||
*/
|
||||
export function getOrderCountByStatus(status) {
|
||||
if (status === 'all') return orders.length
|
||||
return orders.filter(o => o.status === status).length
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态获取订单列表
|
||||
*/
|
||||
export function getOrdersByStatus(status) {
|
||||
if (status === 'all') return orders
|
||||
return orders.filter(o => o.status === status)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取订单详情
|
||||
*/
|
||||
export function getOrderById(id) {
|
||||
return orders.find(o => o.id === id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单流转记录
|
||||
*/
|
||||
export function getOrderFlowById(orderId) {
|
||||
// 模拟订单流转记录
|
||||
const flows = {
|
||||
100001: [
|
||||
{ time: '2025-01-28 14:28:00', status: '创建订单', desc: '订单创建成功' },
|
||||
{ time: '2025-01-28 14:30:00', status: '支付完成', desc: '微信支付成功' },
|
||||
{ time: '2025-01-28 14:32:00', status: '选手接单', desc: '甜心小鹿 接单' },
|
||||
{ time: '2025-01-28 14:35:00', status: '开始服务', desc: '服务进行中...' }
|
||||
],
|
||||
100002: [
|
||||
{ time: '2025-01-27 10:10:00', status: '创建订单', desc: '订单创建成功' },
|
||||
{ time: '2025-01-27 10:15:00', status: '支付完成', desc: '微信支付成功' },
|
||||
{ time: '2025-01-27 10:20:00', status: '选手接单', desc: '狙击之王 接单' },
|
||||
{ time: '2025-01-27 10:30:00', status: '开始服务', desc: '开始一对一教学' },
|
||||
{ time: '2025-01-27 12:30:00', status: '服务完成', desc: '选手提交服务资料' },
|
||||
{ time: '2025-01-27 12:35:00', status: '确认完成', desc: '顾客确认服务完成' }
|
||||
]
|
||||
}
|
||||
return flows[orderId] || []
|
||||
}
|
||||
99
mock/players.js
Normal file
99
mock/players.js
Normal file
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 选手数据
|
||||
*/
|
||||
export const players = [
|
||||
{
|
||||
id: 30005,
|
||||
tenantId: 10001,
|
||||
name: '甜心小鹿',
|
||||
avatar: 'https://picsum.photos/100/100?random=201',
|
||||
gender: 'female',
|
||||
age: 22,
|
||||
gameId: 'Sweet_Deer_123',
|
||||
level: '钻石II',
|
||||
rating: 4.96,
|
||||
orderCount: 3456,
|
||||
completeCount: 3234,
|
||||
tags: ['甜美', '温柔', '技术好'],
|
||||
intro: '温柔小姐姐,陪你开心玩游戏~',
|
||||
voiceSample: '',
|
||||
status: 'online',
|
||||
games: [
|
||||
{ name: '王者荣耀', rank: '钻石II', winRate: 65 },
|
||||
{ name: '英雄联盟', rank: '黄金I', winRate: 58 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 30012,
|
||||
tenantId: 10002,
|
||||
name: '狙击之王',
|
||||
avatar: 'https://picsum.photos/100/100?random=202',
|
||||
gender: 'male',
|
||||
age: 25,
|
||||
gameId: 'Sniper_King_Pro',
|
||||
level: '荣耀王者',
|
||||
rating: 5.0,
|
||||
orderCount: 567,
|
||||
completeCount: 556,
|
||||
tags: ['国服', '射手', '教学'],
|
||||
intro: '前职业选手,专注射手教学3年',
|
||||
status: 'busy',
|
||||
games: [
|
||||
{ name: '王者荣耀', rank: '荣耀王者', winRate: 78 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 30018,
|
||||
tenantId: 10003,
|
||||
name: '冰霜女王',
|
||||
avatar: 'https://picsum.photos/100/100?random=203',
|
||||
gender: 'female',
|
||||
age: 24,
|
||||
gameId: 'FrostQueen',
|
||||
level: '钻石I',
|
||||
rating: 4.92,
|
||||
orderCount: 2234,
|
||||
completeCount: 2156,
|
||||
tags: ['御姐', '技术', 'LOL'],
|
||||
intro: '成熟御姐音,带你轻松上分',
|
||||
status: 'online',
|
||||
games: [
|
||||
{ name: '英雄联盟', rank: '钻石I', winRate: 68 },
|
||||
{ name: 'VALORANT', rank: '白金III', winRate: 62 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 30025,
|
||||
tenantId: 10005,
|
||||
name: '璃月大佬',
|
||||
avatar: 'https://picsum.photos/100/100?random=204',
|
||||
gender: 'male',
|
||||
age: 23,
|
||||
gameId: 'Liyue_Master',
|
||||
level: '满级',
|
||||
rating: 4.97,
|
||||
orderCount: 1567,
|
||||
completeCount: 1534,
|
||||
tags: ['满命', '深渊', '快速'],
|
||||
intro: '原神全角色满命,深渊专家',
|
||||
status: 'online',
|
||||
games: [
|
||||
{ name: '原神', rank: '满级', winRate: 100 }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 根据ID获取选手信息
|
||||
*/
|
||||
export function getPlayerById(id) {
|
||||
return players.find(p => p.id === id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据服务ID获取可用选手列表
|
||||
*/
|
||||
export function getPlayersByServiceId(serviceId) {
|
||||
// 简化处理:返回前3个选手
|
||||
return players.slice(0, 3)
|
||||
}
|
||||
235
mock/services.js
Normal file
235
mock/services.js
Normal file
@ -0,0 +1,235 @@
|
||||
/**
|
||||
* 服务/商品数据
|
||||
*/
|
||||
export const services = [
|
||||
// 王者荣耀服务
|
||||
{
|
||||
id: 1001,
|
||||
tenantId: 10001,
|
||||
categoryId: 1,
|
||||
categoryName: '王者荣耀',
|
||||
name: '王者荣耀 星耀→王者 上分',
|
||||
coverImage: 'https://picsum.photos/400/300?random=1',
|
||||
price: 198.00,
|
||||
originalPrice: 298.00,
|
||||
description: '专业代练,3天快速上分,保证胜率,安全可靠',
|
||||
serviceTime: 180,
|
||||
salesCount: 1245,
|
||||
rating: 4.9,
|
||||
reviewCount: 856,
|
||||
tags: ['热门', '高胜率', '快速'],
|
||||
merchantName: '电竞之星工作室',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=101',
|
||||
playerCount: 15,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 1002,
|
||||
tenantId: 10001,
|
||||
categoryId: 1,
|
||||
categoryName: '王者荣耀',
|
||||
name: '王者荣耀 娱乐陪玩 甜美女声',
|
||||
coverImage: 'https://picsum.photos/400/300?random=2',
|
||||
price: 35.00,
|
||||
originalPrice: 50.00,
|
||||
description: '温柔小姐姐陪你开黑,声音甜美,技术在线',
|
||||
serviceTime: 60,
|
||||
salesCount: 3456,
|
||||
rating: 4.95,
|
||||
reviewCount: 2134,
|
||||
tags: ['甜美', '高评分', '在线'],
|
||||
merchantName: '星光陪玩',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=102',
|
||||
playerCount: 28,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 1003,
|
||||
tenantId: 10002,
|
||||
categoryId: 1,
|
||||
categoryName: '王者荣耀',
|
||||
name: '王者荣耀 一对一教学 国服射手',
|
||||
coverImage: 'https://picsum.photos/400/300?random=3',
|
||||
price: 88.00,
|
||||
originalPrice: 120.00,
|
||||
description: '前职业选手教学,射手专精,助你快速提升',
|
||||
serviceTime: 120,
|
||||
salesCount: 567,
|
||||
rating: 5.0,
|
||||
reviewCount: 445,
|
||||
tags: ['专业', '国服', '教学'],
|
||||
merchantName: '巅峰电竞学院',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=103',
|
||||
playerCount: 8,
|
||||
status: 0
|
||||
},
|
||||
|
||||
// 英雄联盟服务
|
||||
{
|
||||
id: 2001,
|
||||
tenantId: 10001,
|
||||
categoryId: 2,
|
||||
categoryName: '英雄联盟',
|
||||
name: 'LOL 黄金→钻石 上分代练',
|
||||
coverImage: 'https://picsum.photos/400/300?random=4',
|
||||
price: 288.00,
|
||||
originalPrice: 399.00,
|
||||
description: '大师级代练,胜率保证75%+,安全稳定',
|
||||
serviceTime: 240,
|
||||
salesCount: 892,
|
||||
rating: 4.8,
|
||||
reviewCount: 678,
|
||||
tags: ['高胜率', '大师', '稳定'],
|
||||
merchantName: '电竞之星工作室',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=101',
|
||||
playerCount: 12,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 2002,
|
||||
tenantId: 10003,
|
||||
categoryId: 2,
|
||||
categoryName: '英雄联盟',
|
||||
name: 'LOL 娱乐陪玩 御姐音',
|
||||
coverImage: 'https://picsum.photos/400/300?random=5',
|
||||
price: 45.00,
|
||||
originalPrice: 60.00,
|
||||
description: '成熟御姐音,实力钻石,陪你轻松上分',
|
||||
serviceTime: 60,
|
||||
salesCount: 2234,
|
||||
rating: 4.92,
|
||||
reviewCount: 1567,
|
||||
tags: ['御姐', '钻石', '热门'],
|
||||
merchantName: '梦幻陪玩',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=104',
|
||||
playerCount: 35,
|
||||
status: 0
|
||||
},
|
||||
|
||||
// VALORANT服务
|
||||
{
|
||||
id: 3001,
|
||||
tenantId: 10002,
|
||||
categoryId: 3,
|
||||
categoryName: 'VALORANT',
|
||||
name: 'VALORANT 白金→钻石 快速上分',
|
||||
coverImage: 'https://picsum.photos/400/300?random=6',
|
||||
price: 328.00,
|
||||
originalPrice: 450.00,
|
||||
description: '职业战队选手,VALORANT国际赛经验',
|
||||
serviceTime: 300,
|
||||
salesCount: 234,
|
||||
rating: 4.95,
|
||||
reviewCount: 189,
|
||||
tags: ['职业', '国际赛', '快速'],
|
||||
merchantName: '巅峰电竞学院',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=103',
|
||||
playerCount: 6,
|
||||
status: 0
|
||||
},
|
||||
|
||||
// CSGO服务
|
||||
{
|
||||
id: 4001,
|
||||
tenantId: 10004,
|
||||
categoryId: 4,
|
||||
categoryName: 'CSGO',
|
||||
name: 'CSGO 黄金→老鹰 代练',
|
||||
coverImage: 'https://picsum.photos/400/300?random=7',
|
||||
price: 258.00,
|
||||
originalPrice: 350.00,
|
||||
description: '前CNCS职业选手,枪法精准,意识一流',
|
||||
serviceTime: 180,
|
||||
salesCount: 445,
|
||||
rating: 4.88,
|
||||
reviewCount: 334,
|
||||
tags: ['职业', 'CNCS', '精准'],
|
||||
merchantName: 'FPS大师工作室',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=105',
|
||||
playerCount: 10,
|
||||
status: 0
|
||||
},
|
||||
|
||||
// 原神服务
|
||||
{
|
||||
id: 6001,
|
||||
tenantId: 10005,
|
||||
categoryId: 6,
|
||||
categoryName: '原神',
|
||||
name: '原神 深渊12层满星代打',
|
||||
coverImage: 'https://picsum.photos/400/300?random=8',
|
||||
price: 68.00,
|
||||
originalPrice: 88.00,
|
||||
description: '满命大佬,深渊12层一次性满星',
|
||||
serviceTime: 90,
|
||||
salesCount: 1567,
|
||||
rating: 4.97,
|
||||
reviewCount: 1234,
|
||||
tags: ['满命', '深渊', '快速'],
|
||||
merchantName: '提瓦特工作室',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=106',
|
||||
playerCount: 18,
|
||||
status: 0
|
||||
},
|
||||
{
|
||||
id: 6002,
|
||||
tenantId: 10005,
|
||||
categoryId: 6,
|
||||
categoryName: '原神',
|
||||
name: '原神 每日委托代做',
|
||||
coverImage: 'https://picsum.photos/400/300?random=9',
|
||||
price: 15.00,
|
||||
originalPrice: 20.00,
|
||||
description: '每日委托快速完成,解放双手',
|
||||
serviceTime: 30,
|
||||
salesCount: 3456,
|
||||
rating: 4.85,
|
||||
reviewCount: 2567,
|
||||
tags: ['日常', '便宜', '快速'],
|
||||
merchantName: '提瓦特工作室',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=106',
|
||||
playerCount: 25,
|
||||
status: 0
|
||||
},
|
||||
|
||||
// 和平精英服务
|
||||
{
|
||||
id: 7001,
|
||||
tenantId: 10006,
|
||||
categoryId: 7,
|
||||
categoryName: '和平精英',
|
||||
name: '和平精英 钻石→皇冠 上分',
|
||||
coverImage: 'https://picsum.photos/400/300?random=10',
|
||||
price: 188.00,
|
||||
originalPrice: 258.00,
|
||||
description: '职业战队退役选手,吃鸡率80%+',
|
||||
serviceTime: 150,
|
||||
salesCount: 789,
|
||||
rating: 4.91,
|
||||
reviewCount: 567,
|
||||
tags: ['职业', '高吃鸡', '稳定'],
|
||||
merchantName: '绝地求生工作室',
|
||||
merchantAvatar: 'https://picsum.photos/100/100?random=107',
|
||||
playerCount: 14,
|
||||
status: 0
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 热门推荐服务
|
||||
*/
|
||||
export const hotServices = services.filter(s => s.salesCount > 1000).slice(0, 6)
|
||||
|
||||
/**
|
||||
* 根据分类获取服务
|
||||
*/
|
||||
export function getServicesByCategory(categoryId) {
|
||||
return services.filter(s => s.categoryId === categoryId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取服务详情
|
||||
*/
|
||||
export function getServiceById(id) {
|
||||
return services.find(s => s.id === id)
|
||||
}
|
||||
68
mock/user.js
Normal file
68
mock/user.js
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 用户数据
|
||||
*/
|
||||
export const currentUser = {
|
||||
id: 20001,
|
||||
openid: 'oXYZ123456789',
|
||||
nickname: '电竞少年',
|
||||
avatar: 'https://picsum.photos/100/100?random=301',
|
||||
phone: '138****8888',
|
||||
vipLevel: 2,
|
||||
vipName: '黄金会员',
|
||||
points: 2580,
|
||||
balance: 156.50,
|
||||
orderCount: 28,
|
||||
evaluateCount: 15,
|
||||
createTime: '2024-08-15 10:30:00',
|
||||
lastLoginTime: '2025-01-28 14:00:00'
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户统计数据
|
||||
*/
|
||||
export const userStats = {
|
||||
orderCount: {
|
||||
all: 28,
|
||||
waitPay: 0,
|
||||
waitAccept: 1,
|
||||
inProgress: 1,
|
||||
waitConfirm: 1,
|
||||
completed: 25
|
||||
},
|
||||
favoriteCount: 12,
|
||||
couponCount: 3,
|
||||
messageCount: 5
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的优惠券
|
||||
*/
|
||||
export const coupons = [
|
||||
{
|
||||
id: 1,
|
||||
name: '新人专享券',
|
||||
type: 'discount',
|
||||
value: 20,
|
||||
minAmount: 50,
|
||||
expireTime: '2025-02-28',
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '满100减30',
|
||||
type: 'cash',
|
||||
value: 30,
|
||||
minAmount: 100,
|
||||
expireTime: '2025-03-15',
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '8.8折优惠券',
|
||||
type: 'discount',
|
||||
value: 12,
|
||||
minAmount: 80,
|
||||
expireTime: '2025-01-20',
|
||||
status: 'expired'
|
||||
}
|
||||
]
|
||||
28
package.json
Normal file
28
package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "game-service-miniapp",
|
||||
"version": "1.0.0",
|
||||
"description": "游戏服务交易平台 - 电竞赛博朋克风格",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"dev:h5": "uni",
|
||||
"build:h5": "uni build",
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"build:mp-weixin": "uni build -p mp-weixin"
|
||||
},
|
||||
"keywords": ["uni-app", "游戏服务", "赛博朋克"],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue": "^3.2.47",
|
||||
"pinia": "^2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/uni-app": "^3.0.0",
|
||||
"@dcloudio/uni-h5": "^3.0.0",
|
||||
"@dcloudio/uni-mp-weixin": "^3.0.0",
|
||||
"@dcloudio/vite-plugin-uni": "^3.0.0",
|
||||
"vite": "^4.3.9",
|
||||
"sass": "^1.62.1",
|
||||
"sass-loader": "^13.2.2"
|
||||
}
|
||||
}
|
||||
103
pages.json
Normal file
103
pages.json
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"easycom": {
|
||||
"autoscan": true,
|
||||
"custom": {
|
||||
"^service-card": "@/components/service-card/index.vue",
|
||||
"^order-card": "@/components/order-card/index.vue",
|
||||
"^game-category": "@/components/game-category/index.vue"
|
||||
}
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "电竞服务",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"navigationBarTextStyle": "white",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/category/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务列表",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/service/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "服务详情",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/create",
|
||||
"style": {
|
||||
"navigationBarTitleText": "下单确认",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的订单",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/user/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人中心",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tabBar": {
|
||||
"color": "#7a7e9d",
|
||||
"selectedColor": "#00ffff",
|
||||
"backgroundColor": "#0a0e27",
|
||||
"borderStyle": "black",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页",
|
||||
"iconPath": "static/icons/home.png",
|
||||
"selectedIconPath": "static/icons/home-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/order/list",
|
||||
"text": "订单",
|
||||
"iconPath": "static/icons/order.png",
|
||||
"selectedIconPath": "static/icons/order-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/user/index",
|
||||
"text": "我的",
|
||||
"iconPath": "static/icons/user.png",
|
||||
"selectedIconPath": "static/icons/user-active.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "电竞服务平台",
|
||||
"navigationBarBackgroundColor": "#0a0e27",
|
||||
"backgroundColor": "#0f1229",
|
||||
"backgroundColorTop": "#0a0e27",
|
||||
"backgroundColorBottom": "#0f1229"
|
||||
}
|
||||
}
|
||||
190
pages/category/list.vue
Normal file
190
pages/category/list.vue
Normal file
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<view class="category-list-page page-container">
|
||||
<!-- 筛选栏 -->
|
||||
<view class="filter-bar">
|
||||
<scroll-view class="filter-scroll" scroll-x show-scrollbar="false">
|
||||
<view class="filter-tags">
|
||||
<view
|
||||
v-for="(item, index) in filters"
|
||||
:key="index"
|
||||
class="filter-tag"
|
||||
:class="{ active: currentFilter === index }"
|
||||
@click="changeFilter(index)"
|
||||
>
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="sort-btn" @click="toggleSort">
|
||||
<text class="sort-icon">⚡</text>
|
||||
<text class="sort-text">{{ sortText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务列表 -->
|
||||
<view class="services-list">
|
||||
<service-card
|
||||
v-for="service in filteredServices"
|
||||
:key="service.id"
|
||||
:service="service"
|
||||
@click="goServiceDetail"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="filteredServices.length === 0" class="empty-state">
|
||||
<text class="empty-icon">📦</text>
|
||||
<text class="empty-text">暂无服务</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onLoad } from '@dcloudio/uni-app'
|
||||
import { services, getServicesByCategory } from '../../mock'
|
||||
|
||||
const categoryId = ref(0)
|
||||
const categoryName = ref('')
|
||||
const currentFilter = ref(0)
|
||||
const currentSort = ref(0)
|
||||
|
||||
const filters = ['全部', '代练', '陪玩', '教学']
|
||||
const sortOptions = ['默认', '价格', '销量', '评分']
|
||||
const sortText = computed(() => sortOptions[currentSort.value])
|
||||
|
||||
const allServices = ref([])
|
||||
const filteredServices = computed(() => {
|
||||
let result = allServices.value
|
||||
|
||||
// 筛选
|
||||
if (currentFilter.value > 0) {
|
||||
const filterMap = { 1: '代练', 2: '陪玩', 3: '教学' }
|
||||
const keyword = filterMap[currentFilter.value]
|
||||
result = result.filter(s => s.name.includes(keyword))
|
||||
}
|
||||
|
||||
// 排序
|
||||
if (currentSort.value === 1) {
|
||||
result = [...result].sort((a, b) => a.price - b.price)
|
||||
} else if (currentSort.value === 2) {
|
||||
result = [...result].sort((a, b) => b.salesCount - a.salesCount)
|
||||
} else if (currentSort.value === 3) {
|
||||
result = [...result].sort((a, b) => b.rating - a.rating)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.categoryId) {
|
||||
categoryId.value = parseInt(options.categoryId)
|
||||
categoryName.value = options.categoryName || ''
|
||||
allServices.value = getServicesByCategory(categoryId.value)
|
||||
} else {
|
||||
allServices.value = services
|
||||
}
|
||||
|
||||
uni.setNavigationBarTitle({
|
||||
title: categoryName.value || '服务列表'
|
||||
})
|
||||
})
|
||||
|
||||
const changeFilter = (index) => {
|
||||
currentFilter.value = index
|
||||
}
|
||||
|
||||
const toggleSort = () => {
|
||||
currentSort.value = (currentSort.value + 1) % sortOptions.length
|
||||
}
|
||||
|
||||
const goServiceDetail = (service) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/service/detail?id=${service.id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.category-list-page {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.filter-scroll {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-tags {
|
||||
display: inline-flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.filter-tag {
|
||||
display: inline-block;
|
||||
padding: 12rpx 32rpx;
|
||||
background: rgba(20, 25, 50, 0.9);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 50rpx;
|
||||
font-size: 26rpx;
|
||||
color: #a0a4c4;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.active {
|
||||
background: linear-gradient(135deg, #00ffff 0%, #0099ff 100%);
|
||||
color: #0a0e27;
|
||||
border-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
background: rgba(20, 25, 50, 0.9);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 50rpx;
|
||||
font-size: 26rpx;
|
||||
color: #00ffff;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.services-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
margin-bottom: 24rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
</style>
|
||||
368
pages/index/index.vue
Normal file
368
pages/index/index.vue
Normal file
@ -0,0 +1,368 @@
|
||||
<template>
|
||||
<view class="index-page page-container">
|
||||
<!-- 顶部搜索栏 -->
|
||||
<view class="search-bar">
|
||||
<view class="search-input" @click="goSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
<text class="search-placeholder">搜索游戏服务...</text>
|
||||
</view>
|
||||
<view class="notification-btn" @click="goNotification">
|
||||
<text class="notif-icon">🔔</text>
|
||||
<view v-if="hasUnread" class="unread-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 赛博朋克标题 -->
|
||||
<view class="page-title">
|
||||
<view class="title-line"></view>
|
||||
<text class="title-text neon-text">GAMING SERVICES</text>
|
||||
<view class="title-line"></view>
|
||||
</view>
|
||||
|
||||
<!-- 游戏分类 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">游戏分类</text>
|
||||
<text class="section-more" @click="goAllCategories">全部 ></text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="categories-scroll" scroll-x show-scrollbar="false">
|
||||
<view class="categories-container">
|
||||
<game-category
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:category="category"
|
||||
:show-count="true"
|
||||
@click="goCategory"
|
||||
/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 服务类型快捷入口 -->
|
||||
<view class="service-types">
|
||||
<view
|
||||
v-for="type in serviceTypes"
|
||||
:key="type.id"
|
||||
class="type-item cyber-card"
|
||||
@click="goServiceType(type)"
|
||||
>
|
||||
<text class="type-icon">{{ type.icon }}</text>
|
||||
<text class="type-name">{{ type.name }}</text>
|
||||
<view class="type-arrow">→</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 热门推荐 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">🔥 热门推荐</text>
|
||||
<text class="section-subtitle">TRENDING NOW</text>
|
||||
</view>
|
||||
|
||||
<view class="services-list">
|
||||
<service-card
|
||||
v-for="service in hotServices"
|
||||
:key="service.id"
|
||||
:service="service"
|
||||
@click="goServiceDetail"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="loading" class="loading-container">
|
||||
<view class="loading-dot"></view>
|
||||
<view class="loading-dot"></view>
|
||||
<view class="loading-dot"></view>
|
||||
</view>
|
||||
|
||||
<!-- 底部装饰 -->
|
||||
<view class="bottom-decoration">
|
||||
<view class="decoration-line"></view>
|
||||
<text class="decoration-text">// POWERED BY CYBER TECH //</text>
|
||||
<view class="decoration-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { gameCategories, serviceTypes, hotServices } from '../../mock'
|
||||
|
||||
const categories = ref([])
|
||||
const services = ref([])
|
||||
const loading = ref(false)
|
||||
const hasUnread = ref(true)
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
|
||||
// 模拟加载
|
||||
setTimeout(() => {
|
||||
categories.value = gameCategories
|
||||
services.value = hotServices
|
||||
loading.value = false
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const goSearch = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/search'
|
||||
})
|
||||
}
|
||||
|
||||
const goNotification = () => {
|
||||
uni.showToast({
|
||||
title: '暂无新消息',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const goAllCategories = () => {
|
||||
uni.showToast({
|
||||
title: '全部分类',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const goCategory = (category) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/category/list?categoryId=${category.id}&categoryName=${category.name}`
|
||||
})
|
||||
}
|
||||
|
||||
const goServiceType = (type) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/category/list?type=${type.id}&typeName=${type.name}`
|
||||
})
|
||||
}
|
||||
|
||||
const goServiceDetail = (service) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/service/detail?id=${service.id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.index-page {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
background: linear-gradient(135deg, rgba(20, 25, 50, 0.9) 0%, rgba(10, 14, 39, 0.8) 100%);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 50rpx;
|
||||
backdrop-filter: blur(10rpx);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
border-color: rgba(0, 255, 255, 0.6);
|
||||
box-shadow: 0 0 20rpx rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
font-size: 28rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.notification-btn {
|
||||
position: relative;
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, rgba(20, 25, 50, 0.9) 0%, rgba(10, 14, 39, 0.8) 100%);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
border-color: rgba(0, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.notif-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.unread-dot {
|
||||
position: absolute;
|
||||
top: 18rpx;
|
||||
right: 18rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background: #ff3366;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #0a0e27;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.title-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 255, 255, 0.5) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
font-size: 22rpx;
|
||||
color: #7a7e9d;
|
||||
letter-spacing: 2rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
font-size: 26rpx;
|
||||
color: #00ffff;
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.categories-scroll {
|
||||
white-space: nowrap;
|
||||
margin: 0 -24rpx;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
|
||||
.categories-container {
|
||||
display: inline-flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.service-types {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.type-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
padding: 28rpx 32rpx;
|
||||
background: linear-gradient(135deg, rgba(20, 25, 50, 0.9) 0%, rgba(10, 14, 39, 0.8) 100%);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 16rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
border-color: rgba(0, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.type-icon {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.type-name {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.type-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.services-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.bottom-decoration {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
margin-top: 60rpx;
|
||||
padding: 40rpx 0;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.decoration-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(122, 126, 157, 0.3) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.decoration-text {
|
||||
font-size: 20rpx;
|
||||
color: #7a7e9d;
|
||||
letter-spacing: 2rpx;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
356
pages/order/create.vue
Normal file
356
pages/order/create.vue
Normal file
@ -0,0 +1,356 @@
|
||||
<template>
|
||||
<view class="create-order-page page-container">
|
||||
<!-- 服务信息卡片 -->
|
||||
<view class="service-card cyber-card">
|
||||
<image :src="service.coverImage" class="service-cover" mode="aspectFill" />
|
||||
<view class="service-info">
|
||||
<view class="service-name">{{ service.name }}</view>
|
||||
<view class="service-price">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">{{ service.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 游戏信息表单 -->
|
||||
<view class="form-section cyber-card">
|
||||
<view class="section-title">游戏信息</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">游戏ID</text>
|
||||
<input
|
||||
class="input"
|
||||
v-model="formData.gameId"
|
||||
placeholder="请输入您的游戏ID"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">游戏区服</text>
|
||||
<picker mode="selector" :range="servers" @change="onServerChange">
|
||||
<view class="picker">
|
||||
{{ formData.server || '请选择区服' }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">当前段位</text>
|
||||
<input
|
||||
class="input"
|
||||
v-model="formData.currentRank"
|
||||
placeholder="请输入当前段位"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<view class="form-section cyber-card">
|
||||
<view class="section-title">联系方式</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">QQ号</text>
|
||||
<input
|
||||
class="input"
|
||||
v-model="formData.qq"
|
||||
placeholder="请输入QQ号"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">微信号</text>
|
||||
<input
|
||||
class="input"
|
||||
v-model="formData.wechat"
|
||||
placeholder="请输入微信号"
|
||||
placeholder-class="placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 备注 -->
|
||||
<view class="form-section cyber-card">
|
||||
<view class="section-title">订单备注</view>
|
||||
<textarea
|
||||
class="textarea"
|
||||
v-model="formData.remark"
|
||||
placeholder="请输入订单备注(选填)"
|
||||
placeholder-class="placeholder"
|
||||
maxlength="200"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 价格明细 -->
|
||||
<view class="price-detail cyber-card">
|
||||
<view class="detail-row">
|
||||
<text class="label">服务费用</text>
|
||||
<text class="value">¥{{ service.price }}</text>
|
||||
</view>
|
||||
<view class="cyber-divider"></view>
|
||||
<view class="detail-row total">
|
||||
<text class="label">应付金额</text>
|
||||
<text class="value neon-text">¥{{ service.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部提交按钮 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="total-price">
|
||||
<text class="label">总计:</text>
|
||||
<text class="price">¥{{ service.price }}</text>
|
||||
</view>
|
||||
<button class="submit-btn cyber-button" @click="submitOrder">
|
||||
确认下单
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onLoad } from '@dcloudio/uni-app'
|
||||
import { getServiceById } from '../../mock'
|
||||
|
||||
const service = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
coverImage: '',
|
||||
price: 0
|
||||
})
|
||||
|
||||
const servers = ['微信区', 'QQ区', '安卓区', 'iOS区']
|
||||
|
||||
const formData = reactive({
|
||||
gameId: '',
|
||||
server: '',
|
||||
currentRank: '',
|
||||
qq: '',
|
||||
wechat: '',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.serviceId) {
|
||||
const serviceData = getServiceById(parseInt(options.serviceId))
|
||||
if (serviceData) {
|
||||
service.value = serviceData
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const onServerChange = (e) => {
|
||||
formData.server = servers[e.detail.value]
|
||||
}
|
||||
|
||||
const submitOrder = () => {
|
||||
// 验证表单
|
||||
if (!formData.gameId) {
|
||||
uni.showToast({ title: '请输入游戏ID', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.server) {
|
||||
uni.showToast({ title: '请选择区服', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!formData.qq && !formData.wechat) {
|
||||
uni.showToast({ title: '请至少填写一种联系方式', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showLoading({ title: '提交中...' })
|
||||
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '下单成功!',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/order/list'
|
||||
})
|
||||
}, 1500)
|
||||
}, 1000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.create-order-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 180rpx;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.service-cover {
|
||||
width: 160rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.service-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.service-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 24rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #a0a4c4;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.input,
|
||||
.picker {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 28rpx;
|
||||
background: rgba(10, 14, 39, 0.6);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
line-height: 88rpx;
|
||||
}
|
||||
|
||||
.textarea {
|
||||
width: 100%;
|
||||
min-height: 200rpx;
|
||||
padding: 20rpx 28rpx;
|
||||
background: rgba(10, 14, 39, 0.6);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.price-detail {
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&.total {
|
||||
margin-bottom: 0;
|
||||
|
||||
.label,
|
||||
.value {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-row .label {
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.detail-row .value {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 24rpx;
|
||||
background: rgba(10, 14, 39, 0.98);
|
||||
border-top: 1px solid rgba(0, 255, 255, 0.2);
|
||||
backdrop-filter: blur(20rpx);
|
||||
}
|
||||
|
||||
.total-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.total-price .label {
|
||||
font-size: 26rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.total-price .price {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
465
pages/order/detail.vue
Normal file
465
pages/order/detail.vue
Normal file
@ -0,0 +1,465 @@
|
||||
<template>
|
||||
<view class="order-detail-page page-container">
|
||||
<!-- 订单状态 -->
|
||||
<view class="status-card cyber-card">
|
||||
<view class="status-icon">{{ getStatusIcon(order.status) }}</view>
|
||||
<view class="status-text">{{ getStatusText(order.status) }}</view>
|
||||
<view v-if="order.status === 3" class="status-desc">
|
||||
服务进行中,请耐心等待选手完成服务
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选手信息 (已接单) -->
|
||||
<view v-if="order.playerName" class="player-card cyber-card">
|
||||
<view class="card-title">服务选手</view>
|
||||
<view class="player-info">
|
||||
<image :src="order.playerAvatar" class="player-avatar" />
|
||||
<view class="player-detail">
|
||||
<text class="player-name">{{ order.playerName }}</text>
|
||||
<text class="player-status">{{ order.status === 3 ? '服务中...' : '已接单' }}</text>
|
||||
</view>
|
||||
<button class="contact-btn" @click="contactPlayer">联系选手</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务信息 -->
|
||||
<view class="service-card cyber-card">
|
||||
<view class="card-title">服务信息</view>
|
||||
<view class="service-content">
|
||||
<image :src="order.serviceCover" class="service-cover" mode="aspectFill" />
|
||||
<view class="service-info">
|
||||
<text class="service-name">{{ order.serviceName }}</text>
|
||||
<text class="service-category">{{ order.categoryName }}</text>
|
||||
</view>
|
||||
<view class="service-price">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">{{ order.actualPrice }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 游戏信息 -->
|
||||
<view class="info-card cyber-card">
|
||||
<view class="card-title">游戏信息</view>
|
||||
<view class="info-row">
|
||||
<text class="label">游戏ID</text>
|
||||
<text class="value">{{ order.gameInfo?.gameId }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">区服</text>
|
||||
<text class="value">{{ order.gameInfo?.server }}</text>
|
||||
</view>
|
||||
<view v-if="order.gameInfo?.currentRank" class="info-row">
|
||||
<text class="label">段位</text>
|
||||
<text class="value">{{ order.gameInfo?.currentRank }}</text>
|
||||
</view>
|
||||
<view v-if="order.remark" class="info-row">
|
||||
<text class="label">备注</text>
|
||||
<text class="value">{{ order.remark }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单流转 -->
|
||||
<view class="flow-card cyber-card">
|
||||
<view class="card-title">订单流转</view>
|
||||
<view class="flow-list">
|
||||
<view v-for="(item, index) in orderFlow" :key="index" class="flow-item">
|
||||
<view class="flow-dot" :class="{ active: index === 0 }"></view>
|
||||
<view class="flow-line" v-if="index < orderFlow.length - 1"></view>
|
||||
<view class="flow-content">
|
||||
<text class="flow-status">{{ item.status }}</text>
|
||||
<text class="flow-desc">{{ item.desc }}</text>
|
||||
<text class="flow-time">{{ item.time }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<view class="order-info-card cyber-card">
|
||||
<view class="card-title">订单信息</view>
|
||||
<view class="info-row">
|
||||
<text class="label">订单号</text>
|
||||
<text class="value">{{ order.orderNo }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">创建时间</text>
|
||||
<text class="value">{{ order.createTime }}</text>
|
||||
</view>
|
||||
<view v-if="order.payTime" class="info-row">
|
||||
<text class="label">支付时间</text>
|
||||
<text class="value">{{ order.payTime }}</text>
|
||||
</view>
|
||||
<view v-if="order.finishTime" class="info-row">
|
||||
<text class="label">完成时间</text>
|
||||
<text class="value">{{ order.finishTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<button v-if="order.status === 0" class="action-btn btn-cancel" @click="handleCancel">取消订单</button>
|
||||
<button v-if="order.status === 0" class="action-btn btn-primary" @click="handlePay">立即支付</button>
|
||||
|
||||
<button v-if="order.status === 4" class="action-btn btn-outline" @click="handleRefund">申请退款</button>
|
||||
<button v-if="order.status === 4" class="action-btn btn-primary" @click="handleConfirm">确认完成</button>
|
||||
|
||||
<button v-if="order.status === 5" class="action-btn btn-primary" @click="handleEvaluate">去评价</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onLoad } from '@dcloudio/uni-app'
|
||||
import { getOrderById, getOrderFlowById, OrderStatus } from '../../mock'
|
||||
|
||||
const order = ref({})
|
||||
const orderFlow = ref([])
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.id) {
|
||||
const orderData = getOrderById(parseInt(options.id))
|
||||
if (orderData) {
|
||||
order.value = orderData
|
||||
orderFlow.value = getOrderFlowById(orderData.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const getStatusIcon = (status) => {
|
||||
const icons = {
|
||||
0: '⏰',
|
||||
1: '👀',
|
||||
2: '✅',
|
||||
3: '🎮',
|
||||
4: '⭐',
|
||||
5: '✨',
|
||||
6: '💬',
|
||||
9: '❌'
|
||||
}
|
||||
return icons[status] || '📦'
|
||||
}
|
||||
|
||||
const getStatusText = (status) => {
|
||||
const statusObj = Object.values(OrderStatus).find(s => s.code === status)
|
||||
return statusObj ? statusObj.text : '未知状态'
|
||||
}
|
||||
|
||||
const contactPlayer = () => {
|
||||
uni.showToast({ title: '联系选手', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消订单吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '订单已取消', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handlePay = () => {
|
||||
uni.showToast({ title: '跳转支付...', icon: 'none' })
|
||||
}
|
||||
|
||||
const handleRefund = () => {
|
||||
uni.showModal({
|
||||
title: '申请退款',
|
||||
content: '请说明退款原因',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '退款申请已提交', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
uni.showModal({
|
||||
title: '确认完成',
|
||||
content: '确认服务已完成?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '已确认完成', icon: 'success' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleEvaluate = () => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/evaluate?orderId=${order.value.id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-detail-page {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 180rpx;
|
||||
}
|
||||
|
||||
.status-card {
|
||||
padding: 48rpx 32rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
font-size: 120rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
font-size: 26rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.player-card,
|
||||
.service-card,
|
||||
.info-card,
|
||||
.flow-card,
|
||||
.order-info-card {
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.player-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.player-avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.player-detail {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.player-status {
|
||||
font-size: 24rpx;
|
||||
color: #00ff88;
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
padding: 16rpx 32rpx;
|
||||
background: transparent;
|
||||
border: 1px solid #00ffff;
|
||||
border-radius: 8rpx;
|
||||
font-size: 26rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.service-content {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-cover {
|
||||
width: 160rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.service-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.service-category {
|
||||
padding: 4rpx 16rpx;
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 6rpx;
|
||||
font-size: 22rpx;
|
||||
color: #00ffff;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.service-price {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 24rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1px solid rgba(0, 255, 255, 0.1);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.info-row .label {
|
||||
font-size: 26rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.info-row .value {
|
||||
font-size: 26rpx;
|
||||
color: #ffffff;
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
margin-left: 40rpx;
|
||||
}
|
||||
|
||||
.flow-list {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.flow-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
padding-left: 48rpx;
|
||||
padding-bottom: 40rpx;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-dot {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
background: rgba(122, 126, 157, 0.5);
|
||||
border: 2px solid rgba(122, 126, 157, 0.8);
|
||||
border-radius: 50%;
|
||||
|
||||
&.active {
|
||||
background: rgba(0, 255, 255, 0.3);
|
||||
border-color: #00ffff;
|
||||
box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.flow-line {
|
||||
position: absolute;
|
||||
left: 11rpx;
|
||||
top: 28rpx;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: linear-gradient(180deg, rgba(0, 255, 255, 0.3) 0%, transparent 100%);
|
||||
}
|
||||
|
||||
.flow-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.flow-status {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.flow-desc {
|
||||
font-size: 24rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.flow-time {
|
||||
font-size: 22rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
padding: 24rpx;
|
||||
background: rgba(10, 14, 39, 0.98);
|
||||
border-top: 1px solid rgba(0, 255, 255, 0.2);
|
||||
backdrop-filter: blur(20rpx);
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&.btn-primary {
|
||||
background: linear-gradient(135deg, #00ffff 0%, #0099ff 100%);
|
||||
color: #0a0e27;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.btn-outline {
|
||||
background: transparent;
|
||||
border: 1px solid #00ffff;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
&.btn-cancel {
|
||||
background: rgba(122, 126, 157, 0.2);
|
||||
border: 1px solid rgba(122, 126, 157, 0.4);
|
||||
color: #7a7e9d;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
220
pages/order/list.vue
Normal file
220
pages/order/list.vue
Normal file
@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<view class="order-list-page page-container">
|
||||
<!-- Tab标签栏 -->
|
||||
<view class="tabs-bar">
|
||||
<scroll-view class="tabs-scroll" scroll-x show-scrollbar="false">
|
||||
<view class="tabs-container">
|
||||
<view
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="index"
|
||||
class="tab-item"
|
||||
:class="{ active: currentTab === index }"
|
||||
@click="changeTab(index)"
|
||||
>
|
||||
<text class="tab-text">{{ tab.name }}</text>
|
||||
<text v-if="tab.count > 0" class="tab-badge">{{ tab.count }}</text>
|
||||
<view v-if="currentTab === index" class="tab-indicator"></view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<scroll-view class="orders-scroll" scroll-y refresher-enabled :refresher-triggered="refreshing" @refresherrefresh="onRefresh">
|
||||
<view class="orders-list">
|
||||
<order-card
|
||||
v-for="order in displayOrders"
|
||||
:key="order.id"
|
||||
:order="order"
|
||||
@click="goOrderDetail"
|
||||
@action="handleOrderAction"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="displayOrders.length === 0" class="empty-state">
|
||||
<text class="empty-icon">📦</text>
|
||||
<text class="empty-text">暂无订单</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { orders, getOrdersByStatus, getOrderCountByStatus } from '../../mock'
|
||||
|
||||
const currentTab = ref(0)
|
||||
const refreshing = ref(false)
|
||||
|
||||
const tabs = computed(() => [
|
||||
{ name: '全部', status: 'all', count: getOrderCountByStatus('all') },
|
||||
{ name: '待支付', status: 0, count: getOrderCountByStatus(0) },
|
||||
{ name: '待接单', status: 1, count: getOrderCountByStatus(1) },
|
||||
{ name: '进行中', status: 3, count: getOrderCountByStatus(3) },
|
||||
{ name: '待确认', status: 4, count: getOrderCountByStatus(4) },
|
||||
{ name: '已完成', status: 5, count: getOrderCountByStatus(5) }
|
||||
])
|
||||
|
||||
const displayOrders = computed(() => {
|
||||
const status = tabs.value[currentTab.value].status
|
||||
return getOrdersByStatus(status)
|
||||
})
|
||||
|
||||
const changeTab = (index) => {
|
||||
currentTab.value = index
|
||||
}
|
||||
|
||||
const onRefresh = () => {
|
||||
refreshing.value = true
|
||||
setTimeout(() => {
|
||||
refreshing.value = false
|
||||
uni.showToast({ title: '刷新成功', icon: 'none' })
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const goOrderDetail = (order) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/detail?id=${order.id}`
|
||||
})
|
||||
}
|
||||
|
||||
const handleOrderAction = ({ action, order }) => {
|
||||
console.log('Action:', action, 'Order:', order)
|
||||
|
||||
switch (action) {
|
||||
case 'pay':
|
||||
uni.showToast({ title: '跳转支付...', icon: 'none' })
|
||||
break
|
||||
case 'cancel':
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要取消订单吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '订单已取消', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'confirm':
|
||||
uni.showModal({
|
||||
title: '确认完成',
|
||||
content: '确认服务已完成?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({ title: '已确认完成', icon: 'success' })
|
||||
}
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'evaluate':
|
||||
uni.showToast({ title: '去评价', icon: 'none' })
|
||||
break
|
||||
case 'detail':
|
||||
goOrderDetail(order)
|
||||
break
|
||||
default:
|
||||
uni.showToast({ title: action, icon: 'none' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-list-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.tabs-bar {
|
||||
padding: 0 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.tabs-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
display: inline-flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 16rpx 32rpx;
|
||||
background: rgba(20, 25, 50, 0.6);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 50rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.active {
|
||||
background: rgba(0, 255, 255, 0.15);
|
||||
border-color: rgba(0, 255, 255, 0.5);
|
||||
|
||||
.tab-text {
|
||||
color: #00ffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 26rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.tab-badge {
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
padding: 0 8rpx;
|
||||
background: #ff3366;
|
||||
border-radius: 18rpx;
|
||||
font-size: 20rpx;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tab-indicator {
|
||||
position: absolute;
|
||||
bottom: 8rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40%;
|
||||
height: 4rpx;
|
||||
background: linear-gradient(90deg, #00ffff 0%, #0099ff 100%);
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
.orders-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.orders-list {
|
||||
padding: 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 120rpx;
|
||||
margin-bottom: 24rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
</style>
|
||||
467
pages/service/detail.vue
Normal file
467
pages/service/detail.vue
Normal file
@ -0,0 +1,467 @@
|
||||
<template>
|
||||
<view class="service-detail-page page-container">
|
||||
<!-- 服务封面 -->
|
||||
<view class="service-cover">
|
||||
<image :src="service.coverImage" mode="aspectFill" class="cover-image" />
|
||||
<view class="cover-overlay"></view>
|
||||
|
||||
<!-- 返回按钮 -->
|
||||
<view class="back-btn" @click="goBack">
|
||||
<text class="back-icon">←</text>
|
||||
</view>
|
||||
|
||||
<!-- 分享按钮 -->
|
||||
<view class="share-btn" @click="handleShare">
|
||||
<text class="share-icon">📤</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务信息 -->
|
||||
<view class="service-info cyber-card">
|
||||
<view class="service-header">
|
||||
<view class="service-title">{{ service.name }}</view>
|
||||
<view class="price-container">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">{{ service.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="service-stats">
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">评分</text>
|
||||
<text class="stat-value neon-text">{{ service.rating }} ⭐</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">销量</text>
|
||||
<text class="stat-value">{{ service.salesCount }}</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">时长</text>
|
||||
<text class="stat-value">{{ service.serviceTime }}分钟</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="tags-container">
|
||||
<view v-for="tag in service.tags" :key="tag" class="tag-item">
|
||||
{{ tag }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商家信息 -->
|
||||
<view class="merchant-card cyber-card">
|
||||
<view class="merchant-header">
|
||||
<image :src="service.merchantAvatar" class="merchant-avatar" />
|
||||
<view class="merchant-info">
|
||||
<text class="merchant-name">{{ service.merchantName }}</text>
|
||||
<text class="merchant-desc">{{ service.playerCount }}位专业选手</text>
|
||||
</view>
|
||||
<button class="contact-btn" @click="contactMerchant">联系商家</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务详情 -->
|
||||
<view class="service-detail cyber-card">
|
||||
<view class="section-title">服务详情</view>
|
||||
<view class="detail-content">
|
||||
<text class="detail-text">{{ service.description }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户评价 -->
|
||||
<view class="evaluations-card cyber-card">
|
||||
<view class="section-title">
|
||||
用户评价 ({{ service.reviewCount }})
|
||||
</view>
|
||||
<view class="evaluation-item" v-for="i in 3" :key="i">
|
||||
<view class="eval-header">
|
||||
<image src="https://picsum.photos/100/100?random=401" class="user-avatar" />
|
||||
<view class="user-info">
|
||||
<text class="user-name">用户{{ i }}</text>
|
||||
<view class="rating">⭐⭐⭐⭐⭐</view>
|
||||
</view>
|
||||
<text class="eval-time">2天前</text>
|
||||
</view>
|
||||
<text class="eval-content">服务很棒,选手很专业,下次还会再来!</text>
|
||||
</view>
|
||||
|
||||
<view class="view-all" @click="viewAllEvaluations">
|
||||
查看全部评价 >
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="action-btns">
|
||||
<view class="icon-btn" @click="toggleFavorite">
|
||||
<text class="icon">{{ isFavorite ? '❤️' : '🤍' }}</text>
|
||||
<text class="label">收藏</text>
|
||||
</view>
|
||||
<view class="icon-btn" @click="contactService">
|
||||
<text class="icon">💬</text>
|
||||
<text class="label">客服</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="order-btn cyber-button" @click="goOrder">
|
||||
立即下单
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onLoad } from '@dcloudio/uni-app'
|
||||
import { getServiceById } from '../../mock'
|
||||
|
||||
const service = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
coverImage: '',
|
||||
price: 0,
|
||||
originalPrice: 0,
|
||||
description: '',
|
||||
serviceTime: 0,
|
||||
salesCount: 0,
|
||||
rating: 5.0,
|
||||
reviewCount: 0,
|
||||
tags: [],
|
||||
merchantName: '',
|
||||
merchantAvatar: '',
|
||||
playerCount: 0
|
||||
})
|
||||
|
||||
const isFavorite = ref(false)
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.id) {
|
||||
const serviceData = getServiceById(parseInt(options.id))
|
||||
if (serviceData) {
|
||||
service.value = serviceData
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const handleShare = () => {
|
||||
uni.showShareMenu({
|
||||
title: service.value.name,
|
||||
path: `/pages/service/detail?id=${service.value.id}`
|
||||
})
|
||||
}
|
||||
|
||||
const toggleFavorite = () => {
|
||||
isFavorite.value = !isFavorite.value
|
||||
uni.showToast({
|
||||
title: isFavorite.value ? '已收藏' : '取消收藏',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const contactMerchant = () => {
|
||||
uni.showToast({ title: '联系商家', icon: 'none' })
|
||||
}
|
||||
|
||||
const contactService = () => {
|
||||
uni.showToast({ title: '联系客服', icon: 'none' })
|
||||
}
|
||||
|
||||
const viewAllEvaluations = () => {
|
||||
uni.showToast({ title: '查看全部评价', icon: 'none' })
|
||||
}
|
||||
|
||||
const goOrder = () => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/create?serviceId=${service.value.id}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.service-detail-page {
|
||||
padding-bottom: 160rpx;
|
||||
}
|
||||
|
||||
.service-cover {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 600rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cover-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 200rpx;
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(10, 14, 39, 0.9) 100%);
|
||||
}
|
||||
|
||||
.back-btn,
|
||||
.share-btn {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(10, 14, 39, 0.8);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
backdrop-filter: blur(10rpx);
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
left: 24rpx;
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
right: 24rpx;
|
||||
}
|
||||
|
||||
.back-icon,
|
||||
.share-icon {
|
||||
font-size: 36rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.service-info,
|
||||
.merchant-card,
|
||||
.service-detail,
|
||||
.evaluations-card {
|
||||
margin: 0 24rpx 24rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.service-header {
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.service-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.price-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 56rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.service-stats {
|
||||
display: flex;
|
||||
gap: 48rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 24rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
padding: 8rpx 20rpx;
|
||||
background: rgba(0, 255, 255, 0.1);
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
border-radius: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.merchant-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.merchant-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.merchant-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.merchant-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.merchant-desc {
|
||||
font-size: 24rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.contact-btn {
|
||||
padding: 16rpx 32rpx;
|
||||
background: transparent;
|
||||
border: 1px solid #00ffff;
|
||||
border-radius: 8rpx;
|
||||
font-size: 26rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
padding: 24rpx;
|
||||
background: rgba(10, 14, 39, 0.5);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.detail-text {
|
||||
font-size: 28rpx;
|
||||
line-height: 1.8;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.evaluation-item {
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1px solid rgba(0, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.eval-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 26rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.rating {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
.eval-time {
|
||||
font-size: 22rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.eval-content {
|
||||
font-size: 26rpx;
|
||||
line-height: 1.6;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.view-all {
|
||||
text-align: center;
|
||||
padding: 24rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 24rpx;
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(10, 14, 39, 0.95) 20%, rgba(10, 14, 39, 1) 100%);
|
||||
border-top: 1px solid rgba(0, 255, 255, 0.2);
|
||||
backdrop-filter: blur(20rpx);
|
||||
}
|
||||
|
||||
.action-btns {
|
||||
display: flex;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 40rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 22rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.order-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
431
pages/user/index.vue
Normal file
431
pages/user/index.vue
Normal file
@ -0,0 +1,431 @@
|
||||
<template>
|
||||
<view class="user-page page-container">
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="user-card cyber-card">
|
||||
<image :src="user.avatar" class="user-avatar" />
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ user.nickname }}</text>
|
||||
<view class="vip-badge">
|
||||
<text class="vip-icon">👑</text>
|
||||
<text class="vip-text">{{ user.vipName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-btn" @click="goSetting">
|
||||
<text class="setting-icon">⚙️</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 资产信息 -->
|
||||
<view class="assets-card cyber-card">
|
||||
<view class="asset-item" @click="goWallet">
|
||||
<text class="asset-value neon-text">{{ user.balance }}</text>
|
||||
<text class="asset-label">余额</text>
|
||||
</view>
|
||||
<view class="asset-divider"></view>
|
||||
<view class="asset-item" @click="goPoints">
|
||||
<text class="asset-value neon-text">{{ user.points }}</text>
|
||||
<text class="asset-label">积分</text>
|
||||
</view>
|
||||
<view class="asset-divider"></view>
|
||||
<view class="asset-item" @click="goCoupons">
|
||||
<text class="asset-value neon-text">{{ userStats.couponCount }}</text>
|
||||
<text class="asset-label">优惠券</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单统计 -->
|
||||
<view class="order-stats cyber-card">
|
||||
<view class="stats-header">
|
||||
<text class="stats-title">我的订单</text>
|
||||
<text class="stats-more" @click="goAllOrders">全部 ></text>
|
||||
</view>
|
||||
|
||||
<view class="stats-grid">
|
||||
<view class="stat-item" @click="goOrders(0)">
|
||||
<text class="stat-icon">💳</text>
|
||||
<text class="stat-name">待支付</text>
|
||||
<view v-if="userStats.orderCount.waitPay > 0" class="stat-badge">
|
||||
{{ userStats.orderCount.waitPay }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-item" @click="goOrders(1)">
|
||||
<text class="stat-icon">👀</text>
|
||||
<text class="stat-name">待接单</text>
|
||||
<view v-if="userStats.orderCount.waitAccept > 0" class="stat-badge">
|
||||
{{ userStats.orderCount.waitAccept }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-item" @click="goOrders(3)">
|
||||
<text class="stat-icon">🎮</text>
|
||||
<text class="stat-name">进行中</text>
|
||||
<view v-if="userStats.orderCount.inProgress > 0" class="stat-badge">
|
||||
{{ userStats.orderCount.inProgress }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-item" @click="goOrders(4)">
|
||||
<text class="stat-icon">⭐</text>
|
||||
<text class="stat-name">待确认</text>
|
||||
<view v-if="userStats.orderCount.waitConfirm > 0" class="stat-badge">
|
||||
{{ userStats.orderCount.waitConfirm }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-item" @click="goOrders(5)">
|
||||
<text class="stat-icon">✨</text>
|
||||
<text class="stat-name">已完成</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="menu-card cyber-card">
|
||||
<view class="menu-item" @click="goFavorites">
|
||||
<text class="menu-icon">❤️</text>
|
||||
<text class="menu-name">我的收藏</text>
|
||||
<text class="menu-extra">{{ userStats.favoriteCount }}</text>
|
||||
<text class="menu-arrow">→</text>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goMessages">
|
||||
<text class="menu-icon">💬</text>
|
||||
<text class="menu-name">消息中心</text>
|
||||
<view v-if="userStats.messageCount > 0" class="menu-badge">
|
||||
{{ userStats.messageCount }}
|
||||
</view>
|
||||
<text class="menu-arrow">→</text>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goHelp">
|
||||
<text class="menu-icon">❓</text>
|
||||
<text class="menu-name">帮助中心</text>
|
||||
<text class="menu-arrow">→</text>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="goAbout">
|
||||
<text class="menu-icon">ℹ️</text>
|
||||
<text class="menu-name">关于我们</text>
|
||||
<text class="menu-arrow">→</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部装饰 -->
|
||||
<view class="bottom-decoration">
|
||||
<view class="decoration-line"></view>
|
||||
<text class="decoration-text">// GAME SERVICE v1.0.0 //</text>
|
||||
<view class="decoration-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { currentUser, userStats } from '../../mock'
|
||||
|
||||
const user = ref(currentUser)
|
||||
|
||||
const goSetting = () => {
|
||||
uni.showToast({ title: '设置', icon: 'none' })
|
||||
}
|
||||
|
||||
const goWallet = () => {
|
||||
uni.showToast({ title: '我的余额', icon: 'none' })
|
||||
}
|
||||
|
||||
const goPoints = () => {
|
||||
uni.showToast({ title: '我的积分', icon: 'none' })
|
||||
}
|
||||
|
||||
const goCoupons = () => {
|
||||
uni.showToast({ title: '优惠券', icon: 'none' })
|
||||
}
|
||||
|
||||
const goAllOrders = () => {
|
||||
uni.switchTab({
|
||||
url: '/pages/order/list'
|
||||
})
|
||||
}
|
||||
|
||||
const goOrders = (status) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/list?status=${status}`
|
||||
})
|
||||
}
|
||||
|
||||
const goFavorites = () => {
|
||||
uni.showToast({ title: '我的收藏', icon: 'none' })
|
||||
}
|
||||
|
||||
const goMessages = () => {
|
||||
uni.showToast({ title: '消息中心', icon: 'none' })
|
||||
}
|
||||
|
||||
const goHelp = () => {
|
||||
uni.showToast({ title: '帮助中心', icon: 'none' })
|
||||
}
|
||||
|
||||
const goAbout = () => {
|
||||
uni.showToast({ title: '关于我们', icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-page {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28rpx;
|
||||
padding: 40rpx 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
border: 3px solid rgba(0, 255, 255, 0.5);
|
||||
box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.vip-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
background: linear-gradient(135deg, rgba(255, 215, 0, 0.2) 0%, rgba(255, 165, 0, 0.2) 100%);
|
||||
border: 1px solid rgba(255, 215, 0, 0.5);
|
||||
border-radius: 20rpx;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.vip-icon {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.vip-text {
|
||||
font-size: 22rpx;
|
||||
color: #ffd700;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.setting-btn {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(20, 25, 50, 0.6);
|
||||
border: 1px solid rgba(0, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.setting-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.assets-card {
|
||||
display: flex;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.asset-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.asset-value {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.asset-label {
|
||||
font-size: 24rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.asset-divider {
|
||||
width: 1px;
|
||||
background: linear-gradient(180deg,
|
||||
transparent 0%,
|
||||
rgba(0, 255, 255, 0.3) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.order-stats {
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.stats-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.stats-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.stats-more {
|
||||
font-size: 26rpx;
|
||||
color: #00ffff;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
font-size: 48rpx;
|
||||
}
|
||||
|
||||
.stat-name {
|
||||
font-size: 24rpx;
|
||||
color: #a0a4c4;
|
||||
}
|
||||
|
||||
.stat-badge {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
min-width: 32rpx;
|
||||
height: 32rpx;
|
||||
line-height: 32rpx;
|
||||
padding: 0 8rpx;
|
||||
background: #ff3366;
|
||||
border-radius: 16rpx;
|
||||
font-size: 20rpx;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu-card {
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 28rpx 0;
|
||||
border-bottom: 1px solid rgba(0, 255, 255, 0.1);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 40rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.menu-name {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.menu-extra {
|
||||
font-size: 24rpx;
|
||||
color: #7a7e9d;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.menu-badge {
|
||||
min-width: 32rpx;
|
||||
height: 32rpx;
|
||||
line-height: 32rpx;
|
||||
padding: 0 8rpx;
|
||||
background: #ff3366;
|
||||
border-radius: 16rpx;
|
||||
font-size: 20rpx;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #7a7e9d;
|
||||
}
|
||||
|
||||
.bottom-decoration {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
margin-top: 60rpx;
|
||||
padding: 40rpx 0;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.decoration-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(122, 126, 157, 0.3) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.decoration-text {
|
||||
font-size: 20rpx;
|
||||
color: #7a7e9d;
|
||||
letter-spacing: 2rpx;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
41
static/icons/README.md
Normal file
41
static/icons/README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# TabBar 图标说明
|
||||
|
||||
## 图标尺寸要求
|
||||
- 推荐尺寸: 81px * 81px
|
||||
- 格式: PNG 透明背景
|
||||
|
||||
## 需要的图标
|
||||
|
||||
### 首页
|
||||
- `home.png` - 普通状态 (灰色)
|
||||
- `home-active.png` - 选中状态 (青色)
|
||||
|
||||
**建议图标**: 🏠 房子 或 🎮 游戏手柄
|
||||
|
||||
### 订单
|
||||
- `order.png` - 普通状态 (灰色)
|
||||
- `order-active.png` - 选中状态 (青色)
|
||||
|
||||
**建议图标**: 📋 订单 或 📦 包裹
|
||||
|
||||
### 我的
|
||||
- `user.png` - 普通状态 (灰色)
|
||||
- `user-active.png` - 选中状态 (青色)
|
||||
|
||||
**建议图标**: 👤 用户 或 ⚙️ 设置
|
||||
|
||||
## 配色方案
|
||||
- **普通状态**: #7a7e9d (灰紫色)
|
||||
- **选中状态**: #00ffff (青色霓虹)
|
||||
|
||||
## 临时解决方案
|
||||
|
||||
在图标未准备好之前,可以:
|
||||
1. 使用纯文字tabbar (删除pages.json中的iconPath配置)
|
||||
2. 使用在线图标生成工具快速制作
|
||||
3. 使用Emoji作为临时图标
|
||||
|
||||
## 在线工具推荐
|
||||
- Iconfont: https://www.iconfont.cn/
|
||||
- Flaticon: https://www.flaticon.com/
|
||||
- Icons8: https://icons8.com/
|
||||
71
uni.scss
Normal file
71
uni.scss
Normal file
@ -0,0 +1,71 @@
|
||||
/* 电竞赛博朋克配色方案 */
|
||||
|
||||
/* ============ 主色调 ============ */
|
||||
// 主背景色
|
||||
$bg-primary: #0a0e27;
|
||||
$bg-secondary: #0f1229;
|
||||
$bg-tertiary: #1a1d3a;
|
||||
|
||||
// 霓虹色
|
||||
$neon-cyan: #00ffff; // 青色霓虹
|
||||
$neon-magenta: #ff00ff; // 洋红霓虹
|
||||
$neon-purple: #9d00ff; // 紫色霓虹
|
||||
$neon-blue: #0099ff; // 蓝色霓虹
|
||||
$neon-pink: #ff1493; // 粉色霓虹
|
||||
$neon-green: #00ff88; // 绿色霓虹
|
||||
|
||||
// 功能色
|
||||
$color-success: #00ff88;
|
||||
$color-warning: #ffaa00;
|
||||
$color-error: #ff3366;
|
||||
$color-info: #00ffff;
|
||||
|
||||
/* ============ 文字颜色 ============ */
|
||||
$text-primary: #ffffff;
|
||||
$text-secondary: #a0a4c4;
|
||||
$text-tertiary: #7a7e9d;
|
||||
$text-disabled: #4a4e6d;
|
||||
|
||||
/* ============ 边框颜色 ============ */
|
||||
$border-primary: rgba(0, 255, 255, 0.3);
|
||||
$border-secondary: rgba(0, 255, 255, 0.15);
|
||||
$border-glow: rgba(0, 255, 255, 0.6);
|
||||
|
||||
/* ============ 阴影效果 ============ */
|
||||
$shadow-neon-cyan: 0 0 10px rgba(0, 255, 255, 0.5), 0 0 20px rgba(0, 255, 255, 0.3);
|
||||
$shadow-neon-magenta: 0 0 10px rgba(255, 0, 255, 0.5), 0 0 20px rgba(255, 0, 255, 0.3);
|
||||
$shadow-card: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||
|
||||
/* ============ 尺寸 ============ */
|
||||
$spacing-xs: 8rpx;
|
||||
$spacing-sm: 16rpx;
|
||||
$spacing-md: 24rpx;
|
||||
$spacing-lg: 32rpx;
|
||||
$spacing-xl: 48rpx;
|
||||
|
||||
$border-radius-sm: 8rpx;
|
||||
$border-radius-md: 16rpx;
|
||||
$border-radius-lg: 24rpx;
|
||||
|
||||
/* ============ 字体大小 ============ */
|
||||
$font-xs: 20rpx;
|
||||
$font-sm: 24rpx;
|
||||
$font-md: 28rpx;
|
||||
$font-lg: 32rpx;
|
||||
$font-xl: 36rpx;
|
||||
$font-xxl: 48rpx;
|
||||
|
||||
/* ============ 渐变背景 ============ */
|
||||
$gradient-primary: linear-gradient(135deg, #00ffff 0%, #0099ff 100%);
|
||||
$gradient-secondary: linear-gradient(135deg, #ff00ff 0%, #9d00ff 100%);
|
||||
$gradient-card: linear-gradient(135deg, rgba(20, 25, 50, 0.9) 0%, rgba(10, 14, 39, 0.8) 100%);
|
||||
$gradient-overlay: linear-gradient(180deg, transparent 0%, rgba(10, 14, 39, 0.8) 100%);
|
||||
|
||||
/* ============ 游戏等级颜色 ============ */
|
||||
$rank-bronze: #cd7f32;
|
||||
$rank-silver: #c0c0c0;
|
||||
$rank-gold: #ffd700;
|
||||
$rank-platinum: #e5e4e2;
|
||||
$rank-diamond: #b9f2ff;
|
||||
$rank-master: #ff00ff;
|
||||
$rank-king: #ff1493;
|
||||
10
vite.config.js
Normal file
10
vite.config.js
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import uni from '@dcloudio/vite-plugin-uni'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [uni()],
|
||||
server: {
|
||||
port: 8080,
|
||||
open: true
|
||||
}
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user