fix(mobile): recover direct channels after network failure

This commit is contained in:
key
2026-07-29 02:31:19 +08:00
parent 376a7c4b84
commit dc20b92061
6 changed files with 66 additions and 9 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
"appid" : "__UNI__B36D8BE",
"description" : "帮道员工端",
"vueVersion" : "3",
"versionName" : "0.1.9",
"versionCode" : "109",
"versionName" : "0.1.10",
"versionCode" : "110",
"uniStatistics" : {
"enable" : false
},
+41 -4
View File
@@ -25,7 +25,16 @@
<text class="section-note">每次只提交给一个处理部门。</text>
</view>
</view>
<view class="channel-grid" role="radiogroup" aria-label="直达对象">
<view v-if="channelsLoading" class="channel-state" role="status" aria-live="polite">
<text>直达对象加载中…</text>
</view>
<view v-else-if="channelsFailed" class="channel-state error" role="alert">
<text>直达对象加载失败,请检查网络后重试。</text>
<button class="secondary-button channel-retry" :disabled="channelsLoading" @click="loadChannels">
重新加载直达对象
</button>
</view>
<view v-else class="channel-grid" role="radiogroup" aria-label="直达对象">
<button
v-for="channel in channels"
:key="channel.code"
@@ -173,6 +182,8 @@ const path = '/pages/user/direct/index';
const pageSize = 20;
const loggedIn = ref(false);
const channels = ref<DirectChannel[]>([]);
const channelsLoading = ref(false);
const channelsFailed = ref(false);
const items = ref<DirectFeedback[]>([]);
const total = ref(0);
const pageNum = ref(1);
@@ -204,7 +215,12 @@ let voiceTimer: ReturnType<typeof setInterval> | null = null;
let voiceStartedAt = 0;
const hasMore = computed(() => items.value.length < total.value);
const canSubmit = computed(() => Boolean(form.channelCode) && Boolean(form.content.trim()));
const canSubmit = computed(() =>
!channelsLoading.value
&& !channelsFailed.value
&& Boolean(form.channelCode)
&& Boolean(form.content.trim())
);
const voiceBusy = computed(() => voiceStarting.value || voiceRecording.value || voiceStopping.value || voiceTranscribing.value);
const voiceChipLabel = computed(() => {
if (voiceStopping.value) return '结束中…';
@@ -378,8 +394,28 @@ const changeAnonymous = (event: Event | { detail?: { value?: boolean } }) => {
};
const loadChannels = async () => {
channels.value = await listDirectChannels();
if (!form.channelCode && channels.value.length) chooseChannel(channels.value[0]);
if (channelsLoading.value) return;
channelsLoading.value = true;
channelsFailed.value = false;
try {
const rows = await listDirectChannels();
if (!rows.length) {
channels.value = [];
form.channelCode = '';
channelsFailed.value = true;
return;
}
channels.value = rows;
if (!form.channelCode || !rows.some((channel) => channel.code === form.channelCode)) {
chooseChannel(rows[0]);
}
} catch {
channels.value = [];
form.channelCode = '';
channelsFailed.value = true;
} finally {
channelsLoading.value = false;
}
};
const loadMine = async (reset: boolean) => {
@@ -463,6 +499,7 @@ onUnload(() => {
.section-head,.history-head,.feedback-head,.form-foot,.anonymous-control{display:flex;align-items:center;justify-content:space-between;gap:12px}
.section-title{display:block;color:var(--ink);font-size:18px;font-weight:900}.section-note,.muted{display:block;margin-top:4px;color:var(--muted);font-size:12px;line-height:1.6}
.channel-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.channel-button{display:grid;min-height:60px;align-content:center;justify-items:center;gap:3px;padding:10px;border:1px solid #e6e8ec;border-radius:14px;background:#fff;color:var(--ink);font-size:14px;font-weight:900;text-align:center;transition:border-color .2s ease,box-shadow .2s ease,transform .1s ease}.channel-button.active{border-color:var(--brand);background:#fff5f6;color:var(--brand);box-shadow:0 6px 16px rgba(230,0,18,.10)}.channel-button:active{transform:scale(.98)}.channel-button::after,.refresh-button::after{border:0}.channel-hint{font-size:11px;font-weight:700;opacity:.72}
.channel-state{display:grid;justify-items:start;gap:10px;padding:14px;border-radius:12px;background:#f7f8fa;color:var(--muted);font-size:13px;line-height:1.5}.channel-state.error{background:#fff0f1;color:#a9000d}.channel-retry{min-width:150px;margin:0}
.channel-icon{display:grid;width:34px;height:34px;place-items:center;border-radius:12px;background:#f4f5f7;transition:background .2s ease}
.channel-button.active .channel-icon{background:var(--employee-primary-soft)}
.channel-name{line-height:1.3}