增加录音功能调整页面布局。

This commit is contained in:
zhonghua.li
2026-04-16 09:20:13 +08:00
parent 826d9082ad
commit 8f556e2168
11 changed files with 222 additions and 48 deletions

View File

@@ -443,8 +443,7 @@ export default {
detailedAddress: this.formData.detailedAddress?.trim() || "",
salesPhone: trimmedSalesPhone || "",
contactCount: Number(this.formData.contactCount) || 0,
operationType: action,
scenario: "furniture"
operationType: action
};
try {
@@ -455,9 +454,13 @@ export default {
// 获取认证信息
let tenantId = '';
let token = '';
let scenario = '';
try {
const loginResponse = uni.getStorageSync('backend-login-response') || {};
tenantId = uni.getStorageSync('backend-tenant-id') || '';
token = uni.getStorageSync('backend-token') || '';
// 场景值来自登录返回,兜底读取历史缓存字段
scenario = (loginResponse.scenario || uni.getStorageSync('backend-scenario') || '').toString().trim();
} catch (e) {
console.error('获取认证信息失败:', e);
}
@@ -472,6 +475,10 @@ export default {
if (tenantId) {
headers['X-Tenant-Id'] = tenantId;
}
if (scenario) {
headers['X-Scenario'] = scenario;
params.scenario = scenario;
}
const res = await uni.request({
url: getApiUrl('/api/customerManagement/add'),

View File

@@ -19,18 +19,18 @@
<view class="content">
<!-- 标签页 -->
<view class="tabs" :style="{ top: computedNavbarTop || navbarTop }">
<view
class="tab-item"
:class="{ active: activeTab === 'status' }"
@click="switchTab('status')">
<text>服务中</text>
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'reception' }"
@click="switchTab('reception')">
<text>开始接待</text>
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'status' }"
@click="switchTab('status')">
<text>服务中</text>
</view>
<view
class="tab-item"
:class="{ active: activeTab === 'tag' }"

View File

@@ -52,6 +52,20 @@
<text class="staff-name">{{ item.staffName || '未分配销售' }}</text>
</view>
<view class="service-status">
<view
v-if="!isRecordingItem(item.id)"
class="service-action-btn service-action-btn--record"
@click.stop="startPhoneRecord(item)"
>
<text>录音</text>
</view>
<view
v-else
class="service-action-btn service-action-btn--record service-action-btn--recording"
@click.stop="stopPhoneRecordAndUpload(item)"
>
<text>停止</text>
</view>
<view class="service-action-btn service-action-btn--supplement" @click.stop="showSupplementDialog(item)">
<text>补录</text>
</view>
@@ -212,7 +226,11 @@ export default {
contact: "",
recordingName: "",
remarks: ""
}
},
recorderSupported: false,
recorderManager: null,
recordingServiceId: null,
recordingContext: null
}
},
mounted() {
@@ -220,9 +238,102 @@ export default {
this.checkUserRole();
// 获取当前用户手机号
this.loadCurrentUserPhone();
this.initServiceRecorder();
this.fetchServiceStatusList();
},
beforeUnmount() {
if (this.recorderManager && this.recordingServiceId) {
try {
this.recorderManager.stop();
} catch (e) {
/* ignore */
}
}
},
methods: {
initServiceRecorder() {
this.recorderSupported = typeof uni.getRecorderManager === 'function';
if (!this.recorderSupported) {
return;
}
this.recorderManager = uni.getRecorderManager();
// 用户点击「停止」后由系统回调此处,再将临时录音文件上传到 /api/audio/upload
this.recorderManager.onStop((res) => {
const ctx = this.recordingContext;
this.recordingServiceId = null;
this.recordingContext = null;
const tempFilePath = res.tempFilePath || '';
if (!ctx || !ctx.id) {
return;
}
if (!tempFilePath) {
uni.showToast({ title: '未获取到录音文件', icon: 'none' });
return;
}
const name = `service_record_${ctx.id}_${Date.now()}.mp3`;
this.uploadFileForRecord(ctx, { path: tempFilePath, name });
});
this.recorderManager.onError(() => {
this.recordingServiceId = null;
this.recordingContext = null;
uni.showToast({ title: '录音出错', icon: 'none' });
});
},
isRecordingItem(recordId) {
if (!recordId || !this.recordingServiceId) {
return false;
}
return String(recordId) === this.recordingServiceId;
},
startPhoneRecord(item) {
if (!this.recorderSupported || !this.recorderManager) {
uni.showToast({
title: '当前环境不支持录音,请使用微信小程序或 App',
icon: 'none'
});
return;
}
if (!item || !item.id) {
uni.showToast({
title: '无法获取服务记录ID',
icon: 'none'
});
return;
}
if (this.recordingServiceId) {
uni.showToast({ title: '请先停止当前录音', icon: 'none' });
return;
}
if (this.isUploading(item.id)) {
uni.showToast({ title: '正在上传,请稍候', icon: 'none' });
return;
}
this.recordingContext = { ...item };
this.recordingServiceId = String(item.id);
try {
this.recorderManager.start({
duration: 600000,
sampleRate: 44100,
numberOfChannels: 1,
encodeBitRate: 96000,
format: 'mp3'
});
} catch (e) {
this.recordingServiceId = null;
this.recordingContext = null;
uni.showToast({ title: '无法开始录音', icon: 'none' });
}
},
/** 结束录音onStop 回调中会调用 uploadFileForRecord 上传至后端 */
stopPhoneRecordAndUpload(item) {
if (!this.recorderManager || !item?.id) {
return;
}
if (String(item.id) !== this.recordingServiceId) {
return;
}
this.recorderManager.stop();
},
/**
* 检查用户角色判断是否包含admin
* 只要角色中包含admin字符串如admin_furniture就视为admin角色
@@ -467,6 +578,22 @@ export default {
if (!recordId) return false;
return this.uploadingIds.includes(String(recordId));
},
/** 仅允许扩展名为 .mp3 的文件(不区分大小写) */
isMp3UploadFile(selected) {
if (!selected || !selected.path) return false;
const pickBase = (s) => {
if (!s || typeof s !== 'string') return '';
const seg = s.split('/').pop() || s.split('\\').pop() || s;
return seg.trim();
};
const bases = [pickBase(selected.name), pickBase(selected.path)].filter(Boolean);
for (const base of bases) {
if (base.toLowerCase().endsWith('.mp3')) {
return true;
}
}
return false;
},
async chooseAndUploadFile(item) {
if (!item || !item.id) {
uni.showToast({
@@ -508,6 +635,14 @@ export default {
resolve(null);
return;
}
if (!this.isMp3UploadFile(selected)) {
uni.showToast({
title: '仅支持上传 MP3 文件',
icon: 'none'
});
resolve(null);
return;
}
resolve(selected);
};
const onFail = (err) => {
@@ -535,6 +670,7 @@ export default {
if (typeof uni.chooseFile === 'function') {
uni.chooseFile({
count: 1,
extension: ['.mp3'],
success: onSuccess,
fail: onFail
});
@@ -573,11 +709,12 @@ export default {
title: '上传中...'
});
const uploadRes = await uni.uploadFile({
url: getApiUrl('/api/audioManagement/uploadBluetoothAudio'),
url: getApiUrl('/api/audio/upload'),
filePath: selectedFile.path,
name: 'file',
formData: {
id: item.id,
audioId: item.id,
customerId: item.customerId || '',
customerName: item.customerName || '',
fileName: selectedFile.name || `service_file_${Date.now()}`
@@ -1210,6 +1347,19 @@ export default {
opacity: 0.7;
}
.service-action-btn--record {
color: #007AFF;
background-color: transparent;
}
.service-action-btn--record:active {
opacity: 0.7;
}
.service-action-btn--recording {
color: #FF3B30;
}
.service-status-empty {
padding: 48rpx 0;
text-align: center;