Commit 98ab0cdc authored by nick zheng's avatar nick zheng

Merge branch 'beta' into 'master'

chore: h5应用分享页适配移动端

See merge request !206
parents f627afff 1b5de751
......@@ -64,6 +64,8 @@
"@types/validator": "^13.12.2",
"@typescript-eslint/parser": "^7.18.0",
"@unocss/eslint-config": "^0.61.9",
"@unocss/postcss": "66.1.0-beta.10",
"@unocss/transformer-directives": "66.1.0-beta.10",
"@vitejs/plugin-vue": "^4.6.2",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"autoprefixer": "^10.4.20",
......@@ -78,6 +80,7 @@
"naive-ui": "^2.39.0",
"postcss": "^8.4.47",
"postcss-html": "^1.7.0",
"postcss-mobile-forever": "^5.0.0",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"rollup-plugin-visualizer": "^5.12.0",
......
This diff is collapsed.
export default {
plugins: {
'@unocss/postcss': {},
autoprefixer: {},
'postcss-mobile-forever': {
appSelector: '#app',
viewportWidth: 375,
desktopWidth: 500,
maxDisplayWidth: 500,
include: [/src\/views\/share\/share-application-mobile/, /src\/views\/share\/components\/mobile\//],
},
},
}
......@@ -64,7 +64,7 @@ function handleSelectContinueQuestion(continueQuestion: string) {
}
</script>
<template>
<h3 class="mt-[15px] text-[12px] text-[#999]">
<h3 class="continue-question-title">
{{
type === 'featured'
? t('common_module.recommended_questions') + ':'
......@@ -72,12 +72,12 @@ function handleSelectContinueQuestion(continueQuestion: string) {
}}
</h3>
<ul class="w-full select-none">
<ul class="continue-question-wrapper">
<template v-if="continuousQuestionList.length && promptChangeBtnStatus !== 'loading'">
<li
v-for="questionItem in recommendQuestionList"
:key="questionItem.id"
class="mt-[10px] w-fit cursor-pointer rounded-[10px] border border-[#d4d6d9] bg-[#ffffff80] px-[12px] py-[10px] text-[12px] hover:opacity-80"
class="question-item"
@click="handleSelectContinueQuestion(questionItem.content)"
>
{{ questionItem.content }}
......@@ -85,14 +85,14 @@ function handleSelectContinueQuestion(continueQuestion: string) {
</template>
<template v-else>
<n-skeleton :class="'mt-[10px]'" height="41px" width="52%" round />
<n-skeleton :class="'mt-[10px]'" height="41px" width="72%" round />
<n-skeleton :class="'mt-[10px]'" height="41px" width="70%" round />
<n-skeleton class="question-item-skeleton" width="52%" round />
<n-skeleton class="question-item-skeleton" width="72%" round />
<n-skeleton class="question-item-skeleton" width="70%" round />
</template>
<li class="mt-[10px] pl-[16px] text-[12px]">
<li class="recommend-question-change-wrapper">
<span
class="group cursor-pointer text-[#0B7DFF] transition"
class="recommend-question-change-container"
:class="{
'hover:text-[#096EE0]': promptChangeBtnStatus !== 'loading',
'cursor-not-allowed': promptChangeBtnStatus === 'loading',
......@@ -101,7 +101,7 @@ function handleSelectContinueQuestion(continueQuestion: string) {
@click="handleRecommendQuestionListUpdate"
>
<i
class="iconfont icon-huanyihuan mr-[2px] inline-block text-[11px] transition-[rotate] duration-150 ease-in-out"
class="iconfont icon-huanyihuan recommend-question-change-icon"
:class="{ 'group-active:rotate-360': promptChangeBtnStatus !== 'loading' }"
></i>
<span>
......@@ -111,3 +111,33 @@ function handleSelectContinueQuestion(continueQuestion: string) {
</li>
</ul>
</template>
<style lang="scss" scoped>
.continue-question-title {
@apply mt-[15px] text-[12px] text-[#999];
}
.continue-question-wrapper {
@apply w-full select-none;
.question-item {
@apply mt-[10px] w-fit cursor-pointer rounded-[10px] border border-[#d4d6d9] bg-[#ffffff80] px-[12px] py-[10px] text-[12px] hover:opacity-80;
}
.question-item-skeleton {
@apply mt-[10px] h-[40px];
}
.recommend-question-change-wrapper {
@apply mt-[10px] pl-[16px] text-[12px];
.recommend-question-change-container {
@apply group cursor-pointer text-[#0B7DFF] transition;
.recommend-question-change-icon {
@apply mr-[2px] inline-block text-[11px] transition-[rotate] duration-150 ease-in-out;
}
}
}
}
</style>
......@@ -3,7 +3,7 @@ import { computed, ref, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { CheckOne, Down } from '@icon-park/vue-next'
import CustomLoading from '../custom-loading.vue'
import MusicWavesLoading from '../music-waves-loading.vue'
import MusicWavesLoading from './music-waves-loading.vue'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import { PersonalAppConfigState } from '@/store/types/personal-app-config'
......@@ -67,19 +67,16 @@ function handleShowReasoningContentSwitch() {
</script>
<template>
<div
class="mb-[15px] flex flex-row-reverse last:mb-0"
:class="[role === 'assistant' ? 'justify-end' : 'justify-start']"
>
<div class="flex w-full flex-col overflow-x-auto" :class="[role === 'user' ? 'items-end' : 'items-start']">
<div class="message-item-wrapper" :class="[role === 'assistant' ? 'justify-end' : 'justify-start']">
<div class="message-item-container" :class="[role === 'user' ? 'items-end' : 'items-start']">
<!-- 大模型深度思考 -->
<template v-if="role === 'assistant' && isDeepSeekR1">
<div class="my-[7px] select-none text-[12px]">
<div class="inline-flex cursor-pointer" @click="handleShowReasoningContentSwitch">
<span v-if="messageItem.isTextContentLoading" class="mr-[6px]">
<div class="deep-seek-title-container">
<div class="deep-seek-name-container" @click="handleShowReasoningContentSwitch">
<span v-if="messageItem.isTextContentLoading" class="deep-seek-text">
{{ t('common_module.deep_thinking', { modelName: agentApplicationConfig.commModelConfig.largeModel }) }}
</span>
<span v-else class="mr-[6px]">
<span v-else class="deep-seek-text">
{{ t('common_module.have_thought_deeply') }}
</span>
<Down
......@@ -94,10 +91,7 @@ function handleShowReasoningContentSwitch() {
</div>
<n-collapse-transition :show="isShowReasoningContent">
<div
v-if="messageItem.reasoningContent"
class="my-[10px] border-l-[1px] border-solid border-l-[#ccc] px-[13px] py-[8px]"
>
<div v-if="messageItem.reasoningContent" class="deep-seek-content-container">
<MarkdownRender
ref="markdownRenderRef"
:raw-text-content="
......@@ -106,38 +100,28 @@ function handleShowReasoningContentSwitch() {
: t('common_module.dialogue_module.empty_message_content')
"
color="#999"
:font-size="'12px'"
:font-size="'3.2vw'"
/>
</div>
</n-collapse-transition>
</template>
<!-- 模型内容 -->
<div class="flex min-w-[80px] max-w-full flex-col">
<div class="model-content-wrapper">
<div
class="w-full flex-wrap rounded-[10px] border px-[12px] py-[11px]"
class="model-content-container"
:class="[
{ 'rounded-tr-none border-[#DCDEFF] bg-[#DCDEFF] text-white': role === 'user' },
{ 'rounded-tl-none border-[#e8e9eb] bg-white text-[#333]': role === 'assistant' },
{ 'user-model-content-container': role === 'user' },
{ 'assistant-model-content-container': role === 'assistant' },
]"
>
<img
v-show="role === 'user' && messageItem.imageUrl"
:src="messageItem.imageUrl"
class="max-h-[120px]! mb-[11px] rounded-[10px] object-contain"
/>
<div
v-show="role === 'assistant' && messageItem.pluginName"
class="mb-[8px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999]"
>
<div
v-show="messageItem.isTextContentLoading"
class="bg-px-plugin_loading-gif h-[14px] w-[14px] bg-contain bg-center bg-no-repeat"
/>
<CheckOne v-show="!messageItem.isTextContentLoading" theme="outline" :class="'14'" fill="#40bd23" />
<img v-show="role === 'user' && messageItem.imageUrl" :src="messageItem.imageUrl" class="upload-image" />
<div v-show="role === 'assistant' && messageItem.pluginName" class="plugin-container">
<div v-show="messageItem.isTextContentLoading" class="plugin-loading" />
<CheckOne v-show="!messageItem.isTextContentLoading" theme="outline" size="16" fill="#40bd23" />
<span class="text-[12px] leading-5">
<span class="plugin-name">
{{
messageItem.isTextContentLoading
? t('common_module.plugin_in_progress', { pluginName: messageItem.pluginName })
......@@ -146,7 +130,7 @@ function handleShowReasoningContentSwitch() {
</span>
</div>
<div v-if="messageItem.isTextContentLoading" class="py-[6px] pl-[16px]">
<div v-if="messageItem.isTextContentLoading" class="content-loading">
<CustomLoading :active-color="'#000DFF'" />
</div>
......@@ -160,22 +144,19 @@ function handleShowReasoningContentSwitch() {
: messageItem.textContent
"
:color="'#333'"
:font-size="'12px'"
:font-size="'3.2vw'"
/>
</p>
<div
v-show="isShowVoiceLoading || messageItem.isAnswerResponseLoading"
class="mb-[5px] mt-[16px] px-[16px]"
>
<div v-show="isShowVoiceLoading || messageItem.isAnswerResponseLoading" class="answer-response-loading">
<CustomLoading :active-color="'#000DFF'" />
</div>
</div>
<!-- 移动端语音播放 -->
<div v-show="isShowAudioControl" class="mt-[13px] flex items-center gap-[8px]">
<div v-show="isShowAudioControl" class="audio-control-container">
<div
class="h-[18px] w-[18px] cursor-pointer"
class="audio-control-icon"
:class="messageItem.isVoicePlaying ? 'bg-svg-pause' : 'bg-svg-play'"
@click="handleAudioControl"
/>
......@@ -184,7 +165,7 @@ function handleShowReasoningContentSwitch() {
<n-popover style="max-width: 310px">
<template #trigger>
<span v-show="!isPlayableAudio" class="text-[12px]"> {{ t('common_module.unplayable') }} </span>
<span v-show="!isPlayableAudio"> {{ t('common_module.unplayable') }} </span>
</template>
{{ t('common_module.unplayable_tip') }}
</n-popover>
......@@ -194,3 +175,77 @@ function handleShowReasoningContentSwitch() {
</div>
</div>
</template>
<style lang="scss" scoped>
.message-item-wrapper {
@apply mb-[15px] flex flex-row-reverse last:mb-0;
.message-item-container {
@apply flex w-full flex-col overflow-x-auto;
.deep-seek-title-container {
@apply my-[7px] select-none text-[12px];
.deep-seek-name-container {
@apply inline-flex cursor-pointer;
.deep-seek-text {
@apply mr-[6px];
}
}
}
.deep-seek-content-container {
@apply my-[10px] border-l-[1px] border-solid border-l-[#ccc] px-[13px] py-[8px];
}
.model-content-wrapper {
@apply flex min-w-[80px] max-w-full flex-col;
.model-content-container {
@apply w-full flex-wrap rounded-[10px] border px-[12px] py-[11px];
.upload-image {
@apply max-h-[120px]! mb-[10px] rounded-[10px] object-contain;
}
.plugin-container {
@apply mb-[8px] flex items-center gap-[5px] font-['Microsoft_YaHei_UI'] text-[#999];
.plugin-loading {
@apply bg-px-plugin_loading-gif h-[14px] w-[14px] bg-contain bg-center bg-no-repeat;
}
.plugin-name {
@apply text-[12px] leading-4;
}
}
.content-loading {
@apply py-[6px] pl-[16px];
}
.answer-response-loading {
@apply mb-[5px] mt-[16px] px-[16px];
}
.audio-control-container {
@apply mt-[11px] flex items-center gap-[8px] text-[12px];
.audio-control-icon {
@apply h-[16px] w-[16px] cursor-pointer;
}
}
}
.user-model-content-container {
@apply rounded-tr-none border-[#DCDEFF] bg-[#DCDEFF] text-white;
}
.assistant-model-content-container {
@apply rounded-tl-none border-[#e8e9eb] bg-white text-[#333];
}
}
}
}
</style>
......@@ -60,7 +60,7 @@ function handleScrollToBottom() {
</script>
<template>
<main ref="scrollRef" class="h-full overflow-y-auto overflow-x-hidden px-[20px]" @scroll="throttleScrollContainer">
<main ref="scrollRef" class="message-list-container" @scroll="throttleScrollContainer">
<div>
<MessageItem
v-for="[key, messageItem] in messageList"
......@@ -73,7 +73,7 @@ function handleScrollToBottom() {
/>
</div>
<p v-show="isAnswerResponseLoading" class="my-[7px] ml-[4px] text-[12px] text-[#84868c]">
<p v-show="isAnswerResponseLoading" class="answer-response-loading-text">
{{ t('common_module.dialogue_module.do_not_exit_page') }}
</p>
......@@ -81,12 +81,26 @@ function handleScrollToBottom() {
<ContinueQuestion v-model:continuous-question-list="continuousQuestionList" :type="'continuous'" />
</div>
<div
v-show="visible"
class="flex-center hover:text-theme-color absolute bottom-[20px] right-[20px] h-[24px] w-[24px] cursor-pointer rounded-full bg-white shadow-[0_0_0_1px_#ededed]"
@click.stop="clickBackBottom"
>
<i class="iconfont icon-left rotate-270 text-[14px]" />
<div v-show="visible" class="back-bottom-btn" @click.stop="clickBackBottom">
<i class="iconfont icon-left back-bottom-btn-icon" />
</div>
</main>
</template>
<style lang="scss" scoped>
.message-list-container {
@apply h-full overflow-y-auto overflow-x-hidden px-[20px];
.answer-response-loading-text {
@apply my-[7px] ml-[4px] text-[12px] text-[#84868c];
}
.back-bottom-btn {
@apply flex-center hover:text-theme-color absolute bottom-[20px] right-[20px] h-[24px] w-[24px] cursor-pointer rounded-full bg-white shadow-[0_0_0_1px_#ededed];
&-icon {
@apply rotate-270 text-[14px];
}
}
}
</style>
<script setup lang="ts">
interface Props {
barBgColor?: string
height?: string
}
withDefaults(defineProps<Props>(), {
barBgColor: '#363636',
height: '12px',
})
</script>
<template>
<div class="music">
<div class="bar" />
<div class="bar" />
<div class="bar" />
<div class="bar" />
<div class="bar" />
<div class="bar" />
<div class="bar" />
<div class="bar" />
<div class="bar" />
<div class="bar" />
</div>
</template>
<style lang="scss" scoped>
.music {
display: flex;
align-items: center;
justify-content: space-between;
width: 42px;
height: v-bind(height);
.bar {
width: 2px;
/* stylelint-disable-next-line value-keyword-case */
background: v-bind(barBgColor);
border-radius: 50px;
animation: loader 1.5s ease-in-out infinite;
&:nth-child(1) {
/* background: purple; */
animation-delay: 1s;
}
&:nth-child(2) {
/* background: crimson; */
animation-delay: 0.8s;
}
&:nth-child(3) {
/* background: deeppink; */
animation-delay: 0.6s;
}
&:nth-child(4) {
/* background: orange; */
animation-delay: 0.4s;
}
&:nth-child(5) {
/* background: gold; */
animation-delay: 0.2s;
}
&:nth-child(6) {
/* background: gold; */
animation-delay: 0.2s;
}
&:nth-child(7) {
/* background: gold; */
animation-delay: 0.4s;
}
&:nth-child(8) {
/* background: deeppink; */
animation-delay: 0.6s;
}
&:nth-child(9) {
/* background: crimson; */
animation-delay: 0.8s;
}
&:nth-child(10) {
/* background: purple; */
animation-delay: 1s;
}
}
}
@keyframes loader {
0% {
height: 4px;
}
50% {
height: v-bind(height);
}
100% {
height: 4px;
}
}
</style>
......@@ -54,10 +54,10 @@ function handleToLogout() {
</script>
<template>
<header class="flex h-[58px] w-full items-center justify-between px-[20px]">
<header class="page-header-wrapper">
<div>
<div v-show="isEnableVoice" class="flex items-center gap-[8px]">
<span class="text-[12px]">{{ t('common_module.voice_auto_play') }}</span>
<div v-show="isEnableVoice" class="enable-voice-container">
<span class="auto-play">{{ t('common_module.voice_auto_play') }}</span>
<n-switch
v-model:value="answerAudioAutoPlay"
size="small"
......@@ -67,34 +67,63 @@ function handleToLogout() {
<template #checked> {{ t('common_module.open') }} </template>
<template #unchecked> {{ t('common_module.close') }} </template>
<template #checked-icon>
<div class="bg-theme-color h-full w-full rounded-full"></div>
<div class="checked-icon"></div>
</template>
</n-switch>
</div>
</div>
<div class="flex-center gap-[10px]">
<div class="page-header-right-container">
<NButton
v-show="isLogin"
type="primary"
color="#EBECFF"
class="rounded-md! h-[28px]! text-[12px]! min-w-[80px]! text-theme-color!"
class="create-agent-btn"
@click="handleToCreateApplication"
>
{{ t('common_module.create_agent_btn_text') }}
</NButton>
<NButton
v-show="!isLogin"
color="#EBECFF"
class="rounded-md! h-[28px]! text-[12px]! min-w-[80px]! text-theme-color!"
@click="handleToLogin"
>
<NButton v-show="!isLogin" color="#EBECFF" class="login-btn" @click="handleToLogin">
{{ t('common_module.login_now') }}
</NButton>
<div v-show="isLogin" class="flex-center h-[28px] w-[28px] rounded-[5px] bg-[#EBECFF]">
<div v-show="isLogin" class="logout-btn">
<Logout theme="outline" size="15" fill="#000DFF" :stroke-width="4" @click="handleToLogout" />
</div>
</div>
</header>
</template>
<style lang="scss" scoped>
.page-header-wrapper {
@apply flex h-[58px] w-full items-center justify-between px-[20px];
.enable-voice-container {
@apply flex items-center gap-[8px];
.auto-play {
@apply text-[12px];
}
.checked-icon {
@apply bg-theme-color h-full w-full rounded-full;
}
}
.page-header-right-container {
@apply flex-center gap-[10px];
.create-agent-btn {
@apply rounded-md! h-[28px]! text-[12px]! min-w-[80px]! text-theme-color!;
}
.login-btn {
@apply rounded-md! h-[28px]! text-[12px]! min-w-[80px]! text-theme-color!;
}
.logout-btn {
@apply flex-center h-[28px] w-[28px] rounded-[5px] bg-[#EBECFF];
}
}
}
</style>
......@@ -20,21 +20,18 @@ const agentAvatar = computed(() => {
</script>
<template>
<div class="flex w-full flex-1 flex-col px-[20px]">
<div class="mb-[7.5px] mt-[25px] flex w-full justify-center">
<img :src="agentAvatar" class="rounded-theme h-[40px] w-[40px] object-cover" />
<div class="preamble-wrapper">
<div class="avatar-container">
<img :src="agentAvatar" class="agent-avatar" />
</div>
<div class="flex flex-col items-center justify-center">
<p class="font-family-medium mb-[17px] line-clamp-1 text-[13px] text-[#333]">
<div class="preamble-container">
<p class="agent-title">
{{ agentApplicationConfig.baseInfo.agentTitle }}
</p>
<div class="flex w-full flex-col items-start justify-center">
<p
v-show="agentApplicationConfig.commConfig.preamble"
class="mb-0 select-none break-all rounded-[10px] rounded-tl-none bg-[#DCDEFF] px-[12.5px] py-[11px] text-[12px]"
>
<p v-show="agentApplicationConfig.commConfig.preamble" class="preamble">
{{ agentApplicationConfig.commConfig.preamble }}
</p>
......@@ -46,3 +43,29 @@ const agentAvatar = computed(() => {
</div>
</div>
</template>
<style lang="scss" scoped>
.preamble-wrapper {
@apply flex w-full flex-col px-[20px];
.avatar-container {
@apply mb-[7.5px] mt-[25px] flex w-full justify-center;
.agent-avatar {
@apply rounded-theme h-[40px] w-[40px] object-cover;
}
}
.preamble-container {
@apply flex flex-col items-center justify-center;
.agent-title {
@apply font-family-medium mb-[17px] line-clamp-1 text-[13px] text-[#333];
}
.preamble {
@apply select-none break-all rounded-[10px] rounded-tl-none bg-[#DCDEFF] px-[12.5px] py-[11px] text-[12px];
}
}
}
</style>
......@@ -379,7 +379,7 @@ function handleToLogoutPage() {
</script>
<template>
<div v-loading="fullScreenLoading" class="bg-px-share-h5_bg-png h-full w-full bg-cover bg-no-repeat">
<div v-loading="fullScreenLoading" class="share-mobile-container">
<PageHeader
v-model:is-enable-voice="isEnableVoice"
v-model:answer-audio-auto-play="answerAudioAutoPlay"
......@@ -390,7 +390,7 @@ function handleToLogoutPage() {
@update-auto-playing="handleUpdateAutoPlaying"
/>
<div class="flex h-[calc(100%-58px)] w-full flex-col">
<div class="main-container">
<div v-if="messageList.size === 0" class="w-full flex-1 overflow-auto">
<Preamble :agent-application-config="agentApplicationConfig" />
</div>
......@@ -406,14 +406,13 @@ function handleToLogoutPage() {
:is-answer-response-loading="isAnswerResponseLoading"
:create-continue-questions-exception="createContinueQuestionsException"
:is-answer-response-interrupt="isAnswerResponseInterrupt"
class="pt-[20px]"
@audio-play="handleAudioPlay"
@audio-pause="handleAudioPause"
/>
</div>
</div>
<div class="footer-operation bg-white px-[16px]">
<div class="footer-container">
<FooterInput
ref="footerInputRef"
v-model:is-answer-response-loading="isAnswerResponseLoading"
......@@ -445,9 +444,19 @@ function handleToLogoutPage() {
</template>
<style lang="scss" scoped>
.footer-operation {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.share-mobile-container {
@apply bg-px-share-h5_bg-png h-full w-full bg-cover bg-no-repeat;
.main-container {
@apply flex h-[calc(100%-58px)] w-full flex-col;
}
.footer-container {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
@apply bg-white px-[10px] py-[7px];
}
}
@include custom-scrollbar(4px);
......
import { defineConfig } from 'unocss'
import { defineConfig, transformerDirectives, presetUno } from 'unocss'
export default defineConfig({
rules: [
......@@ -87,4 +87,6 @@ export default defineConfig({
shortcuts: {
'flex-center': 'flex items-center justify-center',
},
presets: [presetUno()],
transformers: [transformerDirectives()],
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment