feat: 陪学项目功能优化 - 2026-03-04
This commit is contained in:
parent
58df75a39f
commit
47101329e1
|
|
@ -30,9 +30,9 @@ spring:
|
|||
maintainTimeStats: false
|
||||
|
||||
redis:
|
||||
host: ${REDIS_HOST:your-prod-redis-host}
|
||||
host: ${REDIS_HOST:localhost}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD}
|
||||
password: ${REDIS_PASSWORD:}
|
||||
database: 0
|
||||
timeout: 3000
|
||||
lettuce:
|
||||
|
|
@ -45,10 +45,11 @@ spring:
|
|||
|
||||
# JWT配置 - 生产环境使用环境变量
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:peidu-secret-key-2026-change-this-in-production-min-256-bits} # 默认值
|
||||
expiration: 604800 # 7天(秒)
|
||||
secret: ${JWT_SECRET:peiduSecretKeyForJwtTokenGenerationMustBeAtLeast256BitsLongToWorkProperly} # 默认值(与dev保持一致)
|
||||
expiration: 604800000 # 7天(毫秒)
|
||||
header: Authorization
|
||||
prefix: Bearer
|
||||
prefix: Bearer
|
||||
refresh-time: 518400000 # 6天(毫秒)
|
||||
|
||||
# 文件上传配置 - 生产环境
|
||||
file:
|
||||
|
|
@ -60,15 +61,15 @@ file:
|
|||
|
||||
# 微信小程序配置 - 生产环境
|
||||
wx:
|
||||
appid: ${WX_APPID} # 通过环境变量设置
|
||||
secret: ${WX_SECRET} # 通过环境变量设置
|
||||
appid: ${WX_APPID:your-wx-appid} # 通过环境变量设置
|
||||
secret: ${WX_SECRET:your-wx-secret} # 通过环境变量设置
|
||||
|
||||
# 微信支付配置 - 生产环境
|
||||
wechat:
|
||||
pay:
|
||||
appid: ${WX_PAY_APPID}
|
||||
mchid: ${WX_PAY_MCHID}
|
||||
key: ${WX_PAY_KEY}
|
||||
appid: ${WX_PAY_APPID:your-wx-pay-appid}
|
||||
mchid: ${WX_PAY_MCHID:your-mchid}
|
||||
key: ${WX_PAY_KEY:your-pay-key}
|
||||
cert-path: /data/peidu/cert/apiclient_cert.p12
|
||||
notify-url: https://your-domain.com/api/payment/notify/wechat
|
||||
|
||||
|
|
@ -83,21 +84,21 @@ sms:
|
|||
region: ${ALIYUN_SMS_REGION:cn-hangzhou}
|
||||
endpoint: ${ALIYUN_SMS_ENDPOINT:dysmsapi.aliyuncs.com}
|
||||
provider: aliyun
|
||||
access-key: ${ALIYUN_SMS_ACCESS_KEY}
|
||||
access-secret: ${ALIYUN_SMS_ACCESS_SECRET}
|
||||
access-key: ${ALIYUN_SMS_ACCESS_KEY:}
|
||||
access-secret: ${ALIYUN_SMS_ACCESS_SECRET:}
|
||||
sign-name: ${SMS_SIGN_NAME:陪读服务}
|
||||
template:
|
||||
verify-code: ${SMS_TEMPLATE_VERIFY_CODE}
|
||||
order-notify: ${SMS_TEMPLATE_ORDER_NOTIFY}
|
||||
verify-code: ${SMS_TEMPLATE_VERIFY_CODE:}
|
||||
order-notify: ${SMS_TEMPLATE_ORDER_NOTIFY:}
|
||||
|
||||
# 阿里云OSS配置 - 生产环境
|
||||
aliyun:
|
||||
oss:
|
||||
endpoint: ${ALIYUN_OSS_ENDPOINT:oss-cn-hangzhou.aliyuncs.com}
|
||||
access-key-id: ${ALIYUN_OSS_ACCESS_KEY}
|
||||
access-key-secret: ${ALIYUN_OSS_ACCESS_SECRET}
|
||||
bucket-name: ${ALIYUN_OSS_BUCKET}
|
||||
domain: ${ALIYUN_OSS_DOMAIN}
|
||||
access-key-id: ${ALIYUN_OSS_ACCESS_KEY:}
|
||||
access-key-secret: ${ALIYUN_OSS_ACCESS_SECRET:}
|
||||
bucket-name: ${ALIYUN_OSS_BUCKET:}
|
||||
domain: ${ALIYUN_OSS_DOMAIN:}
|
||||
|
||||
# 备份配置 - 生产环境
|
||||
backup:
|
||||
|
|
|
|||
|
|
@ -102,6 +102,24 @@
|
|||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">服务时段</text>
|
||||
<view class="time-slot-row">
|
||||
<picker :range="startTimeList" @change="onStartTimeChange">
|
||||
<view class="time-picker">
|
||||
{{ form.startTime || '开始时间' }}
|
||||
</view>
|
||||
</picker>
|
||||
<text class="time-separator">至</text>
|
||||
<picker :range="endTimeList" @change="onEndTimeChange">
|
||||
<view class="time-picker">
|
||||
{{ form.endTime || '结束时间' }}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<text class="time-duration" v-if="serviceDuration">预计服务时长:{{ serviceDuration }}小时</text>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="label">其他需求</text>
|
||||
<textarea class="textarea" v-model="form.remark" placeholder="请描述具体需求" />
|
||||
|
|
@ -141,6 +159,8 @@ export default {
|
|||
hobbies: '',
|
||||
serviceTypes: [],
|
||||
serviceDate: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
remark: ''
|
||||
},
|
||||
gradeList: ['幼儿园', '一年级', '二年级', '三年级', '四年级', '五年级', '六年级', '初一', '初二', '初三', '高一', '高二', '高三'],
|
||||
|
|
@ -151,7 +171,21 @@ export default {
|
|||
{ label: '阅读陪伴', value: 'reading' },
|
||||
{ label: '运动陪伴', value: 'sports' },
|
||||
{ label: '其他', value: 'other' }
|
||||
]
|
||||
],
|
||||
startTimeList: ['08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00'],
|
||||
endTimeList: ['09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00']
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
serviceDuration() {
|
||||
if (!this.form.startTime || !this.form.endTime) {
|
||||
return 0
|
||||
}
|
||||
const start = parseInt(this.form.startTime.split(':')[0])
|
||||
const end = parseInt(this.form.endTime.split(':')[0])
|
||||
const duration = end - start
|
||||
return duration > 0 ? duration : 0
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -172,6 +206,32 @@ export default {
|
|||
this.form.serviceTypes = e.detail.value
|
||||
},
|
||||
|
||||
onStartTimeChange(e) {
|
||||
this.form.startTime = this.startTimeList[e.detail.value]
|
||||
// 如果结束时间早于或等于开始时间,清空结束时间
|
||||
if (this.form.endTime) {
|
||||
const start = parseInt(this.form.startTime.split(':')[0])
|
||||
const end = parseInt(this.form.endTime.split(':')[0])
|
||||
if (end <= start) {
|
||||
this.form.endTime = ''
|
||||
uni.showToast({ title: '请重新选择结束时间', icon: 'none' })
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onEndTimeChange(e) {
|
||||
this.form.endTime = this.endTimeList[e.detail.value]
|
||||
// 验证结束时间必须大于开始时间
|
||||
if (this.form.startTime) {
|
||||
const start = parseInt(this.form.startTime.split(':')[0])
|
||||
const end = parseInt(this.form.endTime.split(':')[0])
|
||||
if (end <= start) {
|
||||
this.form.endTime = ''
|
||||
uni.showToast({ title: '结束时间必须晚于开始时间', icon: 'none' })
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async handleSubmit() {
|
||||
// 🔥 游客模式拦截:提示登录
|
||||
if (this.isGuestMode) {
|
||||
|
|
@ -236,8 +296,8 @@ export default {
|
|||
serviceName: '快速预约-陪伴服务',
|
||||
serviceType: this.form.serviceTypes.join(',') || '陪伴服务',
|
||||
serviceDate: this.form.serviceDate || new Date().toISOString().split('T')[0],
|
||||
timeSlot: '待定',
|
||||
duration: 120, // 默认2小时
|
||||
timeSlot: this.form.startTime && this.form.endTime ? `${this.form.startTime}-${this.form.endTime}` : '待定',
|
||||
duration: this.serviceDuration * 60 || 120, // 转换为分钟,默认2小时
|
||||
serviceAddress: this.form.address || this.form.region,
|
||||
price: 0, // 待定价格
|
||||
couponId: null,
|
||||
|
|
@ -255,6 +315,8 @@ export default {
|
|||
grade: this.form.grade,
|
||||
hobbies: this.form.hobbies,
|
||||
serviceTypes: this.form.serviceTypes,
|
||||
startTime: this.form.startTime,
|
||||
endTime: this.form.endTime,
|
||||
remark: this.form.remark
|
||||
}),
|
||||
status: 0, // 待支付
|
||||
|
|
@ -329,6 +391,8 @@ export default {
|
|||
hobbies: '',
|
||||
serviceTypes: [],
|
||||
serviceDate: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
remark: ''
|
||||
}
|
||||
}
|
||||
|
|
@ -439,6 +503,37 @@ export default {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.time-slot-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
|
||||
.time-picker {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
padding: 0 20rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.time-separator {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.time-duration {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.radio-item, .checkbox-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ export default {
|
|||
{ id: 5, image: 'https://i.imglt.com/20260131/8fd69266fcf1cb035df071f9ce335bbb.jpg' }
|
||||
],
|
||||
|
||||
// 功能入口图标(4行×3列)
|
||||
// 功能入口图标(4行×3列 + 第5行1个)
|
||||
functionIcons: [
|
||||
// 第一行
|
||||
{ id: 1, icon: 'https://i.imglt.com/20260201/dcc7793e837b1d4d38eeb58f1a18bf45.jpg', label: '陪伴员', path: '/pages/teacher/list' },
|
||||
|
|
@ -170,7 +170,11 @@ export default {
|
|||
{ id: 8, icon: 'https://i.imglt.com/20260201/c161a0ae0ae15e98fc851d6e4296718d.jpg', label: '专项提升', path: '/service-package/pages/special/list' },
|
||||
{ id: 9, icon: 'https://i.imglt.com/20260201/939405d3dfae415df1a526dfb23e91ed.jpg', label: '兴趣培养', path: '/activity-package/pages/interest/category' },
|
||||
// 第四行
|
||||
{ id: 10, icon: 'https://i.imglt.com/20260201/9aa84915d9739f217a0c4185d26f751c.jpg', label: '线上督学', path: '/activity-package/pages/supervision/category' }
|
||||
{ id: 10, icon: 'https://i.imglt.com/20260201/9aa84915d9739f217a0c4185d26f751c.jpg', label: '线上督学', path: '/activity-package/pages/supervision/category' },
|
||||
{ id: 11, icon: 'https://i.imglt.com/20260201/55a5d2dea2a6376ae0c4372406465231.jpg', label: '热门课程', path: '/activity-package/pages/academy/index' },
|
||||
{ id: 12, icon: 'https://i.imglt.com/20260201/dcc7793e837b1d4d38eeb58f1a18bf45.jpg', label: '优秀陪伴员', path: '/pages/teacher/list' },
|
||||
// 第五行
|
||||
{ id: 13, icon: 'https://i.imglt.com/20260201/c161a0ae0ae15e98fc851d6e4296718d.jpg', label: '专项课程', path: '/service-package/pages/special/list' }
|
||||
],
|
||||
|
||||
// 特惠套餐
|
||||
|
|
@ -203,7 +207,7 @@ export default {
|
|||
// 并行加载多个接口
|
||||
const [unreadCount, packages, homeCourses] = await Promise.all([
|
||||
api.notificationApi.getUnreadCount().catch(() => ({ code: 200, data: 0 })),
|
||||
api.packageApi.getPackageList({ page: 1, size: 10 }).catch(() => ({ records: [] })), // ✅ 改为套餐包API
|
||||
api.packageApi.getPackageList({ page: 1, size: 10 }).catch(() => ({ records: [] })),
|
||||
api.homeApi.getHomeCourses({ hotLimit: 2, discountLimit: 2 }).catch(() => null)
|
||||
])
|
||||
|
||||
|
|
@ -213,44 +217,28 @@ export default {
|
|||
: 0
|
||||
this.unreadCount = count
|
||||
|
||||
// 更新套餐包数据(首页显示为"特惠套餐")
|
||||
// 更新套餐包数据
|
||||
const packageList = packages.data?.records || packages.data?.list || packages.records || packages.list || packages || []
|
||||
if (packageList.length > 0) {
|
||||
this.specialPackages = packageList.slice(0, 2).map(pkg => ({
|
||||
id: pkg.id,
|
||||
title: pkg.packageName || '优惠套餐',
|
||||
price: pkg.price ? pkg.price.toFixed(0) : '24900', // 直接使用后端价格(已经是正确格式)
|
||||
price: pkg.price ? pkg.price.toFixed(0) : '24900',
|
||||
originalPrice: pkg.originalPrice ? pkg.originalPrice.toFixed(0) : '49800',
|
||||
desc: pkg.description || `${pkg.totalHours || 0}小时 · 有效期${pkg.validDays || 365}天`,
|
||||
type: 'package' // 标记为套餐类型
|
||||
type: 'package'
|
||||
}))
|
||||
} else {
|
||||
// 使用默认数据
|
||||
this.specialPackages = [
|
||||
{
|
||||
id: 1,
|
||||
title: '暖心式陪伴200小时',
|
||||
price: '24900',
|
||||
originalPrice: '49800',
|
||||
desc: '200小时 · 有效期365天',
|
||||
type: 'package'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '基准式陪伴100小时',
|
||||
price: '12900',
|
||||
originalPrice: '25800',
|
||||
desc: '100小时 · 有效期365天',
|
||||
type: 'package'
|
||||
}
|
||||
{ id: 1, title: '暖心式陪伴200小时', price: '24900', originalPrice: '49800', desc: '200小时 · 有效期365天', type: 'package' },
|
||||
{ id: 2, title: '基准式陪伴100小时', price: '12900', originalPrice: '25800', desc: '100小时 · 有效期365天', type: 'package' }
|
||||
]
|
||||
}
|
||||
|
||||
// 更新课程数据(从首页课程API获取)
|
||||
// 更新课程数据
|
||||
if (homeCourses && homeCourses.data) {
|
||||
const courseData = homeCourses.data
|
||||
|
||||
// 热门课程
|
||||
if (courseData.hotCourses && courseData.hotCourses.length > 0) {
|
||||
this.hotCourses = courseData.hotCourses.map((course, index) => ({
|
||||
id: course.id,
|
||||
|
|
@ -262,28 +250,12 @@ export default {
|
|||
bgColor: this.getCourseBgColor(index)
|
||||
}))
|
||||
} else {
|
||||
// 使用默认数据
|
||||
this.hotCourses = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'UI/UX设计实战课',
|
||||
price: '129',
|
||||
type: 'parent_academy',
|
||||
iconText: '设计',
|
||||
bgColor: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Python编程入门',
|
||||
price: '99',
|
||||
type: 'parent_academy',
|
||||
iconText: '编程',
|
||||
bgColor: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)'
|
||||
}
|
||||
{ id: 1, name: 'UI/UX设计实战课', price: '129', type: 'parent_academy', iconText: '设计', bgColor: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' },
|
||||
{ id: 2, name: 'Python编程入门', price: '99', type: 'parent_academy', iconText: '编程', bgColor: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }
|
||||
]
|
||||
}
|
||||
|
||||
// 优惠课程
|
||||
if (courseData.discountCourses && courseData.discountCourses.length > 0) {
|
||||
this.discountCourses = courseData.discountCourses.map((course, index) => ({
|
||||
id: course.id,
|
||||
|
|
@ -297,78 +269,24 @@ export default {
|
|||
bgColor: this.getCourseBgColor(index)
|
||||
}))
|
||||
} else {
|
||||
// 使用默认数据
|
||||
this.discountCourses = [
|
||||
{
|
||||
id: 1,
|
||||
name: '小学数学提升班',
|
||||
price: '399',
|
||||
type: 'interest',
|
||||
badge: '暑期特惠',
|
||||
desc: '系统提升数学思维',
|
||||
iconText: '数学',
|
||||
bgColor: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '初中英语语法班',
|
||||
price: '499',
|
||||
type: 'interest',
|
||||
badge: '限时抢购',
|
||||
desc: '掌握核心语法知识',
|
||||
iconText: '英语',
|
||||
bgColor: 'linear-gradient(135deg, #30cfd0 0%, #330867 100%)'
|
||||
}
|
||||
{ id: 1, name: '小学数学提升班', price: '399', type: 'interest', badge: '暑期特惠', desc: '系统提升数学思维', iconText: '数学', bgColor: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)' },
|
||||
{ id: 2, name: '初中英语语法班', price: '499', type: 'interest', badge: '限时抢购', desc: '掌握核心语法知识', iconText: '英语', bgColor: 'linear-gradient(135deg, #30cfd0 0%, #330867 100%)' }
|
||||
]
|
||||
}
|
||||
} else {
|
||||
// API调用失败,使用默认数据
|
||||
this.hotCourses = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'UI/UX设计实战课',
|
||||
price: '129',
|
||||
type: 'parent_academy',
|
||||
iconText: '设计',
|
||||
bgColor: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Python编程入门',
|
||||
price: '99',
|
||||
type: 'parent_academy',
|
||||
iconText: '编程',
|
||||
bgColor: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)'
|
||||
}
|
||||
{ id: 1, name: 'UI/UX设计实战课', price: '129', type: 'parent_academy', iconText: '设计', bgColor: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' },
|
||||
{ id: 2, name: 'Python编程入门', price: '99', type: 'parent_academy', iconText: '编程', bgColor: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }
|
||||
]
|
||||
|
||||
this.discountCourses = [
|
||||
{
|
||||
id: 1,
|
||||
name: '小学数学提升班',
|
||||
price: '399',
|
||||
type: 'interest',
|
||||
badge: '暑期特惠',
|
||||
desc: '系统提升数学思维',
|
||||
iconText: '数学',
|
||||
bgColor: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '初中英语语法班',
|
||||
price: '499',
|
||||
type: 'interest',
|
||||
badge: '限时抢购',
|
||||
desc: '掌握核心语法知识',
|
||||
iconText: '英语',
|
||||
bgColor: 'linear-gradient(135deg, #30cfd0 0%, #330867 100%)'
|
||||
}
|
||||
{ id: 1, name: '小学数学提升班', price: '399', type: 'interest', badge: '暑期特惠', desc: '系统提升数学思维', iconText: '数学', bgColor: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)' },
|
||||
{ id: 2, name: '初中英语语法班', price: '499', type: 'interest', badge: '限时抢购', desc: '掌握核心语法知识', iconText: '英语', bgColor: 'linear-gradient(135deg, #30cfd0 0%, #330867 100%)' }
|
||||
]
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载首页数据失败:', error)
|
||||
// 失败时使用默认数据,不影响页面显示
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -312,25 +312,162 @@ export default {
|
|||
this.form.subject = this.subjects[this.subjectIndex]
|
||||
},
|
||||
|
||||
// 上传封面图片
|
||||
uploadCover() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.form.coverImage = res.tempFilePaths[0]
|
||||
// TODO: 上传到服务器
|
||||
const tempFilePath = res.tempFilePaths[0]
|
||||
|
||||
// 显示上传进度
|
||||
uni.showLoading({ title: '上传封面中...' })
|
||||
|
||||
// 上传图片到服务器
|
||||
uni.uploadFile({
|
||||
url: `${getApp().globalData.baseUrl}/api/file/upload`,
|
||||
filePath: tempFilePath,
|
||||
name: 'file',
|
||||
header: {
|
||||
'Authorization': uni.getStorageSync('token') || ''
|
||||
},
|
||||
success: (uploadRes) => {
|
||||
try {
|
||||
const data = JSON.parse(uploadRes.data)
|
||||
if (data.code === 200) {
|
||||
// 更新封面URL(使用服务器返回的URL)
|
||||
this.form.coverImage = data.data.url || data.data.fileUrl
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '封面上传成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
} else {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: data.message || '上传失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析上传结果失败:', error)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '上传失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('上传封面失败:', err)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '上传失败,请检查网络',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择图片失败:', err)
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 批量上传课程图片
|
||||
uploadImages() {
|
||||
const maxCount = 9 - this.form.images.length
|
||||
if (maxCount <= 0) {
|
||||
uni.showToast({
|
||||
title: '最多上传9张图片',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.chooseImage({
|
||||
count: maxCount,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.form.images.push(...res.tempFilePaths)
|
||||
// TODO: 上传到服务器
|
||||
const tempFilePaths = res.tempFilePaths
|
||||
|
||||
// 显示上传进度
|
||||
uni.showLoading({ title: `上传中 0/${tempFilePaths.length}` })
|
||||
|
||||
// 批量上传图片
|
||||
this.uploadImagesSequentially(tempFilePaths, 0, [])
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择图片失败:', err)
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 顺序上传图片(避免并发过多)
|
||||
uploadImagesSequentially(filePaths, index, uploadedUrls) {
|
||||
if (index >= filePaths.length) {
|
||||
// 所有图片上传完成
|
||||
uni.hideLoading()
|
||||
this.form.images.push(...uploadedUrls)
|
||||
uni.showToast({
|
||||
title: `成功上传${uploadedUrls.length}张图片`,
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新进度提示
|
||||
uni.showLoading({ title: `上传中 ${index + 1}/${filePaths.length}` })
|
||||
|
||||
// 上传当前图片
|
||||
uni.uploadFile({
|
||||
url: `${getApp().globalData.baseUrl}/api/file/upload`,
|
||||
filePath: filePaths[index],
|
||||
name: 'file',
|
||||
header: {
|
||||
'Authorization': uni.getStorageSync('token') || ''
|
||||
},
|
||||
success: (uploadRes) => {
|
||||
try {
|
||||
const data = JSON.parse(uploadRes.data)
|
||||
if (data.code === 200) {
|
||||
const imageUrl = data.data.url || data.data.fileUrl
|
||||
uploadedUrls.push(imageUrl)
|
||||
// 继续上传下一张
|
||||
this.uploadImagesSequentially(filePaths, index + 1, uploadedUrls)
|
||||
} else {
|
||||
console.error(`第${index + 1}张图片上传失败:`, data.message)
|
||||
// 继续上传下一张(跳过失败的)
|
||||
this.uploadImagesSequentially(filePaths, index + 1, uploadedUrls)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`第${index + 1}张图片解析失败:`, error)
|
||||
// 继续上传下一张(跳过失败的)
|
||||
this.uploadImagesSequentially(filePaths, index + 1, uploadedUrls)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error(`第${index + 1}张图片上传失败:`, err)
|
||||
// 继续上传下一张(跳过失败的)
|
||||
this.uploadImagesSequentially(filePaths, index + 1, uploadedUrls)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -347,8 +347,67 @@ export default {
|
|||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
// TODO: 上传图片到服务器
|
||||
this.profile.avatar = res.tempFilePaths[0]
|
||||
const tempFilePath = res.tempFilePaths[0]
|
||||
|
||||
// 显示上传进度
|
||||
uni.showLoading({ title: '上传中...' })
|
||||
|
||||
// 上传图片到服务器
|
||||
uni.uploadFile({
|
||||
url: `${getApp().globalData.baseUrl}/api/file/upload`,
|
||||
filePath: tempFilePath,
|
||||
name: 'file',
|
||||
header: {
|
||||
'Authorization': uni.getStorageSync('token') || ''
|
||||
},
|
||||
success: (uploadRes) => {
|
||||
try {
|
||||
const data = JSON.parse(uploadRes.data)
|
||||
if (data.code === 200) {
|
||||
// 更新头像URL(使用服务器返回的URL)
|
||||
this.profile.avatar = data.data.url || data.data.fileUrl
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '头像上传成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
} else {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: data.message || '上传失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析上传结果失败:', error)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '上传失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('上传头像失败:', err)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '上传失败,请检查网络',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择图片失败:', err)
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { useUserStore } from '@/store/user'
|
|||
// 生产环境:使用线上服务器
|
||||
|
||||
// 开发模式标志(手动切换)
|
||||
const IS_DEV = false // 改为 false 则使用生产环境
|
||||
const IS_DEV = true // 改为 true 则使用本地开发环境
|
||||
|
||||
// 根据开发模式选择BASE_URL
|
||||
const BASE_URL = IS_DEV
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user