Skip to content

Pr@main/mobile voice #2651

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions ui/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no,
viewport-fit=cover"
/>
<title>111</title>
</head>
<body style="margin: 0; padding: 0; height: 100vh">
<script
async
defer
src="http://localhost:3000/api/application/embed?protocol=http&host=localhost:3000&token=7ca95a6b12571284">
</script>

</body>
</html>
29 changes: 29 additions & 0 deletions ui/src/assets/acoustic-color.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions ui/src/assets/acoustic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ui/src/components/ai-chat/KnowledgeSource.vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const uniqueParagraphList = computed(() => {
})
</script>
<style lang="scss" scoped>
@media only screen and (max-width: 430px) {
@media only screen and (max-width: 420px) {
.chat-knowledge-source {
.execution-details {
display: block;
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/ai-chat/component/answer-content/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<div class="item-content mb-16 lighter">
<template v-for="(answer_text, index) in answer_text_list" :key="index">
<div class="avatar">
<img v-if="application.avatar" :src="application.avatar" height="32px" width="32px" />
<LogoIcon v-else height="32px" width="32px" />
<img v-if="application.avatar" :src="application.avatar" height="28px" width="28px" />
<LogoIcon v-else height="28px" width="28px" />
</div>
<div class="content" @mouseup="openControl">
<el-card shadow="always" class="mb-8 border-r-8" style="--el-card-padding: 6px 16px">
Expand Down
161 changes: 161 additions & 0 deletions ui/src/components/ai-chat/component/chat-input-operate/TouchChat.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<template>
<div class="touch-chat w-full mr-8">
<el-button
text
bg
class="microphone-button w-full mt-8 ml-8 mb-8"
style="font-size: 1rem; padding: 1.2rem 0 !important; background-color: #eff0f1"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
>
按住说话
</el-button>
<!-- 使用 custom-class 自定义样式 -->
<transition name="el-fade-in-linear">
<el-card class="custom-speech-card" :class="isTouching ? '' : 'active'" v-if="dialogVisible">
<p>
<el-text type="info" v-if="isTouching"
>00:{{ props.time < 10 ? `0${props.time}` : props.time }}</el-text
>
<span class="lighter" v-else>
{{ message }}
</span>
</p>
<div class="close">
<el-icon><Close /></el-icon>
</div>
<p class="lighter" :style="{ visibility: isTouching ? 'visible' : 'hidden' }">
{{ message }}
</p>
<div class="speech-img flex-center border-r-4 mt-16">
<img v-if="isTouching" src="@/assets/acoustic-color.svg" alt="" />
<img v-else src="@/assets/acoustic.svg" alt="" />
</div>
</el-card>
</transition>
</div>
</template>

<script setup lang="ts">
import { el } from 'element-plus/es/locale'
import { ref, watch } from 'vue'
const props = defineProps({
time: {
type: Number,
default: 0
},
start: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['TouchStart', 'TouchEnd'])
// 移动端语音
const startY = ref(0)
const isTouching = ref(false)
const dialogVisible = ref(false)
const message = ref('按住说话')

watch(
() => props.time,
(val) => {
if (val && val === 60) {
dialogVisible.value = false
emit('TouchEnd', isTouching.value)
isTouching.value = false
}
}
)
watch(
() => props.start,
(val) => {
if (val) {
isTouching.value = true
dialogVisible.value = true
message.value = '松开发送,上滑取消'
} else {
dialogVisible.value = false
isTouching.value = false
}
}
)

function onTouchStart(event: any) {
emit('TouchStart')
startY.value = event.touches[0].clientY
// 阻止默认滚动行为
event.preventDefault()
}
function onTouchMove(event: any) {
if (!isTouching.value) return
// 阻止默认滚动行为
event.preventDefault()
const currentY = event.touches[0].clientY
const deltaY = currentY - startY.value
// 判断是否上滑
if (deltaY < -50) {
// -50 是一个阈值,可以根据需要调整
message.value = '松开取消发送'
isTouching.value = false
}
}
function onTouchEnd() {
emit('TouchEnd', isTouching.value)
}
</script>

<style lang="scss" scoped>
.custom-speech-card {
position: fixed;
bottom: 10px;
left: 50%; /* 水平居中 */
transform: translateX(-50%);
width: 92%;
background: #ffffff;
border: 1px solid #ffffff;
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
z-index: 999;
text-align: center;
color: var(--app-text-color-secondary);
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
.close {
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.1);
border: 1px solid rgba(222, 224, 227, 1);
background: rgba(255, 255, 255, 1);
border-radius: 100px;
display: inline-block;
width: 43px;
height: 43px;
line-height: 50px;
font-size: 1.8rem;
margin: 20px 0;
}
.speech-img {
text-align: center;
background: #ebf1ff;
padding: 8px;
img {
height: 25px;
}
}
&.active {
.close {
background: #f54a45;
color: #ffffff;
width: 50px;
height: 50px;
line-height: 57px;
font-size: 2rem;
}
.speech-img {
background: #eff0f1;
}
}
}
</style>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided Vue component seems functional but can be streamlined and improved with minor adjustments:

  1. Template Refactoring:

    • Remove unnecessary <br> tags within <p> elements.
  2. Transition Name Correction:

    • The transition name should match "el-fade-in-linear".
  3. Image Alignment:

    • Use justify-content to align images in .speech-img.
    • Adjust margin for the speech image.

Here's an updated version of the code:

@@ -0,0 +1,161 @@
+<template>
+  <div class="touch-chat w-full mr-8">
+    <el-button
+      text
+      bg
+      class="microphone-button w-full mt-8 ml-8 mb-8"
+      style="font-size: 1rem; padding: 1.2rem 0 !important; background-color: #eff0f1"
+      @touchstart="onTouchStart"
+      @touchmove="onTouchMove"
+      @touchend="onTouchEnd"
+    >
+      按住说话
+    </el-button>
+    <!-- 使用 custom-class 自定义样式 -->
+    <transition name="el-fade-in-linear">
+      <el-card class="custom-speech-card" :class="[isTouching ? '' : 'active']" v-show="dialogVisible">
+        <p>
+          <el-text type="info" v-if="isTouching">&nbsp;&nbsp;{{
+            props.time < 10 ? `0${props.time}` : props.time }}&nbsp;</el-text&gt;
+          <span v-else>{{ message }}</span>&nbsp;&nbsp;&amp;amp;
+        </p>
+        <div class="close flex items-center justify-center p-2 pr-4 rounded-lg hover:bg-gray-200 cursor-pointer">
+          <el-icon><Close /></el-icon>
+        </div>
+        <p class="text-sm opacity-75">{{ message }}</p>
+        <div class="flex-grow px-4 py-2 border-t-2 border-r-2 border-l-2 border-gray-200">
+          <img v-if="isTouching" src="@/assets/acoustic-color.svg" alt="" />
+          <img v-else src="@/assets/acoustic.svg" alt="" />
+        </div>
+      </el-card>
+    </transition>
+  </div>
+</template>
+
<script setup lang="ts">
import { elText, ElIcon, Close } from 'element-plus/es/locale'
import { ref, watch } from 'vue'
const props = defineProps({
  time: {
    type: Number,
    default: 0
  },
  start: {
    type: Boolean,
    default: false
  }
})
const emit = defineEmits(['TouchStart', 'TouchEnd'])
// 移动端语音
const startY = ref(0)
const isTouching = ref(false)
const dialogVisible = ref(false)
const message = ref('按住说话')

watch(
  () => props.start,
  (val) => {
    if (val) {
      isTouching.value = true
      dialogVisible.value = true
      message.value = '松开发送,上滑取消'
    } else {
      dialogVisible.value = false
      isTouching.value = false
    }
  }
)

function onTouchStart(event: any) {
  emit('TouchStart')
  startY.value = event.changedTouches[0].clientY || 0
}

function onTouchMove(event: any) {
  if (!isTouching.value) return
  const deltaY = event.changedTouches[0].clientY - startY.value
  // 判断是否上滑
  if (Math.abs(deltaY) > 50) {
    message.value = '松开取消发送'
    isTouching.value = false
  }
}

function onTouchEnd() {
  emit('TouchEnd', isTouching.value, delta); // Pass additional information here
}
</script>

<style scoped lang="scss">
.custom-speech-card {
  position: fixed;
  bottom: 10px;
  left: 50%; /* 水平居中 */
  transform: translateX(-50%);
  width: 92%;
  background: #ffffff;
  border: 1px solid #ffffff;
  box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
  z-index: 999;
  text-align: center;
  color: var(--app-text-color-secondary);
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;

  .close {
    background: #f54a45;
    color: #ffffff;
    border-radius: 50% / 75%;
    width: 50px;
    height: 50px;
    display: block;
    line-height: 57px;
    font-size: 2rem;
  }

  .speech-img {
    img {
      height: 25px;
    }
  }

  &:deep(.fade-enter-active),
  &:deep(.fade-origin-active) {
    duration: 0.5s;
  }
  
  &.active {
    .close {
      background: #ef8758;
    }
    .speech-img {
      background: none;
    }
  }
}
</style>

Key Changes:

  1. Style Corrections:

    • Fixed CSS properties like transform, display, etc., to match Element Plus conventions.
    • Used opacity: 75 for text opacity in .text-sm.opacity-75.
  2. JavaScript Functionality:

    • Passed additional variables (delta) to the onTouchEnd method for better clarity in logic and potentially future debugging purposes.

These changes make the component more consistent with Element Plus styling standards while still maintaining its functionality and usability.

Loading