aI问答支持流式输出,效果非常好

This commit is contained in:
zhonghua.li
2026-03-31 21:04:53 +08:00
parent 60da0e2dd5
commit 22f951b514
163 changed files with 4646 additions and 8010 deletions

View File

@@ -171,18 +171,18 @@ if (!Math) {
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return {
a: common_vendor.t($data.customerListTotal || $options.list.length),
b: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args)),
c: common_vendor.o([($event) => $data.salesName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)]),
b: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args), "ba"),
c: common_vendor.o([($event) => $data.salesName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)], "4b"),
d: $data.salesName,
e: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args)),
f: common_vendor.o([($event) => $data.customerName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)]),
e: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args), "f3"),
f: common_vendor.o([($event) => $data.customerName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)], "cc"),
g: $data.customerName,
h: common_vendor.p({
type: "refresh",
size: "18",
color: "#2A68FF"
}),
i: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args)),
i: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args), "16"),
j: common_vendor.f($options.list, (item, index, i0) => {
return common_vendor.e({
a: common_vendor.t(item.name),

View File

@@ -43,7 +43,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
size: "24",
color: "#333"
}),
b: common_vendor.o((...args) => $options.close && $options.close(...args)),
b: common_vendor.o((...args) => $options.close && $options.close(...args), "da"),
c: common_vendor.t($props.profile.name),
d: common_vendor.t($props.profile.intent),
e: common_vendor.n($props.profile.intent === "高" ? "intent-high" : "intent-medium"),
@@ -60,7 +60,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
size: "14",
color: "#666"
}),
o: common_vendor.o((...args) => $options.toggleCollapse && $options.toggleCollapse(...args)),
o: common_vendor.o((...args) => $options.toggleCollapse && $options.toggleCollapse(...args), "71"),
p: !$data.collapsed
}, !$data.collapsed ? {
q: common_vendor.t($props.profile.followStage),

View File

@@ -22,6 +22,49 @@ const _sfc_main = {
};
},
methods: {
durationPresent(val) {
if (val === null || val === void 0 || val === "")
return false;
const n = Number(val);
return !Number.isNaN(n) && n >= 0;
},
/** 后端 duration 为分钟(可小数),页面原样按分钟展示,最多保留两位小数 */
formatRecordingMinutes(minutes) {
const n = Number(minutes);
if (Number.isNaN(n) || n < 0)
return "0";
return String(parseFloat(n.toFixed(2)));
},
formatFileExtension(ext) {
if (ext == null || ext === "")
return "";
const s = String(ext).trim();
if (!s)
return "";
return s.startsWith(".") ? s.slice(1) : s;
},
getRecordingFileExtension(item) {
const raw = item.rawData || {};
const fromField = this.formatFileExtension(item.audioFileExtension ?? raw.audioFileExtension);
if (fromField)
return fromField;
const name = raw.audioFileOriginalName;
if (name && typeof name === "string") {
const idx = name.lastIndexOf(".");
if (idx !== -1 && idx < name.length - 1) {
return name.slice(idx + 1).trim().toLowerCase();
}
}
return "";
},
getRecordingText(item) {
const raw = item.rawData || {};
const t = item.recordingText ?? raw.recordingText;
if (t == null || t === "")
return "";
const s = String(t).trim();
return s;
},
onInputChange() {
},
handleRefresh() {
@@ -89,7 +132,7 @@ const _sfc_main = {
tenantId = common_vendor.index.getStorageSync("backend-tenant-id") || "";
token = common_vendor.index.getStorageSync("backend-token") || "";
} catch (e) {
common_vendor.index.__f__("error", "at pages/furniture_customer/components/ServiceList.vue:196", "获取认证信息失败:", e);
common_vendor.index.__f__("error", "at pages/furniture_customer/components/ServiceList.vue:247", "获取认证信息失败:", e);
}
const headers = {
"Content-Type": "application/json"
@@ -116,10 +159,14 @@ const _sfc_main = {
duration: 2e3
});
if (res.data.data) {
const d = res.data.data;
const updatedItem = {
...item,
rawData: res.data.data,
description: res.data.data.summary || item.description
rawData: d,
description: d.summary || item.description,
duration: d.duration != null ? d.duration : item.duration,
audioFileExtension: d.audioFileExtension != null ? d.audioFileExtension : item.audioFileExtension,
recordingText: d.recordingText != null ? d.recordingText : item.recordingText
};
this.$emit("updateItem", {
recordId,
@@ -138,7 +185,7 @@ const _sfc_main = {
}
} catch (error) {
common_vendor.index.hideLoading();
common_vendor.index.__f__("error", "at pages/furniture_customer/components/ServiceList.vue:257", "AI分析失败:", error);
common_vendor.index.__f__("error", "at pages/furniture_customer/components/ServiceList.vue:311", "AI分析失败:", error);
common_vendor.index.showToast({
title: `AI分析失败: ${error.errMsg || error.message || "未知错误"}`,
icon: "none",
@@ -174,43 +221,55 @@ if (!Math) {
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return common_vendor.e({
a: common_vendor.t($props.total || $props.list.length),
b: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args)),
c: common_vendor.o([($event) => $data.salesName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)]),
b: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args), "ac"),
c: common_vendor.o([($event) => $data.salesName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)], "4b"),
d: $data.salesName,
e: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args)),
f: common_vendor.o([($event) => $data.customerName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)]),
e: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args), "9b"),
f: common_vendor.o([($event) => $data.customerName = $event.detail.value, (...args) => $options.onInputChange && $options.onInputChange(...args)], "cc"),
g: $data.customerName,
h: common_vendor.p({
type: "refresh",
size: "18",
color: "#2A68FF"
}),
i: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args)),
i: common_vendor.o((...args) => $options.handleRefresh && $options.handleRefresh(...args), "e5"),
j: common_vendor.f($props.list, (item, index, i0) => {
return common_vendor.e({
a: common_vendor.t(item.title),
b: "0284e0ab-1-" + i0,
c: common_vendor.o(($event) => $options.onMenuButtonClick(item), index),
d: common_vendor.t(item.date),
e: item.recordingName
}, item.recordingName ? {
f: common_vendor.t(item.recordingName)
e: $options.durationPresent(item.duration)
}, $options.durationPresent(item.duration) ? {
f: common_vendor.t($options.formatRecordingMinutes(item.duration))
} : {}, {
g: common_vendor.t(item.description),
h: common_vendor.t(item.customerName.charAt(0)),
i: common_vendor.t(item.customerName),
j: $data.selectedServiceItem && $data.selectedServiceItem.id === item.id && $data.showServiceActionMenu
g: $options.getRecordingFileExtension(item)
}, $options.getRecordingFileExtension(item) ? {
h: common_vendor.t($options.getRecordingFileExtension(item))
} : {}, {
i: item.recordingName
}, item.recordingName ? {
j: common_vendor.t(item.recordingName)
} : {}, {
k: $options.getRecordingText(item)
}, $options.getRecordingText(item) ? {
l: common_vendor.t($options.getRecordingText(item))
} : {}, {
m: common_vendor.t(item.description),
n: common_vendor.t(item.customerName.charAt(0)),
o: common_vendor.t(item.customerName),
p: $data.selectedServiceItem && $data.selectedServiceItem.id === item.id && $data.showServiceActionMenu
}, $data.selectedServiceItem && $data.selectedServiceItem.id === item.id && $data.showServiceActionMenu ? {
k: common_vendor.o(($event) => $options.handleDelete(item), index),
l: common_vendor.o(($event) => $options.handleView(item), index),
m: common_vendor.o(($event) => $options.handleAnalysis(item), index),
n: common_vendor.o(($event) => $options.handleAudio(item), index),
o: common_vendor.o(() => {
q: common_vendor.o(($event) => $options.handleDelete(item), index),
r: common_vendor.o(($event) => $options.handleView(item), index),
s: common_vendor.o(($event) => $options.handleAnalysis(item), index),
t: common_vendor.o(($event) => $options.handleAudio(item), index),
v: common_vendor.o(() => {
}, index)
} : {}, {
p: index,
q: $data.selectedServiceItem && $data.selectedServiceItem.id === item.id ? 1 : "",
r: common_vendor.o(($event) => $options.handleItemClick(item), index)
w: index,
x: $data.selectedServiceItem && $data.selectedServiceItem.id === item.id ? 1 : "",
y: common_vendor.o(($event) => $options.handleItemClick(item), index)
});
}),
k: common_vendor.p({
@@ -218,10 +277,10 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
size: "20",
color: "#999"
}),
l: common_vendor.o((...args) => $options.onReachBottom && $options.onReachBottom(...args)),
l: common_vendor.o((...args) => $options.onReachBottom && $options.onReachBottom(...args), "4a"),
m: $data.showServiceActionMenu
}, $data.showServiceActionMenu ? {
n: common_vendor.o((...args) => $options.closeServiceActionMenu && $options.closeServiceActionMenu(...args))
n: common_vendor.o((...args) => $options.closeServiceActionMenu && $options.closeServiceActionMenu(...args), "55")
} : {});
}
const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-0284e0ab"]]);

View File

@@ -1 +1 @@
<view class="service-list-container data-v-0284e0ab"><view class="service-status-toolbar data-v-0284e0ab"><text class="toolbar-total data-v-0284e0ab">共{{a}}条</text><input class="toolbar-input data-v-0284e0ab" placeholder="所属销售姓名" placeholder-style="color: #b0b0b0" confirm-type="search" bindconfirm="{{b}}" bindinput="{{c}}" value="{{d}}"/><input class="toolbar-input data-v-0284e0ab" placeholder="客户姓名" placeholder-style="color: #b0b0b0" confirm-type="search" bindconfirm="{{e}}" bindinput="{{f}}" value="{{g}}"/><view class="toolbar-actions data-v-0284e0ab"><view class="toolbar-btn toolbar-btn--refresh data-v-0284e0ab" bindtap="{{i}}"><uni-icons wx:if="{{h}}" class="data-v-0284e0ab" u-i="0284e0ab-0" bind:__l="__l" u-p="{{h}}"></uni-icons><text class="data-v-0284e0ab">刷新</text></view></view></view><scroll-view class="service-list data-v-0284e0ab" scroll-y bindscrolltolower="{{l}}"><view wx:for="{{j}}" wx:for-item="item" wx:key="p" class="{{['service-item', 'data-v-0284e0ab', item.q && 'service-item--active']}}" bindtap="{{item.r}}"><view class="service-header data-v-0284e0ab"><text class="service-title data-v-0284e0ab">{{item.a}}</text><view class="service-menu-button data-v-0284e0ab" catchtap="{{item.c}}"><uni-icons wx:if="{{k}}" class="data-v-0284e0ab" u-i="{{item.b}}" bind:__l="__l" u-p="{{k}}"></uni-icons></view></view><view class="service-meta data-v-0284e0ab"><text class="service-date data-v-0284e0ab">{{item.d}}</text></view><view wx:if="{{item.e}}" class="service-recording-name data-v-0284e0ab"><text class="recording-name-label data-v-0284e0ab">记录名称:</text><text class="recording-name-value data-v-0284e0ab">{{item.f}}</text></view><view class="service-content data-v-0284e0ab"><text class="service-desc data-v-0284e0ab">{{item.g}}</text></view><view class="service-customer data-v-0284e0ab"><view class="customer-avatar data-v-0284e0ab"><text class="avatar-text data-v-0284e0ab">{{item.h}}</text></view><text class="customer-name data-v-0284e0ab">{{item.i}}</text></view><view wx:if="{{item.j}}" class="service-action-menu data-v-0284e0ab" catchtap="{{item.o}}"><view class="service-action-menu-item service-action-menu-item--danger data-v-0284e0ab" bindtap="{{item.k}}"><text class="data-v-0284e0ab">删除</text></view><view class="service-action-menu-divider data-v-0284e0ab"></view><view class="service-action-menu-item data-v-0284e0ab" bindtap="{{item.l}}"><text class="data-v-0284e0ab">查看</text></view><view class="service-action-menu-divider data-v-0284e0ab"></view><view class="service-action-menu-item data-v-0284e0ab" bindtap="{{item.m}}"><text class="data-v-0284e0ab">AI分析</text></view><view class="service-action-menu-divider data-v-0284e0ab"></view><view class="service-action-menu-item data-v-0284e0ab" bindtap="{{item.n}}"><text class="data-v-0284e0ab">录音</text></view></view></view></scroll-view><view wx:if="{{m}}" class="service-action-menu-mask data-v-0284e0ab" bindtap="{{n}}"></view></view>
<view class="service-list-container data-v-0284e0ab"><view class="service-status-toolbar data-v-0284e0ab"><text class="toolbar-total data-v-0284e0ab">共{{a}}条</text><input class="toolbar-input data-v-0284e0ab" placeholder="所属销售姓名" placeholder-style="color: #b0b0b0" confirm-type="search" bindconfirm="{{b}}" bindinput="{{c}}" value="{{d}}"/><input class="toolbar-input data-v-0284e0ab" placeholder="客户姓名" placeholder-style="color: #b0b0b0" confirm-type="search" bindconfirm="{{e}}" bindinput="{{f}}" value="{{g}}"/><view class="toolbar-actions data-v-0284e0ab"><view class="toolbar-btn toolbar-btn--refresh data-v-0284e0ab" bindtap="{{i}}"><uni-icons wx:if="{{h}}" class="data-v-0284e0ab" u-i="0284e0ab-0" bind:__l="__l" u-p="{{h}}"></uni-icons><text class="data-v-0284e0ab">刷新</text></view></view></view><scroll-view class="service-list data-v-0284e0ab" scroll-y bindscrolltolower="{{l}}"><view wx:for="{{j}}" wx:for-item="item" wx:key="w" class="{{['service-item', 'data-v-0284e0ab', item.x && 'service-item--active']}}" bindtap="{{item.y}}"><view class="service-header data-v-0284e0ab"><text class="service-title data-v-0284e0ab">{{item.a}}</text><view class="service-menu-button data-v-0284e0ab" catchtap="{{item.c}}"><uni-icons wx:if="{{k}}" class="data-v-0284e0ab" u-i="{{item.b}}" bind:__l="__l" u-p="{{k}}"></uni-icons></view></view><view class="service-meta data-v-0284e0ab"><text class="service-date data-v-0284e0ab">{{item.d}}</text><text wx:if="{{item.e}}" class="service-duration data-v-0284e0ab"> 录音时长:{{item.f}} 分钟 </text><text wx:if="{{item.g}}" class="service-duration data-v-0284e0ab"> 格式:{{item.h}}</text></view><view wx:if="{{item.i}}" class="service-recording-name data-v-0284e0ab"><text class="recording-name-label data-v-0284e0ab">记录名称:</text><text class="recording-name-value data-v-0284e0ab">{{item.j}}</text></view><view wx:if="{{item.k}}" class="service-recording-text data-v-0284e0ab"><text class="recording-text-label data-v-0284e0ab">录音文本:</text><view class="recording-text-body data-v-0284e0ab">{{item.l}}</view></view><view class="service-content data-v-0284e0ab"><text class="service-desc data-v-0284e0ab">{{item.m}}</text></view><view class="service-customer data-v-0284e0ab"><view class="customer-avatar data-v-0284e0ab"><text class="avatar-text data-v-0284e0ab">{{item.n}}</text></view><text class="customer-name data-v-0284e0ab">{{item.o}}</text></view><view wx:if="{{item.p}}" class="service-action-menu data-v-0284e0ab" catchtap="{{item.v}}"><view class="service-action-menu-item service-action-menu-item--danger data-v-0284e0ab" bindtap="{{item.q}}"><text class="data-v-0284e0ab">删除</text></view><view class="service-action-menu-divider data-v-0284e0ab"></view><view class="service-action-menu-item data-v-0284e0ab" bindtap="{{item.r}}"><text class="data-v-0284e0ab">查看</text></view><view class="service-action-menu-divider data-v-0284e0ab"></view><view class="service-action-menu-item data-v-0284e0ab" bindtap="{{item.s}}"><text class="data-v-0284e0ab">AI分析</text></view><view class="service-action-menu-divider data-v-0284e0ab"></view><view class="service-action-menu-item data-v-0284e0ab" bindtap="{{item.t}}"><text class="data-v-0284e0ab">录音</text></view></view></view></scroll-view><view wx:if="{{m}}" class="service-action-menu-mask data-v-0284e0ab" bindtap="{{n}}"></view></view>

View File

@@ -179,6 +179,26 @@
.recording-name-value.data-v-0284e0ab {
color: #333;
}
.service-recording-text.data-v-0284e0ab {
margin-bottom: 16rpx;
font-size: 24rpx;
}
.recording-text-label.data-v-0284e0ab {
color: #999;
display: block;
margin-bottom: 8rpx;
}
.recording-text-body.data-v-0284e0ab {
color: #333;
line-height: 1.5;
word-break: break-word;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
}
.service-content.data-v-0284e0ab {
margin-bottom: 24rpx;
}