12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744 |
- <template>
- <div class="phoneControls">
- <el-popover :width="240" trigger="hover" v-model:visible="showPop">
- <template #reference>
- <div class="mr15 status-box">
- <span class="color-primary">{{ currentStatusText }}</span>
- <el-text tag="b" v-if="['301', '303'].includes(m_strTelState)" type="danger">{{ formatDuration(talkTime) }}</el-text>
- <el-text tag="b" v-else-if="m_strTelState === '200'">{{ formatDuration(idleTime) }}</el-text>
- <el-text tag="b" v-else-if="m_strTelState === '201'">{{ formatDuration(busyTime) }}</el-text>
- <el-text tag="b" v-else-if="m_strTelState === '320'">{{ formatDuration(conferenceTime) }}</el-text>
- <el-text tag="b" v-else-if="m_strTelState === '900'">{{ formatDuration(arrangeTime) }}</el-text>
- <SvgIcon name="ele-CaretBottom" class="arrow" :class="showPop ? 'is-reverse' : ''" />
- </div>
- </template>
- <template #default>
- <template v-if="m_bLogin">
- <div class="flex-center-between" v-if="m_strUserNo">
- <span class="ml10">分机号</span>
- <el-text tag="b" type="danger">{{ m_strUserNo }}</el-text>
- </div>
- <div class="flex-center-between mt10" v-if="signTime">
- <span class="ml10">签入时长</span>
- <el-text tag="b" type="primary">{{ formatDuration(signTime) }}</el-text>
- </div>
- <div class="flex-center-between mt10" v-if="talkTime && ['301', '303', '310'].includes(m_strTelState)">
- <span class="ml10">通话时长</span>
- <el-text tag="b" type="danger">{{ formatDuration(talkTime) }}</el-text>
- </div>
- <div class="flex-center-between mt10" v-if="idleTime && ['0'].includes(m_strTelState)">
- <span class="ml10">空闲时长</span>
- <el-text tag="b">{{ formatDuration(idleTime) }}</el-text>
- </div>
- <div class="flex-center-between mt10" v-if="busyTime && ['201'].includes(m_strTelState)">
- <span class="ml10">小休时长</span>
- <el-text tag="b">{{ formatDuration(busyTime) }}</el-text>
- </div>
- </template>
- <template v-else> <span class="color-info flex flex-center-center">请签入</span> </template>
- </template>
- </el-popover>
- <div class="btn-container">
- <!-- 签入 -->
- <template v-if="m_bLogin">
- <!-- 签出可用 -->
- <div class="item active" @click="onEvent('signOut')" v-if="activeArr.includes('dutyOff')" title="签出">
- <SvgIcon name="iconfont icon-dutyOff" class="icon mr3" size="18px" />签出
- </div>
- <!-- 签出不可用 -->
- <div class="item disabled" v-else title="签出"><SvgIcon name="iconfont icon-dutyOff" class="icon mr3" size="18px" />签出</div>
- </template>
- <!-- 灰色签入不可用 -->
- <template v-else>
- <div class="item active" @click="onEvent('signIn')" title="签入">
- <SvgIcon name="iconfont icon-dutyOn" class="icon mr3" size="18px" />签入
- </div>
- </template>
- <!-- 呼叫 可用-->
- <template v-if="m_bLogin && activeArr.includes('outbound')">
- <div class="item active" title="呼叫" @click="onEvent('callOut')">
- <SvgIcon name="iconfont icon-outbound" class="icon mr3" size="18px" />
- <span>呼叫</span>
- </div>
- </template>
- <!-- 呼叫 不可用 -->
- <template v-else>
- <div class="item disabled" title="呼叫">
- <SvgIcon name="iconfont icon-outbound" class="icon mr3" size="18px" />
- <span>呼叫</span>
- </div>
- </template>
- <!-- 可用挂断 -->
- <template v-if="m_bLogin && activeArr.includes('hangup')">
- <div class="item active" @click="onEvent('hangup')" title="挂断">
- <SvgIcon name="iconfont icon-hangup" class="icon mr3" size="16px" />挂断
- </div>
- </template>
- <!-- 灰色挂断 不可用 -->
- <template v-else>
- <div class="item disabled" title="挂断"><SvgIcon name="iconfont icon-hangup" class="icon mr3" size="16px" />挂断</div>
- </template>
- <!-- 小休和结束休息 可用 -->
- <template v-if="m_bLogin && activeArr.includes('rest') && !isRestAudit">
- <div class="item active" :title="m_bTelBusy ? '示闲' : '小休'" @click="onEvent(m_bTelBusy ? 'idle' : 'busy')">
- <SvgIcon name="iconfont icon-rest" class="icon mr3" size="16px" />
- {{ m_bTelBusy ? '示闲' : '小休' }}
- </div>
- </template>
- <!-- 小休审核中 -->
- <template v-if="m_bLogin && isRestAudit">
- <div class="item disabled" title="小休审核中">
- <SvgIcon name="iconfont icon-rest" class="icon mr3" size="16px" />
- 审核中
- </div>
- </template>
- <!-- 灰色小休不可用 -->
- <template v-else>
- <div class="item disabled" title="小休"><SvgIcon name="iconfont icon-rest" class="icon mr3" size="18px" />小休</div>
- </template>
- <!-- 保持和取消保持 可用 -->
- <template v-if="m_bLogin && activeArr.includes('hold')">
- <div class="item active" :title="m_IsHold ? '恢复' : '保持'" @click="onEvent(m_IsHold ? 'reHold' : 'hold')">
- <SvgIcon name="iconfont icon-hold" class="icon mr3" size="16px" />
- {{ m_IsHold ? '恢复' : '保持' }}
- </div>
- </template>
- <!-- 灰色保持不可用 -->
- <template v-else>
- <div class="item disabled" title="保持"><SvgIcon name="iconfont icon-hold" class="icon mr3" size="16px" />保持</div>
- </template>
- <!-- 咨询 可用 -->
- <template v-if="m_bLogin && activeArr.includes('consult')">
- <div class="item active" title="咨询" @click="onEvent('consult')">
- <SvgIcon name="iconfont icon-transfer" class="icon mr3" size="16px" />
- 咨询
- </div>
- </template>
- <!-- 灰色咨询不可用 -->
- <template v-else>
- <div class="item disabled" title="咨询"><SvgIcon name="iconfont icon-transfer" class="icon mr3" size="16px" />咨询</div>
- </template>
- <!-- 盲转可用 -->
- <template v-if="m_bLogin && activeArr.includes('transferMz')">
- <div class="item active" title="盲转" @click="onEvent('transferMz')">
- <SvgIcon name="iconfont icon-transfer" class="icon mr3" size="16px" />
- 盲转
- </div>
- </template>
- <!-- 灰色盲转不可用 -->
- <template v-else>
- <div class="item disabled" title="盲转"><SvgIcon name="iconfont icon-transfer" class="icon mr3" size="16px" />盲转</div>
- </template>
- <!-- 转接可用 -->
- <template v-if="m_bLogin && activeArr.includes('transfer')">
- <div class="item active" title="转接" @click="onEvent('transfer')">
- <SvgIcon name="iconfont icon-transfer" class="icon mr3" size="16px" />
- 转接
- </div>
- </template>
- <!-- 灰色转接不可用 -->
- <template v-else>
- <div class="item disabled" title="转接"><SvgIcon name="iconfont icon-transfer" class="icon mr3" size="16px" />转接</div>
- </template>
- <!-- 整理和取消整理 可用 -->
- <template v-if="m_bLogin && activeArr.includes('talkingDeal')">
- <div class="item active" :title="m_IsTalkingDeal ? '取消整理' : '整理'" @click="onEvent(m_IsTalkingDeal ? 'cancelTalkDeal' : '')">
- <SvgIcon name="iconfont icon-talkingDeal" class="icon mr3" size="16px" />
- {{ m_IsTalkingDeal ? '取消整理' : '整理' }}
- </div>
- </template>
- <!-- 灰色整理不可用 -->
- <template v-else>
- <div class="item disabled" title="整理"><SvgIcon name="iconfont icon-talkingDeal" class="icon mr3" size="16px" />整理</div>
- </template>
- <!-- 三方会议可用 -->
- <template v-if="m_bLogin && activeArr.includes('conference')">
- <div class="item active" title="三方会议" @click="onEvent('conference')">
- <SvgIcon name="iconfont icon-conference" class="icon mr3" size="16px" />
- 三方会议
- </div>
- </template>
- <!-- 三方会议不可用 -->
- <template v-else>
- <div class="item disabled" title="三方会议"><SvgIcon name="iconfont icon-conference" class="icon mr3" size="16px" />三方会议</div>
- </template>
- <!-- <!– 评价可用 登录并且是呼入 –>
- <template v-if="m_bLogin && m_IsCallIn && activeArr.includes('evaluate')">
- <div class="item active" title="评价" @click="onEvent('evaluate')">
- <SvgIcon name="ele-Flag" class="icon mr3" size="16px" />
- 评价
- </div>
- </template>
- <!– 评价不可用 –>
- <template v-else>
- <div class="item disabled" title="评价"><SvgIcon name="ele-Flag" class="icon mr3" size="16px" />评价</div>
- </template>-->
- </div>
- <!-- 等待人数 -->
- <div class="wait-box">
- <!-- <div class="today-wait">
- <span class="today-wait-label">今日等待:</span
- ><el-text class="today-wait-num ml5" tag="b" type="danger">{{ todayWaitValue.toFixed(0) }}</el-text>
- </div>-->
- <div class="current-wait">
- <span class="current-wait-label">当前等待:</span
- ><el-text class="current-wait-time ml5" tag="b" type="danger">{{ currentWaitValue.toFixed(0) }}</el-text>
- </div>
- </div>
- </div>
- <!-- 签入弹窗 -->
- <el-dialog v-model="state.dutyDialogVisible" draggable title="签入" width="500px" :show-close="false" @close="dutyFormClose">
- <el-form :model="state.dutyForm" label-width="80px" ref="dutyFormRef" @submit.native.prevent>
- <el-form-item label="分机号" prop="telNo" :rules="[{ required: true, message: '请选择分机号', trigger: 'change' }]">
- <el-select-v2
- v-model="state.dutyForm.telNo"
- :options="telsList"
- placeholder="请选择分机号"
- filterable
- class="w100"
- :props="{
- label: 'no',
- value: 'no',
- }"
- clearable
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="state.dutyDialogVisible = false" class="default-button" :loading="state.loading">取 消</el-button>
- <el-button type="primary" @click="clickOnDuty(dutyFormRef)" :loading="state.loading">确 定</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 呼出弹窗 -->
- <el-dialog v-model="state.outboundDialogVisible" draggable title="呼叫" width="450px" @close="outboundFormClose">
- <el-form :model="state.outboundForm" label-width="80px" ref="outboundFormRef" @submit.native.prevent>
- <el-form-item label="呼叫号码" prop="telNo" :rules="[{ required: true, message: '请选择呼叫号码或填写外部电话', trigger: 'blur' }]">
- <el-select-v2
- v-model="state.outboundForm.telNo"
- :options="threeWayAndTransfer"
- placeholder="请选择呼叫号码或填写外部电话"
- filterable
- class="w100"
- :props="{
- label: 'dicDataName',
- value: 'dicDataValue',
- }"
- clearable
- allow-create
- default-first-option
- :height="500"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="state.outboundDialogVisible = false" class="default-button" :loading="state.loading">取 消</el-button>
- <el-button type="primary" @click="clickOnOutbound(outboundFormRef)" :loading="state.loading">确 定</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 咨询弹窗 -->
- <el-dialog v-model="state.consultDialogVisible" draggable title="咨询" width="450px" @close="consultFormClose">
- <el-form :model="state.consultForm" label-width="80px" ref="consultFormRef" @submit.native.prevent>
- <el-form-item label="咨询类型" prop="strType" :rules="[{ required: false, message: '请选择咨询类型', trigger: 'blur' }]">
- <el-radio-group v-model="state.consultForm.strType">
- <el-radio value="0">内线</el-radio>
- <el-radio value="1">外线</el-radio>
- <el-radio value="2">群组</el-radio>
- </el-radio-group>
- </el-form-item>
- <el-form-item label="咨询" prop="telNo" :rules="[{ required: true, message: '请填写咨询号码', trigger: 'blur' }]">
- <!-- <el-input v-model="state.consultForm.telNo" placeholder="咨询号码" @keyup.enter="clickOnConsult(consultFormRef)" clearable />-->
- <el-select-v2
- v-model="state.consultForm.telNo"
- :options="threeWayAndTransfer"
- placeholder="请选择咨询号码或填写外部电话"
- filterable
- class="w100"
- :props="{
- label: 'dicDataName',
- value: 'dicDataValue',
- }"
- clearable
- allow-create
- default-first-option
- :height="500"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="state.consultDialogVisible = false" class="default-button" :loading="state.loading">取 消</el-button>
- <el-button type="primary" @click="clickOnConsult(consultFormRef)" :loading="state.loading">确 定</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 盲转 -->
- <el-dialog v-model="state.blindDialogVisible" draggable title="盲转" width="450px" @close="blindFormClose">
- <el-form :model="state.blindForm" label-width="80px" ref="blindFormRef" @submit.native.prevent>
- <el-form-item label="盲转" prop="telNo" :rules="[{ required: true, message: '请填写盲转号码', trigger: 'blur' }]">
- <!-- <el-input v-model="state.blindForm.telNo" placeholder="盲转号码" @keyup.enter="clickOnBlind(blindFormRef)" clearable />-->
- <el-select-v2
- v-model="state.blindForm.telNo"
- :options="threeWayAndTransfer"
- placeholder="请选择盲转号码或填写外部电话"
- filterable
- class="w100"
- :props="{
- label: 'dicDataName',
- value: 'dicDataValue',
- }"
- clearable
- allow-create
- default-first-option
- :height="500"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="state.blindDialogVisible = false" class="default-button" :loading="state.loading">取 消</el-button>
- <el-button type="primary" @click="clickOnBlind(blindFormRef)" :loading="state.loading">确 定</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 小休弹窗 -->
- <el-dialog v-model="state.restDialogVisible" draggable title="小休" width="450px" @close="restFormOpened">
- <el-form :model="state.restForm" label-width="80px" ref="restFormRef" @submit.native.prevent>
- <el-form-item label="小休" prop="reason" :rules="[{ required: true, message: '请选择小休原因', trigger: 'change' }]">
- <el-select v-model="state.restForm.reason" placeholder="请选择小休原因" class="w100" clearable>
- <el-option v-for="item in state.restReasonOptions" :key="item.dicDataValue" :label="item.dicDataName" :value="item.dicDataValue" />
- </el-select>
- <!-- <el-text type="danger">注意:小休审批通过,状态才会变成小休</el-text>-->
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="state.restDialogVisible = false" class="default-button" :loading="state.loading">取 消</el-button>
- <el-button type="primary" @click="clickOnRest(restFormRef)" :loading="state.loading">申 请</el-button>
- </span>
- </template>
- </el-dialog>
- <!-- 占位标签 -->
- <div class="seizeSeat-box"></div>
- </template>
- <script setup lang="ts" name="zgTelControl">
- import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
- import { getNowDateTime } from '@/utils/constants';
- import { ElMessage, ElMessageBox, ElNotification, FormInstance } from 'element-plus';
- import { useRouter } from 'vue-router';
- import { useWebSocket } from '@/hooks/useWebsocket';
- import { useIntervalFn } from '@vueuse/shared';
- import { formatDuration } from '@/utils/formatTime';
- import { Local } from '@/utils/storage';
- import { useAppConfig } from '@/stores/appConfig';
- import { storeToRefs } from 'pinia';
- import { useUserInfo } from '@/stores/userInfo';
- import mittBus from '@/utils/mitt';
- import { callCenterSignIn, callCenterSignOut, getCallCenterList, getCallCenterStatus } from '@/api/callCenter';
- import { useTimeoutFn } from '@vueuse/shared/index';
- import { useGlobalState } from '@/utils/callCenter';
- import { useThemeConfig } from '@/stores/themeConfig';
- import { getDataByCode } from '@/api/system/dict';
- import { trimCompat } from '@/utils/tools';
- import XEUtils from 'xe-utils';
- import { submitLog } from '@/api/public/log';
- import { useTransition } from '@vueuse/core';
- import { busyOff, busyOn, telRestBaseData } from '@/api/public/wex';
- import { getSpecialNumberDetailByPhone } from '@/api/auxiliary/specialNumber';
- import signalR from '@/utils/signalR';
- const globalState = useGlobalState(); // 全局变量
- const state = reactive<any>({
- dutyDialogVisible: false,
- loading: false,
- dutyForm: {
- telNo: null,
- skillId: null,
- },
- outboundDialogVisible: false,
- outboundForm: {
- telNo: null,
- },
- consultDialogVisible: false,
- consultForm: {
- telNo: null,
- strType: '0',
- },
- blindDialogVisible: false,
- blindForm: {
- telNo: null,
- },
- restDialogVisible: false,
- restForm: {
- reason: null,
- },
- restReasonOptions: [],
- });
- /*
- * @description: 置当前可用的按钮
- * 电话控件在头部展示时切换;
- * 状态改变
- * 0-未登录;100-登录成功;
- * 200-空闲;201-繁忙;
- * 300-呼入振铃;301-呼入通话;302-呼出振铃;303-呼出通话;310-保持通话;320-会议;330-咨询;333-转接接通
- * 510-监听;520-插话;
- */
- const activeArr = computed(() => {
- const switchCases: any = {
- '0': ['dutyOn'], // 签出状态
- '100': [], // 登录成功
- '200': ['dutyOff', 'rest', 'outbound'], // 空闲
- '201': ['dutyOff', 'rest', 'outbound'], // 小休
- '202': ['dutyOff'], // 小休审批中
- '300': ['hangup'], // 呼入振铃
- '301': ['hangup', 'hold', 'consult', 'transferMz', 'evaluate'], // 呼入通话
- '302': ['hangup'], // 呼出振铃
- '303': ['hangup', 'hold', 'consult', 'transferMz', 'evaluate'], // 呼出通话
- '310': ['hangup', 'hold'], // 通话保持
- '320': ['hangup', 'evaluate'], // 三方会议中
- '330': ['hangup', 'hold', 'transfer', 'conference', 'evaluate'], // 转接 咨询
- '331': ['hangup', 'hold', 'evaluate'], // 咨询 转接
- '900': ['dutyOff', 'talkingDeal'], // 整理
- };
- let arr = <EmptyArrayType>[];
- if (m_strTelState.value in switchCases) {
- arr = switchCases[m_strTelState.value];
- }
- return arr;
- });
- // 当前对应code展示的中文
- const currentStatusText = computed(() => {
- const statusMap: any = {
- '0': '签出',
- '100': '登录成功',
- '200': '空闲',
- '201': '小休',
- '202': '小休审批中',
- '300': '呼入振铃',
- '301': '呼入通话',
- '302': '呼出振铃',
- '303': '呼出通话',
- '310': '通话保持',
- '320': '三方会议',
- '330': '咨询',
- '331': '咨询',
- '900': '整理',
- };
- return statusMap[m_strTelState.value] || '';
- });
- // ws实例对象
- const wsRef = ref();
- const storesThemeConfig = useThemeConfig();
- const { themeConfig } = storeToRefs(storesThemeConfig);
- const initWs = () => {
- wsRef.value = useWebSocket(themeConfig.value.callCenterSocketUrl, {
- heartbeat: {
- message: JSON.stringify({ Action: 'ReqHealthCheck', Param: { Extension: m_strUserNo.value } }),
- interval: 5000,
- pongTimeout: 5000,
- },
- autoReconnect: {
- delay: 2000,
- }, // 自动重连
- immediate: false, // 是否立即链接
- onMessage: e_TelMsgReceive, // 消息接收
- onError: e_websocketError, // 错误
- onDisconnected: e_websocketClose, // 断开
- onConnected: e_websocketOpen, // 链接成功
- });
- };
- // 发送消息
- const e_TelSendMsg = (strObj: Object) => {
- // 客户端当前时间
- const strMsg = JSON.stringify(strObj);
- console.log(`${getNowDateTime()} 发送消息:`, strMsg, wsRef.value.status);
- if (wsRef.value.ws?.readyState === 1) {
- // 已经链接并且可以通讯,则发放文本消息
- wsRef.value.send(strMsg);
- } else {
- ElMessage.error('请先签入');
- state.loading = false;
- }
- };
- const appConfigStore = useAppConfig();
- const { AppConfigInfo } = storeToRefs(appConfigStore); // 系统配置信息
- const storesUserInfo = useUserInfo();
- const { userInfos } = storeToRefs(storesUserInfo); // 用户信息
- const m_strUserNo = ref<string | null>(''); // 分机号码
- const m_strUserName = computed(() => {
- return userInfos.value.name;
- }); // 坐席名称
- const m_strJobNum = ref<string | null>(''); // 坐席工号
- const m_strSkillId = ref(''); // 技能组
- const m_strLevel = ref('1'); // 优先级别
- const m_strGroup = ref('1'); // 分组ID
- const m_strCompanyId = ref(''); // 企业编码
- const m_bLogin = ref(false); // 登录状态
- const m_bTelBusy = ref(false); // 是否小休中
- const m_strIsMonitor = ref('0'); // 是否监控分机 1-是监控分机
- const callId = ref(''); // 通话ID
- const m_IsCallOut = ref(false); // 是否呼出
- const m_IsCallIn = ref(false); // 是否是呼入
- const m_strOpenFlag = ref('2'); // 来电弹屏方式 1-接通弹屏;2-振铃弹屏
- const m_CallOutOpen = ref(false); // 呼出是否弹屏(用于未接统计“回拨”业务处理)
- const m_bIsOpen = ref(false); // 是否已经弹屏
- const m_bCallConnect = ref(false); // 是否在通话状态
- const m_IsConsult = ref(false); // 是否咨询
- const m_strConsultType = ref('-1'); // 咨询类型
- const m_IsHangup = ref(false); // 是否挂机
- const m_IsHold = ref(false); // 是否保持
- const m_IsTalkingDeal = ref(false); // 是否通话整理
- const m_IsMonListen = ref('0'); // 监控状态 0-未监听;1-监控成功;2-监控失败;
- const m_strTelState = ref('0'); // 当前状态
- m_strOpenFlag.value = XEUtils.toValueString(AppConfigInfo.value.callInOpenType)?.trim(); // 转换为字符串
- const showPop = ref(false);
- // 点击事件
- const onEvent = (event: string) => {
- switch (event) {
- case 'signIn': // 签入
- onSignIn();
- break;
- case 'signOut': // 签出
- onSignOut();
- break;
- case 'busy': // 小休
- onBusy();
- break;
- case 'idle': // 示闲
- onIdle();
- break;
- case 'cancelTalkDeal': // 取消整理
- onCancelTalkDeal();
- break;
- case 'answer': // 应答
- break;
- case 'hangup': // 挂机
- onHangup();
- break;
- case 'callOut': // 外呼
- onCallOut();
- break;
- case 'hold': // 保持
- onHold();
- break;
- case 'reHold': // 恢复
- onReHold();
- break;
- // 咨询
- case 'consult':
- e_TopStateChange('331');
- onConsultOpen();
- break;
- // 转接
- case 'transfer':
- onTransfer();
- break;
- // 盲转
- case 'transferMz':
- onTransferMzOpen();
- break;
- case 'conference': // 三方会议
- onConference();
- break;
- case 'DTMF': // 拨号盘
- break;
- // 评价
- case 'evaluate':
- i_evaluate();
- break;
- }
- };
- /*
- * 事件响应状态
- */
- const arrangeTime = ref(0);
- const arrangeTimer = useIntervalFn(
- () => {
- arrangeTime.value += 1;
- },
- 1000,
- { immediate: false }
- );
- // 整理时长开始
- const startArrangeTime = () => {
- arrangeTimer.resume();
- };
- // 整理时长开始结束
- const stopArrangeTime = () => {
- arrangeTime.value = 0;
- arrangeTimer.pause();
- };
- const evtSeatState = (data: any) => {
- if (m_strIsMonitor.value == '1') {
- // 推送分机信息
- const pushExt = data.Param.Extension || '';
- if (pushExt !== m_strUserNo.value) {
- // 如果分机实时推送的分机信息和当前登录分机不一致,则表明当前登录分机为监控分机,开启了状态监控功能
- // 1.不属于当前分机的状态,则不改变当前分机电话条状态
- // 2.调整其他分机大屏监控状态
- const pushState = GetTelState(data.Param.State);
- if (pushState) {
- // 修改后台数据
- console.log(pushExt, pushState);
- // SetMonitorState(pushExt, pushState);
- }
- return;
- }
- }
- if (m_IsHold.value) {
- // 正在保持通话状态下
- return;
- }
- // 状态 0:闲 1:忙 2:会议 3:登出 4:呼入 5:呼出 6:咨询 7:其他 8:通话
- const strState = data.Param.State;
- switch (strState) {
- // 空闲
- case '0':
- m_bIsOpen.value = false;
- m_IsHangup.value = false;
- m_strTelState.value = '200';
- m_bTelBusy.value = false;
- e_TopStateChange(m_strTelState.value);
- startIdleTime(); // 空闲计时器开始
- stopTalkTimer(); // 停止通话时长
- stopConferenceTime(); // 三方会议时长结束
- m_IsTalkingDeal.value = false;
- break;
- // 小休
- case '1':
- m_strTelState.value = '201';
- m_bTelBusy.value = true;
- e_TopStateChange(m_strTelState.value);
- startBusyTime(); // 小休计时器开始
- stopIdleTime(); // 停止空闲时长
- break;
- case '2':
- stopIdleTime();
- break;
- // 登出
- case '3':
- // 附加签出方法(用户分机分离调用业务系统签出)
- // e_TelSignOut(m_strUserNo.value, m_strSkillId.value);
- m_strTelState.value = '0';
- e_TopStateChange(m_strTelState.value);
- break;
- // 通话振铃
- case '4':
- if (false === m_IsHangup.value) {
- if (m_IsCallOut.value == true) {
- // 呼出振铃
- m_strTelState.value = '302';
- } else {
- // 呼入振铃
- m_strTelState.value = '300';
- }
- e_TopStateChange(m_strTelState.value);
- }
- stopIdleTime();
- break;
- // 通话振铃
- case '5':
- if (!m_IsHangup.value) {
- m_strTelState.value = '302';
- e_TopStateChange(m_strTelState.value);
- }
- stopIdleTime();
- break;
- // 咨询
- case '6':
- break;
- // 其他
- case '7':
- break;
- // 接通
- case '8':
- if (!m_IsHangup.value) {
- if (m_IsCallOut.value) {
- // 呼出接通
- m_strTelState.value = '303';
- } else {
- // 呼入接通
- m_strTelState.value = '301';
- // 是否是保持通话
- }
- e_TopStateChange(m_strTelState.value);
- }
- stopIdleTime();
- break;
- case '9': // 工单整理
- m_strTelState.value = '900';
- startArrangeTime(); // 开始整理时长
- e_TopStateChange(m_strTelState.value);
- m_IsTalkingDeal.value = true;
- break;
- }
- };
- // 获取分机状态
- const GetTelState = (strState: any) => {
- let strResult = '';
- switch (strState) {
- // 空闲
- case '0':
- strResult = '200';
- break;
- // 小休
- case '1':
- strResult = '201';
- break;
- case '2':
- break;
- // 登出
- case '3':
- strResult = '0';
- break;
- // 通话振铃
- case '4':
- strResult = '302'; // 默认呼入振铃
- break;
- // 通话振铃
- case '5':
- strResult = '302'; // 默认呼出振铃
- break;
- // 咨询
- case '6':
- break;
- // 其他
- case '7':
- break;
- // 接通
- case '8':
- strResult = '301';
- break;
- case '9': // 工单整理
- strResult = '900';
- break;
- }
- return strResult;
- };
- /**
- * 状态初始化请求
- * */
- const ReqAgentMonitor = () => {
- // 开始坐席状态监控
- // SkillId 技能组为0则监控所有分机
- const msgObj = {
- Action: 'ReqAgentMonitor',
- Param: {
- Extension: m_strUserNo.value,
- CompanyId: m_strCompanyId.value,
- SkillId: '0',
- },
- };
- // 发送请求
- e_TelSendMsg(msgObj);
- };
- /**
- * {“Action”:”ResAgentMonitor”,”Param”:[{“Extension”:,”JobNumber”:,”SkillId”:,”Name”:,”Caller”:,”Called”:,”State”}]}
- * State:0:闲 1:忙 2:会议 3:登出 4:呼入 5:呼出 6:咨询 7:其他 8:通话 9:工单整理
- * 监控状态初始化返回状态
- * @param {any} data
- */
- const ResAgentMonitor = (data: any) => {
- if (null != data && null != data.Param && data.Param.length > 0) {
- // 监控分机集合
- const strMonitorInfo = JSON.stringify(data.Param);
- console.log(`${getNowDateTime()} 监控分机集合:`, strMonitorInfo);
- }
- };
- /*
- * 登录
- * ReqAgentLogin - 登录方法名
- * JobNum - 工号
- * Name - 姓名
- * Extension - 分机号
- * SkillId - 技能组
- * Level - 为要设置的级别,分为9个级别,从高到低分别为0-8;同一技能组中级别越高的坐席优先被分配
- * Role - 角色,保留,不做设置
- * GroupName - 技能组名称
- * OrgId - 组织ID
- * 返回内容:
- {“Action”:”ResAgentLogin”,”Param”:{“Result”:}}
- Result: 3:分机错误 7:已登录 0:登录成功
- */
- const sendSignIn = () => {
- globalState.callCenterWs = wsRef.value;
- const sendObj = {
- Action: 'ReqAgentLogin',
- Param: {
- JobNum: m_strJobNum.value,
- Name: m_strUserName.value,
- Extension: m_strUserNo.value,
- SkillId: m_strSkillId.value,
- Level: m_strLevel.value,
- Role: '',
- GroupName: m_strGroup.value,
- OrgId: m_strCompanyId.value,
- Userdata: userInfos.value.id + ':' + userInfos.value.name, // 签入时携带的参数,用于后台记录 用户ID
- },
- };
- // 发送请求
- e_TelSendMsg(sendObj);
- console.log(`${getNowDateTime()} 呼叫中心发起签入`);
- const request = {
- creationTime: new Date(),
- name: `向兴唐呼叫中心发起签入,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(sendObj),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- const signTime = ref(0); //签入时长
- // 使用 useInterval 创建定时器
- const signInTimer = useIntervalFn(
- () => {
- signTime.value += 1;
- },
- 1000,
- { immediate: false }
- );
- // 签入时长及时开始
- const startSignTime = (second?: number) => {
- if (second) {
- // 从后台获取签入时长
- if (second < 0) second = 0; // 防止后台返回的签入时间大于当前时间
- signTime.value = second;
- signInTimer.resume();
- } else {
- signInTimer.resume();
- }
- };
- // 签入时长计时结束
- const stopSignTime = () => {
- signTime.value = 0;
- signInTimer.pause();
- };
- // 空闲时长
- const idleTime = ref(0);
- const idleTimer = useIntervalFn(
- () => {
- idleTime.value += 1;
- },
- 1000,
- { immediate: false }
- );
- // 空闲时长开始
- const startIdleTime = () => {
- idleTimer.resume();
- stopBusyTime(); // 结束小休时长
- stopArrangeTime(); // 结束整理时长
- };
- // 空闲时长计时结束
- const stopIdleTime = () => {
- idleTime.value = 0;
- idleTimer.pause();
- };
- const onSignIn = () => {
- if (AppConfigInfo.value.isNeedTelNo) {
- // 需要填写分机号
- state.dutyDialogVisible = true;
- } else {
- if (!userInfos.value.defaultTelNo) {
- ElMessage.error('请先配置用户默认分机');
- return;
- }
- if (!userInfos.value.defaultTelGroup) {
- ElMessage.error('请先配置用户技能组');
- return;
- }
- state.loading = true;
- m_strUserNo.value = userInfos.value.defaultTelNo; // 默认分机
- m_strJobNum.value = <string>userInfos.value.staffNo; // 工号
- m_strSkillId.value = userInfos.value.defaultTelGroup; // 技能组
- globalState.currentTel = {
- telNo: userInfos.value.defaultTelNo,
- telGroup: userInfos.value.defaultTelGroup,
- jobNum: <string>userInfos.value.staffNo,
- };
- wsRef.value.open();
- }
- };
- const dutyFormRef = ref();
- const clickOnDuty = (formEl: FormInstance | undefined) => {
- if (!formEl) return;
- if (!userInfos.value.defaultTelGroup) {
- ElMessage.error('请先配置用户默认分机组');
- return;
- }
- formEl.validate((valid: boolean) => {
- if (!valid) return;
- state.loading = true;
- const currentTel: any = telsList.value.find((item: any) => item.no === state.dutyForm.telNo);
- m_strUserNo.value = state.dutyForm.telNo;
- m_strJobNum.value = <string>userInfos.value.staffNo; // 默认取用户工号
- m_strSkillId.value = userInfos.value.defaultTelGroup; // 技能组
- globalState.currentTel = {
- telNo: state.dutyForm.telNo,
- telGroup: m_strSkillId.value,
- jobNum: state.dutyForm.telNo,
- };
- wsRef.value.open();
- state.dutyDialogVisible = false;
- state.loading = false;
- });
- };
- // 签入弹窗关闭清空
- const dutyFormClose = () => {
- dutyFormRef.value?.resetFields();
- dutyFormRef.value?.clearValidate();
- };
- // 签入消息回调
- const retSignIn = (data: any) => {
- state.loading = false;
- if (data.Param.Result === '0') {
- // 登录成功
- m_bLogin.value = true;
- m_strTelState.value = '100';
- e_TopStateChange(m_strTelState.value);
- if (!userAlreadyLogin.value) {
- // 检查用户没有登录才需要调用签入
- // 刷新页面自动签出不需要调用业务系统签入
- // 附加签入方法(用户分机分离) 调用业务系统登录
- e_TelSignIn(m_strUserNo.value, m_strSkillId.value);
- }
- // 登录成功
- startSignTime(signTime.value); // 签入计时器
- globalState.callCenterIsSignIn = true; // 签入状态
- if (m_strIsMonitor.value === '1') {
- // 监控初始化状态
- ReqAgentMonitor();
- }
- console.log(`${getNowDateTime()} 呼叫中心签入成功回调`);
- } else if (data.Param.Result === '3') {
- // 分机错误
- ElMessage.error('分机错误');
- userAlreadyLogin.value = false; // 将登录状态重置
- wsRef.value.close(); // 关闭链接
- // 登录不成功 调用业务系统
- // e_TelSignOut(m_strUserNo.value, m_strSkillId.value);
- // 分机错误
- } else if (data.Param.Result === '7') {
- // 已经处于登录状态
- // 先签出再签入
- /* sendSignOut();
- userAlreadyLogin.value = false; // 将登录状态重置*/
- ElMessage.error('当前分机已签入');
- userAlreadyLogin.value = false; // 将登录状态重置
- wsRef.value.close(); // 关闭链接
- }
- const request = {
- creationTime: new Date(),
- name: `兴唐呼叫中心签入返回消息,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(data),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- // ws链接开启成功
- const e_websocketOpen = () => {
- if (userAlreadyLogin.value) {
- // 检查到用户已经登录需要先签出 再签入
- sendSignOut();
- useTimeoutFn(() => {
- sendSignIn();
- }, 500);
- } else {
- if (m_strUserNo.value && m_strSkillId.value) sendSignIn();
- else {
- ElMessage.error('分机号或技能组配置有误,请联系管理员');
- wsRef.value.close();
- }
- }
- const sendObj = {
- Action: 'wsOpen',
- Param: {
- JobNum: m_strJobNum.value,
- Name: m_strUserName.value,
- Extension: m_strUserNo.value,
- SkillId: m_strSkillId.value,
- Level: m_strLevel.value,
- Role: '',
- GroupName: m_strGroup.value,
- OrgId: m_strCompanyId.value,
- UserData: userInfos.value.id + ':' + userInfos.value.name,
- },
- };
- const request = {
- creationTime: new Date(),
- name: `兴唐呼叫中心链接开启成功,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(sendObj),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- // 链接关闭
- const e_websocketClose = () => {
- // 需要将话机状态重置
- resetTelState();
- console.log(`${getNowDateTime()} 呼叫中心链接关闭`);
- const sendObj = {
- Action: 'wsClose',
- Param: {
- JobNum: m_strJobNum.value,
- Name: m_strUserName.value,
- Extension: m_strUserNo.value,
- SkillId: m_strSkillId.value,
- Level: m_strLevel.value,
- Role: '',
- GroupName: m_strGroup.value,
- OrgId: m_strCompanyId.value,
- },
- };
- const request = {
- creationTime: new Date(),
- name: `兴唐呼叫中心链接关闭,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(sendObj),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- // 链接错误
- const e_websocketError = () => {
- // 需要将话机状态重置
- resetTelState();
- console.log(`${getNowDateTime()} 呼叫中心链接错误`);
- };
- // 重置话机状态
- const resetTelState = () => {
- state.loading = false;
- // 登出成功
- m_strTelState.value = '0';
- e_TopStateChange(m_strTelState.value);
- // 签出
- e_ActionUpdate('1'); // 更新话机动作
- m_bLogin.value = false;
- userAlreadyLogin.value = false; // 将登录状态重置
- globalState.callCenterWs = null;
- globalState.callCenterIsSignIn = false; // 签出状态
- globalState.callCenterIsOnThePhone = false; // 清除通话状态
- stopBusyTime(); // 停止小休计时器
- stopIdleTime(); // 停止空闲计时器
- stopTalkTimer(); // 停止通话计时器
- stopArrangeTime(); // 停止整理时长
- };
- // 记录日志
- const submitLogFn = async (request: any) => {
- try {
- await submitLog(request);
- Local.remove('telNo');
- } catch (error) {
- console.log(`${getNowDateTime()}:日志记录失败,${error}`);
- }
- };
- // 消息接收
- const e_TelMsgReceive = (ws: any, restMsg: any) => {
- console.log(`${getNowDateTime()} 接收消息:${restMsg.data}`);
- if (restMsg.data) {
- const data = JSON.parse(restMsg.data);
- if (data) {
- // 方法
- const strAction = data.Action;
- switch (strAction) {
- // 登录返回值
- case 'ResAgentLogin':
- retSignIn(data);
- break;
- // 示闲
- case 'ResAgentIdle':
- retIdle(data);
- break;
- // 小休
- case 'ResAgentBusy':
- retBusy(data);
- break;
- // 外呼状态
- case 'ResMakeCall':
- retCallOut(data);
- break;
- // 保持
- case 'ResHoldCall':
- retHold(data);
- break;
- // 取消保持
- case 'ResRetrieve':
- retRehold(data);
- break;
- // 咨询内线
- case 'ResConsultInline':
- retConsult(data, '0');
- break;
- // 咨询外线
- case 'ResConsultOutline':
- retConsult(data, '1');
- break;
- // 咨询群组
- case 'ResConsultSkillGroup':
- retConsult(data, '2');
- break;
- // 咨询转移
- case 'ResTransfer':
- retResTransfer(data);
- break;
- // 三方会议
- case 'ResConference':
- retResConference(data);
- break;
- // 三方会议
- case 'ResMonConf':
- retResConference(data);
- break;
- // 坐席实时状态
- case 'ResAgentMinitor':
- ResAgentMonitor(data);
- break;
- // 监听
- case 'ResMonListen':
- retResMonListen(data);
- break;
- // 取消监听返回
- case 'ResStopListen':
- retResStopListen(data);
- break;
- }
- // 事件
- const strEvent = data.Event;
- switch (strEvent) {
- // 签出事件
- case 'EvtLogout':
- retSignOut();
- break;
- // 呼入振铃事件
- case 'EvtCallAlerting':
- evtCallAlerting(data);
- break;
- // 应答事件
- case 'EvtCallAnswer':
- evtEvtCallAnswer(data);
- break;
- // 挂机事件
- case 'EvtHangup':
- retHangup(data);
- break;
- // 状态
- case 'EvtSeatState':
- evtSeatState(data);
- break;
- // 队列等待
- case 'EvtAcdInfo':
- i_QueueNum(data);
- break;
- // 语音识别结果通知事件
- case 'EvtRecognize':
- e_EvtRecognize(data.Param);
- break;
- // 转三方接通状态
- case 'EvtDispatchState':
- e_EvtDispatchState(data.Param);
- break;
- // 签出事件
- case 'ResStopMonitor':
- retSignOut();
- break;
- case 'ResError': // 异常
- retHangup(data); // 挂机
- // 异常处理
- retResError(data);
- break;
- // 呼出接通事件
- case 'EvtOutCalling':
- evtEvtCalling(data); // 呼出振铃事件
- break;
- // 签出事件
- case 'EvtQuit':
- retSignOut();
- break;
- }
- }
- }
- };
- /*
- * 签出
- * ReqAgentLogout - 签出方法名称
- * Extension - 分机号码
- */
- const sendSignOut = () => {
- const objMsg = {
- Action: 'ReqAgentLogout',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- const request = {
- creationTime: new Date(),
- name: `向兴唐呼叫中心发起签出,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(objMsg),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- const onSignOut = () => {
- ElMessageBox.confirm(`确定要签出,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- sendSignOut();
- userAlreadyLogin.value = false; // 将登录状态重置
- })
- .catch(() => {
- state.loading = false;
- });
- };
- /*
- * 签出事件
- */
- const retSignOut = () => {
- if (!userAlreadyLogin.value) {
- // 检查用户没有登录才需要调用签出
- // 刷新页面自动签出不需要调用业务系统签入
- // 附加签出方法(用户分机分离 调用业务系统等处)
- e_TelSignOut(m_strUserNo.value, m_strSkillId.value);
- }
- state.loading = false;
- // 登出成功
- m_strTelState.value = '0';
- e_TopStateChange(m_strTelState.value);
- // 签出
- e_ActionUpdate('1'); // 更新话机动作
- m_bLogin.value = false;
- globalState.callCenterIsSignIn = false; // 签出状态
- stopBusyTime(); // 停止小休计时器
- stopIdleTime(); // 停止空闲计时器
- stopTalkTimer(); // 停止通话计时器
- stopArrangeTime(); // 停止整理时长
- // 如果用户没有登录 关闭ws
- if (!userAlreadyLogin.value) {
- wsRef.value.close();
- stopSignTime(); // 停止签入计时器
- }
- currentWait.value = 0; // 当前等待重置
- globalState.callCenterWs = null;
- isRestAudit.value = false; // 重置小休审核状态
- console.log(`${getNowDateTime()} 呼叫中心签出回调`);
- const objMsg = {
- Action: 'ResAgentLogout',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- const request = {
- creationTime: new Date(),
- name: `兴唐呼叫中心签出事件回调,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(objMsg),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- /*
- * 小休
- * ReqAgentBusy - 方法名
- * Extension:分机号
- */
- // 定义标志变量,用于标记是否已经弹过窗
- const hasShownBusyAlert = ref(false);
- // 小休时长
- const busyTime = ref(0);
- const busyTimer = useIntervalFn(
- () => {
- busyTime.value += 1;
- if (AppConfigInfo.value.isTelRest) {
- // 配置开关
- // 将秒转换为分钟
- const minutes = Math.floor(AppConfigInfo.value.telRestNum / 60);
- // 检查示忙时长是否超过 分钟
- if (busyTime.value >= AppConfigInfo.value.telRestNum && !hasShownBusyAlert.value) {
- // 如果示忙时长超过 分钟需要弹窗提示 并且只执行一次
- // 弹窗提示
- ElMessageBox.alert(`小休时长已超过${minutes}分钟,请及时处理!`, '提示', {
- confirmButtonText: '确定',
- type: 'warning',
- draggable: true,
- showClose: false,
- });
- // 更新标志变量,确保只弹一次
- hasShownBusyAlert.value = true;
- }
- }
- },
- 1000,
- { immediate: false }
- );
- // 小休时长开始
- const startBusyTime = () => {
- hasShownBusyAlert.value = false; // 重置标志变量
- busyTimer.resume();
- };
- // 小休时长计时结束
- const stopBusyTime = () => {
- busyTime.value = 0;
- busyTimer.pause();
- hasShownBusyAlert.value = false; // 重置标志变量
- };
- const restFormRef = ref<RefType>(); // 小休表单
- const onBusy = () => {
- if (AppConfigInfo.value.isRestApproval) {
- // 需要小休审批
- state.restDialogVisible = true;
- } else {
- ElMessageBox.confirm(`确定要小休,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- const objMsg = {
- Action: 'ReqAgentBusy',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- })
- .catch(() => {
- state.loading = false;
- });
- }
- };
- // 确认小休
- const restReason = ref(''); // 小休原因
- const isRestAudit = ref(false); // 是否小休审核
- const clickOnRest = (formEl: FormInstance | undefined) => {
- if (!formEl) return;
- formEl.validate((valid: boolean) => {
- if (!valid) return;
- state.loading = true;
- restReason.value = state.restForm.reason; // 小休原因
- const objMsg = {
- Action: 'ReqAgentBusy',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- isRestAudit.value = true; // 正在小休审核中
- });
- };
- const restFormOpened = () => {
- restFormRef.value?.resetFields();
- restFormRef.value?.clearValidate();
- };
- /*
- * 小休返回值
- {“Action”:”ResAgentBusy”,”Param”:{“Result”:}}
- Result:0-1
- 0:成功 1:失败
- */
- const retBusy = (data: any) => {
- if (data.Param.Result === '0') {
- m_bTelBusy.value = true;
- m_strTelState.value = '201';
- e_TopStateChange(m_strTelState.value);
- console.log('小休开始了');
- // 小休开始了
- // const restReasons = state.restReasonOptions.find((item: any) => item.dicDataValue === reason);
- busyOn({ reason: '' }) // 开始小休 设置示忙 业务系统统计需要
- .then(() => {
- console.log(`${getNowDateTime()}:业务系统调用示忙开始成功`);
- state.loading = false;
- })
- .catch((err: any) => {
- console.log(`${getNowDateTime()}:业务系统调用示忙开始失败,${err}`);
- state.loading = false;
- });
- } else {
- ElMessage.error('小休失败');
- }
- };
- /*
- * 示闲
- * ReqAgentIdle - 方法名
- * Extension:分机号
- */
- const onIdle = () => {
- ElMessageBox.confirm(`确定要示闲,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- const objMsg = {
- Action: 'ReqAgentIdle',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- console.log('小休结束');
- // 结束小休 设置示忙 业务系统统计需要
- busyOff()
- .then(() => {
- console.log(`${getNowDateTime()}:业务系统调用示忙结束成功`);
- })
- .catch((err) => {
- console.log(`${getNowDateTime()}:业务系统调用示忙结束失败,${err}`);
- }); // 结束小休(调用业务系统接口,统计需要)
- })
- .catch(() => {
- state.loading = false;
- });
- };
- /*
- * 示闲返回值
- * {“Action”:”ResAgentIdle”,”Param”:{“Result”:}}
- Result:0-1
- 0:成功 1:失败
- */
- const retIdle = (data: any) => {
- if (data.Param.Result == '0') {
- m_bTelBusy.value = false;
- m_strTelState.value = '200';
- e_TopStateChange(m_strTelState.value);
- m_IsTalkingDeal.value = false;
- } else {
- ElMessage.error('示闲失败');
- }
- };
- // 手动取消整理
- const onCancelTalkDeal = () => {
- ElMessageBox.confirm(`确定要取消整理,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- console.log(m_strTelStateBeforeCallOut.value, '这是手动取消整理之前的状态');
- let objMsg = {};
- if (m_strTelStateBeforeCallOut.value === '201') {
- // 如果之前的状态是示忙 调用示忙
- objMsg = {
- Action: 'ReqAgentBusy',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- } else {
- // 默认调用示闲
- objMsg = {
- Action: 'ReqAgentIdle',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- }
- // 发送请求
- e_TelSendMsg(objMsg);
- })
- .catch(() => {
- state.loading = false;
- });
- };
- /*
- * 保持
- */
- const onHold = () => {
- ElMessageBox.confirm(`确定要保持,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- const objMsg = {
- Action: 'ReqHoldCall',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- })
- .catch(() => {
- state.loading = false;
- });
- };
- /*
- * 保持返回
- */
- const retHold = (data: any) => {
- if (data.Param.Result == '0') {
- m_IsHold.value = true;
- m_strTelState.value = '310';
- e_TopStateChange(m_strTelState.value);
- } else {
- ElMessage.error('保持操作失败');
- }
- };
- /*
- * 取消保持·······
- */
- const onReHold = () => {
- ElMessageBox.confirm(`确定要恢复,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- const objMsg = {
- Action: 'ReqRetrieve',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- })
- .catch(() => {
- state.loading = false;
- });
- };
- /*
- * 取消保持返回状态
- */
- const retRehold = (data: any) => {
- if (data.Param.Result == '0') {
- // 是否是保持通话
- m_IsHold.value = false;
- if (m_IsCallIn.value) {
- m_strTelState.value = '301';
- } else {
- m_strTelState.value = '303';
- }
- e_TopStateChange(m_strTelState.value);
- } else {
- ElMessage.error('取消保持操作失败');
- }
- };
- // 外呼
- const onCallOut = () => {
- state.loading = false;
- state.outboundDialogVisible = true;
- };
- // 记录一下呼叫之前的状态
- const m_strTelStateBeforeCallOut = ref('');
- const outboundFormRef = ref<RefType>();
- const clickOnOutbound = (formEl: FormInstance | undefined) => {
- if (!formEl) return;
- formEl.validate((valid: boolean) => {
- if (!valid) return;
- state.loading = true;
- m_strTelStateBeforeCallOut.value = m_strTelState.value;
- callout(state.outboundForm.telNo);
- state.outboundDialogVisible = false;
- });
- };
- // 外呼弹窗关闭清空
- const outboundFormClose = () => {
- outboundFormRef.value?.resetFields();
- outboundFormRef.value?.clearValidate();
- };
- /*
- * 外呼
- * ReqMakeCall - 方法名
- * Extension:分机号
- * Called:被叫
- * CustomerId:客户ID
- */
- const callout = (strCallNumber: string | number | null) => {
- if (!strCallNumber) {
- ElMessage.error('电话号码不能为空');
- return;
- }
- const strCallNumberTrim = trimCompat(strCallNumber);
- const obkMsg = {
- Action: 'ReqMakeCall',
- Param: {
- Extension: m_strUserNo.value,
- Called: strCallNumberTrim,
- CustomerId: '',
- },
- };
- // 是否外呼
- m_IsCallOut.value = true;
- // 发送请求
- e_TelSendMsg(obkMsg);
- };
- /*
- * 外呼返回事件
- {“Action”:”ResMakeCall”,”Param”:{“Result”:}}
- Result:0-1
- 0:成功 1:失败
- */
- const retCallOut = (data: any) => {
- state.loading = false;
- if (data.Param.Result == '0') {
- // 是否外呼
- m_IsCallOut.value = true; // 呼出
- m_strTelState.value = '302';
- e_TopStateChange(m_strTelState.value);
- } else {
- ElMessage.error('呼叫失败');
- }
- };
- /*
- * 咨询
- */
- const onConsultOpen = () => {
- m_strTelState.value = '331';
- // 保持状态
- m_IsHold.value = true;
- state.loading = false;
- state.consultDialogVisible = true;
- };
- const consultFormRef = ref<RefType>();
- const clickOnConsult = (formEl: FormInstance | undefined) => {
- if (!formEl) return;
- formEl.validate((valid: boolean) => {
- if (!valid) return;
- state.loading = true;
- onConsult(state.consultForm.telNo, state.consultForm.strType);
- state.consultDialogVisible = false;
- });
- };
- // 咨询弹窗关闭清空
- const consultFormClose = () => {
- consultFormRef.value?.resetFields();
- consultFormRef.value?.clearValidate();
- };
- const onConsult = (strCallNumber: string | number | null, strType: string) => {
- let strObj: EmptyObjectType;
- const strCallNumberTrim = trimCompat(strCallNumber);
- switch (strType) {
- // 内线
- case '0':
- strObj = {
- Action: 'ReqConsultInline',
- Param: {
- Extension: m_strUserNo.value,
- TargetExtension: strCallNumberTrim,
- },
- };
- break;
- // 外线
- case '1':
- strObj = {
- Action: 'ReqConsultOutline',
- Param: {
- Extension: m_strUserNo.value,
- TargetCalled: strCallNumberTrim,
- },
- };
- break;
- // 群组
- case '2':
- strObj = {
- Action: 'ReqConsultSkillGroup',
- Param: {
- Extension: m_strUserNo.value,
- TargetSkillGroup: strCallNumberTrim,
- },
- };
- break;
- }
- // 转接类型
- m_strConsultType.value = strType;
- // 外呼
- m_IsCallOut.value = true;
- // 咨询状态
- m_IsConsult.value = true;
- // 发送请求
- e_TelSendMsg(strObj);
- };
- /*
- * 转接返回
- * data
- * {“Action”:”ResConsultSkillGroup”,”Param”:{“Result”:}}Result:0-1
- * 0:成功 1:失败
- * strType:0-转内线;1-转外线;2-转群组
- */
- const retConsult = (data: any, strType: string) => {
- state.loading = false;
- if (data.Param.Result == '0') {
- // 咨询成功
- // m_IsConsult = true;
- //m_strTelState = "330";
- // e_TopStateChange(m_strTelState);
- } else {
- //alert("转接失败");
- if (!m_IsHangup.value) {
- if (m_IsCallOut.value) {
- // 呼出接通
- m_strTelState.value = '303';
- } else {
- // 呼入接通
- m_strTelState.value = '301';
- }
- e_TopStateChange(m_strTelState.value);
- }
- let strMsg = '咨询失败';
- if (strType == '0') {
- strMsg = '咨询内线失败';
- } else if (strType == '1') {
- strMsg = '咨询外线失败';
- } else if (strType == '2') {
- strMsg = '咨询技能组失败';
- }
- ElMessage.error(strMsg);
- }
- };
- /*
- * 转接
- */
- const onTransfer = () => {
- const objMsg = {
- Action: 'ReqTransfer',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- };
- /*
- * 转接返回
- */
- const retResTransfer = (data: any) => {
- if (data.Param.Result == '0') {
- // 咨询成功
- m_IsConsult.value = true;
- m_strTelState.value = '320';
- e_TopStateChange(m_strTelState.value);
- } else {
- ElMessage.error('转接失败');
- }
- };
- /*
- * 盲转
- */
- const onTransferMzOpen = () => {
- state.loading = false;
- state.blindDialogVisible = true;
- };
- const blindFormRef = ref<RefType>();
- const clickOnBlind = (formEl: FormInstance | undefined) => {
- if (!formEl) return;
- formEl.validate((valid: boolean) => {
- if (!valid) return;
- state.loading = true;
- onTransferMz(state.blindForm.telNo);
- state.blindDialogVisible = false;
- });
- };
- // 盲转弹窗关闭清空
- const blindFormClose = () => {
- blindFormRef.value?.resetFields();
- blindFormRef.value?.clearValidate();
- };
- const onTransferMz = (strCallNumber: string | number | null) => {
- const strCallNumberTrim = trimCompat(strCallNumber);
- const objMsg = {
- Action: 'ReqBlindTransfer',
- Param: {
- Extension: m_strUserNo.value,
- TargetCalled: strCallNumberTrim,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- state.loading = false;
- console.log(`${getNowDateTime()}盲转消息:${JSON.stringify(objMsg)}`);
- // 结束挂机
- useTimeoutFn(() => {
- sendHangup();
- }, 300);
- };
- /*
- * 三方会议
- */
- const onConference = () => {
- ElMessageBox.confirm(`确定要发起三方会议,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- state.loading = true;
- const objMsg = {
- Action: 'ReqConference',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- })
- .catch(() => {
- state.loading = false;
- });
- };
- // 会议时长
- const conferenceTime = ref(0);
- const conferenceTimer = useIntervalFn(
- () => {
- conferenceTime.value += 1;
- },
- 1000,
- { immediate: false }
- );
- // 三方会议开始
- const startConferenceTime = () => {
- conferenceTimer.resume();
- };
- // 三方会议时长计时结束
- const stopConferenceTime = () => {
- conferenceTime.value = 0;
- conferenceTimer.pause();
- };
- /*
- * 三方会议返回
- * { "Action": "ReqConference", "Param": {"Extension": "1002"} }
- */
- const retResConference = (data: any) => {
- state.loading = false;
- if (data.Param.Result == '0') {
- // 咨询成功
- m_IsConsult.value = true;
- m_strTelState.value = '320';
- e_TopStateChange(m_strTelState.value);
- } else {
- ElMessage.error('三方会议失败');
- }
- };
- /*
- * 挂机
- * ReqHangup - 方法名
- * Extension:分机号
- {“Event”: “EvtHangup”}
- */
- const sendHangup = () => {
- const objMsg = {
- Action: 'ReqHangup',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- };
- const onHangup = () => {
- ElMessageBox.confirm(`确定要挂机,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- sendHangup();
- })
- .catch(() => {
- state.loading = false;
- });
- };
- /*
- * 挂机事件
- * {“Event”: “EvtHangup”}
- */
- const retHangup = (data?: any) => {
- // 挂机后该状态为 false
- console.log(`${getNowDateTime()}:接收消息:呼叫中心挂机回调`, data);
- // 挂机后该状态为 false
- // 挂机
- m_IsHangup.value = true;
- m_IsCallIn.value = false;
- m_IsCallOut.value = false;
- m_bCallConnect.value = false;
- globalState.callCenterIsOnThePhone = false;
- m_IsConsult.value = false;
- m_IsHold.value = false;
- // 是否弹屏
- m_bIsOpen.value = false;
- m_strConsultType.value = '-1';
- /*
- m_strTelState.value = '200';
- e_TopStateChange(m_strTelState.value);*/
- // 未监听
- m_IsMonListen.value = '0';
- // 呼出是否弹屏(用于未接统计“回拨”业务处理)
- m_CallOutOpen.value = false;
- // 清除呼叫ID
- callId.value = '';
- };
- /*
- * 评价
- */
- const i_evaluate = () => {
- ElMessageBox.confirm(`确定要开启评价,评价后将直接挂机,是否继续?`, '提示', {
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- type: 'warning',
- draggable: true,
- cancelButtonClass: 'default-button',
- autofocus: false,
- })
- .then(() => {
- const objMsg = {
- Action: 'ReqSatisfaction',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- state.loading = false;
- console.log(`${getNowDateTime()}评价消息:${JSON.stringify(objMsg)}`);
- // 结束挂机
- useTimeoutFn(() => {
- sendHangup();
- }, 300);
- })
- .catch(() => {
- state.loading = false;
- });
- };
- /*
- * 监听
- */
- const reqMonListen = (strTargetNum: string) => {
- const objMsg = {
- Action: 'ReqMonListen',
- Param: {
- Extension: m_strUserNo.value,
- TargetExtension: strTargetNum,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- };
- /*
- * 监听返回
- */
- const retResMonListen = (data: any) => {
- if (data.Param.Result == '0') {
- m_IsMonListen.value = '1';
- // 成功
- } else {
- m_IsMonListen.value = '2';
- }
- };
- /*
- * 取消监听
- */
- const reqStopListen = (strTargetNum: string) => {
- const objMsg = {
- Action: 'ReqStopListen',
- Param: {
- Extension: m_strUserNo.value,
- TargetExtension: strTargetNum,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- };
- /*
- * 取消监听返回
- */
- const retResStopListen = (data: any) => {
- if (data.Param.Result == '0') {
- m_IsMonListen.value = '1';
- // 成功
- } else {
- m_IsMonListen.value = '2';
- }
- };
- /*
- * 呼入(振铃)事件
- * EvtCallAlerting - 事件名称
- * Caller:主叫
- * Called:被叫
- * Customerid:客户ID
- * Callid:呼叫ID
- {“Event”:”EvtCallAlerting”,”Param”:{“Caller”:”123”,”Called”:”456”,”Customerid”:”675”,”Callid”:”94593939”}}
- */
- const router = useRouter();
- const evtCallAlerting = (data: any) => {
- let strCalledNum: string;
- let strTelNumber: string;
- console.log(
- `开始振铃,是否呼出:${m_IsCallOut.value},是否呼出弹屏:${m_CallOutOpen.value},是否弹屏:${m_bIsOpen.value},弹屏方式:${
- m_strOpenFlag.value === '1' ? '接通弹屏' : '振铃弹屏'
- }`
- );
- if (m_IsCallOut.value) {
- // 呼出
- // 呼出振铃
- m_strTelState.value = '302';
- e_TopStateChange(m_strTelState.value);
- if (m_CallOutOpen.value) {
- // 呼出是否弹屏(用于未接统计“回拨”业务处理)
- // 主叫号码
- strTelNumber = data.Param.Caller;
- // 被叫号码
- strCalledNum = data.Param.Called;
- // 呼叫ID
- callId.value = data.Param.Callid;
- if (!m_bIsOpen.value && m_strOpenFlag.value == '2') {
- // 振铃呼出弹屏
- console.log(
- '呼出是否弹屏[' +
- m_bIsOpen.value +
- '];弹屏方式[' +
- m_strOpenFlag.value +
- '];记录ID[' +
- callId.value +
- '];主叫号码[' +
- strTelNumber +
- '];被叫号码[' +
- strCalledNum +
- ']'
- );
- m_bIsOpen.value = true;
- // 呼出不再弹单
- m_CallOutOpen.value = false;
- // 去电振铃弹屏
- router.push({
- name: 'orderAccept',
- query: {
- createBy: 'tel',
- fromTel: strTelNumber, // 来电号码
- callId: callId.value, // 通话ID
- transfer: strCalledNum, // 转接来源(如12345,12333)
- telArea: '',
- identityType: '', // 按键接收(2:市民 1:企业) 默认市民
- },
- });
- }
- }
- } else {
- // 呼入振铃该状态为呼入
- m_IsCallIn.value = true;
- // 呼入振铃
- m_strTelState.value = '300';
- e_TopStateChange(m_strTelState.value);
- // 主叫号码
- strTelNumber = data.Param.Caller;
- // 被叫号码
- strCalledNum = data.Param.Called;
- // 呼叫ID
- callId.value = data.Param.Callid;
- if (strTelNumber.length == strCalledNum.length && strTelNumber.length == 4) {
- // 如果主叫号码、被叫号码都是分机号码,则不弹屏
- m_bIsOpen.value = true;
- }
- if (!m_bIsOpen.value && m_strOpenFlag.value === '2') {
- m_bIsOpen.value = true;
- // 用户按键
- const strDigit = data.Param.Digit;
- console.log(
- '用户按键[' + strDigit + '];记录ID[' + callId.value + '];主叫号码[' + strTelNumber + '];被叫号码[' + strCalledNum + ']',
- '来电弹屏'
- );
- // 来电 振铃弹单
- router.push({
- name: 'orderAccept',
- query: {
- createBy: 'tel',
- fromTel: strTelNumber, // 来电号码
- callId: callId.value, // 通话ID
- transfer: strCalledNum, // 转接来源(如12345,12333)
- telArea: '',
- identityType: strDigit, // 按键接收(2:市民 1:企业) 默认市民
- },
- });
- if (AppConfigInfo.value.isOpenSpecialPhone) {
- // 配置开关
- getSpecialNumberDetailByPhone({ PhoneNumber: strTelNumber }).then((res: any) => {
- // 如果来电电话在在特殊号码配置中 需要提示信息
- if (res.result.notes) {
- ElNotification({
- title: '特殊号码提醒',
- message: res.result.notes,
- type: 'info',
- });
- }
- });
- }
- }
- }
- const request = {
- creationTime: new Date(),
- name: `兴唐呼叫中心呼入振铃事件消息,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(data),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- /*
- * 应答事件
- * Caller:主叫
- * Called:被叫
- * Callid:呼叫ID
- {“Event”:”EvtCallAnswer”,”Param”:{“Caller”:”123”,”Called”:”456”,”Callid”:”94593939”}}
- */
- const talkTime = ref(0); // 通话时长
- const talkTimer = useIntervalFn(
- () => {
- talkTime.value += 1;
- Local.set('talkTime', String(talkTime.value));
- },
- 1000,
- { immediate: false }
- );
- // 开始通话计时
- const startTalkTimer = () => {
- let localTalkTime = Local.get('talkTime');
- if (talkTime.value) {
- talkTime.value = Number(localTalkTime);
- }
- talkTimer.resume();
- stopIdleTime();
- };
- // 结束通话计时
- const stopTalkTimer = () => {
- talkTimer.pause();
- talkTime.value = 0;
- Local.remove('talkTime');
- };
- const evtEvtCallAnswer = (data: any) => {
- let strCalledNum: string;
- let strTelNumber: string;
- // 通话接通
- m_bCallConnect.value = true;
- globalState.callCenterIsOnThePhone = true;
- console.log(
- `接通电话,是否呼出:${m_IsCallOut.value},是否咨询:${m_IsConsult.value},是否呼出弹屏:${m_CallOutOpen.value},是否弹屏:${
- m_bIsOpen.value
- },弹屏方式:${m_strOpenFlag.value === '1' ? '接通弹屏' : '振铃弹屏'}`
- );
- if (m_IsCallOut.value) {
- // 呼出
- if (!m_IsConsult.value) {
- // 不是转接、三方会议呼出
- // 呼出应答
- m_strTelState.value = '303';
- e_TopStateChange(m_strTelState.value);
- // 主叫号码
- strTelNumber = data.Param.Caller;
- // 被叫号码
- strCalledNum = data.Param.Called;
- // 呼叫ID
- callId.value = data.Param.Callid;
- mittBus.emit('outboundConnect', {
- callNumber: strCalledNum, // 被叫号码
- callId: callId.value, //呼叫ID
- }); // 外呼接通之后收到的消息
- if (m_CallOutOpen.value) {
- // 呼出是否弹屏(用于未接统计“回拨”业务处理)
- if (!m_bIsOpen.value && m_strOpenFlag.value === '1') {
- console.log(
- '呼出是否弹屏[' +
- m_bIsOpen.value +
- '];弹屏方式[' +
- m_strOpenFlag.value +
- '];记录ID[' +
- callId.value +
- '];主叫号码[' +
- strTelNumber +
- '];被叫号码[' +
- strCalledNum +
- ']'
- );
- m_bIsOpen.value = true;
- // 呼出不再弹单 接通弹单
- m_CallOutOpen.value = false;
- router.push({
- name: 'orderAccept',
- query: {
- createBy: 'tel',
- fromTel: strTelNumber, // 来电号码
- callId: callId.value, // 通话ID
- transfer: strCalledNum, // 转接来源(如12345,12333)
- telArea: '',
- identityType: '', // 按键接收(2:市民 1:企业) 默认市民
- },
- });
- if (AppConfigInfo.value.isOpenSpecialPhone) {
- // 配置开关
- getSpecialNumberDetailByPhone({ PhoneNumber: strTelNumber }).then((res: any) => {
- // 如果来电电话在在特殊号码配置中 需要提示信息
- if (res.result.notes) {
- ElNotification({
- title: '特殊号码提醒',
- message: res.result.notes,
- type: 'info',
- });
- }
- });
- }
- }
- }
- }
- } else {
- // 呼入应答
- m_strTelState.value = '301';
- e_TopStateChange(m_strTelState.value);
- // 呼入应答
- // 主叫号码
- strTelNumber = data.Param.Caller;
- // 被叫号码
- strCalledNum = data.Param.Called;
- // 呼叫ID
- callId.value = data.Param.Callid;
- console.log(
- '是否弹屏[' +
- m_bIsOpen.value +
- '];弹屏方式[' +
- m_strOpenFlag.value +
- '];记录ID[' +
- callId.value +
- '];主叫号码[' +
- strTelNumber +
- '];被叫号码[' +
- strCalledNum +
- ']'
- );
- if (strTelNumber.length == strCalledNum.length && strTelNumber.length == 4) {
- // 如果主叫号码、被叫号码都是分机号码,则不弹屏
- m_bIsOpen.value = true;
- }
- if (!m_bIsOpen.value && m_strOpenFlag.value === '1') {
- m_bIsOpen.value = true;
- // 用户按键
- const strDigit = data.Param.Digit;
- console.log(
- '用户按键' +
- strDigit +
- '是否弹屏[' +
- m_bIsOpen.value +
- '];弹屏方式[' +
- m_strOpenFlag.value +
- '];记录ID[' +
- callId.value +
- '];主叫号码[' +
- strTelNumber +
- '];被叫号码[' +
- strCalledNum +
- ']'
- );
- // 来电 接通弹单
- router.push({
- name: 'orderAccept',
- query: {
- createBy: 'tel',
- fromTel: strTelNumber, // 来电号码
- callId: callId.value, // 通话ID
- transfer: strCalledNum, // 转接来源(如12345,12333)
- telArea: '',
- identityType: strDigit, // 按键接收(2:市民 1:企业) 默认市民
- },
- });
- }
- }
- if (!m_IsConsult.value) {
- // 如果不是咨询,则启动通话时间计时器
- startTalkTimer(); // 通话计时器开始
- }
- const request = {
- creationTime: new Date(),
- name: `兴唐呼叫中心应答事件消息,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(data),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- /*
- * 39.外呼振铃事件
- * Caller:主叫
- * Called:被叫
- * Callid:呼叫ID
- {“Event”:”EvtOutCalling”,”Param”:{“Caller”:”123”,”Called”:”456”,”Customerid”:”675”,”Callid”:”94593939”}}
- */
- const evtEvtCalling = (data: any) => {
- let strCalledNum: string;
- let strTelNumber: string;
- m_IsCallOut.value = true; // 呼出
- console.log(
- `开始振铃,是否呼出:${m_IsCallOut.value},是否呼出弹屏:${m_CallOutOpen.value},是否弹屏:${m_bIsOpen.value},弹屏方式:${
- m_strOpenFlag.value === '1' ? '接通弹屏' : '振铃弹屏'
- }`
- );
- if (m_CallOutOpen.value) {
- // 主叫号码
- strTelNumber = data.Param.Caller;
- // 被叫号码
- strCalledNum = data.Param.Called;
- // 呼叫ID
- callId.value = data.Param.Callid;
- if (!m_bIsOpen.value && m_strOpenFlag.value === '2') {
- //振铃弹屏
- m_bIsOpen.value = true;
- console.log(
- '呼出是否弹屏[' +
- m_bIsOpen.value +
- '];弹屏方式[' +
- m_strOpenFlag.value +
- '];记录ID[' +
- callId.value +
- '];主叫号码[' +
- strTelNumber +
- '];被叫号码[' +
- strCalledNum +
- ']'
- );
- // 外呼振铃弹单
- router.push({
- name: 'orderAccept',
- query: {
- createBy: 'tel',
- fromTel: strTelNumber, // 来电号码
- callId: callId.value, // 通话ID
- transfer: strCalledNum, // 转接来源(如12345,12333)
- telArea: '',
- identityType: '', // 按键接收(2:市民 1:企业) 默认市民
- },
- });
- }
- }
- const request = {
- creationTime: new Date(),
- name: `兴唐呼叫中心外呼振铃消息,分机号:${m_strUserNo.value},工号:${m_strJobNum.value},技能组:${m_strSkillId.value}`,
- remark: JSON.stringify(data),
- executeUrl: themeConfig.value.callCenterSocketUrl,
- };
- submitLogFn(request);
- };
- /*
- * 计算接通时长
- */
- // 是否在队列状态
- const m_bIsQueue = ref(false);
- /*
- * 队列等待
- */
- const i_QueueNum = (data: any) => {
- // 40,87098300,85961020,1,80|40,87098300,85961020,1,80|40,87098300,85961020,1,80
- // 处于队列状态
- m_bIsQueue.value = true;
- // 队列信息
- const strInfo = data.Param.Info;
- if (undefined != strInfo && '' != strInfo) {
- const arrFirst = strInfo.split('|');
- if (null != arrFirst && 0 < arrFirst.length) {
- // 设置队列等待数量
- console.log(arrFirst.length - 1);
- currentWait.value = arrFirst.length - 1;
- e_SetQueryWait(arrFirst.length - 1, strInfo);
- } else {
- currentWait.value = 0;
- }
- console.log(`${getNowDateTime()}:队列消息1:`, strInfo, arrFirst.length - 1);
- } else {
- currentWait.value = 0;
- }
- console.log(`${getNowDateTime()}:队列消息2:`, data.Param);
- };
- /*
- 语音识别结果保存
- */
- const e_EvtRecognize = (data: any) => {
- console.log('EvtRecognize' + JSON.stringify(data));
- const strDirection = data.Direciton || '';
- const strResult = data.Result || '';
- console.log(`${getNowDateTime()}:识别结果`, strResult, strDirection);
- };
- /*
- 转三方接通状态
- */
- const e_EvtDispatchState = (data: any) => {
- const strState = data.State || '';
- console.log(`${getNowDateTime()}:转接三方状态-EvtDispatchState:`, data, `是否咨询状态:${m_IsConsult.value},当前str:${strState}`);
- if (strState === '2') {
- if (m_IsConsult.value) {
- // 咨询成功
- m_strTelState.value = '330';
- e_TopStateChange(m_strTelState.value);
- }
- } else if (strState === '3') {
- if (m_IsConsult.value) {
- m_strTelState.value = '301';
- m_IsHold.value = false; // 三方有一方挂断 取消保持
- e_TopStateChange(m_strTelState.value);
- }
- } else {
- //alert("转接失败");
- }
- };
- /**
- * 异常处理
- * @param {any} data
- */
- const retResError = (data: any) => {
- if (data.Param.Result == '99') {
- // 掉线
- m_strTelState.value = '0';
- e_TopStateChange(m_strTelState.value);
- globalState.callCenterWs = null;
- ElMessage.error('连接已断开');
- // 自动签入
- onSignIn();
- }
- };
- // 改变状态方法
- const e_TopStateChange = (state: string) => {
- console.log(`${getNowDateTime()}:状态改变:`, state);
- switch (state) {
- case '0': // 签出
- break;
- case '100': // 登录成功
- break;
- case '200': // 空闲
- break;
- case '201': // 小休
- break;
- case '300': //呼入振铃
- break;
- case '301': // 呼入通话
- stopConferenceTime();
- break;
- case '302': // 呼出振铃
- break;
- case '303': // 呼出通话
- stopConferenceTime();
- break;
- case '310': // 通话保持
- break;
- case '320': // 三方会议
- startConferenceTime(); // 三方会议时长开始
- break;
- case '330': // 转接
- break;
- case '331': // 转接
- break;
- case '900': // 整理
- break;
- }
- // console.log(state);
- };
- /**
- * 更新话机动作
- * 1:登录登出;2:小休示闲;3:摘机
- */
- const e_ActionUpdate = (strActionType: string) => {
- const data = {
- Action: 'ActionUpdate',
- ActType: strActionType,
- };
- };
- /*
- * 用户分机签入签出(用户分机分离业务)
- * 业务改造,先签入我方业务系统,再签入呼叫中心
- *
- */
- const e_TelSignIn = async (telNo: string | null, groupId: string) => {
- const data = {
- telNo,
- groupId,
- telModelState: 1,
- };
- try {
- const { result } = await callCenterSignIn(data);
- console.log(`${getNowDateTime()}:业务系统:签入成功`, result);
- } catch (e) {
- console.log(e);
- }
- };
- /*
- * 用户分机签出(用户分机分离业务)
- */
- const e_TelSignOut = async (telNo: string | null, groupId: string) => {
- const data = {
- telNo,
- groupId,
- telModelState: 1,
- };
- try {
- await callCenterSignOut(data);
- console.log(`${getNowDateTime()}:业务系统:签出成功`);
- } catch (e) {
- console.log(e);
- }
- };
- /**
- * 保存队列信息
- * @param {any} strUserNum
- * @param strQueueInfo
- */
- const e_SetQueryWait = (strUserNum: string | number, strQueueInfo: any) => {
- const data = {
- Action: 'SaveQueueNumNew',
- QueueNum: strUserNum,
- QueueInfo: strQueueInfo,
- };
- };
- // 获取基础信息
- const telsList = ref([]);
- const getBaseInfo = async () => {
- try {
- const { result } = await getCallCenterList();
- telsList.value = result ?? [];
- } catch (e) {
- console.log(e);
- }
- };
- // 查询三方会议和转接的号码
- const threeWayAndTransfer = ref<EmptyArrayType>([]);
- const getThreeWayAndTransfer = async () => {
- try {
- const { result } = await getDataByCode('TransferNumber');
- threeWayAndTransfer.value = result ?? [];
- } catch (err) {
- console.log(`${getNowDateTime()}:获取转接和三方会议的号码错误`, err);
- }
- };
- // 检查登录状态
- const userAlreadyLogin = ref(false); // 用户已经登录
- const checkLogin = async () => {
- try {
- const { result } = await getCallCenterStatus();
- console.log(`${getNowDateTime()}:检测呼叫中心签入状态:`, result);
- if (result) {
- // 如果查询到登录状态
- wsRef.value.open();
- userAlreadyLogin.value = true;
- m_strUserNo.value = result.telNo; // 分机号
- m_strJobNum.value = <string>result.staffNo; // 工号
- m_strSkillId.value = result.queueId; // 技能组
- signTime.value = result.second ?? 0; // 签入时长
- globalState.currentTel = {
- telNo: m_strUserNo.value,
- telGroup: m_strSkillId.value,
- jobNum: m_strJobNum.value,
- };
- } else {
- userAlreadyLogin.value = false;
- }
- } catch (e) {
- console.log(e);
- userAlreadyLogin.value = false;
- }
- };
- // 今日等待
- const todayWait = ref(0);
- const todayWaitValue = useTransition(todayWait, {
- duration: 500,
- });
- // 当前等待
- const currentWait = ref<number>(0);
- const currentWaitValue = useTransition(currentWait, {
- duration: 500,
- });
- // 获取小休原因
- const getReason = async () => {
- try {
- // 查询小休原因
- const { result } = await telRestBaseData();
- state.restReasonOptions = result?.restReason ?? [];
- } catch (err) {
- console.log(err);
- }
- };
- // 监听消息
- const signalRStart = () => {
- signalR?.SR?.on('RestApplyPass', (data: any) => {
- // 小休审批通过消息
- console.log(`${getNowDateTime()}:小休审批通过消息`, data);
- RestApplyPassFn(data);
- });
- };
- // 小休审批通过消息
- const RestApplyPassFn = (data: any) => {
- ElNotification({
- title: '成功',
- message: '小休审批通过,开始小休',
- type: 'success',
- });
- isRestAudit.value = false;
- const objMsg = {
- Action: 'ReqAgentBusy',
- Param: {
- Extension: m_strUserNo.value,
- },
- };
- // 发送请求
- e_TelSendMsg(objMsg);
- console.log(`${getNowDateTime()}:小休审核通过,${data}`);
- };
- onMounted(async () => {
- await getBaseInfo(); // 查询可以签入的分机
- await getThreeWayAndTransfer(); // 查询呼叫转接的号码列表
- // await getReason(); // 获取小休原因
- initWs();
- await checkLogin();
- signalRStart();
- // 是否在通话中
- window.onbeforeunload = function (e: any) {
- if (m_bCallConnect.value) {
- const dialogText = '正在通话中,您确定要刷新吗?';
- e.returnValue = dialogText;
- return dialogText;
- }
- };
- });
- onBeforeUnmount(() => {
- signalR.SR.off('RestApplyPass');
- });
- </script>
- <style scoped lang="scss">
- .seizeSeat-box {
- display: none;
- }
- .phoneControls {
- display: flex;
- flex: 1;
- padding: 0 10px;
- color: var(--hotline-color-text-main);
- height: 100%;
- align-items: center;
- .status-box {
- width: 240px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- position: relative;
- box-sizing: border-box;
- cursor: pointer;
- text-align: left;
- padding: 4px 26px 4px 12px;
- gap: 6px;
- min-height: 32px;
- line-height: 24px;
- border-radius: var(--el-border-radius-round);
- background-color: var(--el-color-info-light-8);
- transition: var(--el-transition-duration);
- .arrow {
- position: absolute;
- right: 10px;
- transition: transform var(--el-transition-duration);
- }
- .is-reverse {
- transform: rotate(180deg);
- }
- }
- // 按钮列表
- .btn-container {
- display: flex;
- justify-content: space-between;
- width: calc(100% - 100px);
- height: 100%;
- border-right: 1px solid var(--el-border-color);
- border-left: 1px solid var(--el-border-color);
- overflow: hidden;
- .item {
- text-align: center;
- cursor: pointer;
- width: 100%;
- user-select: none;
- display: flex;
- align-items: center;
- justify-content: center;
- //border-right: 1px solid var(--el-border-color);
- .icon {
- color: var(--el-color-primary);
- }
- &.disabled {
- cursor: not-allowed;
- overflow: hidden;
- .icon {
- color: #cccccc;
- }
- }
- }
- .active {
- &:hover {
- color: var(--hotline-color-white);
- background-color: var(--el-color-primary);
- .icon {
- color: var(--hotline-color-white);
- }
- }
- }
- }
- .wait-box {
- width: 150px;
- display: flex;
- flex-direction: column;
- justify-content: center;
- text-align: center;
- border-right: 1px solid var(--el-border-color);
- height: 100%;
- }
- }
- </style>
|