Compare commits
2
Commits
f4f06e8079
...
143df07f51
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
143df07f51 | ||
|
|
ff92ab75ef |
@@ -3,7 +3,28 @@
|
||||
</template>
|
||||
|
||||
<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>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
+1
-2
@@ -19,8 +19,7 @@ export function login(data) {
|
||||
url: '/login',
|
||||
method: 'POST',
|
||||
data,
|
||||
needToken: false,
|
||||
contentType: 'application/x-www-form-urlencoded;charset=utf-8'
|
||||
needToken: false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -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) {
|
||||
return request({
|
||||
@@ -23,7 +34,7 @@ export function reportTaskStep(data) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报任务最终结果。
|
||||
* 上报任务最终结果(二期预留)。
|
||||
*/
|
||||
export function reportTaskResult(data) {
|
||||
return request({
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
export const appConfig = {
|
||||
appName: '充值自动化',
|
||||
appVersion: '0.1.0',
|
||||
baseUrl: 'http://your-backend-domain',
|
||||
wsBaseUrl: 'ws://your-backend-domain',
|
||||
baseUrl: 'http://10.0.2.2:8111',
|
||||
wsBaseUrl: 'ws://10.0.2.2:8111',
|
||||
rechargeUrl: 'https://your-recharge-page',
|
||||
primaryColor: '#007aff'
|
||||
}
|
||||
|
||||
+10
-1
@@ -45,10 +45,19 @@
|
||||
"backgroundColor": "#f2f2f7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/task/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "任务列表",
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"backgroundColor": "#f2f2f7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/log/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "执行日志",
|
||||
"navigationBarTitleText": "接收日志",
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"backgroundColor": "#f2f2f7"
|
||||
|
||||
+299
-44
@@ -2,7 +2,7 @@
|
||||
<view class="page">
|
||||
<view class="page-header">
|
||||
<view class="page-title">工作台</view>
|
||||
<view class="page-subtitle">展示账号、设备和连接状态,后续任务和 WebSocket 都从这里进入。</view>
|
||||
<view class="page-subtitle">登录后自动绑定账号设备,持续心跳并在空闲时拉取分发任务。</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
@@ -33,7 +33,7 @@
|
||||
<view class="row">
|
||||
<view class="row-left">
|
||||
<view class="row-title">设备在线状态</view>
|
||||
<view class="row-desc">用于后续设备注册和心跳结果展示</view>
|
||||
<view class="row-desc">依据绑定和心跳刷新</view>
|
||||
</view>
|
||||
<view class="tag">{{ onlineStatusText }}</view>
|
||||
</view>
|
||||
@@ -41,17 +41,17 @@
|
||||
<view class="list-item">
|
||||
<view class="row">
|
||||
<view class="row-left">
|
||||
<view class="row-title">WebSocket 连接状态</view>
|
||||
<view class="row-desc">任务下发和服务端通知入口</view>
|
||||
<view class="row-title">账号设备绑定状态</view>
|
||||
<view class="row-desc">登录后自动调用 register</view>
|
||||
</view>
|
||||
<view class="tag">{{ wsStatusText }}</view>
|
||||
<view class="tag">{{ registerStatus }}</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 class="row-desc">空闲时轮询 pull,收到后自动 receive</view>
|
||||
</view>
|
||||
<view class="tag">{{ taskStatusText }}</view>
|
||||
</view>
|
||||
@@ -59,26 +59,44 @@
|
||||
<view class="list-item">
|
||||
<view class="row">
|
||||
<view class="row-left">
|
||||
<view class="row-title">抖音登录状态</view>
|
||||
<view class="row-desc">账号切换页后续会更新这里的值</view>
|
||||
<view class="row-title">当前任务号</view>
|
||||
<view class="row-desc">本期只做到接收确认,不做执行回调</view>
|
||||
</view>
|
||||
<view class="tag">{{ douyinStatusText }}</view>
|
||||
<view class="field-value">{{ currentTaskNo }}</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 class="row-title">订单号</view>
|
||||
<view class="row-desc">分发任务里携带的订单信息</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 class="list-item">
|
||||
<view class="row">
|
||||
<view class="row-left">
|
||||
<view class="row-title">最近心跳时间</view>
|
||||
<view class="row-desc">设备心跳模块后续会回写这个时间</view>
|
||||
<view class="row-desc">心跳上报成功后刷新</view>
|
||||
</view>
|
||||
<view class="field-value">{{ lastHeartbeatTime }}</view>
|
||||
</view>
|
||||
@@ -87,22 +105,50 @@
|
||||
|
||||
<view class="section">
|
||||
<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="goAlipay">切换支付宝账号</button>
|
||||
<button class="button-secondary button-space" @tap="reconnectWebSocket">重新连接 WebSocket</button>
|
||||
<button class="button-danger button-space" @tap="logout">退出登录</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import appConfig from '../../config/index'
|
||||
import { heartbeatDevice, registerDevice } from '../../api/device'
|
||||
import { pullTask, receiveTask } from '../../api/task'
|
||||
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 {
|
||||
data() {
|
||||
@@ -110,9 +156,15 @@ export default {
|
||||
deviceInfo: {
|
||||
deviceCode: '',
|
||||
appVersion: '',
|
||||
systemVersion: ''
|
||||
systemVersion: '',
|
||||
platform: ''
|
||||
},
|
||||
lastHeartbeatTime: '--'
|
||||
registerStatus: '未绑定',
|
||||
deviceOnlineStatus: 'OFFLINE',
|
||||
lastHeartbeatTime: '--',
|
||||
heartbeatTimer: null,
|
||||
pullTimer: null,
|
||||
pulling: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -120,20 +172,32 @@ export default {
|
||||
const userInfo = this.$store.state.userInfo || getUserInfo() || {}
|
||||
return userInfo.userName || userInfo.nickName || userInfo.username || '未登录'
|
||||
},
|
||||
onlineStatusText() {
|
||||
return this.deviceInfo.deviceCode ? '在线' : '未注册'
|
||||
currentTask() {
|
||||
return this.$store.state.currentTask || null
|
||||
},
|
||||
wsStatusText() {
|
||||
return this.$store.state.wsStatus === 'connected' ? '已连接' : '未连接'
|
||||
onlineStatusText() {
|
||||
return this.deviceOnlineStatus === 'ONLINE' ? '在线' : '离线'
|
||||
},
|
||||
taskStatusText() {
|
||||
return this.$store.state.currentTask ? '执行中' : '空闲'
|
||||
const status = this.currentTask && this.currentTask.status
|
||||
return status || 'IDLE'
|
||||
},
|
||||
douyinStatusText() {
|
||||
return this.$store.state.douyinLoginStatus === 'success' ? '已登录' : '未知'
|
||||
currentTaskNo() {
|
||||
const task = this.currentTask || {}
|
||||
return task.taskNo || task.task_no || '--'
|
||||
},
|
||||
alipayStatusText() {
|
||||
return this.$store.state.alipayLoginStatus === 'success' ? '已登录' : '未知'
|
||||
currentOrderNo() {
|
||||
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() {
|
||||
@@ -146,16 +210,220 @@ export default {
|
||||
}
|
||||
|
||||
this.syncDeviceInfo()
|
||||
this.startDispatchFlow()
|
||||
},
|
||||
onHide() {
|
||||
this.stopDispatchFlow()
|
||||
},
|
||||
onUnload() {
|
||||
this.stopDispatchFlow()
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 刷新当前设备信息,供首页和后续注册接口复用。
|
||||
* 刷新当前设备信息,供绑定、心跳和任务拉取统一复用。
|
||||
*/
|
||||
syncDeviceInfo() {
|
||||
const profile = getDeviceProfile()
|
||||
this.deviceInfo = profile
|
||||
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() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/recharge/index'
|
||||
@@ -171,21 +439,8 @@ export default {
|
||||
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() {
|
||||
this.stopDispatchFlow()
|
||||
clearAuth()
|
||||
this.$store.commit('resetState')
|
||||
uni.reLaunch({
|
||||
|
||||
+12
-6
@@ -1,26 +1,32 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="page-header">
|
||||
<view class="page-title">执行日志</view>
|
||||
<view class="page-subtitle">后续任务、步骤、失败原因都会在这里按时间顺序展示。</view>
|
||||
<view class="page-title">接收日志</view>
|
||||
<view class="page-subtitle">记录设备绑定、心跳、任务拉取和接收确认轨迹。</view>
|
||||
</view>
|
||||
|
||||
<view class="section">
|
||||
<view class="section-title">日志列表</view>
|
||||
<view v-if="logs.length" class="log-list">
|
||||
<view v-for="(item, index) in logs" :key="index" class="list-item">
|
||||
<view class="log-time">{{ item.time }}</view>
|
||||
<view class="log-text">{{ item.text }}</view>
|
||||
<view class="row">
|
||||
<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 v-else class="empty-state">当前还没有日志记录</view>
|
||||
</view>
|
||||
<view v-else class="empty-state">当前还没有接收轨迹</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 日志页负责承载后续任务步骤和失败现场的本地展示。
|
||||
* 日志页负责展示一期任务接收轨迹,便于联调排查。
|
||||
*/
|
||||
export default {
|
||||
computed: {
|
||||
|
||||
+12
-1
@@ -58,6 +58,7 @@
|
||||
<script>
|
||||
import { getCaptcha, getInfo, login } from '../../api/auth'
|
||||
import {
|
||||
getToken,
|
||||
getRememberUsername,
|
||||
removeRememberUsername,
|
||||
setRememberUsername,
|
||||
@@ -82,6 +83,16 @@ export default {
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
/**
|
||||
* 已有登录态时直接进入首页,避免重复展示登录页。
|
||||
*/
|
||||
if (getToken()) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/home/index'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.username = getRememberUsername()
|
||||
this.rememberAccount = !!this.username
|
||||
this.refreshCaptcha()
|
||||
@@ -97,7 +108,7 @@ export default {
|
||||
this.uuid = captcha.uuid || ''
|
||||
this.captchaEnabled = captcha.captchaEnabled !== false
|
||||
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) {
|
||||
this.captchaImg = ''
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -11,6 +11,7 @@ export default new Vuex.Store({
|
||||
deviceInfo: null,
|
||||
wsStatus: 'disconnected',
|
||||
currentTask: null,
|
||||
taskHistory: [],
|
||||
currentStep: '',
|
||||
douyinLoginStatus: 'unknown',
|
||||
alipayLoginStatus: 'unknown',
|
||||
@@ -32,6 +33,34 @@ export default new Vuex.Store({
|
||||
setCurrentTask(state, 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) {
|
||||
state.currentStep = step
|
||||
},
|
||||
@@ -53,6 +82,7 @@ export default new Vuex.Store({
|
||||
state.deviceInfo = null
|
||||
state.wsStatus = 'disconnected'
|
||||
state.currentTask = null
|
||||
state.taskHistory = []
|
||||
state.currentStep = ''
|
||||
state.douyinLoginStatus = 'unknown'
|
||||
state.alipayLoginStatus = 'unknown'
|
||||
|
||||
@@ -63,6 +63,7 @@ export function request(options) {
|
||||
resolve(result)
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('[request fail]', buildUrl(url), error)
|
||||
uni.showToast({
|
||||
title: '后端接口连接异常',
|
||||
icon: 'none'
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user