Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bcf5e011f | ||
|
|
5da2ea2330 |
+13
@@ -54,3 +54,16 @@ export function reportTaskResult(data) {
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 按付款手机号为当前任务取短信验证码。
|
||||
* 轮询期间静默请求,避免无可用验证码时反复弹出错误提示。
|
||||
*/
|
||||
export function fetchSmsCode(params) {
|
||||
return request({
|
||||
url: '/af/mobile/sms/code',
|
||||
method: 'GET',
|
||||
data: params,
|
||||
silent: true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,6 +37,16 @@ export default [
|
||||
retry: 2,
|
||||
retryDelay: 800
|
||||
},
|
||||
{
|
||||
code: 'CLICK_ADD_DOUYIN_ACCOUNT',
|
||||
name: '点击添加抖音号',
|
||||
action: 'click',
|
||||
locator: { type: 'text', value: '添加抖音号' },
|
||||
waitTimeout: 5000,
|
||||
delay: 0,
|
||||
retry: 2,
|
||||
retryDelay: 800
|
||||
},
|
||||
{
|
||||
code: 'INPUT_PHONE',
|
||||
name: '输入手机号',
|
||||
@@ -48,26 +58,27 @@ export default [
|
||||
retry: 2,
|
||||
retryDelay: 800
|
||||
},
|
||||
{
|
||||
code: 'CLICK_CONFIRM',
|
||||
name: '点击完成',
|
||||
action: 'click',
|
||||
locator: { type: 'css', value: 'button[class*="switch_account_simple_content_wrapper_confirm_button"]' },
|
||||
waitTimeout: 3000,
|
||||
delay: 0
|
||||
},
|
||||
// 新页面输入账号后通过状态节点提示用户不存在;未出现该提示时继续点击立即添加。
|
||||
{
|
||||
code: 'CHECK_ACCOUNT_NOT_FOUND',
|
||||
name: '检查账号是否存在',
|
||||
action: 'textCheck',
|
||||
locator: { type: 'css', value: '.dux-toast-text' },
|
||||
locator: { type: 'css', value: 'div[class*="account_check_status-"]' },
|
||||
value: '用户不存在',
|
||||
failMessage: '账号不存在',
|
||||
passMessage: '未检测到账号不存在提示',
|
||||
scriptTimeout: 600,
|
||||
pollInterval: 150,
|
||||
passMessage: '未检测到账号不存在提示,账号可添加',
|
||||
scriptTimeout: 3000,
|
||||
pollInterval: 200,
|
||||
detectMode: 'instant'
|
||||
},
|
||||
{
|
||||
code: 'CLICK_CONFIRM',
|
||||
name: '点击立即添加',
|
||||
action: 'click',
|
||||
locator: { type: 'text', value: '立即添加' },
|
||||
waitTimeout: 3000,
|
||||
delay: 0
|
||||
},
|
||||
{
|
||||
code: 'CLICK_DIALOG_CONFIRM',
|
||||
name: '确认账号弹窗',
|
||||
@@ -131,21 +142,6 @@ export default [
|
||||
absentMessage: '未出现账号确认弹窗,继续执行',
|
||||
notClickedMessage: '检测到账号确认弹窗,但未能点击确认按钮'
|
||||
},
|
||||
{
|
||||
code: 'ASSERT_PAY_AMOUNT',
|
||||
name: '校验支付页金额',
|
||||
action: 'amountAssert',
|
||||
locator: { type: 'css', value: '.common-header__price' },
|
||||
waitTimeout: 5000,
|
||||
scriptTimeout: 3000,
|
||||
delay: 0,
|
||||
retry: 1,
|
||||
retryDelay: 300,
|
||||
assertOptions: {
|
||||
expectedFixed: 2,
|
||||
actualFixed: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'CLICK_BROWSER_PAY',
|
||||
name: '点击继续浏览器付款',
|
||||
@@ -214,6 +210,21 @@ export default [
|
||||
retry: 1,
|
||||
retryDelay: 500
|
||||
},
|
||||
{
|
||||
code: 'HANDLE_SMS_VERIFY',
|
||||
name: '处理短信验证码',
|
||||
action: 'fetchSmsCodeIfPresent',
|
||||
scriptTimeout: 12000,
|
||||
delay: 400,
|
||||
skipOnFail: false,
|
||||
smsOptions: {
|
||||
pagePresentLocator: { type: 'css', value: 'input[type="tel"][maxlength="6"]', requireInteractable: false },
|
||||
inputLocator: { type: 'css', value: 'input[type="tel"][maxlength="6"]', requireInteractable: false },
|
||||
pollInterval: 5000,
|
||||
pollTimeout: 120000,
|
||||
inputMode: 'nativeSetValue'
|
||||
}
|
||||
},
|
||||
{
|
||||
code: 'WAIT_PAY_SUCCESS_RESULT',
|
||||
name: '等待支付成功结果',
|
||||
|
||||
@@ -98,14 +98,25 @@
|
||||
placeholder="请输入充值支付密码"
|
||||
/>
|
||||
</view>
|
||||
<button class="button-secondary button-space" @tap="saveRechargePayPassword">保存充值密码</button>
|
||||
<view class="field">
|
||||
<view class="field-label">付款手机号</view>
|
||||
<input
|
||||
v-model="payerPhone"
|
||||
class="input"
|
||||
type="number"
|
||||
maxlength="11"
|
||||
placeholder="请输入接收验证码的付款手机号"
|
||||
/>
|
||||
<view class="field-value">需与收码真机转发模板中的 receiverPhone 保持一致。</view>
|
||||
</view>
|
||||
<button class="button-secondary button-space" @tap="saveRechargeConfig">保存配置</button>
|
||||
<button class="button-primary button-space" @tap="openRechargePage">配置账号</button>
|
||||
<button class="button-secondary button-space" @tap="clearAccountConfig">清除已有账号</button>
|
||||
</view>
|
||||
|
||||
<view v-if="!hasTask" class="section">
|
||||
<view class="section-title">说明</view>
|
||||
<view class="field-value">充值密码会保存在本地缓存中,清除已有账号会同时清理 WebView 缓存和充值密码。</view>
|
||||
<view class="field-value">充值密码与付款手机号会保存在本地缓存中,清除已有账号会同时清理 WebView 缓存、充值密码和付款手机号。</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -119,7 +130,7 @@ import { updateDeviceWorkStatus } from '../../api/device'
|
||||
// dryRun 由 appConfig.dryRun 控制,true 时跳过所有后端上报
|
||||
import { runStepEngine } from '../../services/stepEngine'
|
||||
import douyinSteps from '../../config/steps/douyin-recharge'
|
||||
import { getRechargePayPasswordStorage, getUserInfo } from '../../utils/storage'
|
||||
import { getPayerPhoneStorage, getRechargePayPasswordStorage, getUserInfo } from '../../utils/storage'
|
||||
import { captureAndUploadErrorScreenshot } from '../../services/screenshot'
|
||||
import { resolveFeishuEnvironment, sendFeishuErrorNotification } from '../../services/feishuNotifier'
|
||||
|
||||
@@ -132,6 +143,7 @@ export default {
|
||||
taskId: null,
|
||||
rechargeUrl: appConfig.rechargeUrl,
|
||||
rechargePayPassword: '',
|
||||
payerPhone: '',
|
||||
failReason: '',
|
||||
lastBlockedAppSchemeUrl: '',
|
||||
lastBlockedAppSchemeAt: 0
|
||||
@@ -165,6 +177,7 @@ export default {
|
||||
},
|
||||
onLoad(options) {
|
||||
this.rechargePayPassword = getRechargePayPasswordStorage()
|
||||
this.payerPhone = getPayerPhoneStorage()
|
||||
if (options && options.taskId) {
|
||||
this.taskId = Number(options.taskId) || options.taskId
|
||||
this.$nextTick(() => {
|
||||
@@ -260,6 +273,8 @@ export default {
|
||||
const task = this.task
|
||||
const url = task.rechargeUrl || this.rechargeUrl
|
||||
const rechargePayPassword = this.$store.state.rechargePayPassword || getRechargePayPasswordStorage()
|
||||
// 任务开始时读取一次付款手机号并锁进运行时上下文,执行过程中不再回读本地配置。
|
||||
const payerPhone = this.$store.state.payerPhone || getPayerPhoneStorage()
|
||||
if (!url) {
|
||||
uni.showToast({ title: '充值页地址未配置', icon: 'none' })
|
||||
return
|
||||
@@ -351,6 +366,9 @@ export default {
|
||||
amount: task.amount,
|
||||
rechargeAccount: task.rechargeAccount || task.recharge_account,
|
||||
rechargePayPassword,
|
||||
payerPhone,
|
||||
taskId: this.taskId,
|
||||
orderNo: task.orderNo || task.order_no,
|
||||
rechargeUrl: url,
|
||||
runtimeSnapshots: {},
|
||||
// 订单成功后直接结束,本地页面复位统一放到下一单开始前处理。
|
||||
@@ -570,6 +588,9 @@ export default {
|
||||
if (this.rechargePayPassword && !this.persistRechargePayPassword(false)) {
|
||||
return
|
||||
}
|
||||
if (this.payerPhone && !this.persistPayerPhone(false)) {
|
||||
return
|
||||
}
|
||||
const currentWebview = this.$scope && this.$scope.$getAppWebview ? this.$scope.$getAppWebview() : null
|
||||
const webview = openRechargeWebview(this.rechargeUrl, currentWebview, {
|
||||
onBlockedAppScheme: this.handleBlockedAppScheme
|
||||
@@ -591,8 +612,36 @@ export default {
|
||||
}
|
||||
return true
|
||||
},
|
||||
saveRechargePayPassword() {
|
||||
this.persistRechargePayPassword(true)
|
||||
/**
|
||||
* 校验并保存付款手机号。允许留空,非空时必须为 11 位手机号。
|
||||
*
|
||||
* @param {boolean} showToast 保存成功时是否提示
|
||||
* @returns {boolean} 是否保存成功(格式不合法返回 false)
|
||||
*/
|
||||
persistPayerPhone(showToast) {
|
||||
const phone = String(this.payerPhone || '').trim()
|
||||
if (phone && !/^1\d{10}$/.test(phone)) {
|
||||
uni.showToast({ title: '付款手机号需为11位手机号', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
this.payerPhone = phone
|
||||
this.$store.commit('setPayerPhone', phone)
|
||||
if (showToast) {
|
||||
uni.showToast({ title: '付款手机号已保存', icon: 'none' })
|
||||
}
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* 一次性保存充值密码与付款手机号配置。
|
||||
*/
|
||||
saveRechargeConfig() {
|
||||
if (!this.persistRechargePayPassword(false)) {
|
||||
return
|
||||
}
|
||||
if (!this.persistPayerPhone(false)) {
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: '配置已保存', icon: 'none' })
|
||||
},
|
||||
goConfig() {
|
||||
this.$store.commit('setExecutionState', 'IDLE')
|
||||
@@ -611,7 +660,9 @@ export default {
|
||||
this.$store.commit('setDouyinLoginStatus', 'unknown')
|
||||
this.$store.commit('setAlipayLoginStatus', 'unknown')
|
||||
this.$store.commit('clearRechargePayPassword')
|
||||
this.$store.commit('clearPayerPhone')
|
||||
this.rechargePayPassword = ''
|
||||
this.payerPhone = ''
|
||||
uni.showToast({ title: '已清除账号缓存,可重新配置', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,54 @@ import {
|
||||
buildPasswordPageDiagnoseScript,
|
||||
buildCallbackPageDiagnoseScript
|
||||
} from './locator'
|
||||
import { fetchSmsCode } from '../api/task'
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成读取验证码页掩码手机号的 JS 代码。
|
||||
* 在页面文本中查找形如 187******99 的掩码串。
|
||||
*
|
||||
* @returns {string} JS 代码,执行后返回 { masked }
|
||||
*/
|
||||
function buildReadMaskedPhoneScript() {
|
||||
return `
|
||||
var text = '';
|
||||
var nodes = document.querySelectorAll('div[class*="mainHeaderSub"], [class*="phone"], [class*="mobile"]');
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
text = String(nodes[i].innerText || nodes[i].textContent || '').replace(/\\s+/g, '');
|
||||
if (/\\d{3}\\*+\\d{2}/.test(text)) break;
|
||||
}
|
||||
if (!/\\d{3}\\*+\\d{2}/.test(text)) {
|
||||
text = (document.body && (document.body.innerText || document.body.textContent)) || '';
|
||||
text = String(text).replace(/\\s+/g, '');
|
||||
}
|
||||
var m = text.match(/(\\d{3})\\*+(\\d{2})(?!\\d)/) || text.match(/(\\d{3})\\*+(\\d{2})/);
|
||||
return { masked: m ? m[0] : '' };
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验页面掩码手机号是否与付款手机号一致。
|
||||
* 规则:前 3 位 + 后 N 位(N 取掩码尾部数字长度)一致。
|
||||
*
|
||||
* @param {string} payerPhone 本地配置付款手机号
|
||||
* @param {string} maskedPhone 页面掩码手机号,如 187******99
|
||||
* @returns {boolean} 是否一致
|
||||
*/
|
||||
function isMaskedPhoneMatch(payerPhone, maskedPhone) {
|
||||
const matched = String(maskedPhone || '').match(/(\d{3})\*+(\d{2})/)
|
||||
if (!matched) {
|
||||
return false
|
||||
}
|
||||
const front = matched[1]
|
||||
const tail = matched[2]
|
||||
const phone = String(payerPhone || '')
|
||||
return phone.slice(0, 3) === front && phone.slice(-tail.length) === tail
|
||||
}
|
||||
|
||||
export async function waitForElement(locator, timeout = 10000, interval = 300, guards = []) {
|
||||
await runGuardSweep(guards)
|
||||
const script = buildExistScript(locator)
|
||||
@@ -30,6 +73,23 @@ export async function waitForElement(locator, timeout = 10000, interval = 300, g
|
||||
return found
|
||||
}
|
||||
|
||||
async function waitForElementAcrossNavigation(locator, timeout = 10000, interval = 500, guards = []) {
|
||||
const deadline = Date.now() + timeout
|
||||
const script = buildExistScript(locator)
|
||||
console.log('[actionExecutor] waitForElementAcrossNavigation: 开始检测, ', locator.value, ', ', timeout + 'ms')
|
||||
while (Date.now() < deadline) {
|
||||
await runGuardSweep(guards)
|
||||
const found = await evalJSOnce(script, Math.min(interval, 1000))
|
||||
if (found === true) {
|
||||
console.log('[actionExecutor] waitForElementAcrossNavigation: 找到元素, ', locator.value)
|
||||
return true
|
||||
}
|
||||
await sleep(interval)
|
||||
}
|
||||
console.log('[actionExecutor] waitForElementAcrossNavigation: 超时未找到, ', locator.value)
|
||||
return false
|
||||
}
|
||||
|
||||
async function runGuardSweep(guards = []) {
|
||||
if (!Array.isArray(guards) || !guards.length) {
|
||||
return { clicked: false, count: 0, names: [] }
|
||||
@@ -338,6 +398,69 @@ export async function executeAction(step, context, options = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'fetchSmsCodeIfPresent') {
|
||||
const opts = step.smsOptions || {}
|
||||
const pagePresentLocator = opts.pagePresentLocator || { type: 'text', value: '输入短信验证码' }
|
||||
const inputLocator = opts.inputLocator || { type: 'css', value: 'input[type="tel"][maxlength="6"]', requireInteractable: false }
|
||||
const pollInterval = Number(opts.pollInterval || 2000)
|
||||
const pollTimeout = Number(opts.pollTimeout || 120000)
|
||||
const inputMode = opts.inputMode || 'nativeSetValue'
|
||||
|
||||
// 1. 验证码页通常由支付密码页跳转而来,需跨页面重复注入检测脚本。
|
||||
const present = await waitForElementAcrossNavigation(pagePresentLocator, step.scriptTimeout || 4000, 500, guards)
|
||||
if (!present) {
|
||||
return { success: true, message: '未出现验证码页,跳过取码' }
|
||||
}
|
||||
|
||||
// 2. 校验付款手机号已配置。
|
||||
const payerPhone = context && context.payerPhone ? String(context.payerPhone).trim() : ''
|
||||
if (!payerPhone) {
|
||||
return { success: false, message: '验证码页已出现,但未配置付款手机号' }
|
||||
}
|
||||
|
||||
// 3. 读取页面掩码手机号并比对;读不到掩码时记录告警但不阻断(DOM 结构可能变动)。
|
||||
const maskResult = await evalJSOnce(buildReadMaskedPhoneScript(), 2000)
|
||||
const maskedPhone = maskResult && maskResult.masked ? String(maskResult.masked) : ''
|
||||
if (maskedPhone) {
|
||||
if (!isMaskedPhoneMatch(payerPhone, maskedPhone)) {
|
||||
return { success: false, message: 'SMS_PHONE_MISMATCH 页面手机号(' + maskedPhone + ')与付款手机号不一致' }
|
||||
}
|
||||
} else {
|
||||
console.warn('[actionExecutor] fetchSmsCodeIfPresent: 未读取到页面掩码手机号,跳过手机号比对')
|
||||
}
|
||||
|
||||
// 4. 按付款手机号轮询后端取码,命中一条后立即停止,不再抢新码。
|
||||
const taskId = context && context.taskId
|
||||
const orderNo = (context && context.orderNo) || ''
|
||||
const deadline = Date.now() + pollTimeout
|
||||
let verifyCode = ''
|
||||
while (Date.now() < deadline) {
|
||||
try {
|
||||
const res = await fetchSmsCode({ receiverPhone: payerPhone, taskId, orderNo })
|
||||
if (res && res.data && res.data.verifyCode) {
|
||||
verifyCode = String(res.data.verifyCode)
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[actionExecutor] fetchSmsCodeIfPresent: 取码请求异常,继续轮询', e && (e.msg || e.message || e))
|
||||
}
|
||||
await sleep(pollInterval)
|
||||
}
|
||||
|
||||
if (!verifyCode) {
|
||||
return { success: false, message: '取码超时,未获取到验证码' }
|
||||
}
|
||||
|
||||
// 5. 填入验证码,满 6 位后页面通常自动提交,本期不额外点击确认。
|
||||
const inputScript = buildInputScript(inputLocator, verifyCode, { mode: inputMode })
|
||||
const inputOk = await evalJSOnce(inputScript, 3000)
|
||||
if (inputOk !== true) {
|
||||
return { success: false, message: '验证码已取到,但填入输入框失败' }
|
||||
}
|
||||
await sleep(step.delay || 400)
|
||||
return { success: true, message: '验证码已自动填入: ' + verifyCode }
|
||||
}
|
||||
|
||||
return { success: false, message: '未知动作类型: ' + action }
|
||||
} catch (err) {
|
||||
return { success: false, message: '执行异常: ' + (err.message || err) }
|
||||
|
||||
@@ -20,10 +20,11 @@ const STEP_STAGE_LIST = [
|
||||
successMessage: '账号步骤完成,已确认充值账号',
|
||||
steps: [
|
||||
'CLICK_SWITCH_ACCOUNT',
|
||||
'CLICK_ADD_DOUYIN_ACCOUNT',
|
||||
'CAPTURE_DOUYIN_ACCOUNT',
|
||||
'INPUT_PHONE',
|
||||
'CLICK_CONFIRM',
|
||||
'CHECK_ACCOUNT_NOT_FOUND',
|
||||
'CLICK_CONFIRM',
|
||||
'CLICK_DIALOG_CONFIRM'
|
||||
]
|
||||
},
|
||||
@@ -35,8 +36,7 @@ const STEP_STAGE_LIST = [
|
||||
'CLICK_CUSTOM_AMOUNT',
|
||||
'INPUT_CUSTOM_AMOUNT',
|
||||
'CLICK_CUSTOM_AMOUNT_CONFIRM',
|
||||
'HANDLE_ACCOUNT_CONFIRM_DIALOG',
|
||||
'ASSERT_PAY_AMOUNT'
|
||||
'HANDLE_ACCOUNT_CONFIRM_DIALOG'
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,15 +3,18 @@ import Vuex from 'vuex'
|
||||
import {
|
||||
getCurrentTaskStorage,
|
||||
getExecutionStateStorage,
|
||||
getPayerPhoneStorage,
|
||||
getRechargePayPasswordStorage,
|
||||
getTaskHistoryStorage,
|
||||
getWorkStatusStorage,
|
||||
getToken,
|
||||
getUserInfo,
|
||||
removeCurrentTaskStorage,
|
||||
removePayerPhoneStorage,
|
||||
removeRechargePayPasswordStorage,
|
||||
setCurrentTaskStorage,
|
||||
setExecutionStateStorage,
|
||||
setPayerPhoneStorage,
|
||||
setRechargePayPasswordStorage,
|
||||
setTaskHistoryStorage,
|
||||
setWorkStatusStorage
|
||||
@@ -34,6 +37,7 @@ export default new Vuex.Store({
|
||||
executionState: getExecutionStateStorage(),
|
||||
workStatus: getWorkStatusStorage(),
|
||||
rechargePayPassword: getRechargePayPasswordStorage(),
|
||||
payerPhone: getPayerPhoneStorage(),
|
||||
cooldownUntil: '',
|
||||
currentStepIndex: -1,
|
||||
currentStepCode: '',
|
||||
@@ -121,6 +125,14 @@ export default new Vuex.Store({
|
||||
state.rechargePayPassword = ''
|
||||
removeRechargePayPasswordStorage()
|
||||
},
|
||||
setPayerPhone(state, value) {
|
||||
state.payerPhone = value || ''
|
||||
setPayerPhoneStorage(state.payerPhone)
|
||||
},
|
||||
clearPayerPhone(state) {
|
||||
state.payerPhone = ''
|
||||
removePayerPhoneStorage()
|
||||
},
|
||||
setCooldownUntil(state, value) {
|
||||
state.cooldownUntil = value || ''
|
||||
},
|
||||
@@ -156,6 +168,7 @@ export default new Vuex.Store({
|
||||
state.executionState = 'IDLE'
|
||||
state.workStatus = 'NOT_READY'
|
||||
state.rechargePayPassword = ''
|
||||
state.payerPhone = ''
|
||||
state.cooldownUntil = ''
|
||||
state.currentStepIndex = -1
|
||||
state.currentStepCode = ''
|
||||
@@ -166,6 +179,7 @@ export default new Vuex.Store({
|
||||
setExecutionStateStorage('IDLE')
|
||||
setWorkStatusStorage('NOT_READY')
|
||||
removeRechargePayPasswordStorage()
|
||||
removePayerPhoneStorage()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ const TASK_HISTORY_KEY = 'deposit_task_history'
|
||||
const EXECUTION_STATE_KEY = 'deposit_execution_state'
|
||||
const WORK_STATUS_KEY = 'deposit_work_status'
|
||||
const RECHARGE_PAY_PASSWORD_KEY = 'deposit_recharge_pay_password'
|
||||
const PAYER_PHONE_KEY = 'deposit_payer_phone'
|
||||
|
||||
/**
|
||||
* 保存登录 token,供请求封装自动携带。
|
||||
@@ -112,6 +113,27 @@ export function removeRechargePayPasswordStorage() {
|
||||
uni.removeStorageSync(RECHARGE_PAY_PASSWORD_KEY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存付款手机号,作为充值流程中按手机号取验证码的设备级固定配置。
|
||||
*/
|
||||
export function setPayerPhoneStorage(payerPhone) {
|
||||
uni.setStorageSync(PAYER_PHONE_KEY, payerPhone || '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取付款手机号。
|
||||
*/
|
||||
export function getPayerPhoneStorage() {
|
||||
return uni.getStorageSync(PAYER_PHONE_KEY) || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除付款手机号。
|
||||
*/
|
||||
export function removePayerPhoneStorage() {
|
||||
uni.removeStorageSync(PAYER_PHONE_KEY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存记住账号时的用户名。
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user