diff --git a/common/env.js b/common/env.js index 9066d70..1a46984 100644 --- a/common/env.js +++ b/common/env.js @@ -3,6 +3,6 @@ * apiEnv 可选:'local' | 'prod' */ export default { - apiEnv: 'prod' + apiEnv: 'local' } diff --git a/pages-subpackage/furniture_reception/serviceListFurniture.vue b/pages-subpackage/furniture_reception/serviceListFurniture.vue index 391ec15..5998236 100644 --- a/pages-subpackage/furniture_reception/serviceListFurniture.vue +++ b/pages-subpackage/furniture_reception/serviceListFurniture.vue @@ -401,7 +401,7 @@ export default { this.stopRecordingTicker(); const tempFilePath = res.tempFilePath || ''; if (ctx && ctx.id) { - const name = `service_record_${ctx.id}_${Date.now()}_part${normalizedSegmentNo}.mp3`; + const name = this.getAudioUploadFileName({ segmentNo: normalizedSegmentNo }); if (!tempFilePath) { this.pushUploadResult(String(ctx.id), { id: `seg_${String(ctx.id)}_${normalizedSegmentNo}_${Date.now()}`, @@ -1035,10 +1035,88 @@ export default { } return headers; }, + /** 上传音频文件名:租户id_手机号_年月日时分秒(租户id、手机号后各有一个下划线;多段录音段号>1 时末尾再追加 _段号) */ + getAudioUploadFileName(options = {}) { + let tenantId = ''; + let phone = ''; + try { + tenantId = String(uni.getStorageSync('backend-tenant-id') || '').trim(); + } catch (e) {} + try { + const raw = this.currentUserPhone || ''; + const login = uni.getStorageSync('backend-login-response') || {}; + const fallback = login.phone || login.userName || ''; + phone = String(raw || fallback || '').replace(/\D/g, ''); + } catch (e) {} + const d = new Date(); + const pad = (n) => String(n).padStart(2, '0'); + const ts = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`; + const seg = Number(options.segmentNo); + const segSuffix = Number.isFinite(seg) && seg > 1 ? `_${seg}` : ''; + const ext = typeof options.ext === 'string' && options.ext ? options.ext : '.mp3'; + const dotExt = ext.startsWith('.') ? ext : `.${ext}`; + const tenantPrefix = tenantId ? `${tenantId}_` : ''; + const phonePrefix = phone ? `${phone}_` : ''; + const base = `${tenantPrefix}${phonePrefix}${ts}${segSuffix}`.replace(/[/\\:*?"<>|]/g, '_') || `upload_${ts}${segSuffix}`; + return `${base}${dotExt}`; + }, + /** + * 微信小程序等环境下 multipart 的原始文件名来自本地路径最后一段;临时路径常为随机串甚至带异常后缀。 + * 复制到 USER_DATA_PATH 并使用规范文件名再上传,后端 getOriginalFilename / 落盘名才能与 fileName 参数一致。 + */ + prepareAudioFileForUpload(tempPath, displayFileName) { + return new Promise((resolve) => { + if (!tempPath || !displayFileName) { + resolve({ uploadPath: tempPath, tempCopyPath: null }); + return; + } + const baseName = (() => { + const seg = String(tempPath).split(/[/\\]/).pop() || ''; + return seg.split('?')[0] || ''; + })(); + const needsRename = + baseName !== displayFileName || + baseName.includes('=') || + /\.durationTime=/i.test(baseName); + let userDataPath = ''; + try { + userDataPath = (uni.env && uni.env.USER_DATA_PATH) || ''; + } catch (e) {} + if (!userDataPath && typeof wx !== 'undefined' && wx.env) { + userDataPath = wx.env.USER_DATA_PATH || ''; + } + if (!needsRename || !userDataPath || typeof uni.getFileSystemManager !== 'function') { + resolve({ uploadPath: tempPath, tempCopyPath: null }); + return; + } + const destPath = `${String(userDataPath).replace(/\/+$/, '')}/${displayFileName}`; + uni.getFileSystemManager().copyFile({ + srcPath: tempPath, + destPath, + success: () => resolve({ uploadPath: destPath, tempCopyPath: destPath }), + fail: (err) => { + console.warn('prepareAudioFileForUpload copyFile failed:', err); + resolve({ uploadPath: tempPath, tempCopyPath: null }); + }, + }); + }); + }, + safeUnlinkAudioCopy(filePath) { + if (!filePath || typeof uni.getFileSystemManager !== 'function') { + return; + } + try { + uni.getFileSystemManager().unlink({ filePath, fail: () => {} }); + } catch (e) {} + }, async uploadFileForRecord(item, selectedFile, options = {}) { const recordId = String(item.id); const segmentNo = Number(options.segmentNo) || 1; const durationSeconds = Math.max(0, Number(options.durationSeconds) || 0); + const audioDurationMinutesStr = (() => { + const m = durationSeconds / 60; + return Number.isFinite(m) ? String(Math.round(m * 1e6) / 1e6) : '0'; + })(); const fileSize = Math.max(0, Number(selectedFile?.size) || 0); const resultId = `seg_${recordId}_${segmentNo}_${Date.now()}`; this.pushUploadResult(recordId, { @@ -1051,20 +1129,26 @@ export default { message: '', }); this.increaseUploadingCount(recordId); + let tempCopyPath = null; try { + const targetFileName = this.getAudioUploadFileName({ segmentNo }); + const prep = await this.prepareAudioFileForUpload(selectedFile.path, targetFileName); + const uploadPath = prep.uploadPath; + tempCopyPath = prep.tempCopyPath; const maxAttempt = options.retryOnceOnFail ? 2 : 1; let lastErrorMessage = ''; for (let attempt = 1; attempt <= maxAttempt; attempt += 1) { const uploadRes = await uni.uploadFile({ url: getApiUrl('/api/audio/upload'), - filePath: selectedFile.path, + filePath: uploadPath, name: 'file', formData: { id: item.id, audioId: item.id, + audioDuration: audioDurationMinutesStr, customerId: item.customerId || '', customerName: item.customerName || '', - fileName: selectedFile.name || `service_file_${Date.now()}`, + fileName: targetFileName, compress: true }, header: this.getAuthHeaders(), @@ -1110,6 +1194,7 @@ export default { }); return false; } finally { + this.safeUnlinkAudioCopy(tempCopyPath); this.decreaseUploadingCount(recordId); } }, diff --git a/unpackage/dist/build/mp-weixin/common/env.js b/unpackage/dist/build/mp-weixin/common/env.js index 44a2c0a..7b23038 100644 --- a/unpackage/dist/build/mp-weixin/common/env.js +++ b/unpackage/dist/build/mp-weixin/common/env.js @@ -1 +1 @@ -"use strict";exports.envConfig={apiEnv:"prod"}; +"use strict";exports.envConfig={apiEnv:"local"}; diff --git a/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.js b/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.js index 09f1960..7e9fca0 100644 --- a/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.js +++ b/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.js @@ -1 +1 @@ -"use strict";const e=require("../../common/vendor.js"),t=require("../../common/config.js"),r=require("../../common/store.js");function i(...e){for(const t of e){if(null==t)continue;const e=String(t).trim();if(e)return e}return""}const s={recordingServiceId:null,recordingContext:null,recordingElapsedSeconds:0,recordingEstimatedSizeBytes:0,recordingManualStopRequested:!1,recordingCurrentSegmentNo:0,recordingCurrentSegmentStartAt:0,recordingCurrentSegmentElapsedSeconds:0,uploadingCountMap:{},uploadResultMap:{}},n={props:{recordStateLabel:{type:String,default:"服务中"},emptyListHint:{type:String,default:"暂无服务中记录"},showReceptionEntryShortcut:{type:Boolean,default:!1},showRecordStateIndicator:{type:Boolean,default:!0}},data:()=>({serviceStatusLoading:!1,serviceStatusTotal:0,serviceStatusPage:{current:1,size:10},serviceStatusQuery:{salesName:"",customerName:"",salesPhone:""},isAdmin:!1,currentUserPhone:"",serviceStatusList:[],showSupplementModal:!1,currentSupplementItem:null,uploadingIds:[],supplementForm:{customerName:"",contact:"",recordingName:"",remarks:""},recorderSupported:!1,recorderManager:null,recordingServiceId:s.recordingServiceId,recordingContext:s.recordingContext,recordingElapsedSeconds:s.recordingElapsedSeconds,recordingEstimatedSizeBytes:s.recordingEstimatedSizeBytes,recordingTicker:null,keepScreenOnEnabled:!1,recordingSampleRate:16e3,recordingEncodeBitRate:32e3,recordingSegmentDurationMs:3e5,recordingManualStopRequested:s.recordingManualStopRequested,recordingCurrentSegmentNo:s.recordingCurrentSegmentNo,recordingCurrentSegmentStartAt:s.recordingCurrentSegmentStartAt,recordingCurrentSegmentElapsedSeconds:s.recordingCurrentSegmentElapsedSeconds,uploadingCountMap:{...s.uploadingCountMap},uploadResultMap:{...s.uploadResultMap}}),mounted(){var e;this.checkUserRole(),this.loadCurrentUserPhone(),this.initServiceRecorder(),this.restoreRecordingSessionFromCache(),this.recordingServiceId&&(null==(e=this.recordingContext)?void 0:e.id)&&this.startRecordingTicker({reset:!1}),this.fetchServiceStatusList()},beforeUnmount(){this.pauseRecordingTicker(),this.persistRecordingSessionToCache()},methods:{persistRecordingSessionToCache(){s.recordingServiceId=this.recordingServiceId||null,s.recordingContext=this.recordingContext?{...this.recordingContext}:null,s.recordingElapsedSeconds=Number(this.recordingElapsedSeconds)||0,s.recordingEstimatedSizeBytes=Number(this.recordingEstimatedSizeBytes)||0,s.recordingManualStopRequested=!!this.recordingManualStopRequested,s.recordingCurrentSegmentNo=Number(this.recordingCurrentSegmentNo)||0,s.recordingCurrentSegmentStartAt=Number(this.recordingCurrentSegmentStartAt)||0,s.recordingCurrentSegmentElapsedSeconds=Number(this.recordingCurrentSegmentElapsedSeconds)||0,s.uploadingCountMap={...this.uploadingCountMap},s.uploadResultMap={...this.uploadResultMap}},restoreRecordingSessionFromCache(){this.recordingServiceId=s.recordingServiceId||null,this.recordingContext=s.recordingContext?{...s.recordingContext}:null,this.recordingElapsedSeconds=Number(s.recordingElapsedSeconds)||0,this.recordingEstimatedSizeBytes=Number(s.recordingEstimatedSizeBytes)||0,this.recordingManualStopRequested=!!s.recordingManualStopRequested,this.recordingCurrentSegmentNo=Number(s.recordingCurrentSegmentNo)||0,this.recordingCurrentSegmentStartAt=Number(s.recordingCurrentSegmentStartAt)||0,this.recordingCurrentSegmentElapsedSeconds=Number(s.recordingCurrentSegmentElapsedSeconds)||0,this.uploadingCountMap={...s.uploadingCountMap},this.uploadResultMap={...s.uploadResultMap}},initServiceRecorder(){this.recorderSupported="function"==typeof e.index.getRecorderManager,this.recorderSupported&&(this.ensureRecorderManager(),this.bindRecorderEvents())},ensureRecorderManager(){return!!this.recorderSupported&&(this.recorderManager||(this.recorderManager=e.index.getRecorderManager()),!!this.recorderManager)},bindRecorderEvents(){this.ensureRecorderManager()&&("function"==typeof this.recorderManager.offStop&&this.recorderManager.offStop(),"function"==typeof this.recorderManager.offError&&this.recorderManager.offError(),this.recorderManager.onStop((e=>{const t=this.recordingContext,r=this.recordingServiceId,i=this.recordingCurrentSegmentNo,s=this.recordingCurrentSegmentElapsedSeconds||this.recordingElapsedSeconds||0,n=!(!r||!t||!t.id||this.recordingManualStopRequested),o=i>0?i:1;this.stopRecordingTicker();const a=e.tempFilePath||"";if(t&&t.id){const r=`service_record_${t.id}_${Date.now()}_part${o}.mp3`;a?this.uploadFileForRecord(t,{path:a,name:r,size:Number(e.fileSize)||0},{silent:!0,segmentNo:o,durationSeconds:s,retryOnceOnFail:!0}):this.pushUploadResult(String(t.id),{id:`seg_${String(t.id)}_${o}_${Date.now()}`,segmentNo:o,durationText:this.formatRecordingElapsed(s),sizeText:"--",status:"failed",statusText:"上传失败",message:"未获取到录音文件(已重试1次)"})}if(n)return this.setKeepScreenOn(!0),void setTimeout((()=>{this.startNewSegmentRecording()}),80);this.recordingServiceId=null,this.recordingContext=null,this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.setKeepScreenOn(!1),this.persistRecordingSessionToCache()})),this.recorderManager.onError((()=>{this.recordingServiceId=null,this.recordingContext=null,this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.stopRecordingTicker(),this.setKeepScreenOn(!1),this.persistRecordingSessionToCache(),e.index.showToast({title:"录音出错",icon:"none"})})))},setKeepScreenOn(t){"function"==typeof e.index.setKeepScreenOn&&e.index.setKeepScreenOn({keepScreenOn:!!t,success:()=>{this.keepScreenOnEnabled=!!t},fail:()=>{this.keepScreenOnEnabled=!1}})},startRecordingTicker(e={}){const{reset:t=!0}=e;if(this.pauseRecordingTicker(),t)this.recordingElapsedSeconds=0,this.recordingEstimatedSizeBytes=0;else if(this.recordingCurrentSegmentStartAt){const e=Math.max(0,Math.floor((Date.now()-Number(this.recordingCurrentSegmentStartAt))/1e3));this.recordingElapsedSeconds=e;const t=(Number(this.recordingEncodeBitRate)||32e3)/8;this.recordingEstimatedSizeBytes=Math.floor(e*t)}const r=(Number(this.recordingEncodeBitRate)||32e3)/8;this.recordingTicker=setInterval((()=>{this.recordingCurrentSegmentStartAt?this.recordingElapsedSeconds=Math.max(0,Math.floor((Date.now()-Number(this.recordingCurrentSegmentStartAt))/1e3)):this.recordingElapsedSeconds+=1,this.recordingCurrentSegmentElapsedSeconds=this.recordingElapsedSeconds,this.recordingEstimatedSizeBytes=Math.floor(this.recordingElapsedSeconds*r),this.persistRecordingSessionToCache()}),1e3)},pauseRecordingTicker(){this.recordingTicker&&clearInterval(this.recordingTicker),this.recordingTicker=null},stopRecordingTicker(){this.pauseRecordingTicker(),this.recordingCurrentSegmentElapsedSeconds=this.recordingElapsedSeconds,this.recordingElapsedSeconds=0,this.recordingEstimatedSizeBytes=0,this.persistRecordingSessionToCache()},startNewSegmentRecording(){var t;if(this.recorderManager&&this.recordingServiceId&&(null==(t=this.recordingContext)?void 0:t.id)){this.recordingCurrentSegmentNo+=1,this.recordingCurrentSegmentStartAt=Date.now(),this.recordingCurrentSegmentElapsedSeconds=0;try{this.recorderManager.start({duration:this.recordingSegmentDurationMs,sampleRate:this.recordingSampleRate,numberOfChannels:1,encodeBitRate:this.recordingEncodeBitRate,format:"mp3"}),this.startRecordingTicker(),this.persistRecordingSessionToCache()}catch(r){this.recordingServiceId=null,this.recordingContext=null,this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.stopRecordingTicker(),this.setKeepScreenOn(!1),this.persistRecordingSessionToCache(),e.index.showToast({title:"无法开始录音",icon:"none"})}}},formatRecordingElapsed(e){const t=Math.max(0,Number(e)||0);return`${String(Math.floor(t/60)).padStart(2,"0")}:${String(t%60).padStart(2,"0")}`},formatRecordingSize(e){const t=Math.max(0,Number(e)||0);return t<1024?`${t} B`:t<1048576?`${(t/1024).toFixed(1)} KB`:`${(t/1048576).toFixed(2)} MB`},isRecordingItem(e){return!(!e||!this.recordingServiceId)&&String(e)===this.recordingServiceId},startPhoneRecord(t){this.ensureRecorderManager(),this.bindRecorderEvents(),this.recorderSupported&&this.recorderManager?t&&t.id?this.recordingServiceId?e.index.showToast({title:"请先停止当前录音",icon:"none"}):this.isUploading(t.id)?e.index.showToast({title:"正在上传,请稍候",icon:"none"}):(this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.setUploadResultList(String(t.id),[]),this.recordingContext={...t},this.recordingServiceId=String(t.id),this.setKeepScreenOn(!0),this.persistRecordingSessionToCache(),this.startNewSegmentRecording()):e.index.showToast({title:"无法获取服务记录ID",icon:"none"}):e.index.showToast({title:"当前环境不支持录音,请使用微信小程序或 App",icon:"none"})},stopPhoneRecordAndUpload(e){this.recorderManager&&(null==e?void 0:e.id)&&String(e.id)===this.recordingServiceId&&(this.recordingManualStopRequested=!0,this.persistRecordingSessionToCache(),this.recorderManager.stop())},checkUserRole(){try{const t=e=>"string"==typeof e?e.split(",").map((e=>e.trim().toLowerCase())).filter(Boolean):[],r=e.index.getStorageSync("backend-role-name")||"",i=(e.index.getStorageSync("backend-login-response")||{}).roleName||"",s=[...t(r),...t(i)];this.isAdmin=s.some((e=>e.includes("admin"))),console.log("用户角色检查:",{storedRole:r,respRole:i,roles:s,isAdmin:this.isAdmin})}catch(t){console.error("检查用户角色失败:",t),this.isAdmin=!1}},loadCurrentUserPhone(){try{const t=e.index.getStorageSync("backend-login-response")||{};t.phone?this.currentUserPhone=t.phone:t.userName?this.currentUserPhone=t.userName:this.currentUserPhone=""}catch(t){console.error("加载当前用户手机号或登录账户失败:",t),this.currentUserPhone=""}},onServiceStatusSearch(){this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})},onServiceStatusRefresh(){this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})},goReceptionEntry(){e.index.navigateTo({url:"/pages-subpackage/furniture_reception/furniture_reception_entry?tab=reception",fail:()=>{e.index.showToast({title:"跳转接待失败",icon:"none"})}})},onServiceStatusReachBottom(){this.serviceStatusLoading||this.serviceStatusList.length>=this.serviceStatusTotal||(this.serviceStatusPage.current+=1,this.fetchServiceStatusList())},async fetchServiceStatusList({force:r=!1}={}){var i,s,n,o,a,c,d;if(!this.serviceStatusLoading||r){this.serviceStatusLoading=!0;try{const l={current:this.serviceStatusPage.current,size:this.serviceStatusPage.size,serviceStatus:"服务中"},g=null==(s=null==(i=this.serviceStatusQuery)?void 0:i.salesName)?void 0:s.trim(),h=null==(o=null==(n=this.serviceStatusQuery)?void 0:n.customerName)?void 0:o.trim();if(g&&(l.salesName=g),h&&(l.customerName=h),this.isAdmin){const e=null==(c=null==(a=this.serviceStatusQuery)?void 0:a.salesPhone)?void 0:c.trim();e&&(l.salesPhone=e)}else this.currentUserPhone&&(l.salesPhone=this.currentUserPhone);l.serviceStatus="服务中";const p=Object.keys(l).filter((e=>null!==l[e]&&void 0!==l[e]&&""!==l[e])).map((e=>`${encodeURIComponent(e)}=${encodeURIComponent(l[e])}`)).join("&");console.log("服务状态查询参数:",JSON.stringify(l)),console.log("查询字符串:",p);const m=t.getApiUrl("/api/audioManagement/list"),S=p?`${m}?${p}`:m;let v="",f="";try{v=e.index.getStorageSync("backend-tenant-id")||"",f=e.index.getStorageSync("backend-token")||""}catch(u){console.error("获取认证信息失败:",u)}const R={"Content-Type":"application/json"};f&&(R.Authorization=`Bearer ${f}`),v&&(R["X-Tenant-Id"]=v);const C=await e.index.request({url:S,method:"POST",data:{},header:R,timeout:3e4});if(200===C.statusCode&&C.data&&C.data.success){const e=Array.isArray(C.data.data)?C.data.data:[];if(0===e.length)return this.serviceStatusList=[],void(this.serviceStatusTotal=0);const t=e.map((e=>this.buildServiceStatusItem(e))).filter((e=>null!==e));1===this.serviceStatusPage.current||r?this.serviceStatusList=[...t]:this.serviceStatusList=[...this.serviceStatusList,...t],this.serviceStatusTotal=Number(C.data.total)||0}else this.serviceStatusList=[],this.serviceStatusTotal=0,e.index.showToast({title:(null==(d=C.data)?void 0:d.message)||"获取服务中列表失败",icon:"none"})}catch(l){console.error("获取服务中列表失败:",l);let t="获取服务状态失败,请稍后重试";l.errMsg&&(l.errMsg.includes("timeout")?t="请求超时,请检查网络连接后重试":l.errMsg.includes("fail")&&(t="网络请求失败,请检查网络连接")),e.index.showToast({title:t,icon:"none",duration:3e3})}finally{this.serviceStatusLoading=!1}}},buildServiceStatusItem(e={}){if(!e||"object"!=typeof e)return null;const t=this.formatDateTime(e.createTime),r=[];e.recordingName&&r.push({text:`录音:${e.recordingName}`,color:"blue"}),e.intentionLevel&&r.push({text:`意向:${e.intentionLevel}`,color:"orange"}),e.projectName&&r.push({text:`项目:${e.projectName}`,color:"blue"});const s=i(e.recordingName)||(i(e.customerName)?`${String(e.customerName).trim()}的接待记录`:"")||"";return{id:e.id||"",staffName:e.salesName||"未分配销售",status:e.syncStatus||"服务中",customerName:e.customerName||"",customerPhone:e.customerPhone||"",customerId:e.customerId||"",recordingName:e.recordingName||"",title:s,remarks:e.remarks||"",tags:r,durationText:t?`开始时间:${t}`:"暂无开始时间"}},formatDateTime(e){if(!e)return"";const t=new Date(e);if(Number.isNaN(t.getTime()))return"";return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}:${String(t.getSeconds()).padStart(2,"0")}`},viewServiceDetail(e){console.log("查看服务详情",e)},isUploading(e){return!!e&&Number(this.uploadingCountMap[String(e)]||0)>0},increaseUploadingCount(e){const t=String(e),r=Number(this.uploadingCountMap[t]||0)+1;this.uploadingCountMap={...this.uploadingCountMap,[t]:r},this.persistRecordingSessionToCache()},decreaseUploadingCount(e){const t=String(e),r=Number(this.uploadingCountMap[t]||0),i=Math.max(0,r-1);this.uploadingCountMap={...this.uploadingCountMap,[t]:i},this.persistRecordingSessionToCache()},getUploadResultList(e){const t=String(e||""),r=this.uploadResultMap[t];return Array.isArray(r)?r:[]},setUploadResultList(e,t){const r=String(e||"");this.uploadResultMap={...this.uploadResultMap,[r]:Array.isArray(t)?t:[]},this.persistRecordingSessionToCache()},pushUploadResult(e,t){const r=this.getUploadResultList(e);this.setUploadResultList(e,[...r,t])},updateUploadResult(e,t,r={}){const i=this.getUploadResultList(e).map((e=>e.id!==t?e:{...e,...r}));this.setUploadResultList(e,i)},isMp3UploadFile(e){if(!e||!e.path)return!1;const t=e=>{if(!e||"string"!=typeof e)return"";return(e.split("/").pop()||e.split("\\").pop()||e).trim()},r=[t(e.name),t(e.path)].filter(Boolean);for(const i of r)if(i.toLowerCase().endsWith(".mp3"))return!0;return!1},async chooseAndUploadFile(t){if(!t||!t.id)return void e.index.showToast({title:"无法获取服务记录ID",icon:"none"});if(this.isUploading(t.id))return;const r=await this.selectUploadFile();r&&r.path&&await this.uploadFileForRecord(t,r)},selectUploadFile(){return new Promise((t=>{const r=r=>{const i=(e=>{const t=Array.isArray(null==e?void 0:e.tempFiles)?e.tempFiles:[];if(!t.length)return null;const r=t[0]||{},i=r.path||r.tempFilePath||r.url||"",s=r.name||i.split("/").pop()||`service_file_${Date.now()}`;return i?{path:i,name:s}:null})(r);return i?this.isMp3UploadFile(i)?void t(i):(e.index.showToast({title:"仅支持上传 MP3 文件",icon:"none"}),void t(null)):(e.index.showToast({title:"未选择有效文件",icon:"none"}),void t(null))},i=r=>{((null==r?void 0:r.errMsg)||"").includes("cancel")||(console.error("选择文件失败:",r),e.index.showToast({title:"选择文件失败",icon:"none"})),t(null)};"function"!=typeof e.index.chooseMessageFile?"function"!=typeof e.index.chooseFile?e.index.chooseImage({count:1,success:r,fail:i}):e.index.chooseFile({count:1,extension:[".mp3"],success:r,fail:i}):e.index.chooseMessageFile({count:1,type:"file",success:r,fail:i})}))},getAuthHeaders(){let t="",r="";try{t=e.index.getStorageSync("backend-tenant-id")||"",r=e.index.getStorageSync("backend-token")||""}catch(s){console.error("获取认证信息失败:",s)}const i={};return r&&(i.Authorization=`Bearer ${r}`),t&&(i["X-Tenant-Id"]=t),i},async uploadFileForRecord(r,i,s={}){const n=String(r.id),o=Number(s.segmentNo)||1,a=Math.max(0,Number(s.durationSeconds)||0),c=Math.max(0,Number(null==i?void 0:i.size)||0),d=`seg_${n}_${o}_${Date.now()}`;this.pushUploadResult(n,{id:d,segmentNo:o,durationText:this.formatRecordingElapsed(a),sizeText:this.formatRecordingSize(c),status:"uploading",statusText:"上传中",message:""}),this.increaseUploadingCount(n);try{const o=s.retryOnceOnFail?2:1;let a="";for(let s=1;s<=o;s+=1){const c=await e.index.uploadFile({url:t.getApiUrl("/api/audio/upload"),filePath:i.path,name:"file",formData:{id:r.id,audioId:r.id,customerId:r.customerId||"",customerName:r.customerName||"",fileName:i.name||`service_file_${Date.now()}`,compress:!0},header:this.getAuthHeaders(),timeout:6e4});let l={};try{l="string"==typeof(null==c?void 0:c.data)?JSON.parse(c.data):(null==c?void 0:c.data)||{}}catch(u){l=(null==c?void 0:c.data)||{}}if(200===(null==c?void 0:c.statusCode)&&!1!==(null==l?void 0:l.success))return this.updateUploadResult(n,d,{status:"success",statusText:"上传成功",message:s>1?`第${s}次尝试成功`:""}),this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0}),!0;a=(null==l?void 0:l.message)||`上传失败(HTTP ${(null==c?void 0:c.statusCode)||"--"})`,s{var s,n;if(i.confirm)try{e.index.showLoading({title:"结束中..."});const i=t.getApiUrl("/api/audioManagement/finishServiceById");let a="",c="";try{a=e.index.getStorageSync("backend-tenant-id")||"",c=e.index.getStorageSync("backend-token")||""}catch(o){console.error("获取认证信息失败:",o)}const d={"Content-Type":"application/json"};c&&(d.Authorization=`Bearer ${c}`),a&&(d["X-Tenant-Id"]=a);const u={id:r.id},l=await e.index.request({url:i,method:"POST",data:u,header:d,timeout:3e4});if(e.index.hideLoading(),200===l.statusCode&&l.data&&l.data.success){const t=((null==(s=l.data)?void 0:s.message)||"结束服务成功").replace(/[A-Za-z]+/g,"").split(/\n/).map((e=>e.trim())).filter(Boolean).join("\n")||"结束服务成功";e.index.showToast({title:t,icon:"success"}),this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})}else e.index.showToast({title:(null==(n=l.data)?void 0:n.message)||"结束服务失败",icon:"none"})}catch(a){e.index.hideLoading(),console.error("结束服务失败:",a);let t="结束服务失败,请重试";a.errMsg&&(a.errMsg.includes("timeout")?t="请求超时,请检查网络连接后重试":a.errMsg.includes("fail")&&(t="网络请求失败,请检查网络连接")),e.index.showToast({title:t,icon:"none",duration:3e3})}}}):e.index.showToast({title:"无法获取服务记录ID",icon:"none"})},showSupplementDialog(e){this.currentSupplementItem=e,this.supplementForm={customerName:e.customerName||"",contact:e.customerPhone||"",recordingName:e.recordingName||"",remarks:e.remarks||""},this.showSupplementModal=!0},closeSupplementDialog(){this.showSupplementModal=!1,this.currentSupplementItem=null,this.supplementForm={customerName:"",contact:"",recordingName:"",remarks:""}},async saveSupplement(){var s,n,o,a,c;const d=null==(s=this.supplementForm.contact)?void 0:s.trim();if(!d||this.isValidPhoneNumber(d))if(this.currentSupplementItem&&this.currentSupplementItem.id)try{e.index.showLoading({title:"保存中..."});let s="",l="";try{s=e.index.getStorageSync("backend-tenant-id")||"",l=e.index.getStorageSync("backend-token")||""}catch(u){console.error("获取认证信息失败:",u)}const g={"Content-Type":"application/json"};l&&(g.Authorization=`Bearer ${l}`),s&&(g["X-Tenant-Id"]=s);let h={};try{h=e.index.getStorageSync("backend-login-response")||{}}catch(u){console.error("读取登录信息失败:",u)}const p=r.store.userInfo||{},m=i(h.phone,h.userName,p.username),S=i(h.realName,h.name,h.nickName,p.nickname,h.userName,p.username),v={id:this.currentSupplementItem.id,customerId:this.currentSupplementItem.customerId||"",customerName:(null==(n=this.supplementForm.customerName)?void 0:n.trim())||"",customerPhone:d||"",recordingName:(null==(o=this.supplementForm.recordingName)?void 0:o.trim())||"",remarks:(null==(a=this.supplementForm.remarks)?void 0:a.trim())||"",salesPhone:m,salesName:S},f=await e.index.request({url:t.getApiUrl("/api/audioManagement/updateForCustomerInfo"),method:"PUT",data:v,header:g,timeout:3e4});e.index.hideLoading(),200===f.statusCode&&f.data&&f.data.success?(e.index.showToast({title:"补录成功",icon:"success"}),this.closeSupplementDialog(),this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})):e.index.showToast({title:(null==(c=f.data)?void 0:c.message)||"补录失败",icon:"none"})}catch(l){e.index.hideLoading(),console.error("补录失败:",l);let t="补录失败,请重试";l.errMsg&&(l.errMsg.includes("timeout")?t="请求超时,请检查网络连接后重试":l.errMsg.includes("fail")&&(t="网络请求失败,请检查网络连接")),e.index.showToast({title:t,icon:"none",duration:3e3})}else e.index.showToast({title:"无法获取服务记录ID",icon:"none"});else e.index.showToast({title:"请输入有效的客户电话",icon:"none"})},isValidPhoneNumber(e){const t=null==e?void 0:e.trim();return!!t&&/^1[3-9]\d{9}$/.test(t)}}};if(!Array){e.resolveComponent("uni-icons")()}Math;const o=e._export_sfc(n,[["render",function(t,r,i,s,n,o){return e.e({a:e.t(n.serviceStatusTotal),b:e.o(((...e)=>o.onServiceStatusSearch&&o.onServiceStatusSearch(...e)),"1e"),c:n.serviceStatusQuery.salesName,d:e.o((e=>n.serviceStatusQuery.salesName=e.detail.value),"5b"),e:e.o(((...e)=>o.onServiceStatusSearch&&o.onServiceStatusSearch(...e)),"4f"),f:n.serviceStatusQuery.customerName,g:e.o((e=>n.serviceStatusQuery.customerName=e.detail.value),"ff"),h:n.isAdmin},n.isAdmin?{i:e.o(((...e)=>o.onServiceStatusSearch&&o.onServiceStatusSearch(...e)),"91"),j:n.serviceStatusQuery.salesPhone,k:e.o((e=>n.serviceStatusQuery.salesPhone=e.detail.value),"86")}:{},{l:i.showReceptionEntryShortcut},i.showReceptionEntryShortcut?{m:e.p({type:"home",size:"18",color:"#2A68FF"}),n:e.o(((...e)=>o.goReceptionEntry&&o.goReceptionEntry(...e)),"5d")}:{},{o:e.p({type:"refresh",size:"18",color:"#2A68FF"}),p:e.o(((...e)=>o.onServiceStatusRefresh&&o.onServiceStatusRefresh(...e)),"84"),q:e.f(n.serviceStatusList,((t,r,i)=>e.e({a:e.t(t.staffName?t.staffName.charAt(0):"未"),b:e.t(t.staffName||"未分配销售"),c:"ba2ce898-2-"+i,d:t.title},t.title?{e:e.t(t.title)}:{},{f:e.t(t.customerName||"未知客户"),g:t.customerPhone},t.customerPhone?{h:e.t(t.customerPhone)}:{},{i:e.f(t.tags,((t,r,i)=>({a:e.t(t.text),b:e.n("tag-"+t.color),c:r}))),j:t.alert},t.alert?e.e({k:e.t(t.alert.title),l:"risk"===t.alert.type},"risk"===t.alert.type?{m:e.t(t.alert.message)}:{n:e.f(t.alert.messages,((t,r,i)=>({a:e.t(t),b:r})))},{o:e.n("risk"===t.alert.type?"alert-risk-text":"alert-reminder-text"),p:e.n("risk"===t.alert.type?"alert-risk-box":"alert-reminder-box")}):{},{q:e.t(t.durationText),r:!o.isRecordingItem(t.id)},o.isRecordingItem(t.id)?{t:e.o((e=>o.stopPhoneRecordAndUpload(t)),t.id||r)}:{s:e.o((e=>o.startPhoneRecord(t)),t.id||r)},{v:e.o((e=>o.showSupplementDialog(t)),t.id||r),w:e.o((e=>o.chooseAndUploadFile(t)),t.id||r),x:e.o((e=>o.finishService(t)),t.id||r),y:o.isRecordingItem(t.id)},o.isRecordingItem(t.id)?{z:e.t(o.formatRecordingElapsed(n.recordingElapsedSeconds)),A:e.t(o.formatRecordingSize(n.recordingEstimatedSizeBytes))}:{},{B:o.getUploadResultList(t.id).length},o.getUploadResultList(t.id).length?{C:e.f(o.getUploadResultList(t.id),((t,r,i)=>e.e({a:e.t(t.segmentNo),b:e.t(t.durationText||"--:--"),c:e.t(t.sizeText||"--"),d:e.t(t.statusText),e:t.message},t.message?{f:e.t(t.message)}:{},{g:t.id})))}:{},{D:t.id||r,E:e.o((e=>o.viewServiceDetail(t)),t.id||r)}))),r:e.p({type:"bars",size:"16",color:"#007AFF"}),s:e.t(i.recordStateLabel),t:!n.serviceStatusLoading&&!n.serviceStatusList.length},n.serviceStatusLoading||n.serviceStatusList.length?{}:{v:e.t(i.emptyListHint)},{w:n.serviceStatusLoading&&n.serviceStatusList.length},(n.serviceStatusLoading&&n.serviceStatusList.length,{}),{x:e.o(((...e)=>o.onServiceStatusReachBottom&&o.onServiceStatusReachBottom(...e)),"d2"),y:n.showSupplementModal},n.showSupplementModal?{z:e.p({type:"close",size:"20",color:"#999"}),A:e.o(((...e)=>o.closeSupplementDialog&&o.closeSupplementDialog(...e)),"8e"),B:n.supplementForm.customerName,C:e.o((e=>n.supplementForm.customerName=e.detail.value),"72"),D:n.supplementForm.contact,E:e.o((e=>n.supplementForm.contact=e.detail.value),"04"),F:n.supplementForm.recordingName,G:e.o((e=>n.supplementForm.recordingName=e.detail.value),"7b"),H:n.supplementForm.remarks,I:e.o((e=>n.supplementForm.remarks=e.detail.value),"36"),J:e.o(((...e)=>o.closeSupplementDialog&&o.closeSupplementDialog(...e)),"c5"),K:e.o(((...e)=>o.saveSupplement&&o.saveSupplement(...e)),"13"),L:e.o((()=>{}),"b8"),M:e.o(((...e)=>o.closeSupplementDialog&&o.closeSupplementDialog(...e)),"7c")}:{})}],["__scopeId","data-v-ba2ce898"]]);wx.createComponent(o); +"use strict";const e=require("../../common/vendor.js"),t=require("../../common/config.js"),r=require("../../common/store.js");function i(...e){for(const t of e){if(null==t)continue;const e=String(t).trim();if(e)return e}return""}const n={recordingServiceId:null,recordingContext:null,recordingElapsedSeconds:0,recordingEstimatedSizeBytes:0,recordingManualStopRequested:!1,recordingCurrentSegmentNo:0,recordingCurrentSegmentStartAt:0,recordingCurrentSegmentElapsedSeconds:0,uploadingCountMap:{},uploadResultMap:{}},s={props:{recordStateLabel:{type:String,default:"服务中"},emptyListHint:{type:String,default:"暂无服务中记录"},showReceptionEntryShortcut:{type:Boolean,default:!1},showRecordStateIndicator:{type:Boolean,default:!0}},data:()=>({serviceStatusLoading:!1,serviceStatusTotal:0,serviceStatusPage:{current:1,size:10},serviceStatusQuery:{salesName:"",customerName:"",salesPhone:""},isAdmin:!1,currentUserPhone:"",serviceStatusList:[],showSupplementModal:!1,currentSupplementItem:null,uploadingIds:[],supplementForm:{customerName:"",contact:"",recordingName:"",remarks:""},recorderSupported:!1,recorderManager:null,recordingServiceId:n.recordingServiceId,recordingContext:n.recordingContext,recordingElapsedSeconds:n.recordingElapsedSeconds,recordingEstimatedSizeBytes:n.recordingEstimatedSizeBytes,recordingTicker:null,keepScreenOnEnabled:!1,recordingSampleRate:16e3,recordingEncodeBitRate:32e3,recordingSegmentDurationMs:3e5,recordingManualStopRequested:n.recordingManualStopRequested,recordingCurrentSegmentNo:n.recordingCurrentSegmentNo,recordingCurrentSegmentStartAt:n.recordingCurrentSegmentStartAt,recordingCurrentSegmentElapsedSeconds:n.recordingCurrentSegmentElapsedSeconds,uploadingCountMap:{...n.uploadingCountMap},uploadResultMap:{...n.uploadResultMap}}),mounted(){var e;this.checkUserRole(),this.loadCurrentUserPhone(),this.initServiceRecorder(),this.restoreRecordingSessionFromCache(),this.recordingServiceId&&(null==(e=this.recordingContext)?void 0:e.id)&&this.startRecordingTicker({reset:!1}),this.fetchServiceStatusList()},beforeUnmount(){this.pauseRecordingTicker(),this.persistRecordingSessionToCache()},methods:{persistRecordingSessionToCache(){n.recordingServiceId=this.recordingServiceId||null,n.recordingContext=this.recordingContext?{...this.recordingContext}:null,n.recordingElapsedSeconds=Number(this.recordingElapsedSeconds)||0,n.recordingEstimatedSizeBytes=Number(this.recordingEstimatedSizeBytes)||0,n.recordingManualStopRequested=!!this.recordingManualStopRequested,n.recordingCurrentSegmentNo=Number(this.recordingCurrentSegmentNo)||0,n.recordingCurrentSegmentStartAt=Number(this.recordingCurrentSegmentStartAt)||0,n.recordingCurrentSegmentElapsedSeconds=Number(this.recordingCurrentSegmentElapsedSeconds)||0,n.uploadingCountMap={...this.uploadingCountMap},n.uploadResultMap={...this.uploadResultMap}},restoreRecordingSessionFromCache(){this.recordingServiceId=n.recordingServiceId||null,this.recordingContext=n.recordingContext?{...n.recordingContext}:null,this.recordingElapsedSeconds=Number(n.recordingElapsedSeconds)||0,this.recordingEstimatedSizeBytes=Number(n.recordingEstimatedSizeBytes)||0,this.recordingManualStopRequested=!!n.recordingManualStopRequested,this.recordingCurrentSegmentNo=Number(n.recordingCurrentSegmentNo)||0,this.recordingCurrentSegmentStartAt=Number(n.recordingCurrentSegmentStartAt)||0,this.recordingCurrentSegmentElapsedSeconds=Number(n.recordingCurrentSegmentElapsedSeconds)||0,this.uploadingCountMap={...n.uploadingCountMap},this.uploadResultMap={...n.uploadResultMap}},initServiceRecorder(){this.recorderSupported="function"==typeof e.index.getRecorderManager,this.recorderSupported&&(this.ensureRecorderManager(),this.bindRecorderEvents())},ensureRecorderManager(){return!!this.recorderSupported&&(this.recorderManager||(this.recorderManager=e.index.getRecorderManager()),!!this.recorderManager)},bindRecorderEvents(){this.ensureRecorderManager()&&("function"==typeof this.recorderManager.offStop&&this.recorderManager.offStop(),"function"==typeof this.recorderManager.offError&&this.recorderManager.offError(),this.recorderManager.onStop((e=>{const t=this.recordingContext,r=this.recordingServiceId,i=this.recordingCurrentSegmentNo,n=this.recordingCurrentSegmentElapsedSeconds||this.recordingElapsedSeconds||0,s=!(!r||!t||!t.id||this.recordingManualStopRequested),o=i>0?i:1;this.stopRecordingTicker();const a=e.tempFilePath||"";if(t&&t.id){const r=this.getAudioUploadFileName({segmentNo:o});a?this.uploadFileForRecord(t,{path:a,name:r,size:Number(e.fileSize)||0},{silent:!0,segmentNo:o,durationSeconds:n,retryOnceOnFail:!0}):this.pushUploadResult(String(t.id),{id:`seg_${String(t.id)}_${o}_${Date.now()}`,segmentNo:o,durationText:this.formatRecordingElapsed(n),sizeText:"--",status:"failed",statusText:"上传失败",message:"未获取到录音文件(已重试1次)"})}if(s)return this.setKeepScreenOn(!0),void setTimeout((()=>{this.startNewSegmentRecording()}),80);this.recordingServiceId=null,this.recordingContext=null,this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.setKeepScreenOn(!1),this.persistRecordingSessionToCache()})),this.recorderManager.onError((()=>{this.recordingServiceId=null,this.recordingContext=null,this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.stopRecordingTicker(),this.setKeepScreenOn(!1),this.persistRecordingSessionToCache(),e.index.showToast({title:"录音出错",icon:"none"})})))},setKeepScreenOn(t){"function"==typeof e.index.setKeepScreenOn&&e.index.setKeepScreenOn({keepScreenOn:!!t,success:()=>{this.keepScreenOnEnabled=!!t},fail:()=>{this.keepScreenOnEnabled=!1}})},startRecordingTicker(e={}){const{reset:t=!0}=e;if(this.pauseRecordingTicker(),t)this.recordingElapsedSeconds=0,this.recordingEstimatedSizeBytes=0;else if(this.recordingCurrentSegmentStartAt){const e=Math.max(0,Math.floor((Date.now()-Number(this.recordingCurrentSegmentStartAt))/1e3));this.recordingElapsedSeconds=e;const t=(Number(this.recordingEncodeBitRate)||32e3)/8;this.recordingEstimatedSizeBytes=Math.floor(e*t)}const r=(Number(this.recordingEncodeBitRate)||32e3)/8;this.recordingTicker=setInterval((()=>{this.recordingCurrentSegmentStartAt?this.recordingElapsedSeconds=Math.max(0,Math.floor((Date.now()-Number(this.recordingCurrentSegmentStartAt))/1e3)):this.recordingElapsedSeconds+=1,this.recordingCurrentSegmentElapsedSeconds=this.recordingElapsedSeconds,this.recordingEstimatedSizeBytes=Math.floor(this.recordingElapsedSeconds*r),this.persistRecordingSessionToCache()}),1e3)},pauseRecordingTicker(){this.recordingTicker&&clearInterval(this.recordingTicker),this.recordingTicker=null},stopRecordingTicker(){this.pauseRecordingTicker(),this.recordingCurrentSegmentElapsedSeconds=this.recordingElapsedSeconds,this.recordingElapsedSeconds=0,this.recordingEstimatedSizeBytes=0,this.persistRecordingSessionToCache()},startNewSegmentRecording(){var t;if(this.recorderManager&&this.recordingServiceId&&(null==(t=this.recordingContext)?void 0:t.id)){this.recordingCurrentSegmentNo+=1,this.recordingCurrentSegmentStartAt=Date.now(),this.recordingCurrentSegmentElapsedSeconds=0;try{this.recorderManager.start({duration:this.recordingSegmentDurationMs,sampleRate:this.recordingSampleRate,numberOfChannels:1,encodeBitRate:this.recordingEncodeBitRate,format:"mp3"}),this.startRecordingTicker(),this.persistRecordingSessionToCache()}catch(r){this.recordingServiceId=null,this.recordingContext=null,this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.stopRecordingTicker(),this.setKeepScreenOn(!1),this.persistRecordingSessionToCache(),e.index.showToast({title:"无法开始录音",icon:"none"})}}},formatRecordingElapsed(e){const t=Math.max(0,Number(e)||0);return`${String(Math.floor(t/60)).padStart(2,"0")}:${String(t%60).padStart(2,"0")}`},formatRecordingSize(e){const t=Math.max(0,Number(e)||0);return t<1024?`${t} B`:t<1048576?`${(t/1024).toFixed(1)} KB`:`${(t/1048576).toFixed(2)} MB`},isRecordingItem(e){return!(!e||!this.recordingServiceId)&&String(e)===this.recordingServiceId},startPhoneRecord(t){this.ensureRecorderManager(),this.bindRecorderEvents(),this.recorderSupported&&this.recorderManager?t&&t.id?this.recordingServiceId?e.index.showToast({title:"请先停止当前录音",icon:"none"}):this.isUploading(t.id)?e.index.showToast({title:"正在上传,请稍候",icon:"none"}):(this.recordingManualStopRequested=!1,this.recordingCurrentSegmentNo=0,this.recordingCurrentSegmentStartAt=0,this.recordingCurrentSegmentElapsedSeconds=0,this.setUploadResultList(String(t.id),[]),this.recordingContext={...t},this.recordingServiceId=String(t.id),this.setKeepScreenOn(!0),this.persistRecordingSessionToCache(),this.startNewSegmentRecording()):e.index.showToast({title:"无法获取服务记录ID",icon:"none"}):e.index.showToast({title:"当前环境不支持录音,请使用微信小程序或 App",icon:"none"})},stopPhoneRecordAndUpload(e){this.recorderManager&&(null==e?void 0:e.id)&&String(e.id)===this.recordingServiceId&&(this.recordingManualStopRequested=!0,this.persistRecordingSessionToCache(),this.recorderManager.stop())},checkUserRole(){try{const t=e=>"string"==typeof e?e.split(",").map((e=>e.trim().toLowerCase())).filter(Boolean):[],r=e.index.getStorageSync("backend-role-name")||"",i=(e.index.getStorageSync("backend-login-response")||{}).roleName||"",n=[...t(r),...t(i)];this.isAdmin=n.some((e=>e.includes("admin"))),console.log("用户角色检查:",{storedRole:r,respRole:i,roles:n,isAdmin:this.isAdmin})}catch(t){console.error("检查用户角色失败:",t),this.isAdmin=!1}},loadCurrentUserPhone(){try{const t=e.index.getStorageSync("backend-login-response")||{};t.phone?this.currentUserPhone=t.phone:t.userName?this.currentUserPhone=t.userName:this.currentUserPhone=""}catch(t){console.error("加载当前用户手机号或登录账户失败:",t),this.currentUserPhone=""}},onServiceStatusSearch(){this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})},onServiceStatusRefresh(){this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})},goReceptionEntry(){e.index.navigateTo({url:"/pages-subpackage/furniture_reception/furniture_reception_entry?tab=reception",fail:()=>{e.index.showToast({title:"跳转接待失败",icon:"none"})}})},onServiceStatusReachBottom(){this.serviceStatusLoading||this.serviceStatusList.length>=this.serviceStatusTotal||(this.serviceStatusPage.current+=1,this.fetchServiceStatusList())},async fetchServiceStatusList({force:r=!1}={}){var i,n,s,o,a,c,d;if(!this.serviceStatusLoading||r){this.serviceStatusLoading=!0;try{const l={current:this.serviceStatusPage.current,size:this.serviceStatusPage.size,serviceStatus:"服务中"},g=null==(n=null==(i=this.serviceStatusQuery)?void 0:i.salesName)?void 0:n.trim(),h=null==(o=null==(s=this.serviceStatusQuery)?void 0:s.customerName)?void 0:o.trim();if(g&&(l.salesName=g),h&&(l.customerName=h),this.isAdmin){const e=null==(c=null==(a=this.serviceStatusQuery)?void 0:a.salesPhone)?void 0:c.trim();e&&(l.salesPhone=e)}else this.currentUserPhone&&(l.salesPhone=this.currentUserPhone);l.serviceStatus="服务中";const p=Object.keys(l).filter((e=>null!==l[e]&&void 0!==l[e]&&""!==l[e])).map((e=>`${encodeURIComponent(e)}=${encodeURIComponent(l[e])}`)).join("&");console.log("服务状态查询参数:",JSON.stringify(l)),console.log("查询字符串:",p);const m=t.getApiUrl("/api/audioManagement/list"),S=p?`${m}?${p}`:m;let v="",f="";try{v=e.index.getStorageSync("backend-tenant-id")||"",f=e.index.getStorageSync("backend-token")||""}catch(u){console.error("获取认证信息失败:",u)}const y={"Content-Type":"application/json"};f&&(y.Authorization=`Bearer ${f}`),v&&(y["X-Tenant-Id"]=v);const R=await e.index.request({url:S,method:"POST",data:{},header:y,timeout:3e4});if(200===R.statusCode&&R.data&&R.data.success){const e=Array.isArray(R.data.data)?R.data.data:[];if(0===e.length)return this.serviceStatusList=[],void(this.serviceStatusTotal=0);const t=e.map((e=>this.buildServiceStatusItem(e))).filter((e=>null!==e));1===this.serviceStatusPage.current||r?this.serviceStatusList=[...t]:this.serviceStatusList=[...this.serviceStatusList,...t],this.serviceStatusTotal=Number(R.data.total)||0}else this.serviceStatusList=[],this.serviceStatusTotal=0,e.index.showToast({title:(null==(d=R.data)?void 0:d.message)||"获取服务中列表失败",icon:"none"})}catch(l){console.error("获取服务中列表失败:",l);let t="获取服务状态失败,请稍后重试";l.errMsg&&(l.errMsg.includes("timeout")?t="请求超时,请检查网络连接后重试":l.errMsg.includes("fail")&&(t="网络请求失败,请检查网络连接")),e.index.showToast({title:t,icon:"none",duration:3e3})}finally{this.serviceStatusLoading=!1}}},buildServiceStatusItem(e={}){if(!e||"object"!=typeof e)return null;const t=this.formatDateTime(e.createTime),r=[];e.recordingName&&r.push({text:`录音:${e.recordingName}`,color:"blue"}),e.intentionLevel&&r.push({text:`意向:${e.intentionLevel}`,color:"orange"}),e.projectName&&r.push({text:`项目:${e.projectName}`,color:"blue"});const n=i(e.recordingName)||(i(e.customerName)?`${String(e.customerName).trim()}的接待记录`:"")||"";return{id:e.id||"",staffName:e.salesName||"未分配销售",status:e.syncStatus||"服务中",customerName:e.customerName||"",customerPhone:e.customerPhone||"",customerId:e.customerId||"",recordingName:e.recordingName||"",title:n,remarks:e.remarks||"",tags:r,durationText:t?`开始时间:${t}`:"暂无开始时间"}},formatDateTime(e){if(!e)return"";const t=new Date(e);if(Number.isNaN(t.getTime()))return"";return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")} ${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}:${String(t.getSeconds()).padStart(2,"0")}`},viewServiceDetail(e){console.log("查看服务详情",e)},isUploading(e){return!!e&&Number(this.uploadingCountMap[String(e)]||0)>0},increaseUploadingCount(e){const t=String(e),r=Number(this.uploadingCountMap[t]||0)+1;this.uploadingCountMap={...this.uploadingCountMap,[t]:r},this.persistRecordingSessionToCache()},decreaseUploadingCount(e){const t=String(e),r=Number(this.uploadingCountMap[t]||0),i=Math.max(0,r-1);this.uploadingCountMap={...this.uploadingCountMap,[t]:i},this.persistRecordingSessionToCache()},getUploadResultList(e){const t=String(e||""),r=this.uploadResultMap[t];return Array.isArray(r)?r:[]},setUploadResultList(e,t){const r=String(e||"");this.uploadResultMap={...this.uploadResultMap,[r]:Array.isArray(t)?t:[]},this.persistRecordingSessionToCache()},pushUploadResult(e,t){const r=this.getUploadResultList(e);this.setUploadResultList(e,[...r,t])},updateUploadResult(e,t,r={}){const i=this.getUploadResultList(e).map((e=>e.id!==t?e:{...e,...r}));this.setUploadResultList(e,i)},isMp3UploadFile(e){if(!e||!e.path)return!1;const t=e=>{if(!e||"string"!=typeof e)return"";return(e.split("/").pop()||e.split("\\").pop()||e).trim()},r=[t(e.name),t(e.path)].filter(Boolean);for(const i of r)if(i.toLowerCase().endsWith(".mp3"))return!0;return!1},async chooseAndUploadFile(t){if(!t||!t.id)return void e.index.showToast({title:"无法获取服务记录ID",icon:"none"});if(this.isUploading(t.id))return;const r=await this.selectUploadFile();r&&r.path&&await this.uploadFileForRecord(t,r)},selectUploadFile(){return new Promise((t=>{const r=r=>{const i=(e=>{const t=Array.isArray(null==e?void 0:e.tempFiles)?e.tempFiles:[];if(!t.length)return null;const r=t[0]||{},i=r.path||r.tempFilePath||r.url||"",n=r.name||i.split("/").pop()||`service_file_${Date.now()}`;return i?{path:i,name:n}:null})(r);return i?this.isMp3UploadFile(i)?void t(i):(e.index.showToast({title:"仅支持上传 MP3 文件",icon:"none"}),void t(null)):(e.index.showToast({title:"未选择有效文件",icon:"none"}),void t(null))},i=r=>{((null==r?void 0:r.errMsg)||"").includes("cancel")||(console.error("选择文件失败:",r),e.index.showToast({title:"选择文件失败",icon:"none"})),t(null)};"function"!=typeof e.index.chooseMessageFile?"function"!=typeof e.index.chooseFile?e.index.chooseImage({count:1,success:r,fail:i}):e.index.chooseFile({count:1,extension:[".mp3"],success:r,fail:i}):e.index.chooseMessageFile({count:1,type:"file",success:r,fail:i})}))},getAuthHeaders(){let t="",r="";try{t=e.index.getStorageSync("backend-tenant-id")||"",r=e.index.getStorageSync("backend-token")||""}catch(n){console.error("获取认证信息失败:",n)}const i={};return r&&(i.Authorization=`Bearer ${r}`),t&&(i["X-Tenant-Id"]=t),i},getAudioUploadFileName(t={}){let r="",i="";try{r=String(e.index.getStorageSync("backend-tenant-id")||"").trim()}catch(l){}try{const t=this.currentUserPhone||"",r=e.index.getStorageSync("backend-login-response")||{},n=r.phone||r.userName||"";i=String(t||n||"").replace(/\D/g,"")}catch(l){}const n=new Date,s=e=>String(e).padStart(2,"0"),o=`${n.getFullYear()}${s(n.getMonth()+1)}${s(n.getDate())}${s(n.getHours())}${s(n.getMinutes())}${s(n.getSeconds())}`,a=Number(t.segmentNo),c=Number.isFinite(a)&&a>1?`_${a}`:"",d="string"==typeof t.ext&&t.ext?t.ext:".mp3",u=d.startsWith(".")?d:`.${d}`;return`${`${r?`${r}_`:""}${i?`${i}_`:""}${o}${c}`.replace(/[/\\:*?"<>|]/g,"_")||`upload_${o}${c}`}${u}`},prepareAudioFileForUpload:(t,r)=>new Promise((i=>{if(!t||!r)return void i({uploadPath:t,tempCopyPath:null});const n=(String(t).split(/[/\\]/).pop()||"").split("?")[0]||"",s=n!==r||n.includes("=")||/\.durationTime=/i.test(n);let o="";try{o=e.index.env&&e.index.env.USER_DATA_PATH||""}catch(c){}if(!o&&void 0!==e.wx$1&&e.wx$1.env&&(o=e.wx$1.env.USER_DATA_PATH||""),!s||!o||"function"!=typeof e.index.getFileSystemManager)return void i({uploadPath:t,tempCopyPath:null});const a=`${String(o).replace(/\/+$/,"")}/${r}`;e.index.getFileSystemManager().copyFile({srcPath:t,destPath:a,success:()=>i({uploadPath:a,tempCopyPath:a}),fail:e=>{console.warn("prepareAudioFileForUpload copyFile failed:",e),i({uploadPath:t,tempCopyPath:null})}})})),safeUnlinkAudioCopy(t){if(t&&"function"==typeof e.index.getFileSystemManager)try{e.index.getFileSystemManager().unlink({filePath:t,fail:()=>{}})}catch(r){}},async uploadFileForRecord(r,i,n={}){const s=String(r.id),o=Number(n.segmentNo)||1,a=Math.max(0,Number(n.durationSeconds)||0),c=(()=>{const e=a/60;return Number.isFinite(e)?String(Math.round(1e6*e)/1e6):"0"})(),d=Math.max(0,Number(null==i?void 0:i.size)||0),u=`seg_${s}_${o}_${Date.now()}`;this.pushUploadResult(s,{id:u,segmentNo:o,durationText:this.formatRecordingElapsed(a),sizeText:this.formatRecordingSize(d),status:"uploading",statusText:"上传中",message:""}),this.increaseUploadingCount(s);let l=null;try{const a=this.getAudioUploadFileName({segmentNo:o}),d=await this.prepareAudioFileForUpload(i.path,a),h=d.uploadPath;l=d.tempCopyPath;const p=n.retryOnceOnFail?2:1;let m="";for(let i=1;i<=p;i+=1){const n=await e.index.uploadFile({url:t.getApiUrl("/api/audio/upload"),filePath:h,name:"file",formData:{id:r.id,audioId:r.id,audioDuration:c,customerId:r.customerId||"",customerName:r.customerName||"",fileName:a,compress:!0},header:this.getAuthHeaders(),timeout:6e4});let o={};try{o="string"==typeof(null==n?void 0:n.data)?JSON.parse(n.data):(null==n?void 0:n.data)||{}}catch(g){o=(null==n?void 0:n.data)||{}}if(200===(null==n?void 0:n.statusCode)&&!1!==(null==o?void 0:o.success))return this.updateUploadResult(s,u,{status:"success",statusText:"上传成功",message:i>1?`第${i}次尝试成功`:""}),this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0}),!0;m=(null==o?void 0:o.message)||`上传失败(HTTP ${(null==n?void 0:n.statusCode)||"--"})`,i{var n,s;if(i.confirm)try{e.index.showLoading({title:"结束中..."});const i=t.getApiUrl("/api/audioManagement/finishServiceById");let a="",c="";try{a=e.index.getStorageSync("backend-tenant-id")||"",c=e.index.getStorageSync("backend-token")||""}catch(o){console.error("获取认证信息失败:",o)}const d={"Content-Type":"application/json"};c&&(d.Authorization=`Bearer ${c}`),a&&(d["X-Tenant-Id"]=a);const u={id:r.id},l=await e.index.request({url:i,method:"POST",data:u,header:d,timeout:3e4});if(e.index.hideLoading(),200===l.statusCode&&l.data&&l.data.success){const t=((null==(n=l.data)?void 0:n.message)||"结束服务成功").replace(/[A-Za-z]+/g,"").split(/\n/).map((e=>e.trim())).filter(Boolean).join("\n")||"结束服务成功";e.index.showToast({title:t,icon:"success"}),this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})}else e.index.showToast({title:(null==(s=l.data)?void 0:s.message)||"结束服务失败",icon:"none"})}catch(a){e.index.hideLoading(),console.error("结束服务失败:",a);let t="结束服务失败,请重试";a.errMsg&&(a.errMsg.includes("timeout")?t="请求超时,请检查网络连接后重试":a.errMsg.includes("fail")&&(t="网络请求失败,请检查网络连接")),e.index.showToast({title:t,icon:"none",duration:3e3})}}}):e.index.showToast({title:"无法获取服务记录ID",icon:"none"})},showSupplementDialog(e){this.currentSupplementItem=e,this.supplementForm={customerName:e.customerName||"",contact:e.customerPhone||"",recordingName:e.recordingName||"",remarks:e.remarks||""},this.showSupplementModal=!0},closeSupplementDialog(){this.showSupplementModal=!1,this.currentSupplementItem=null,this.supplementForm={customerName:"",contact:"",recordingName:"",remarks:""}},async saveSupplement(){var n,s,o,a,c;const d=null==(n=this.supplementForm.contact)?void 0:n.trim();if(!d||this.isValidPhoneNumber(d))if(this.currentSupplementItem&&this.currentSupplementItem.id)try{e.index.showLoading({title:"保存中..."});let n="",l="";try{n=e.index.getStorageSync("backend-tenant-id")||"",l=e.index.getStorageSync("backend-token")||""}catch(u){console.error("获取认证信息失败:",u)}const g={"Content-Type":"application/json"};l&&(g.Authorization=`Bearer ${l}`),n&&(g["X-Tenant-Id"]=n);let h={};try{h=e.index.getStorageSync("backend-login-response")||{}}catch(u){console.error("读取登录信息失败:",u)}const p=r.store.userInfo||{},m=i(h.phone,h.userName,p.username),S=i(h.realName,h.name,h.nickName,p.nickname,h.userName,p.username),v={id:this.currentSupplementItem.id,customerId:this.currentSupplementItem.customerId||"",customerName:(null==(s=this.supplementForm.customerName)?void 0:s.trim())||"",customerPhone:d||"",recordingName:(null==(o=this.supplementForm.recordingName)?void 0:o.trim())||"",remarks:(null==(a=this.supplementForm.remarks)?void 0:a.trim())||"",salesPhone:m,salesName:S},f=await e.index.request({url:t.getApiUrl("/api/audioManagement/updateForCustomerInfo"),method:"PUT",data:v,header:g,timeout:3e4});e.index.hideLoading(),200===f.statusCode&&f.data&&f.data.success?(e.index.showToast({title:"补录成功",icon:"success"}),this.closeSupplementDialog(),this.serviceStatusPage.current=1,this.fetchServiceStatusList({force:!0})):e.index.showToast({title:(null==(c=f.data)?void 0:c.message)||"补录失败",icon:"none"})}catch(l){e.index.hideLoading(),console.error("补录失败:",l);let t="补录失败,请重试";l.errMsg&&(l.errMsg.includes("timeout")?t="请求超时,请检查网络连接后重试":l.errMsg.includes("fail")&&(t="网络请求失败,请检查网络连接")),e.index.showToast({title:t,icon:"none",duration:3e3})}else e.index.showToast({title:"无法获取服务记录ID",icon:"none"});else e.index.showToast({title:"请输入有效的客户电话",icon:"none"})},isValidPhoneNumber(e){const t=null==e?void 0:e.trim();return!!t&&/^1[3-9]\d{9}$/.test(t)}}};if(!Array){e.resolveComponent("uni-icons")()}Math;const o=e._export_sfc(s,[["render",function(t,r,i,n,s,o){return e.e({a:e.t(s.serviceStatusTotal),b:e.o(((...e)=>o.onServiceStatusSearch&&o.onServiceStatusSearch(...e)),"1e"),c:s.serviceStatusQuery.salesName,d:e.o((e=>s.serviceStatusQuery.salesName=e.detail.value),"5b"),e:e.o(((...e)=>o.onServiceStatusSearch&&o.onServiceStatusSearch(...e)),"4f"),f:s.serviceStatusQuery.customerName,g:e.o((e=>s.serviceStatusQuery.customerName=e.detail.value),"ff"),h:s.isAdmin},s.isAdmin?{i:e.o(((...e)=>o.onServiceStatusSearch&&o.onServiceStatusSearch(...e)),"91"),j:s.serviceStatusQuery.salesPhone,k:e.o((e=>s.serviceStatusQuery.salesPhone=e.detail.value),"86")}:{},{l:i.showReceptionEntryShortcut},i.showReceptionEntryShortcut?{m:e.p({type:"home",size:"18",color:"#2A68FF"}),n:e.o(((...e)=>o.goReceptionEntry&&o.goReceptionEntry(...e)),"5d")}:{},{o:e.p({type:"refresh",size:"18",color:"#2A68FF"}),p:e.o(((...e)=>o.onServiceStatusRefresh&&o.onServiceStatusRefresh(...e)),"84"),q:e.f(s.serviceStatusList,((t,r,i)=>e.e({a:e.t(t.staffName?t.staffName.charAt(0):"未"),b:e.t(t.staffName||"未分配销售"),c:"885c6cec-2-"+i,d:t.title},t.title?{e:e.t(t.title)}:{},{f:e.t(t.customerName||"未知客户"),g:t.customerPhone},t.customerPhone?{h:e.t(t.customerPhone)}:{},{i:e.f(t.tags,((t,r,i)=>({a:e.t(t.text),b:e.n("tag-"+t.color),c:r}))),j:t.alert},t.alert?e.e({k:e.t(t.alert.title),l:"risk"===t.alert.type},"risk"===t.alert.type?{m:e.t(t.alert.message)}:{n:e.f(t.alert.messages,((t,r,i)=>({a:e.t(t),b:r})))},{o:e.n("risk"===t.alert.type?"alert-risk-text":"alert-reminder-text"),p:e.n("risk"===t.alert.type?"alert-risk-box":"alert-reminder-box")}):{},{q:e.t(t.durationText),r:!o.isRecordingItem(t.id)},o.isRecordingItem(t.id)?{t:e.o((e=>o.stopPhoneRecordAndUpload(t)),t.id||r)}:{s:e.o((e=>o.startPhoneRecord(t)),t.id||r)},{v:e.o((e=>o.showSupplementDialog(t)),t.id||r),w:e.o((e=>o.chooseAndUploadFile(t)),t.id||r),x:e.o((e=>o.finishService(t)),t.id||r),y:o.isRecordingItem(t.id)},o.isRecordingItem(t.id)?{z:e.t(o.formatRecordingElapsed(s.recordingElapsedSeconds)),A:e.t(o.formatRecordingSize(s.recordingEstimatedSizeBytes))}:{},{B:o.getUploadResultList(t.id).length},o.getUploadResultList(t.id).length?{C:e.f(o.getUploadResultList(t.id),((t,r,i)=>e.e({a:e.t(t.segmentNo),b:e.t(t.durationText||"--:--"),c:e.t(t.sizeText||"--"),d:e.t(t.statusText),e:t.message},t.message?{f:e.t(t.message)}:{},{g:t.id})))}:{},{D:t.id||r,E:e.o((e=>o.viewServiceDetail(t)),t.id||r)}))),r:e.p({type:"bars",size:"16",color:"#007AFF"}),s:e.t(i.recordStateLabel),t:!s.serviceStatusLoading&&!s.serviceStatusList.length},s.serviceStatusLoading||s.serviceStatusList.length?{}:{v:e.t(i.emptyListHint)},{w:s.serviceStatusLoading&&s.serviceStatusList.length},(s.serviceStatusLoading&&s.serviceStatusList.length,{}),{x:e.o(((...e)=>o.onServiceStatusReachBottom&&o.onServiceStatusReachBottom(...e)),"d2"),y:s.showSupplementModal},s.showSupplementModal?{z:e.p({type:"close",size:"20",color:"#999"}),A:e.o(((...e)=>o.closeSupplementDialog&&o.closeSupplementDialog(...e)),"8e"),B:s.supplementForm.customerName,C:e.o((e=>s.supplementForm.customerName=e.detail.value),"72"),D:s.supplementForm.contact,E:e.o((e=>s.supplementForm.contact=e.detail.value),"04"),F:s.supplementForm.recordingName,G:e.o((e=>s.supplementForm.recordingName=e.detail.value),"7b"),H:s.supplementForm.remarks,I:e.o((e=>s.supplementForm.remarks=e.detail.value),"36"),J:e.o(((...e)=>o.closeSupplementDialog&&o.closeSupplementDialog(...e)),"c5"),K:e.o(((...e)=>o.saveSupplement&&o.saveSupplement(...e)),"13"),L:e.o((()=>{}),"b8"),M:e.o(((...e)=>o.closeSupplementDialog&&o.closeSupplementDialog(...e)),"7c")}:{})}],["__scopeId","data-v-885c6cec"]]);wx.createComponent(o); diff --git a/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.wxml b/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.wxml index 44fd124..d4abf1a 100644 --- a/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.wxml +++ b/unpackage/dist/build/mp-weixin/pages-subpackage/furniture_reception/serviceListFurniture.wxml @@ -1 +1 @@ -共{{a}}条接待刷新{{item.a}}{{item.b}}{{s}}{{item.e}}客户:{{item.f}}电话:{{item.h}}{{tag.a}}{{item.k}}{{item.m}}{{msg.a}}{{item.q}}录音停止录音补录客户上传录音结束服务已录音:{{item.z}}当前大小:{{item.A}} 第{{segment.a}}段({{segment.b}} / {{segment.c}}):{{segment.d}}{{segment.f}}{{v}}正在加载更多...补录信息客户姓名客户电话录音名客户详细地址