|
@@ -1,5 +1,5 @@
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import {ref, watch, onMounted, defineAsyncComponent} from 'vue'
|
|
|
|
|
|
|
+import {ref, watch, onMounted, defineAsyncComponent, computed} from 'vue'
|
|
|
|
|
|
|
|
import {BasicApi} from '@/api/basic'
|
|
import {BasicApi} from '@/api/basic'
|
|
|
import {RequestError} from '@/utils/request'
|
|
import {RequestError} from '@/utils/request'
|
|
@@ -39,6 +39,20 @@ const mainBoxFlag = ref(true)
|
|
|
const timeoutFlag = ref()
|
|
const timeoutFlag = ref()
|
|
|
const mainContentClass = ref({})
|
|
const mainContentClass = ref({})
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 立式一体机设备检测
|
|
|
|
|
+ * 检测条件:宽度 1000-1200px,高度 1700-2000px
|
|
|
|
|
+ */
|
|
|
|
|
+const isKioskDevice = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 立式一体机模式下的常见问题列表
|
|
|
|
|
+ * 从 global.commonProblem 派生,取前 5 条用于底部展示
|
|
|
|
|
+ */
|
|
|
|
|
+const kioskFaqList = computed(() => {
|
|
|
|
|
+ return global.commonProblem.slice(0, 5)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
const inputBox = ref<InstanceType<typeof SsInputBox> | null>(null)
|
|
const inputBox = ref<InstanceType<typeof SsInputBox> | null>(null)
|
|
|
const commonProblem = ref<InstanceType<typeof SsCommonProblem> | null>(null)
|
|
const commonProblem = ref<InstanceType<typeof SsCommonProblem> | null>(null)
|
|
|
const hotlineRef = ref<InstanceType<typeof SsHotline> | null>(null)
|
|
const hotlineRef = ref<InstanceType<typeof SsHotline> | null>(null)
|
|
@@ -118,11 +132,51 @@ const openPolicyFile = () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 返回首页:重置对话状态
|
|
|
|
|
+ */
|
|
|
|
|
+const goHome = () => {
|
|
|
|
|
+ global.setSpread(false)
|
|
|
|
|
+ global.setMenuSwitch(false)
|
|
|
|
|
+ global.setInReply(false)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取服务项样式类(复用 SsService 组件逻辑)
|
|
|
|
|
+ */
|
|
|
|
|
+const getServiceClass = (index: number) => {
|
|
|
|
|
+ switch (index % 5) {
|
|
|
|
|
+ case 0:
|
|
|
|
|
+ return 'hover-scale'
|
|
|
|
|
+ case 1:
|
|
|
|
|
+ return 'hover-scale yellow'
|
|
|
|
|
+ case 2:
|
|
|
|
|
+ return 'center'
|
|
|
|
|
+ case 3:
|
|
|
|
|
+ return 'hover-scale yellow'
|
|
|
|
|
+ case 4:
|
|
|
|
|
+ return 'hover-scale'
|
|
|
|
|
+ default:
|
|
|
|
|
+ return ''
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 检测是否为立式一体机设备
|
|
|
|
|
+ * 判断依据:屏幕宽度 1000-1200px,高度 1700-2000px
|
|
|
|
|
+ */
|
|
|
|
|
+const detectKioskDevice = () => {
|
|
|
|
|
+ const w = window.innerWidth
|
|
|
|
|
+ const h = window.innerHeight
|
|
|
|
|
+ isKioskDevice.value = (w >= 1000 && w <= 1200 && h >= 1700 && h <= 2000)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const initPage = () => {
|
|
const initPage = () => {
|
|
|
let w = window.screen.width
|
|
let w = window.screen.width
|
|
|
if (w <= 750) {
|
|
if (w <= 750) {
|
|
|
global.setMenuSwitch(false)
|
|
global.setMenuSwitch(false)
|
|
|
}
|
|
}
|
|
|
|
|
+ detectKioskDevice()
|
|
|
setMainContentClass()
|
|
setMainContentClass()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -155,8 +209,40 @@ watch(() => global.inputLine, () => {
|
|
|
|
|
|
|
|
<template>
|
|
<template>
|
|
|
<div class="root">
|
|
<div class="root">
|
|
|
- <ss-header @loaded="componentLoaded"/>
|
|
|
|
|
|
|
+ <!-- 1080x1920 立式一体机对话模式:顶部导航栏替代 Header -->
|
|
|
|
|
+ <div v-if="global.spread && isKioskDevice" class="kiosk-top-nav">
|
|
|
|
|
+ <div class="back-home-btn" @click="goHome">
|
|
|
|
|
+ <span class="back-icon">←</span>
|
|
|
|
|
+ <span>返回首页</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="app-title">商小川智能助手</div>
|
|
|
|
|
+ <div class="nav-spacer"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 立式一体机对话模式:服务快捷入口栏(复用原有设计) -->
|
|
|
|
|
+ <div v-if="global.spread && isKioskDevice" class="kiosk-service-bar">
|
|
|
|
|
+ <div class="service-bar-container">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(item, index) in global.categories"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ class="service-quick-item"
|
|
|
|
|
+ :class="getServiceClass(index)"
|
|
|
|
|
+ @click="loadService(item.id, item.categoryName, item.imgUrl)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="service-item-content">
|
|
|
|
|
+ <div class="service-title">{{ item.categoryName }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-image class="service-item-image" fit="contain" :src="getImageUrl(item.imgUrl)"/>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 默认 Header(非立式一体机模式) -->
|
|
|
|
|
+ <ss-header v-else @loaded="componentLoaded"/>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 侧边栏导航:立式一体机对话模式下隐藏 -->
|
|
|
<ss-navigation
|
|
<ss-navigation
|
|
|
|
|
+ v-if="!(global.spread && isKioskDevice)"
|
|
|
@open-hotline="openHotlineDialog"
|
|
@open-hotline="openHotlineDialog"
|
|
|
@open-opinion="openOpinionDialog"
|
|
@open-opinion="openOpinionDialog"
|
|
|
@quick-send="quickSend"
|
|
@quick-send="quickSend"
|
|
@@ -164,11 +250,23 @@ watch(() => global.inputLine, () => {
|
|
|
@load-common-problem="loadCommonProblem"
|
|
@load-common-problem="loadCommonProblem"
|
|
|
@loaded="componentLoaded"
|
|
@loaded="componentLoaded"
|
|
|
/>
|
|
/>
|
|
|
- <div class="container" :class="{spread: global.spread, 'menu-close': !global.menuSwitch}">
|
|
|
|
|
|
|
+ <div class="container" :class="{spread: global.spread, 'menu-close': !global.menuSwitch, 'kiosk-mode': isKioskDevice && global.spread}">
|
|
|
<div class="main">
|
|
<div class="main">
|
|
|
<div class="main-content" :class="mainContentClass">
|
|
<div class="main-content" :class="mainContentClass">
|
|
|
<ss-headline v-if="mainBoxFlag"/>
|
|
<ss-headline v-if="mainBoxFlag"/>
|
|
|
<ss-input-box ref="inputBox" @loaded="componentLoaded"/>
|
|
<ss-input-box ref="inputBox" @loaded="componentLoaded"/>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 立式一体机对话模式:底部常见问题展示区 -->
|
|
|
|
|
+ <div v-if="global.spread && isKioskDevice" class="kiosk-bottom-faq">
|
|
|
|
|
+ <div class="faq-title">热门问题</div>
|
|
|
|
|
+ <div class="faq-list">
|
|
|
|
|
+ <div v-for="(item, index) in kioskFaqList" :key="index" class="faq-item">
|
|
|
|
|
+ {{ item.questionContent }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 默认布局:服务导航 + 常见问题 -->
|
|
|
<div v-if="mainBoxFlag" class="main-box">
|
|
<div v-if="mainBoxFlag" class="main-box">
|
|
|
<div class="ss-row ss-row-vertical">
|
|
<div class="ss-row ss-row-vertical">
|
|
|
<div class="ss-col ss-col-service is-mobile">
|
|
<div class="ss-col ss-col-service is-mobile">
|
|
@@ -293,13 +391,17 @@ watch(() => global.inputLine, () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 1080x1920 中等尺寸设备优化 */
|
|
|
|
|
|
|
+/* 1080x1920 立式一体机专用布局优化 */
|
|
|
@media screen and (min-width: 751px) and (max-width: 1200px) and (min-height: 1700px) {
|
|
@media screen and (min-width: 751px) and (max-width: 1200px) and (min-height: 1700px) {
|
|
|
.root {
|
|
.root {
|
|
|
|
|
+ /* 定义统一的内容区域宽度:屏幕宽度减去左右 padding */
|
|
|
|
|
+ --kiosk-content-padding: 2rem;
|
|
|
|
|
+ --kiosk-content-width: calc(100vw - var(--kiosk-content-padding) * 2);
|
|
|
|
|
+
|
|
|
.container {
|
|
.container {
|
|
|
.main {
|
|
.main {
|
|
|
.main-content {
|
|
.main-content {
|
|
|
- padding: 5rem 0 5rem 0; /* 增加底部 padding 为 Footer 留空间 */
|
|
|
|
|
|
|
+ padding: 5rem 0 5rem 0;
|
|
|
|
|
|
|
|
.main-box {
|
|
.main-box {
|
|
|
max-width: calc(1100px + 2rem);
|
|
max-width: calc(1100px + 2rem);
|
|
@@ -342,6 +444,215 @@ watch(() => global.inputLine, () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /* 立式一体机对话模式:三段式布局 */
|
|
|
|
|
+ &.kiosk-mode {
|
|
|
|
|
+ padding-left: 0 !important;
|
|
|
|
|
+
|
|
|
|
|
+ .main {
|
|
|
|
|
+ .main-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ height: 100vh;
|
|
|
|
|
+ padding: 220px 0 0 0; /* 顶部留出导航栏(100px) + 服务栏(120px)空间 */
|
|
|
|
|
+
|
|
|
|
|
+ /* 对话区域:占据主要空间 */
|
|
|
|
|
+ .chat-container {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ min-height: 0; /* 允许 flex 子元素收缩 */
|
|
|
|
|
+
|
|
|
|
|
+ /* 聊天消息区域:保持原有宽度(768px),无需覆盖 */
|
|
|
|
|
+ /* 输入框:保持原有宽度(768px),无需覆盖 */
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 底部常见问题展示区:固定 420px,与输入框宽度对齐 */
|
|
|
|
|
+ .kiosk-bottom-faq {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ height: 420px;
|
|
|
|
|
+ background: linear-gradient(180deg, #F9FAFB 0%, #FFFFFF 100%);
|
|
|
|
|
+ border-top: 1px solid #E5E7EB;
|
|
|
|
|
+ padding: 2rem 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .faq-title {
|
|
|
|
|
+ font-size: 1.5rem;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #101333;
|
|
|
|
|
+ margin-bottom: 1.5rem;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .faq-list {
|
|
|
|
|
+ width: var(--kiosk-content-width); /* 与顶部导航栏内容区域对齐 */
|
|
|
|
|
+ max-width: 100%;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 1rem;
|
|
|
|
|
+
|
|
|
|
|
+ .faq-item {
|
|
|
|
|
+ font-size: 1.125rem;
|
|
|
|
|
+ color: #545764;
|
|
|
|
|
+ line-height: 1.6;
|
|
|
|
|
+ padding: 0.75rem 1rem;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ border-radius: 0.75rem;
|
|
|
|
|
+ border: 1px solid #E5E7EB;
|
|
|
|
|
+ /* 仅展示模式:不可点击 */
|
|
|
|
|
+ cursor: default;
|
|
|
|
|
+ transition: none;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 立式一体机顶部导航栏:固定 100px */
|
|
|
|
|
+ .kiosk-top-nav {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ height: 100px;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ border-bottom: 1px solid #E5E7EB;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ padding: 0 2rem;
|
|
|
|
|
+ z-index: 1000;
|
|
|
|
|
+
|
|
|
|
|
+ .back-home-btn {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 0.5rem;
|
|
|
|
|
+ padding: 0.75rem 1.5rem;
|
|
|
|
|
+ background: #2942D4;
|
|
|
|
|
+ color: #ffffff;
|
|
|
|
|
+ border-radius: 0.75rem;
|
|
|
|
|
+ font-size: 1.125rem;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+ min-width: 120px;
|
|
|
|
|
+ min-height: 60px;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ background: #1e32a8;
|
|
|
|
|
+ transform: scale(1.02);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .back-icon {
|
|
|
|
|
+ font-size: 1.5rem;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .app-title {
|
|
|
|
|
+ font-size: 1.75rem;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #101333;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .nav-spacer {
|
|
|
|
|
+ width: 120px; /* 占位,保持标题居中 */
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 立式一体机服务快捷入口栏:紧贴顶部导航栏下方,复用原有设计 */
|
|
|
|
|
+ .kiosk-service-bar {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 100px;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ height: 120px;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ border-bottom: 1px solid #E5E7EB;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ padding: 1rem 0;
|
|
|
|
|
+ z-index: 999;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+
|
|
|
|
|
+ .service-bar-container {
|
|
|
|
|
+ width: var(--kiosk-content-width); /* 与顶部导航栏内容区域对齐 */
|
|
|
|
|
+ max-width: 100%;
|
|
|
|
|
+ margin: 0 auto; /* 居中对齐 */
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 1rem;
|
|
|
|
|
+ overflow-x: auto;
|
|
|
|
|
+ overflow-y: hidden;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+
|
|
|
|
|
+ /* 隐藏滚动条但保持可滚动 */
|
|
|
|
|
+ scrollbar-width: none;
|
|
|
|
|
+ &::-webkit-scrollbar {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .service-quick-item {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ width: 140px;
|
|
|
|
|
+ height: 90px;
|
|
|
|
|
+ border-radius: 0.75rem;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: transform 0.3s ease;
|
|
|
|
|
+ /* 复用原有渐变背景 */
|
|
|
|
|
+ background: linear-gradient(180deg, rgba(250, 255, 253, 0.8), rgba(225, 245, 248, 0.8));
|
|
|
|
|
+ box-shadow: 0 0.25rem 1.25rem 0 rgba(52, 149, 239, 0.4);
|
|
|
|
|
+
|
|
|
|
|
+ &.yellow {
|
|
|
|
|
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(246, 240, 239, 0.7));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.center {
|
|
|
|
|
+ background: linear-gradient(90deg, #c2dff9, #c6e2fa);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.hover-scale:hover {
|
|
|
|
|
+ transform: scale(1.05);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .service-item-content {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ z-index: 2;
|
|
|
|
|
+ padding: 1rem;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .service-title {
|
|
|
|
|
+ color: #101333;
|
|
|
|
|
+ font-size: 1rem;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ line-height: 1.2;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .service-item-image {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ right: 0.5rem;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ width: 4rem;
|
|
|
|
|
+ height: 3rem;
|
|
|
|
|
+ opacity: 0.2;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|