增加录音功能调整页面布局。
This commit is contained in:
@@ -7,7 +7,7 @@ import envConfig from '@/common/env.js'
|
|||||||
|
|
||||||
const API_TARGETS = {
|
const API_TARGETS = {
|
||||||
local: 'http://localhost:8091/',
|
local: 'http://localhost:8091/',
|
||||||
prod: 'https://api.huayang-star.com/'
|
prod: 'http://api01ai.huayang-star.com/'
|
||||||
}
|
}
|
||||||
|
|
||||||
const ENV_ALIAS = {
|
const ENV_ALIAS = {
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
* apiEnv 可选:'local' | 'prod'
|
* apiEnv 可选:'local' | 'prod'
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
apiEnv: 'local'
|
apiEnv: 'prod'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export function request(options = {}) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 每次请求打印完整 URL,格式示例:
|
// 每次请求打印完整 URL,格式示例:
|
||||||
// H5环境(local)直接使用完整URL: http://localhost:8090/api/audioManagement/list
|
// H5环境(local)直接使用完整URL: http://localhost:8091/api/audioManagement/list
|
||||||
console.log(`[Request] 完整请求URL: ${fullUrl}`)
|
console.log(`[Request] 完整请求URL: ${fullUrl}`)
|
||||||
|
|
||||||
// 构建请求头
|
// 构建请求头
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 上传到服务器(需要根据实际API调整)
|
// 上传到服务器(需要根据实际API调整)
|
||||||
const url = getApiUrl('/api/audioManagement/uploadBluetoothAudio')
|
const url = getApiUrl('/api/audioManagement/upload')
|
||||||
const res = await post(url, formData, {
|
const res = await post(url, formData, {
|
||||||
header: {
|
header: {
|
||||||
'Content-Type': 'multipart/form-data'
|
'Content-Type': 'multipart/form-data'
|
||||||
|
|||||||
@@ -21,15 +21,15 @@
|
|||||||
<view class="tabs" :style="{ top: computedNavbarTop || navbarTop }">
|
<view class="tabs" :style="{ top: computedNavbarTop || navbarTop }">
|
||||||
<view
|
<view
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: activeTab === 'service' }"
|
:class="{ active: activeTab === 'customer' }"
|
||||||
@click="switchTab('service')">
|
@click="switchTab('customer')">
|
||||||
<text>服务记录</text>
|
<text>客户列表</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: activeTab === 'customer' }"
|
:class="{ active: activeTab === 'service' }"
|
||||||
@click="switchTab('customer')">
|
@click="switchTab('service')">
|
||||||
<text>客户</text>
|
<text>服务记录</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -518,8 +518,9 @@
|
|||||||
return (totalNavbarHeight + 72) + 'rpx'; // tab栏高度72rpx
|
return (totalNavbarHeight + 72) + 'rpx'; // tab栏高度72rpx
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad(options = {}) {
|
||||||
console.log('[FurnitureCustomer][onLoad] 页面加载');
|
console.log('[FurnitureCustomer][onLoad] 页面加载');
|
||||||
|
this.applyIncomingTab(options?.tab);
|
||||||
// 更新导航栏位置
|
// 更新导航栏位置
|
||||||
this.updateNavbarPosition();
|
this.updateNavbarPosition();
|
||||||
// 加载服务记录列表
|
// 加载服务记录列表
|
||||||
@@ -618,6 +619,13 @@
|
|||||||
this.playingAudioId = null;
|
this.playingAudioId = null;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
applyIncomingTab(tab) {
|
||||||
|
const allowedTabs = ['service', 'customer'];
|
||||||
|
if (!allowedTabs.includes(tab)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.activeTab = tab;
|
||||||
|
},
|
||||||
// 计算并更新导航栏和内容区域的位置
|
// 计算并更新导航栏和内容区域的位置
|
||||||
updateNavbarPosition() {
|
updateNavbarPosition() {
|
||||||
console.log('[FurnitureCustomer][updateNavbarPosition] 开始更新导航栏位置');
|
console.log('[FurnitureCustomer][updateNavbarPosition] 开始更新导航栏位置');
|
||||||
|
|||||||
@@ -443,8 +443,7 @@ export default {
|
|||||||
detailedAddress: this.formData.detailedAddress?.trim() || "",
|
detailedAddress: this.formData.detailedAddress?.trim() || "",
|
||||||
salesPhone: trimmedSalesPhone || "",
|
salesPhone: trimmedSalesPhone || "",
|
||||||
contactCount: Number(this.formData.contactCount) || 0,
|
contactCount: Number(this.formData.contactCount) || 0,
|
||||||
operationType: action,
|
operationType: action
|
||||||
scenario: "furniture"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -455,9 +454,13 @@ export default {
|
|||||||
// 获取认证信息
|
// 获取认证信息
|
||||||
let tenantId = '';
|
let tenantId = '';
|
||||||
let token = '';
|
let token = '';
|
||||||
|
let scenario = '';
|
||||||
try {
|
try {
|
||||||
|
const loginResponse = uni.getStorageSync('backend-login-response') || {};
|
||||||
tenantId = uni.getStorageSync('backend-tenant-id') || '';
|
tenantId = uni.getStorageSync('backend-tenant-id') || '';
|
||||||
token = uni.getStorageSync('backend-token') || '';
|
token = uni.getStorageSync('backend-token') || '';
|
||||||
|
// 场景值来自登录返回,兜底读取历史缓存字段
|
||||||
|
scenario = (loginResponse.scenario || uni.getStorageSync('backend-scenario') || '').toString().trim();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取认证信息失败:', e);
|
console.error('获取认证信息失败:', e);
|
||||||
}
|
}
|
||||||
@@ -472,6 +475,10 @@ export default {
|
|||||||
if (tenantId) {
|
if (tenantId) {
|
||||||
headers['X-Tenant-Id'] = tenantId;
|
headers['X-Tenant-Id'] = tenantId;
|
||||||
}
|
}
|
||||||
|
if (scenario) {
|
||||||
|
headers['X-Scenario'] = scenario;
|
||||||
|
params.scenario = scenario;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await uni.request({
|
const res = await uni.request({
|
||||||
url: getApiUrl('/api/customerManagement/add'),
|
url: getApiUrl('/api/customerManagement/add'),
|
||||||
|
|||||||
@@ -19,18 +19,18 @@
|
|||||||
<view class="content">
|
<view class="content">
|
||||||
<!-- 标签页 -->
|
<!-- 标签页 -->
|
||||||
<view class="tabs" :style="{ top: computedNavbarTop || navbarTop }">
|
<view class="tabs" :style="{ top: computedNavbarTop || navbarTop }">
|
||||||
<view
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: activeTab === 'status' }"
|
|
||||||
@click="switchTab('status')">
|
|
||||||
<text>服务中</text>
|
|
||||||
</view>
|
|
||||||
<view
|
<view
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: activeTab === 'reception' }"
|
:class="{ active: activeTab === 'reception' }"
|
||||||
@click="switchTab('reception')">
|
@click="switchTab('reception')">
|
||||||
<text>开始接待</text>
|
<text>开始接待</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === 'status' }"
|
||||||
|
@click="switchTab('status')">
|
||||||
|
<text>服务中</text>
|
||||||
|
</view>
|
||||||
<view
|
<view
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ active: activeTab === 'tag' }"
|
:class="{ active: activeTab === 'tag' }"
|
||||||
|
|||||||
@@ -52,6 +52,20 @@
|
|||||||
<text class="staff-name">{{ item.staffName || '未分配销售' }}</text>
|
<text class="staff-name">{{ item.staffName || '未分配销售' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="service-status">
|
<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)">
|
<view class="service-action-btn service-action-btn--supplement" @click.stop="showSupplementDialog(item)">
|
||||||
<text>补录</text>
|
<text>补录</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -212,7 +226,11 @@ export default {
|
|||||||
contact: "",
|
contact: "",
|
||||||
recordingName: "",
|
recordingName: "",
|
||||||
remarks: ""
|
remarks: ""
|
||||||
}
|
},
|
||||||
|
recorderSupported: false,
|
||||||
|
recorderManager: null,
|
||||||
|
recordingServiceId: null,
|
||||||
|
recordingContext: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -220,9 +238,102 @@ export default {
|
|||||||
this.checkUserRole();
|
this.checkUserRole();
|
||||||
// 获取当前用户手机号
|
// 获取当前用户手机号
|
||||||
this.loadCurrentUserPhone();
|
this.loadCurrentUserPhone();
|
||||||
|
this.initServiceRecorder();
|
||||||
this.fetchServiceStatusList();
|
this.fetchServiceStatusList();
|
||||||
},
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
if (this.recorderManager && this.recordingServiceId) {
|
||||||
|
try {
|
||||||
|
this.recorderManager.stop();
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
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字符串(如admin_furniture),就视为admin角色
|
* 只要角色中包含admin字符串(如admin_furniture),就视为admin角色
|
||||||
@@ -467,6 +578,22 @@ export default {
|
|||||||
if (!recordId) return false;
|
if (!recordId) return false;
|
||||||
return this.uploadingIds.includes(String(recordId));
|
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) {
|
async chooseAndUploadFile(item) {
|
||||||
if (!item || !item.id) {
|
if (!item || !item.id) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@@ -508,6 +635,14 @@ export default {
|
|||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this.isMp3UploadFile(selected)) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '仅支持上传 MP3 文件',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
resolve(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
resolve(selected);
|
resolve(selected);
|
||||||
};
|
};
|
||||||
const onFail = (err) => {
|
const onFail = (err) => {
|
||||||
@@ -535,6 +670,7 @@ export default {
|
|||||||
if (typeof uni.chooseFile === 'function') {
|
if (typeof uni.chooseFile === 'function') {
|
||||||
uni.chooseFile({
|
uni.chooseFile({
|
||||||
count: 1,
|
count: 1,
|
||||||
|
extension: ['.mp3'],
|
||||||
success: onSuccess,
|
success: onSuccess,
|
||||||
fail: onFail
|
fail: onFail
|
||||||
});
|
});
|
||||||
@@ -573,11 +709,12 @@ export default {
|
|||||||
title: '上传中...'
|
title: '上传中...'
|
||||||
});
|
});
|
||||||
const uploadRes = await uni.uploadFile({
|
const uploadRes = await uni.uploadFile({
|
||||||
url: getApiUrl('/api/audioManagement/uploadBluetoothAudio'),
|
url: getApiUrl('/api/audio/upload'),
|
||||||
filePath: selectedFile.path,
|
filePath: selectedFile.path,
|
||||||
name: 'file',
|
name: 'file',
|
||||||
formData: {
|
formData: {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
|
audioId: item.id,
|
||||||
customerId: item.customerId || '',
|
customerId: item.customerId || '',
|
||||||
customerName: item.customerName || '',
|
customerName: item.customerName || '',
|
||||||
fileName: selectedFile.name || `service_file_${Date.now()}`
|
fileName: selectedFile.name || `service_file_${Date.now()}`
|
||||||
@@ -1210,6 +1347,19 @@ export default {
|
|||||||
opacity: 0.7;
|
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 {
|
.service-status-empty {
|
||||||
padding: 48rpx 0;
|
padding: 48rpx 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -18,47 +18,47 @@
|
|||||||
<view class="content">
|
<view class="content">
|
||||||
<!-- 功能分类展示 -->
|
<!-- 功能分类展示 -->
|
||||||
<scroll-view class="content-scroll" scroll-y="true" :style="{ top: computedContentTop || contentTop, bottom: contentBottom || '96rpx' }">
|
<scroll-view class="content-scroll" scroll-y="true" :style="{ top: computedContentTop || contentTop, bottom: contentBottom || '96rpx' }">
|
||||||
<!-- 接待页面快捷入口 -->
|
<!-- 接待 -->
|
||||||
<view class="category-section quick-links-section">
|
<view class="category-section quick-links-section">
|
||||||
<view class="category-header">
|
<view class="category-header">
|
||||||
<text class="category-icon">🚀</text>
|
<text class="category-icon">🚀</text>
|
||||||
<text class="category-title">接待页面快捷入口</text>
|
<text class="category-title">接待</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-grid">
|
<view class="function-grid">
|
||||||
<view class="function-item" @click="quickJumpReceptionTab('status')">
|
|
||||||
<view class="function-icon">📋</view>
|
|
||||||
<text class="function-name">服务状态</text>
|
|
||||||
<text class="function-desc">跳转到接待页服务状态tab</text>
|
|
||||||
</view>
|
|
||||||
<view class="function-item" @click="quickJumpReceptionTab('reception')">
|
<view class="function-item" @click="quickJumpReceptionTab('reception')">
|
||||||
<view class="function-icon">🤝</view>
|
<view class="function-icon">🤝</view>
|
||||||
<text class="function-name">开始接待</text>
|
<text class="function-name">开始接待</text>
|
||||||
<text class="function-desc">跳转到接待页开始接待tab</text>
|
<text class="function-desc"></text>
|
||||||
|
</view>
|
||||||
|
<view class="function-item" @click="quickJumpReceptionTab('status')">
|
||||||
|
<view class="function-icon">📋</view>
|
||||||
|
<text class="function-name">服务状态</text>
|
||||||
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-item" @click="quickJumpReceptionTab('tag')">
|
<view class="function-item" @click="quickJumpReceptionTab('tag')">
|
||||||
<view class="function-icon">🏷️</view>
|
<view class="function-icon">🏷️</view>
|
||||||
<text class="function-name">标签管理</text>
|
<text class="function-name">标签管理</text>
|
||||||
<text class="function-desc">跳转到接待页标签管理tab</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 客户页面快捷入口 -->
|
<!-- AI分析 -->
|
||||||
<view class="category-section two-column">
|
<view class="category-section two-column">
|
||||||
<view class="category-header">
|
<view class="category-header">
|
||||||
<text class="category-icon">👤</text>
|
<text class="category-icon">👤</text>
|
||||||
<text class="category-title">客户页面快捷入口</text>
|
<text class="category-title">AI分析</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-grid">
|
<view class="function-grid">
|
||||||
<view class="function-item" @click="quickJumpCustomerTab('service')">
|
|
||||||
<view class="function-icon">📝</view>
|
|
||||||
<text class="function-name">服务记录</text>
|
|
||||||
<text class="function-desc">跳转到客户页服务记录tab</text>
|
|
||||||
</view>
|
|
||||||
<view class="function-item" @click="quickJumpCustomerTab('customer')">
|
<view class="function-item" @click="quickJumpCustomerTab('customer')">
|
||||||
<view class="function-icon">👥</view>
|
<view class="function-icon">👥</view>
|
||||||
<text class="function-name">客户列表</text>
|
<text class="function-name">客户列表</text>
|
||||||
<text class="function-desc">跳转到客户页客户列表tab</text>
|
<text class="function-desc"></text>
|
||||||
|
</view>
|
||||||
|
<view class="function-item" @click="quickJumpCustomerTab('service')">
|
||||||
|
<view class="function-icon">📝</view>
|
||||||
|
<text class="function-name">服务记录</text>
|
||||||
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -73,12 +73,12 @@
|
|||||||
<view class="function-item" @click="navigateToPage('recording_duration')">
|
<view class="function-item" @click="navigateToPage('recording_duration')">
|
||||||
<view class="function-icon">⏱️</view>
|
<view class="function-icon">⏱️</view>
|
||||||
<text class="function-name">录音时长</text>
|
<text class="function-name">录音时长</text>
|
||||||
<text class="function-desc">统计录音总时长</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-item" @click="navigateToPage('customer_count')">
|
<view class="function-item" @click="navigateToPage('customer_count')">
|
||||||
<view class="function-icon">👥</view>
|
<view class="function-icon">👥</view>
|
||||||
<text class="function-name">接待客户数</text>
|
<text class="function-name">接待客户数</text>
|
||||||
<text class="function-desc">统计接待客户数量</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -93,12 +93,12 @@
|
|||||||
<view class="function-item" @click="showQualityCoveragePopup">
|
<view class="function-item" @click="showQualityCoveragePopup">
|
||||||
<view class="function-icon">📊</view>
|
<view class="function-icon">📊</view>
|
||||||
<text class="function-name">质检覆盖率</text>
|
<text class="function-name">质检覆盖率</text>
|
||||||
<text class="function-desc">质检录音覆盖统计</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-item" @click="navigateToPage('speech_rating')">
|
<view class="function-item" @click="navigateToPage('speech_rating')">
|
||||||
<view class="function-icon">🗣️</view>
|
<view class="function-icon">🗣️</view>
|
||||||
<text class="function-name">员工话术评分</text>
|
<text class="function-name">员工话术评分</text>
|
||||||
<text class="function-desc">员工沟通质量评分</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -113,17 +113,17 @@
|
|||||||
<view class="function-item full-row-item" @click="navigateToPage('intent_level')">
|
<view class="function-item full-row-item" @click="navigateToPage('intent_level')">
|
||||||
<view class="function-icon">🎯</view>
|
<view class="function-icon">🎯</view>
|
||||||
<text class="function-name">客户意向分级</text>
|
<text class="function-name">客户意向分级</text>
|
||||||
<text class="function-desc">客户购买意向分析</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-item full-row-item" @click="navigateToPage('deal_attribution')">
|
<view class="function-item full-row-item" @click="navigateToPage('deal_attribution')">
|
||||||
<view class="function-icon">💰</view>
|
<view class="function-icon">💰</view>
|
||||||
<text class="function-name">成交归因</text>
|
<text class="function-name">成交归因</text>
|
||||||
<text class="function-desc">成交成功因素分析</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-item full-row-item new-line-item" @click="navigateToPage('opening_rate')">
|
<view class="function-item full-row-item new-line-item" @click="navigateToPage('opening_rate')">
|
||||||
<view class="function-icon">🚪</view>
|
<view class="function-icon">🚪</view>
|
||||||
<text class="function-name">开口率</text>
|
<text class="function-name">开口率</text>
|
||||||
<text class="function-desc">客户主动开口统计</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -138,12 +138,12 @@
|
|||||||
<view class="function-item" @click="navigateToPage('over_commitment')">
|
<view class="function-item" @click="navigateToPage('over_commitment')">
|
||||||
<view class="function-icon">🚨</view>
|
<view class="function-icon">🚨</view>
|
||||||
<text class="function-name">过度承诺统计</text>
|
<text class="function-name">过度承诺统计</text>
|
||||||
<text class="function-desc">监控过度承诺情况</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
<view class="function-item" @click="navigateToPage('red_line_touch')">
|
<view class="function-item" @click="navigateToPage('red_line_touch')">
|
||||||
<view class="function-icon">🔴</view>
|
<view class="function-icon">🔴</view>
|
||||||
<text class="function-name">触及红线统计</text>
|
<text class="function-name">触及红线统计</text>
|
||||||
<text class="function-desc">违规行为监控统计</text>
|
<text class="function-desc"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ export default {
|
|||||||
const allowed = ['status', 'reception', 'tag'];
|
const allowed = ['status', 'reception', 'tag'];
|
||||||
const tab = (uni.getStorageSync('workbench-reception-tab') || '').toString();
|
const tab = (uni.getStorageSync('workbench-reception-tab') || '').toString();
|
||||||
if (allowed.includes(tab)) {
|
if (allowed.includes(tab)) {
|
||||||
this.incomingTab = tab;
|
// 先清空再赋值,确保同一个 tabKey 也能触发子组件 watcher
|
||||||
|
this.incomingTab = '';
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.incomingTab = tab;
|
||||||
|
});
|
||||||
uni.removeStorageSync('workbench-reception-tab');
|
uni.removeStorageSync('workbench-reception-tab');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
<text class="info-label">当前角色:</text>
|
<text class="info-label">当前角色:</text>
|
||||||
<text class="info-value">{{loginUserInfo.roleName || '未设置'}}</text>
|
<text class="info-value">{{loginUserInfo.roleName || '未设置'}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">场景:</text>
|
||||||
|
<text class="info-value">{{loginUserInfo.scenario || '未设置'}}</text>
|
||||||
|
</view>
|
||||||
<view class="info-row" v-if="loginUserInfo.phone">
|
<view class="info-row" v-if="loginUserInfo.phone">
|
||||||
<text class="info-label">手机号:</text>
|
<text class="info-label">手机号:</text>
|
||||||
<text class="info-value">{{loginUserInfo.phone}}</text>
|
<text class="info-value">{{loginUserInfo.phone}}</text>
|
||||||
@@ -299,6 +303,7 @@
|
|||||||
this.loginUserInfo = {
|
this.loginUserInfo = {
|
||||||
userName: loginResponse.userName || '',
|
userName: loginResponse.userName || '',
|
||||||
roleName: loginResponse.roleName || '',
|
roleName: loginResponse.roleName || '',
|
||||||
|
scenario: loginResponse.scenario || uni.getStorageSync('backend-scenario') || '',
|
||||||
phone: loginResponse.phone || '',
|
phone: loginResponse.phone || '',
|
||||||
email: loginResponse.email || '',
|
email: loginResponse.email || '',
|
||||||
userId: loginResponse.userId || ''
|
userId: loginResponse.userId || ''
|
||||||
|
|||||||
Reference in New Issue
Block a user