|
|
@@ -0,0 +1,194 @@
|
|
|
+<template>
|
|
|
+ <view class="page">
|
|
|
+ <view class="nav-bar">
|
|
|
+ <view class="nav-left" @tap="goBack">
|
|
|
+ <text class="nav-icon">‹</text>
|
|
|
+ <text class="nav-title">外部商品打卡</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <scroll-view class="content" scroll-y>
|
|
|
+ <!-- 任务说明 -->
|
|
|
+ <view class="card">
|
|
|
+ <view class="card-header">
|
|
|
+ <text class="card-icon">🛒</text>
|
|
|
+ <text class="card-title">{{ taskTitle }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="card-body">
|
|
|
+ <text class="task-desc">按以下步骤完成本次外部平台商品购买打卡:</text>
|
|
|
+ <view class="step-list">
|
|
|
+ <view class="step-item">
|
|
|
+ <text class="step-num">1</text>
|
|
|
+ <text class="step-text">前往外部平台完成商品购买</text>
|
|
|
+ </view>
|
|
|
+ <view class="step-item">
|
|
|
+ <text class="step-num">2</text>
|
|
|
+ <text class="step-text">收到商品后,拍摄实物照片</text>
|
|
|
+ </view>
|
|
|
+ <view class="step-item">
|
|
|
+ <text class="step-num">3</text>
|
|
|
+ <text class="step-text">上传照片,提交完成打卡任务</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 拍照上传 -->
|
|
|
+ <view class="card">
|
|
|
+ <view class="card-header">
|
|
|
+ <text class="card-icon">📸</text>
|
|
|
+ <text class="card-title">上传商品实物照片</text>
|
|
|
+ </view>
|
|
|
+ <view class="card-body">
|
|
|
+ <view class="upload-area" @tap="choosePhoto">
|
|
|
+ <image v-if="photoUrl" :src="photoUrl" mode="aspectFill" class="photo-preview" />
|
|
|
+ <view v-else class="upload-placeholder">
|
|
|
+ <text class="upload-icon">📷</text>
|
|
|
+ <text class="upload-text">拍照 / 相册选择商品实物照片</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="submit-btn" :class="{ submitting: submitting }" @tap="submit">
|
|
|
+ <text>{{ submitting ? '提交中...' : '✓ 提交完成打卡' }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="bottom-spacer"></view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import config from '../../config'
|
|
|
+
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ taskId: null,
|
|
|
+ memberId: null,
|
|
|
+ taskTitle: '',
|
|
|
+ photoUrl: '',
|
|
|
+ submitting: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onLoad(options) {
|
|
|
+ this.taskId = options && options.taskId ? parseInt(options.taskId) : null
|
|
|
+ this.taskTitle = options && options.title ? decodeURIComponent(options.title) : '外部平台商品任务'
|
|
|
+ var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
|
|
|
+ if (role !== 'child') {
|
|
|
+ this.memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || null
|
|
|
+ } else {
|
|
|
+ this.memberId = uni.getStorageSync('currentMemberId') || null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ goBack() { uni.navigateBack() },
|
|
|
+ choosePhoto: function() {
|
|
|
+ var self = this
|
|
|
+ uni.chooseImage({
|
|
|
+ count: 1,
|
|
|
+ sizeType: ['compressed'],
|
|
|
+ sourceType: ['album', 'camera'],
|
|
|
+ success: function(res) {
|
|
|
+ self.photoUrl = res.tempFilePaths[0]
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ uploadFile: function(filePath) {
|
|
|
+ var self = this
|
|
|
+ return new Promise(function(resolve, reject) {
|
|
|
+ uni.uploadFile({
|
|
|
+ url: config.API_BASE_URL + '/api/media/upload',
|
|
|
+ filePath: filePath,
|
|
|
+ name: 'file',
|
|
|
+ header: { 'Authorization': 'Bearer ' + uni.getStorageSync('token') },
|
|
|
+ success: function(res) {
|
|
|
+ try {
|
|
|
+ var data = JSON.parse(res.data)
|
|
|
+ resolve(data.data || data.url || '')
|
|
|
+ } catch (e) {
|
|
|
+ resolve(res.data || '')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: function() { reject() }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ submit: async function() {
|
|
|
+ var self = this
|
|
|
+ if (self.submitting) return
|
|
|
+ if (!self.photoUrl) { uni.showToast({ title: '请先上传商品实物照片', icon: 'none' }); return }
|
|
|
+ if (!self.memberId) { uni.showToast({ title: '成员ID无效', icon: 'none' }); return }
|
|
|
+ self.submitting = true
|
|
|
+ var photoUrl = ''
|
|
|
+ try { photoUrl = await self.uploadFile(self.photoUrl) } catch (e) {}
|
|
|
+ try {
|
|
|
+ const res = await new Promise(function(resolve, reject) {
|
|
|
+ uni.request({
|
|
|
+ url: config.API_BASE_URL + '/api/tasks/' + self.taskId + '/complete',
|
|
|
+ method: 'POST',
|
|
|
+ data: { memberId: self.memberId, photoUrl: photoUrl, content: '外部平台商品实物已上传', contentType: 'image' },
|
|
|
+ header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + uni.getStorageSync('token') },
|
|
|
+ success: function(r) { r.statusCode && r.statusCode !== 200 ? reject(r) : resolve(r) },
|
|
|
+ fail: function() { reject() }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ if (res.data && res.data.code === 200) {
|
|
|
+ uni.showToast({ title: '任务已完成', icon: 'success' })
|
|
|
+ setTimeout(function() { uni.navigateBack() }, 800)
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: (res.data && res.data.message) || '提交失败', icon: 'none' })
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ uni.showToast({ title: '提交失败,请重试', icon: 'none' })
|
|
|
+ }
|
|
|
+ self.submitting = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.page { min-height: 100vh; background: #F9FAFB; }
|
|
|
+.nav-bar { height: 88rpx; padding: 24rpx 24rpx 0; display: flex; align-items: flex-end; }
|
|
|
+.nav-left { display: flex; align-items: center; gap: 10rpx; }
|
|
|
+.nav-icon { font-size: 48rpx; color: #333; line-height: 1; }
|
|
|
+.nav-title { font-size: 34rpx; font-weight: 600; color: #333; }
|
|
|
+.content { padding-bottom: 40rpx; }
|
|
|
+.card { margin: 16rpx 20rpx; background: #fff; border-radius: 20rpx; overflow: hidden; box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.05); }
|
|
|
+.card-header { display: flex; align-items: center; gap: 10rpx; padding: 20rpx 24rpx 8rpx; border-bottom: 1rpx solid #f5f5f5; background: linear-gradient(90deg, #FFF7ED, #fff); }
|
|
|
+.card-icon { font-size: 32rpx; }
|
|
|
+.card-title { font-size: 30rpx; font-weight: 600; color: #333; }
|
|
|
+.card-body { padding: 24rpx; }
|
|
|
+.task-desc { font-size: 26rpx; color: #6B7280; line-height: 1.6; display: block; margin-bottom: 20rpx; }
|
|
|
+.step-list { display: flex; flex-direction: column; gap: 16rpx; }
|
|
|
+.step-item { display: flex; align-items: center; gap: 16rpx; }
|
|
|
+.step-num {
|
|
|
+ width: 44rpx; height: 44rpx; border-radius: 50%;
|
|
|
+ background: linear-gradient(135deg, #FF8C42, #F97316);
|
|
|
+ color: #fff; font-size: 24rpx; font-weight: 600;
|
|
|
+ display: flex; align-items: center; justify-content: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.step-text { font-size: 26rpx; color: #374151; }
|
|
|
+.upload-area {
|
|
|
+ width: 100%; height: 320rpx;
|
|
|
+ border: 2rpx dashed #D1D5DB; border-radius: 16rpx;
|
|
|
+ display: flex; align-items: center; justify-content: center;
|
|
|
+ background: #F9FAFB; overflow: hidden;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+}
|
|
|
+.photo-preview { width: 100%; height: 100%; }
|
|
|
+.upload-placeholder { display: flex; flex-direction: column; align-items: center; gap: 12rpx; }
|
|
|
+.upload-icon { font-size: 72rpx; }
|
|
|
+.upload-text { font-size: 26rpx; color: #9CA3AF; }
|
|
|
+.submit-btn {
|
|
|
+ background: linear-gradient(135deg, #FF8C42, #F97316);
|
|
|
+ color: #fff; text-align: center; padding: 22rpx 0;
|
|
|
+ border-radius: 14rpx; font-size: 30rpx; font-weight: 600;
|
|
|
+ box-shadow: 0 6rpx 20rpx rgba(249,115,22,0.3);
|
|
|
+}
|
|
|
+.submit-btn:active { transform: scale(0.98); }
|
|
|
+.submitting { opacity: 0.6; }
|
|
|
+.bottom-spacer { height: 40rpx; }
|
|
|
+</style>
|