fix(mobile): highlight new-chat button and flex-center composer labels

- AskSourceHeader: solid brand-red new-chat button, flex-centered label
- replace padding+line-height centering with flex on chips, hold-talk,
  send, bubble-action, transcript-toggle, system-text, scope-note
- carries the in-flight AskSourceHeader extraction on sop/web-ai pages
  (parallel session work; its test updates intentionally left uncommitted)
This commit is contained in:
2026-07-19 23:21:24 +08:00
parent 61bc7191d1
commit 46a6156d8c
4 changed files with 210 additions and 222 deletions
@@ -0,0 +1,156 @@
<template>
<view class="ask-source-head">
<view class="head-row">
<text class="head-title">问 · 数字师傅</text>
<view class="head-actions">
<text class="head-context">{{ context }}</text>
<button class="new-chat-button" :disabled="loading" @click="emit('newConversation')">新对话</button>
</view>
</view>
<view class="source-switch" aria-label="问答来源">
<button
class="source-option"
:class="{ active: active === 'assistant' }"
:aria-current="active === 'assistant' ? 'page' : undefined"
@click="selectAssistant"
>
工作助手
</button>
<button
class="source-option"
:class="{ active: active === 'web' }"
:aria-current="active === 'web' ? 'page' : undefined"
@click="selectWeb"
>
查全网
</button>
</view>
</view>
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
active: 'assistant' | 'web';
context: string;
loading?: boolean;
}>(), {
loading: false
});
const emit = defineEmits<{
selectAssistant: [];
selectWeb: [];
newConversation: [];
}>();
const selectAssistant = () => {
if (props.active !== 'assistant') emit('selectAssistant');
};
const selectWeb = () => {
if (props.active !== 'web') emit('selectWeb');
};
</script>
<style scoped>
.ask-source-head {
flex-shrink: 0;
padding: 8px 12px 6px;
border-bottom: 1px solid #ebedf0;
background: #ffffff;
}
.head-row {
display: flex;
min-height: 44px;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-bottom: 6px;
}
.head-title {
flex-shrink: 0;
color: #18202b;
font-size: 16px;
font-weight: 800;
}
.head-actions {
display: flex;
min-width: 0;
align-items: center;
justify-content: flex-end;
gap: 6px;
}
.head-context {
min-width: 0;
overflow: hidden;
color: #7b8492;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
.new-chat-button {
flex-shrink: 0;
display: flex;
min-height: 44px;
align-items: center;
justify-content: center;
margin: 0;
padding: 0 14px;
border: 1px solid var(--employee-primary, #e60012);
border-radius: 999px;
background: var(--employee-primary, #e60012);
color: #ffffff;
font-size: 12px;
font-weight: 600;
line-height: 1;
}
.new-chat-button[disabled] {
opacity: 0.6;
}
.new-chat-button::after,
.source-option::after {
border: none;
}
.source-switch {
display: flex;
gap: 0;
margin: 0;
padding: 3px;
border-radius: 999px;
background: #f2f3f5;
}
.source-option {
display: flex;
flex: 1;
box-sizing: border-box;
height: 44px;
min-height: 44px;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
border: none;
border-radius: 999px;
background: transparent;
color: #5a6270;
font-size: 13px;
font-weight: 500;
line-height: 1.2;
}
.source-option.active {
background: #ffffff;
color: #c8001a;
font-weight: 700;
box-shadow: 0 1px 4px rgba(20, 24, 31, 0.08);
}
</style>
@@ -271,14 +271,18 @@ defineExpose({ teardown, setDraft });
.chip {
flex-shrink: 0;
display: flex;
min-height: 30px;
align-items: center;
justify-content: center;
margin: 0;
padding: 5px 12px;
padding: 0 13px;
border: 1px solid #f0c0c5;
border-radius: 999px;
background: #ffffff;
color: #c8001a;
font-size: 13px;
line-height: 1.4;
line-height: 1;
}
.media-chip {
@@ -359,7 +363,6 @@ defineExpose({ teardown, setDraft });
color: #232a33;
font-size: 16px;
font-weight: 600;
line-height: 1.2;
user-select: none;
-webkit-user-select: none;
}
@@ -397,7 +400,6 @@ defineExpose({ teardown, setDraft });
color: #ffffff;
font-size: 15px;
font-weight: 600;
line-height: 1.2;
}
.composer-panel {
+31 -132
View File
@@ -1,18 +1,12 @@
<template>
<view class="page sop-chat-page">
<view class="chat-head">
<view class="chat-head-row">
<text class="chat-title">问 · 数字师傅</text>
<view class="chat-head-actions">
<text class="chat-context">{{ positionLabel }} · 住宅项目</text>
<button class="new-chat-button" :disabled="loading" @click="startNewConversation">新对话</button>
</view>
</view>
<view class="source-switch chat-source-switch" aria-label="问答来源">
<button class="source-option active" aria-current="page">工作助手</button>
<button class="source-option" @click="openWebAi">查全网</button>
</view>
</view>
<AskSourceHeader
active="assistant"
:context="`${positionLabel} · 住宅项目`"
:loading="loading || voiceTranscribing"
@select-web="openWebAi"
@new-conversation="startNewConversation"
/>
<scroll-view scroll-y class="chat-thread" :scroll-top="threadScrollTop" :scroll-with-animation="true">
<view class="thread-inner">
@@ -300,6 +294,7 @@ import type {
SummaryCardResponse
} from '@/types/api';
import ChatComposer from '@/components/chat/ChatComposer.vue';
import AskSourceHeader from '@/components/chat/AskSourceHeader.vue';
import MemoryCandidateCard from '@/components/chat/MemoryCandidateCard.vue';
import {
chooseKnowledgeMedia,
@@ -1023,26 +1018,6 @@ onUnload(() => {
content: '';
}
.source-switch {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4px;
margin-top: 16px;
padding: 4px;
}
.source-option {
min-height: 44px;
background: transparent;
color: var(--employee-muted);
font-weight: 800;
}
.source-option.active {
background: var(--employee-primary-soft);
color: var(--employee-primary);
}
.sop-eyebrow {
display: inline-flex;
margin-bottom: 12px;
@@ -2059,79 +2034,6 @@ onUnload(() => {
overflow: hidden;
}
.chat-head {
flex-shrink: 0;
padding: 10px 14px 8px;
background: #ffffff;
border-bottom: 1px solid #ebedf0;
}
.chat-head-row {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 8px;
}
.chat-title {
color: #18202b;
font-size: 18px;
font-weight: 800;
}
.chat-context {
color: #8a93a0;
font-size: 12px;
}
.chat-head-actions {
display: flex;
align-items: center;
gap: 8px;
}
.new-chat-button {
margin: 0;
padding: 3px 9px;
border: 1px solid #e3e6ea;
border-radius: 999px;
background: #ffffff;
color: #5a6270;
font-size: 12px;
line-height: 1.5;
}
.chat-source-switch {
display: flex;
padding: 3px;
border-radius: 999px;
background: #f2f3f5;
}
.chat-source-switch .source-option {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
box-sizing: border-box;
height: 32px;
margin: 0;
padding: 0;
border: none;
border-radius: 999px;
background: transparent;
color: #5a6270;
font-size: 14px;
line-height: 1.2;
}
.chat-source-switch .source-option.active {
background: #ffffff;
color: #c8001a;
font-weight: 700;
box-shadow: 0 1px 4px rgba(20, 24, 31, 0.08);
}
.chat-thread {
flex: 1;
min-height: 0;
@@ -2263,14 +2165,18 @@ onUnload(() => {
}
.bubble-action {
display: flex;
min-height: 28px;
align-items: center;
justify-content: center;
margin: 0;
padding: 4px 12px;
padding: 0 12px;
border: 1px solid #e3e6ea;
border-radius: 999px;
background: #f7f8fa;
color: #38424e;
font-size: 12px;
line-height: 1.5;
line-height: 1;
}
.bubble-action.active {
@@ -2429,14 +2335,18 @@ onUnload(() => {
}
.system-text {
display: flex;
max-width: 86%;
padding: 4px 12px;
min-height: 26px;
align-items: center;
justify-content: center;
padding: 0 12px;
border-radius: 999px;
background: rgba(20, 24, 31, 0.06);
color: #6b7480;
font-size: 12px;
text-align: center;
line-height: 1.5;
line-height: 1.3;
}
.pending-bubble .bubble-text {
@@ -2504,32 +2414,18 @@ onUnload(() => {
.transcript-toggle {
flex-shrink: 0;
display: flex;
min-height: 28px;
align-items: center;
justify-content: center;
margin: 0;
padding: 4px 12px;
padding: 0 12px;
border: none;
border-radius: 8px;
background: rgba(20, 24, 31, 0.06);
color: #6b7480;
font-size: 12px;
line-height: 1.5;
}
/* compact header */
.chat-head {
padding: 8px 12px 6px;
}
.chat-head-row {
margin-bottom: 6px;
}
.chat-title {
font-size: 16px;
}
.chat-source-switch .source-option {
height: 30px;
font-size: 13px;
line-height: 1;
}
.transcript-collapse {
@@ -2546,12 +2442,15 @@ onUnload(() => {
.scope-note {
flex-shrink: 0;
padding: 6px 14px;
display: flex;
min-height: 30px;
align-items: center;
padding: 0 14px;
border-top: 1px solid #eceef1;
background: #ffffff;
color: #8a93a0;
font-size: 12px;
line-height: 1.4;
line-height: 1.3;
}
.master-col :deep(.memory-card) {
+17 -86
View File
@@ -1,18 +1,12 @@
<template>
<view class="page webai-chat">
<view class="chat-head">
<view class="chat-head-row">
<view class="chat-head-left">
<button class="chat-back" aria-label="返回工作助手" @click="goBack"><uni-icons type="back" size="20" color="#25282d" /></button>
<text class="chat-title">查全网</text>
</view>
<text class="chat-context">互联网信息 · 独立来源</text>
</view>
<view class="source-switch chat-source-switch" aria-label="问答来源">
<button class="source-option" @click="goBack">工作助手</button>
<button class="source-option active" aria-current="page">查全网</button>
</view>
</view>
<AskSourceHeader
active="web"
context="互联网信息 · 独立来源"
:loading="loading || voiceTranscribing"
@select-assistant="openWorkAssistant"
@new-conversation="startNewConversation"
/>
<scroll-view scroll-y class="chat-thread" :scroll-top="threadScrollTop" :scroll-with-animation="true">
<view class="thread-inner">
@@ -122,6 +116,7 @@
import { nextTick, ref } from 'vue';
import { onShow, onUnload } from '@dcloudio/uni-app';
import type { KnowledgeMediaSelectedFile, SpeechPlaybackStatus, WebAiCapabilities, WebAiResponse } from '@/types/api';
import AskSourceHeader from '@/components/chat/AskSourceHeader.vue';
import ChatComposer from '@/components/chat/ChatComposer.vue';
import { isLoggedIn, rememberLoginRedirect } from '@/services/auth';
import { chooseKnowledgeMedia } from '@/services/knowledge';
@@ -203,7 +198,15 @@ const replaceMsg = (id: number, next: ChatMsg | null) => {
const pushSystem = (text: string) => pushMsg<SystemMsg>({ role: 'system', text });
const goBack = () => uni.switchTab({ url: '/pages/user/sop/index' });
const openWorkAssistant = () => uni.switchTab({ url: '/pages/user/sop/index' });
const startNewConversation = () => {
speechPlayback.stop();
messages.value = [];
msgSeq = 0;
threadScrollTop.value = 0;
pushSystem('已开始新对话');
};
const goLogin = () => {
rememberLoginRedirect(path);
@@ -415,78 +418,6 @@ onUnload(() => {
padding-bottom: calc(10px + env(safe-area-inset-bottom));
}
.chat-head {
flex-shrink: 0;
padding: 8px 12px 6px;
background: #ffffff;
border-bottom: 1px solid #ebedf0;
}
.chat-head-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.chat-head-left {
display: flex;
align-items: center;
gap: 8px;
}
.chat-back {
display: flex;
width: 32px;
height: 32px;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
border: 1px solid #e3e6ea;
border-radius: 10px;
background: #ffffff;
color: #232a33;
line-height: 1;
}
.chat-title {
color: #18202b;
font-size: 16px;
font-weight: 800;
}
.chat-context {
color: #8a93a0;
font-size: 12px;
}
.chat-source-switch {
display: flex;
padding: 3px;
border-radius: 999px;
background: #f2f3f5;
}
.chat-source-switch .source-option {
flex: 1;
margin: 0;
padding: 5px 0;
border: none;
border-radius: 999px;
background: transparent;
color: #5a6270;
font-size: 13px;
line-height: 1.3;
}
.chat-source-switch .source-option.active {
background: #ffffff;
color: #c8001a;
font-weight: 700;
box-shadow: 0 1px 4px rgba(20, 24, 31, 0.08);
}
.chat-thread {
flex: 1;
min-height: 0;