110 lines
3.7 KiB
Vue
110 lines
3.7 KiB
Vue
<template>
|
||
<section v-if="!isChat" class="m-support">
|
||
<div class="m-support-hero" :style="{ backgroundImage: `url(${mobileAssets.supportHero})` }">
|
||
<PageHeader title="客服中心" :back="false" />
|
||
<div><h1>在线客服</h1><p>账户与业务问题请联系在线客服,全程跟进处理</p></div>
|
||
</div>
|
||
<div class="m-support-body">
|
||
<AsyncState
|
||
:loading="loading"
|
||
:error="error"
|
||
:empty="!services.length"
|
||
empty-text="在线客服暂未开放"
|
||
@retry="load"
|
||
>
|
||
<button
|
||
v-for="item in services"
|
||
:key="item.id"
|
||
class="m-service-card"
|
||
@click="$router.push(`/app/support/${item.id}`)"
|
||
>
|
||
<div class="m-service-avatar"
|
||
><img
|
||
v-if="safeImage(item.avatar_url)"
|
||
:src="safeImage(item.avatar_url)"
|
||
:alt="item.name" /><n-icon v-else :component="HeadsetOutline"
|
||
/></div>
|
||
<div
|
||
><h2>{{ item.name }}</h2
|
||
><p>工作时间:{{ item.work_time || '以客服在线状态为准' }}</p
|
||
><small v-if="item.backup_contact">{{ item.backup_contact }}</small></div
|
||
>
|
||
<span :class="`is-${item.online_status}`">{{ statusText(item.online_status) }}</span>
|
||
</button>
|
||
</AsyncState>
|
||
</div>
|
||
</section>
|
||
|
||
<section v-else class="m-chat">
|
||
<PageHeader :title="current?.name || '在线客服'" />
|
||
<div class="m-chat-time">会话内容由客服系统提供</div>
|
||
<div class="m-chat-empty">
|
||
<n-icon :component="ChatbubbleEllipsesOutline" />
|
||
<h2>正在连接客服</h2>
|
||
<p>{{ current?.privacy_notice || '请勿在聊天中发送登录密码或支付密码' }}</p>
|
||
<a
|
||
v-if="safeServiceUrl"
|
||
:href="safeServiceUrl"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
class="m-primary-btn m-btn-link"
|
||
>进入客服会话</a
|
||
>
|
||
<p v-else class="m-form-error">客服会话地址尚未配置</p>
|
||
</div>
|
||
<div class="m-chat-input"
|
||
><button aria-label="语音"><img :src="mobileAssets.chatVoice" alt="" /></button
|
||
><input disabled placeholder="请先连接客服会话" /><button aria-label="更多"
|
||
><img :src="mobileAssets.chatMore" alt="" /></button
|
||
></div>
|
||
</section>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, onMounted, ref } from 'vue';
|
||
import { useRoute } from 'vue-router';
|
||
import { ChatbubbleEllipsesOutline, HeadsetOutline } from '@vicons/ionicons5';
|
||
import PageHeader from '../components/PageHeader.vue';
|
||
import AsyncState from '../components/AsyncState.vue';
|
||
import { errorText, request, safeImage } from '../api';
|
||
import { mobileAssets } from '../assets';
|
||
const route = useRoute();
|
||
const isChat = computed(() => Boolean(route.params.id));
|
||
const loading = ref(true);
|
||
const error = ref('');
|
||
const services = ref<any[]>([]);
|
||
const current = computed(
|
||
() =>
|
||
services.value.find((item) => String(item.id) === String(route.params.id)) ||
|
||
services.value[0]
|
||
);
|
||
const safeServiceUrl = computed(() => {
|
||
const value = current.value?.service_url;
|
||
if (!value) return '';
|
||
try {
|
||
const u = new URL(value);
|
||
return u.protocol === 'https:' ? u.href : '';
|
||
} catch {
|
||
return '';
|
||
}
|
||
});
|
||
async function load() {
|
||
loading.value = true;
|
||
error.value = '';
|
||
try {
|
||
services.value = await request<any[]>({ url: '/v1/customer-services' });
|
||
} catch (err) {
|
||
error.value = errorText(err);
|
||
services.value = [];
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
function statusText(status: string) {
|
||
if (status === 'online') return '在线咨询';
|
||
if (status === 'busy') return '忙碌';
|
||
return '离线留言';
|
||
}
|
||
onMounted(load);
|
||
</script>
|