商城c端前端
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user