feat: improve member payment and order flows
This commit is contained in:
@@ -1036,10 +1036,27 @@ body {
|
||||
border-radius: 24px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.m-order-recharge-link {
|
||||
min-height: 46px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 24px;
|
||||
color: #fff;
|
||||
background: linear-gradient(90deg, #ffa32b, #f15b20);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
.m-order-submit-panel button:disabled,
|
||||
.m-order-submit-actions button:disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.m-order-submit-panel > button:disabled {
|
||||
color: #999;
|
||||
background: #e9e9e9;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.m-order-submit-confirm {
|
||||
width: min(90vw, 430px);
|
||||
overflow: hidden;
|
||||
@@ -3362,6 +3379,13 @@ body {
|
||||
background: #ffb21f;
|
||||
color: #fff;
|
||||
}
|
||||
.m-match-steps--single {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.m-match-steps--single li {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Olons 2 manual task mall */
|
||||
.m-task-mall {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
v-model.trim="form.username"
|
||||
autocomplete="username"
|
||||
maxlength="10"
|
||||
placeholder="请输入账户名"
|
||||
placeholder="请输入名字"
|
||||
/>
|
||||
</label>
|
||||
<label class="m-input-row">
|
||||
@@ -94,7 +94,7 @@
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const mode = ref<'login' | 'register'>('login');
|
||||
const accepted = ref(false);
|
||||
const accepted = ref(true);
|
||||
const submitting = ref(false);
|
||||
const error = ref('');
|
||||
const form = reactive({ username: '', phone: '', password: '', referral_code: '' });
|
||||
@@ -104,7 +104,7 @@
|
||||
form.phone = '';
|
||||
form.password = '';
|
||||
form.referral_code = '';
|
||||
accepted.value = false;
|
||||
accepted.value = true;
|
||||
error.value = '';
|
||||
});
|
||||
|
||||
@@ -113,8 +113,8 @@
|
||||
if (!/^1\d{10}$/.test(form.phone)) return void (error.value = '请输入正确的手机号码');
|
||||
if (form.password.length < 6) return void (error.value = '密码至少为 6 位');
|
||||
if (!accepted.value) return void (error.value = '请先阅读并同意用户协议和隐私政策');
|
||||
if (mode.value === 'register' && !/^[a-z0-9]{1,10}$/.test(form.username)) {
|
||||
return void (error.value = '账户名须为 1 至 10 位小写字母或数字');
|
||||
if (mode.value === 'register' && !/^[\p{L}\p{M}·]{1,10}$/u.test(form.username)) {
|
||||
return void (error.value = '名字须为 1 至 10 个字符,支持汉字');
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
|
||||
@@ -53,10 +53,8 @@
|
||||
</div>
|
||||
<div v-if="isAutoMatching" class="m-match-progress"><i :style="{ width: `${matchProgress}%` }"></i></div>
|
||||
<strong v-if="isAutoMatching" class="m-match-percent">{{ matchProgress }}%</strong>
|
||||
<ol class="m-match-steps">
|
||||
<li v-for="(step, index) in matchSteps" :key="step" :class="{ active: index <= matchStep }">
|
||||
<span>✓</span>{{ step }}
|
||||
</li>
|
||||
<ol class="m-match-steps m-match-steps--single">
|
||||
<li class="active"><span>✓</span>正在匹配任务</li>
|
||||
</ol>
|
||||
<button class="m-result-secondary" @click="openOrders">稍后去记录查看</button>
|
||||
</template>
|
||||
@@ -139,8 +137,6 @@
|
||||
const end = String(eligibility.value?.business_end_at || '21:00').slice(0, 5);
|
||||
return `${start}-${end}`;
|
||||
});
|
||||
const matchStep = computed(() => (matchProgress.value < 35 ? 0 : matchProgress.value < 60 ? 1 : matchProgress.value < 85 ? 2 : 3));
|
||||
const matchSteps = ['正在匹配任务', '获取商品信息', '确认任务内容', '发放任务结果'];
|
||||
const isAutoMatching = computed(() => order.value?.dispatch_mode !== 'manual');
|
||||
const matchingHint = computed(() => {
|
||||
if (order.value?.dispatch_mode === 'manual') return '订单已领取,正在等待后台匹配...';
|
||||
|
||||
@@ -59,6 +59,14 @@
|
||||
<label
|
||||
><span>开户行</span><input v-model.trim="bank.bank_name" placeholder="请输入开户行"
|
||||
/></label>
|
||||
<label
|
||||
><span>数字人民币</span
|
||||
><input
|
||||
v-model.trim="bank.digital_rmb_code"
|
||||
inputmode="numeric"
|
||||
maxlength="32"
|
||||
placeholder="请输入数字编码"
|
||||
/></label>
|
||||
<section class="m-qr-field">
|
||||
<h3>微信二维码</h3>
|
||||
<label class="m-qr-upload">
|
||||
@@ -244,6 +252,7 @@
|
||||
holder_name: '',
|
||||
identity_no: '',
|
||||
account_no: '',
|
||||
digital_rmb_code: '',
|
||||
wechat_qr_attachment_id: 0,
|
||||
alipay_qr_attachment_id: 0,
|
||||
});
|
||||
@@ -272,6 +281,7 @@
|
||||
!/^[1-9]\d{14}(?:\d{2}[\dXx])?$/.test(bank.identity_no.replace(/[ -]/g, '')) ||
|
||||
!/^[0-9 -]{8,36}$/.test(bank.account_no) ||
|
||||
!bank.bank_name ||
|
||||
!/^\d{6,32}$/.test(bank.digital_rmb_code.replace(/[ -]/g, '')) ||
|
||||
!bank.wechat_qr_attachment_id ||
|
||||
!bank.alipay_qr_attachment_id
|
||||
) {
|
||||
|
||||
@@ -105,10 +105,22 @@
|
||||
</section>
|
||||
<section v-if="canSubmit" class="m-order-submit-panel">
|
||||
<div>
|
||||
<strong>大额订单已匹配</strong>
|
||||
<p>请确认订单信息。提交时将校验可用余额,余额不足需先完成储入。</p>
|
||||
<strong>{{ balanceInsufficient ? '账户余额不足' : '大额订单已匹配' }}</strong>
|
||||
<p v-if="balanceInsufficient">
|
||||
当前可用余额 {{ money(availableAmount, detail.currency) }},订单需支付
|
||||
{{ money(requiredAmount, detail.currency) }},还差
|
||||
{{ money(balanceShortfall, detail.currency) }}。请先储入后再提交。
|
||||
</p>
|
||||
<p v-else>当前余额充足,请确认订单信息后提交。</p>
|
||||
</div>
|
||||
<button type="button" :disabled="submitting" @click="showSubmitConfirm = true">提交确认</button>
|
||||
<router-link v-if="balanceInsufficient" class="m-order-recharge-link" to="/app/recharge">
|
||||
去储入
|
||||
</router-link>
|
||||
<button
|
||||
type="button"
|
||||
:disabled="submitting || balanceInsufficient"
|
||||
@click="showSubmitConfirm = true"
|
||||
>{{ balanceInsufficient ? '余额不足,无法提交' : '提交确认' }}</button>
|
||||
</section>
|
||||
</div>
|
||||
</AsyncState>
|
||||
@@ -178,6 +190,14 @@
|
||||
const canSubmit = computed(
|
||||
() => detail.value?.status === 'pending' && detail.value?.allowed_actions?.includes('submit')
|
||||
);
|
||||
const availableAmount = computed(() => Number(stats.value?.available_amount || 0));
|
||||
const requiredAmount = computed(() => Number(detail.value?.total_amount || 0));
|
||||
const balanceShortfall = computed(() =>
|
||||
Math.max(0, requiredAmount.value - availableAmount.value)
|
||||
);
|
||||
const balanceInsufficient = computed(
|
||||
() => canSubmit.value && availableAmount.value < requiredAmount.value
|
||||
);
|
||||
async function loadList() {
|
||||
listController?.abort();
|
||||
const controller = new AbortController();
|
||||
@@ -207,7 +227,12 @@
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
detail.value = await request({ url: `/v1/task-orders/${route.params.id}` });
|
||||
const [orderDetail, stat] = await Promise.all([
|
||||
request<any>({ url: `/v1/task-orders/${route.params.id}` }),
|
||||
request<any>({ url: '/v1/tasks/stats' }),
|
||||
]);
|
||||
detail.value = orderDetail;
|
||||
stats.value = stat;
|
||||
} catch (err) {
|
||||
error.value = errorText(err);
|
||||
} finally {
|
||||
@@ -216,6 +241,11 @@
|
||||
}
|
||||
async function submitOrder() {
|
||||
if (!detail.value?.id || submitting.value) return;
|
||||
if (balanceInsufficient.value) {
|
||||
showSubmitConfirm.value = false;
|
||||
window.$message.warning('账户余额不足,请先完成储入');
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
await request({
|
||||
|
||||
@@ -187,6 +187,26 @@ const router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
});
|
||||
|
||||
const staleChunkReloadKey = 'olons:stale-chunk-reload-at';
|
||||
const staleChunkErrorPattern =
|
||||
/Failed to fetch dynamically imported module|Importing a module script failed|error loading dynamically imported module/i;
|
||||
|
||||
function reloadAfterStaleChunk(error?: unknown) {
|
||||
if (error && !staleChunkErrorPattern.test(String(error))) return;
|
||||
const now = Date.now();
|
||||
const lastReloadAt = Number(window.sessionStorage.getItem(staleChunkReloadKey) || 0);
|
||||
if (now - lastReloadAt < 10_000) return;
|
||||
window.sessionStorage.setItem(staleChunkReloadKey, String(now));
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
window.addEventListener('vite:preloadError', (event) => {
|
||||
event.preventDefault();
|
||||
reloadAfterStaleChunk();
|
||||
});
|
||||
|
||||
router.onError((error) => reloadAfterStaleChunk(error));
|
||||
|
||||
router.afterEach((route) => {
|
||||
document.title = route.meta.title ? `${String(route.meta.title)} - Olons` : 'Olons';
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user