Compare commits

..
2 Commits
Author SHA1 Message Date
fenghp 143df07f51 添加设备管理功能以及任务分发功能 2026-05-10 21:06:48 +08:00
fenghp ff92ab75ef 联调登录功能 2026-05-09 22:41:14 +08:00
12 changed files with 522 additions and 59 deletions
+22 -1
View File
@@ -3,7 +3,28 @@
</template> </template>
<script> <script>
export default {} import { getToken } from './utils/storage'
import { initVConsole } from './utils/vconsole'
export default {
/**
* App 启动时按登录态决定首屏入口,避免用户手动判断跳转。
*/
onLaunch() {
initVConsole()
if (getToken()) {
uni.reLaunch({
url: '/pages/home/index'
})
return
}
uni.reLaunch({
url: '/pages/login/index'
})
}
}
</script> </script>
<style lang="scss"> <style lang="scss">
+1 -2
View File
@@ -19,8 +19,7 @@ export function login(data) {
url: '/login', url: '/login',
method: 'POST', method: 'POST',
data, data,
needToken: false, needToken: false
contentType: 'application/x-www-form-urlencoded;charset=utf-8'
}) })
} }
+13 -2
View File
@@ -12,7 +12,18 @@ export function pullTask(params) {
} }
/** /**
* 上报任务步骤状态。 * 确认任务已被当前设备接收。
*/
export function receiveTask(data) {
return request({
url: '/af/mobile/task/receive',
method: 'POST',
data
})
}
/**
* 上报任务步骤状态(二期预留)。
*/ */
export function reportTaskStep(data) { export function reportTaskStep(data) {
return request({ return request({
@@ -23,7 +34,7 @@ export function reportTaskStep(data) {
} }
/** /**
* 上报任务最终结果。 * 上报任务最终结果(二期预留)。
*/ */
export function reportTaskResult(data) { export function reportTaskResult(data) {
return request({ return request({
+2 -2
View File
@@ -5,8 +5,8 @@
export const appConfig = { export const appConfig = {
appName: '充值自动化', appName: '充值自动化',
appVersion: '0.1.0', appVersion: '0.1.0',
baseUrl: 'http://your-backend-domain', baseUrl: 'http://10.0.2.2:8111',
wsBaseUrl: 'ws://your-backend-domain', wsBaseUrl: 'ws://10.0.2.2:8111',
rechargeUrl: 'https://your-recharge-page', rechargeUrl: 'https://your-recharge-page',
primaryColor: '#007aff' primaryColor: '#007aff'
} }
+10 -1
View File
@@ -45,10 +45,19 @@
"backgroundColor": "#f2f2f7" "backgroundColor": "#f2f2f7"
} }
}, },
{
"path": "pages/task/index",
"style": {
"navigationBarTitleText": "任务列表",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff",
"backgroundColor": "#f2f2f7"
}
},
{ {
"path": "pages/log/index", "path": "pages/log/index",
"style": { "style": {
"navigationBarTitleText": "执行日志", "navigationBarTitleText": "接收日志",
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff", "navigationBarBackgroundColor": "#ffffff",
"backgroundColor": "#f2f2f7" "backgroundColor": "#f2f2f7"
+299 -44
View File
@@ -2,7 +2,7 @@
<view class="page"> <view class="page">
<view class="page-header"> <view class="page-header">
<view class="page-title">工作台</view> <view class="page-title">工作台</view>
<view class="page-subtitle">展示账号、设备和连接状态,后续任务和 WebSocket 都从这里进入。</view> <view class="page-subtitle">登录后自动绑定账号设备,持续心跳并在空闲时拉取分发任务。</view>
</view> </view>
<view class="section"> <view class="section">
@@ -33,7 +33,7 @@
<view class="row"> <view class="row">
<view class="row-left"> <view class="row-left">
<view class="row-title">设备在线状态</view> <view class="row-title">设备在线状态</view>
<view class="row-desc">用于后续设备注册和心跳结果展示</view> <view class="row-desc">依据绑定和心跳刷新</view>
</view> </view>
<view class="tag">{{ onlineStatusText }}</view> <view class="tag">{{ onlineStatusText }}</view>
</view> </view>
@@ -41,17 +41,17 @@
<view class="list-item"> <view class="list-item">
<view class="row"> <view class="row">
<view class="row-left"> <view class="row-left">
<view class="row-title">WebSocket 连接状态</view> <view class="row-title">账号设备绑定状态</view>
<view class="row-desc">任务下发和服务端通知入口</view> <view class="row-desc">登录后自动调用 register</view>
</view> </view>
<view class="tag">{{ wsStatusText }}</view> <view class="tag">{{ registerStatus }}</view>
</view> </view>
</view> </view>
<view class="list-item"> <view class="list-item">
<view class="row"> <view class="row">
<view class="row-left"> <view class="row-left">
<view class="row-title">当前任务状态</view> <view class="row-title">当前任务状态</view>
<view class="row-desc">后续由任务拉取和步骤上报驱动</view> <view class="row-desc">空闲时轮询 pull,收到后自动 receive</view>
</view> </view>
<view class="tag">{{ taskStatusText }}</view> <view class="tag">{{ taskStatusText }}</view>
</view> </view>
@@ -59,26 +59,44 @@
<view class="list-item"> <view class="list-item">
<view class="row"> <view class="row">
<view class="row-left"> <view class="row-left">
<view class="row-title">抖音登录状态</view> <view class="row-title">当前任务号</view>
<view class="row-desc">账号切换页后续会更新这里的值</view> <view class="row-desc">本期只做到接收确认,不做执行回调</view>
</view> </view>
<view class="tag">{{ douyinStatusText }}</view> <view class="field-value">{{ currentTaskNo }}</view>
</view> </view>
</view> </view>
<view class="list-item"> <view class="list-item">
<view class="row"> <view class="row">
<view class="row-left"> <view class="row-left">
<view class="row-title">支付宝登录状态</view> <view class="row-title">订单号</view>
<view class="row-desc">支付环节的账号状态入口</view> <view class="row-desc">分发任务里携带的订单信息</view>
</view> </view>
<view class="tag">{{ alipayStatusText }}</view> <view class="field-value">{{ currentOrderNo }}</view>
</view>
</view>
<view class="list-item">
<view class="row">
<view class="row-left">
<view class="row-title">充值金额</view>
<view class="row-desc">用于首页快速确认任务内容</view>
</view>
<view class="field-value">{{ currentAmount }}</view>
</view>
</view>
<view class="list-item">
<view class="row">
<view class="row-left">
<view class="row-title">充值账号</view>
<view class="row-desc">任务指定充值目标账号</view>
</view>
<view class="field-value">{{ currentRechargeAccount }}</view>
</view> </view>
</view> </view>
<view class="list-item"> <view class="list-item">
<view class="row"> <view class="row">
<view class="row-left"> <view class="row-left">
<view class="row-title">最近心跳时间</view> <view class="row-title">最近心跳时间</view>
<view class="row-desc">设备心跳模块后续会回写这个时间</view> <view class="row-desc">心跳上报成功后刷新</view>
</view> </view>
<view class="field-value">{{ lastHeartbeatTime }}</view> <view class="field-value">{{ lastHeartbeatTime }}</view>
</view> </view>
@@ -87,22 +105,50 @@
<view class="section"> <view class="section">
<view class="section-title">功能入口</view> <view class="section-title">功能入口</view>
<button class="button-primary" @tap="goRecharge">开始充值</button> <button class="button-primary" @tap="manualPullTask">立即拉取任务</button>
<button class="button-secondary button-space" @tap="goTask">任务列表</button>
<button class="button-secondary button-space" @tap="goLog">接收日志</button>
<button class="button-secondary button-space" @tap="goRecharge">开始充值</button>
<button class="button-secondary button-space" @tap="goDouyin">切换抖音账号</button> <button class="button-secondary button-space" @tap="goDouyin">切换抖音账号</button>
<button class="button-secondary button-space" @tap="goAlipay">切换支付宝账号</button> <button class="button-secondary button-space" @tap="goAlipay">切换支付宝账号</button>
<button class="button-secondary button-space" @tap="reconnectWebSocket">重新连接 WebSocket</button>
<button class="button-danger button-space" @tap="logout">退出登录</button> <button class="button-danger button-space" @tap="logout">退出登录</button>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import appConfig from '../../config/index' import { heartbeatDevice, registerDevice } from '../../api/device'
import { pullTask, receiveTask } from '../../api/task'
import { getDeviceProfile } from '../../services/device' import { getDeviceProfile } from '../../services/device'
import { clearAuth, getUserInfo } from '../../utils/storage' import { clearAuth, getUserInfo, setDeviceInfo } from '../../utils/storage'
const HEARTBEAT_INTERVAL = 15000
const PULL_INTERVAL = 8000
function formatTime(value) {
if (!value) {
return '--'
}
const date = new Date(value)
if (Number.isNaN(date.getTime())) {
return String(value)
}
const pad = (num) => (num < 10 ? `0${num}` : `${num}`)
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
function resolveTaskId(task) {
if (!task) {
return ''
}
return task.taskId || task.id || task.task_no || task.taskNo || ''
}
/** /**
* 首页负责展示设备、账号和连接状态,并提供后续功能入口。 * 首页负责设备自动绑定、心跳上报和任务接收一期闭环。
*/ */
export default { export default {
data() { data() {
@@ -110,9 +156,15 @@ export default {
deviceInfo: { deviceInfo: {
deviceCode: '', deviceCode: '',
appVersion: '', appVersion: '',
systemVersion: '' systemVersion: '',
platform: ''
}, },
lastHeartbeatTime: '--' registerStatus: '未绑定',
deviceOnlineStatus: 'OFFLINE',
lastHeartbeatTime: '--',
heartbeatTimer: null,
pullTimer: null,
pulling: false
} }
}, },
computed: { computed: {
@@ -120,20 +172,32 @@ export default {
const userInfo = this.$store.state.userInfo || getUserInfo() || {} const userInfo = this.$store.state.userInfo || getUserInfo() || {}
return userInfo.userName || userInfo.nickName || userInfo.username || '未登录' return userInfo.userName || userInfo.nickName || userInfo.username || '未登录'
}, },
onlineStatusText() { currentTask() {
return this.deviceInfo.deviceCode ? '在线' : '未注册' return this.$store.state.currentTask || null
}, },
wsStatusText() { onlineStatusText() {
return this.$store.state.wsStatus === 'connected' ? '已连接' : '未连接' return this.deviceOnlineStatus === 'ONLINE' ? '在线' : '离线'
}, },
taskStatusText() { taskStatusText() {
return this.$store.state.currentTask ? '执行中' : '空闲' const status = this.currentTask && this.currentTask.status
return status || 'IDLE'
}, },
douyinStatusText() { currentTaskNo() {
return this.$store.state.douyinLoginStatus === 'success' ? '已登录' : '未知' const task = this.currentTask || {}
return task.taskNo || task.task_no || '--'
}, },
alipayStatusText() { currentOrderNo() {
return this.$store.state.alipayLoginStatus === 'success' ? '已登录' : '未知' const task = this.currentTask || {}
return task.orderNo || task.order_no || '--'
},
currentAmount() {
const task = this.currentTask || {}
const amount = task.amount
return amount === undefined || amount === null || amount === '' ? '--' : String(amount)
},
currentRechargeAccount() {
const task = this.currentTask || {}
return task.rechargeAccount || task.recharge_account || '--'
} }
}, },
onShow() { onShow() {
@@ -146,16 +210,220 @@ export default {
} }
this.syncDeviceInfo() this.syncDeviceInfo()
this.startDispatchFlow()
},
onHide() {
this.stopDispatchFlow()
},
onUnload() {
this.stopDispatchFlow()
}, },
methods: { methods: {
/** /**
* 刷新当前设备信息,供首页和后续注册接口复用。 * 刷新当前设备信息,供绑定、心跳和任务拉取统一复用。
*/ */
syncDeviceInfo() { syncDeviceInfo() {
const profile = getDeviceProfile() const profile = getDeviceProfile()
this.deviceInfo = profile this.deviceInfo = profile
setDeviceInfo(profile)
this.$store.commit('setDeviceInfo', profile) this.$store.commit('setDeviceInfo', profile)
}, },
/**
* 启动一期分发流程:先绑定,再心跳和轮询任务。
*/
async startDispatchFlow() {
this.stopDispatchFlow()
await this.registerCurrentDevice()
this.startHeartbeatTimer()
this.startPullTimer()
},
/**
* 停止心跳和拉取定时器,避免页面切换后重复上报。
*/
stopDispatchFlow() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = null
}
if (this.pullTimer) {
clearInterval(this.pullTimer)
this.pullTimer = null
}
},
/**
* 登录后自动绑定账号与设备,满足一期账号设备登记要求。
*/
async registerCurrentDevice() {
const userName = this.userName
const deviceCode = this.deviceInfo.deviceCode
if (!userName || userName === '未登录' || !deviceCode) {
this.registerStatus = '绑定失败'
return
}
try {
await registerDevice({
userName,
deviceCode,
appVersion: this.deviceInfo.appVersion,
systemVersion: this.deviceInfo.systemVersion,
platform: this.deviceInfo.platform
})
this.registerStatus = '已绑定'
this.pushTraceLog('DEVICE_REGISTER', `账号 ${userName} 绑定设备 ${deviceCode} 成功`)
} catch (error) {
this.registerStatus = '绑定失败'
this.pushTraceLog('DEVICE_REGISTER_FAIL', error.msg || error.message || '账号设备绑定失败')
}
},
/**
* 开启心跳定时上报,并在首次进入页面时立即上报一次。
*/
startHeartbeatTimer() {
this.reportHeartbeat()
this.heartbeatTimer = setInterval(() => {
this.reportHeartbeat()
}, HEARTBEAT_INTERVAL)
},
/**
* 上报当前账号设备心跳,用于后端判断在线与空闲状态。
*/
async reportHeartbeat() {
const userName = this.userName
const deviceCode = this.deviceInfo.deviceCode
if (!userName || userName === '未登录' || !deviceCode) {
return
}
try {
const result = await heartbeatDevice({
userName,
deviceCode,
taskStatus: this.currentTask ? 'HAS_TASK' : 'IDLE',
currentTaskId: resolveTaskId(this.currentTask)
})
const heartbeatTime = (result.data && result.data.lastHeartbeatTime) || result.lastHeartbeatTime || Date.now()
this.lastHeartbeatTime = formatTime(heartbeatTime)
if (this.deviceOnlineStatus !== 'ONLINE') {
this.pushTraceLog('HEARTBEAT_ONLINE', '心跳恢复成功,设备在线')
}
this.deviceOnlineStatus = 'ONLINE'
} catch (error) {
if (this.deviceOnlineStatus !== 'OFFLINE') {
this.pushTraceLog('HEARTBEAT_FAIL', error.msg || error.message || '心跳上报失败')
}
this.deviceOnlineStatus = 'OFFLINE'
}
},
/**
* 启动空闲轮询,按一期要求通过 pull 接收任务。
*/
startPullTimer() {
this.tryPullTask()
this.pullTimer = setInterval(() => {
this.tryPullTask()
}, PULL_INTERVAL)
},
/**
* 空闲时拉取任务,拿到任务后立即确认接收。
*/
async tryPullTask() {
if (this.currentTask || this.pulling) {
return
}
const userName = this.userName
const deviceCode = this.deviceInfo.deviceCode
if (!userName || userName === '未登录' || !deviceCode) {
return
}
this.pulling = true
try {
const result = await pullTask({ userName, deviceCode })
const task = result.data || result
const taskId = resolveTaskId(task)
// 无任务返回时直接结束本次轮询。
if (!taskId) {
return
}
const nextTask = {
...task,
taskId,
status: task.status || 'PUSHED'
}
this.$store.commit('setCurrentTask', nextTask)
this.$store.commit('upsertTaskHistory', nextTask)
this.pushTraceLog('TASK_PULL', `收到任务 ${nextTask.taskNo || nextTask.taskId}`)
await this.confirmTaskReceive(nextTask)
} catch (error) {
this.pushTraceLog('TASK_PULL_FAIL', error.msg || error.message || '任务拉取失败')
} finally {
this.pulling = false
}
},
/**
* 对拉取到的任务调用 receive,明确任务已送达当前账号设备。
*/
async confirmTaskReceive(task) {
const userName = this.userName
const deviceCode = this.deviceInfo.deviceCode
const taskId = resolveTaskId(task)
if (!taskId || !userName || userName === '未登录' || !deviceCode) {
return
}
try {
await receiveTask({
taskId,
userName,
deviceCode
})
const receivedTask = {
...task,
status: 'RECEIVED',
receiveTime: Date.now()
}
this.$store.commit('setCurrentTask', receivedTask)
this.$store.commit('upsertTaskHistory', receivedTask)
this.pushTraceLog('TASK_RECEIVE', `任务 ${task.taskNo || taskId} 已确认接收`)
} catch (error) {
this.pushTraceLog('TASK_RECEIVE_FAIL', error.msg || error.message || '任务接收确认失败')
}
},
/**
* 手动触发一次任务拉取,方便联调时快速验证拉取接口。
*/
manualPullTask() {
this.tryPullTask()
},
/**
* 写入接收轨迹日志,供日志页和排查联调问题使用。
*/
pushTraceLog(event, text) {
this.$store.commit('pushLog', {
event,
time: formatTime(Date.now()),
taskNo: this.currentTaskNo,
text
})
},
goTask() {
uni.navigateTo({
url: '/pages/task/index'
})
},
goLog() {
uni.navigateTo({
url: '/pages/log/index'
})
},
goRecharge() { goRecharge() {
uni.navigateTo({ uni.navigateTo({
url: '/pages/recharge/index' url: '/pages/recharge/index'
@@ -171,21 +439,8 @@ export default {
url: '/pages/account/alipay' url: '/pages/account/alipay'
}) })
}, },
reconnectWebSocket() {
if (!appConfig.wsBaseUrl || appConfig.wsBaseUrl.indexOf('your-backend-domain') > -1) {
uni.showToast({
title: '请先配置 WebSocket 地址',
icon: 'none'
})
return
}
uni.showToast({
title: 'WebSocket 待接入',
icon: 'none'
})
},
logout() { logout() {
this.stopDispatchFlow()
clearAuth() clearAuth()
this.$store.commit('resetState') this.$store.commit('resetState')
uni.reLaunch({ uni.reLaunch({
+12 -6
View File
@@ -1,26 +1,32 @@
<template> <template>
<view class="page"> <view class="page">
<view class="page-header"> <view class="page-header">
<view class="page-title">执行日志</view> <view class="page-title">接收日志</view>
<view class="page-subtitle">后续任务、步骤、失败原因都会在这里按时间顺序展示。</view> <view class="page-subtitle">记录设备绑定、心跳、任务拉取和接收确认轨迹。</view>
</view> </view>
<view class="section"> <view class="section">
<view class="section-title">日志列表</view> <view class="section-title">日志列表</view>
<view v-if="logs.length" class="log-list"> <view v-if="logs.length" class="log-list">
<view v-for="(item, index) in logs" :key="index" class="list-item"> <view v-for="(item, index) in logs" :key="index" class="list-item">
<view class="log-time">{{ item.time }}</view> <view class="row">
<view class="log-text">{{ item.text }}</view> <view class="log-time">{{ item.time || '--' }}</view>
<view class="tag">{{ item.event || 'TRACE' }}</view>
</view>
<view class="log-text">{{ item.text || '--' }}</view>
<view class="row" style="margin-top: 8rpx;">
<view class="row-desc">任务号:{{ item.taskNo || '--' }}</view>
</view>
</view> </view>
</view> </view>
<view v-else class="empty-state">当前还没有日志记录</view> <view v-else class="empty-state">当前还没有接收轨迹</view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
/** /**
* 日志页负责承载后续任务步骤和失败现场的本地展示。 * 日志页负责展示一期任务接收轨迹,便于联调排查。
*/ */
export default { export default {
computed: { computed: {
+12 -1
View File
@@ -58,6 +58,7 @@
<script> <script>
import { getCaptcha, getInfo, login } from '../../api/auth' import { getCaptcha, getInfo, login } from '../../api/auth'
import { import {
getToken,
getRememberUsername, getRememberUsername,
removeRememberUsername, removeRememberUsername,
setRememberUsername, setRememberUsername,
@@ -82,6 +83,16 @@ export default {
} }
}, },
onShow() { onShow() {
/**
* 已有登录态时直接进入首页,避免重复展示登录页。
*/
if (getToken()) {
uni.reLaunch({
url: '/pages/home/index'
})
return
}
this.username = getRememberUsername() this.username = getRememberUsername()
this.rememberAccount = !!this.username this.rememberAccount = !!this.username
this.refreshCaptcha() this.refreshCaptcha()
@@ -97,7 +108,7 @@ export default {
this.uuid = captcha.uuid || '' this.uuid = captcha.uuid || ''
this.captchaEnabled = captcha.captchaEnabled !== false this.captchaEnabled = captcha.captchaEnabled !== false
const img = captcha.img || '' const img = captcha.img || ''
this.captchaImg = img.indexOf('data:image') === 0 ? img : `data:image/gif;base64,${img}` this.captchaImg = img.indexOf('data:image') === 0 ? img : `data:image/jpeg;base64,${img}`
} catch (error) { } catch (error) {
this.captchaImg = '' this.captchaImg = ''
} }
+100
View File
@@ -0,0 +1,100 @@
<template>
<view class="page">
<view class="page-header">
<view class="page-title">任务列表</view>
<view class="page-subtitle">展示当前任务和最近接收任务,便于一期联调核对任务状态。</view>
</view>
<view class="section">
<view class="section-title">当前任务</view>
<view v-if="currentTask" class="summary-grid">
<view class="summary-item">
<view class="summary-label">任务号</view>
<view class="summary-value">{{ formatTaskNo(currentTask) }}</view>
</view>
<view class="summary-item">
<view class="summary-label">任务状态</view>
<view class="summary-value">{{ formatStatus(currentTask) }}</view>
</view>
<view class="summary-item">
<view class="summary-label">订单号</view>
<view class="summary-value">{{ formatOrderNo(currentTask) }}</view>
</view>
<view class="summary-item">
<view class="summary-label">金额</view>
<view class="summary-value">{{ formatAmount(currentTask) }}</view>
</view>
</view>
<view v-else class="empty-state">当前没有执行中的任务</view>
</view>
<view class="section">
<view class="section-title">历史任务</view>
<view v-if="taskHistory.length">
<view v-for="item in taskHistory" :key="item.taskId" class="list-item">
<view class="row">
<view class="row-left">
<view class="row-title">{{ formatTaskNo(item) }}</view>
<view class="row-desc">订单号:{{ formatOrderNo(item) }}</view>
</view>
<view class="tag">{{ formatStatus(item) }}</view>
</view>
<view class="row" style="margin-top: 10rpx;">
<view class="row-desc">金额:{{ formatAmount(item) }}</view>
<view class="row-desc">充值账号:{{ formatRechargeAccount(item) }}</view>
</view>
</view>
</view>
<view v-else class="empty-state">暂未接收到任务</view>
</view>
</view>
</template>
<script>
/**
* 任务页负责展示当前任务和最近任务接收结果。
*/
export default {
computed: {
currentTask() {
return this.$store.state.currentTask || null
},
taskHistory() {
return this.$store.state.taskHistory || []
}
},
methods: {
/**
* 统一读取任务号字段,兼容不同接口字段命名。
*/
formatTaskNo(task) {
return task.taskNo || task.task_no || task.taskId || '--'
},
/**
* 统一读取订单号字段,便于列表展示。
*/
formatOrderNo(task) {
return task.orderNo || task.order_no || '--'
},
/**
* 统一读取状态字段,保证接收状态可见。
*/
formatStatus(task) {
return task.status || '--'
},
/**
* 统一读取金额字段,展示任务关键金额信息。
*/
formatAmount(task) {
const amount = task.amount
return amount === undefined || amount === null || amount === '' ? '--' : String(amount)
},
/**
* 统一读取充值账号字段,便于核对任务目标账号。
*/
formatRechargeAccount(task) {
return task.rechargeAccount || task.recharge_account || '--'
}
}
}
</script>
+30
View File
@@ -11,6 +11,7 @@ export default new Vuex.Store({
deviceInfo: null, deviceInfo: null,
wsStatus: 'disconnected', wsStatus: 'disconnected',
currentTask: null, currentTask: null,
taskHistory: [],
currentStep: '', currentStep: '',
douyinLoginStatus: 'unknown', douyinLoginStatus: 'unknown',
alipayLoginStatus: 'unknown', alipayLoginStatus: 'unknown',
@@ -32,6 +33,34 @@ export default new Vuex.Store({
setCurrentTask(state, task) { setCurrentTask(state, task) {
state.currentTask = task state.currentTask = task
}, },
/**
* 记录最近接收到的任务,供任务页展示当前任务和历史任务。
*/
upsertTaskHistory(state, task) {
if (!task) {
return
}
const taskId = task.taskId || task.id || task.task_no || task.taskNo
if (!taskId) {
return
}
const nextTask = {
...task,
taskId
}
const index = state.taskHistory.findIndex((item) => (item.taskId || item.id || item.task_no || item.taskNo) === taskId)
if (index > -1) {
state.taskHistory.splice(index, 1)
}
state.taskHistory.unshift(nextTask)
if (state.taskHistory.length > 50) {
state.taskHistory.length = 50
}
},
setCurrentStep(state, step) { setCurrentStep(state, step) {
state.currentStep = step state.currentStep = step
}, },
@@ -53,6 +82,7 @@ export default new Vuex.Store({
state.deviceInfo = null state.deviceInfo = null
state.wsStatus = 'disconnected' state.wsStatus = 'disconnected'
state.currentTask = null state.currentTask = null
state.taskHistory = []
state.currentStep = '' state.currentStep = ''
state.douyinLoginStatus = 'unknown' state.douyinLoginStatus = 'unknown'
state.alipayLoginStatus = 'unknown' state.alipayLoginStatus = 'unknown'
+1
View File
@@ -63,6 +63,7 @@ export function request(options) {
resolve(result) resolve(result)
}, },
fail: (error) => { fail: (error) => {
console.error('[request fail]', buildUrl(url), error)
uni.showToast({ uni.showToast({
title: '后端接口连接异常', title: '后端接口连接异常',
icon: 'none' icon: 'none'
+20
View File
@@ -0,0 +1,20 @@
let vConsoleLoaded = false
/**
* 仅在 H5 开发环境注入 vConsole,方便排查接口、路由和运行时错误。
*/
export function initVConsole() {
if (process.env.UNI_PLATFORM !== 'h5' || process.env.NODE_ENV !== 'development' || vConsoleLoaded) {
return
}
const script = document.createElement('script')
script.src = 'https://unpkg.com/vconsole@3.15.1/dist/vconsole.min.js'
script.onload = () => {
// 只在开发环境挂载,不影响正式包。
// eslint-disable-next-line no-new
new window.VConsole()
vConsoleLoaded = true
}
document.head.appendChild(script)
}