PageSourceSearch

https://www.ruanfujia.com/wp-content/themes/bigboom/js/crm-followup.js?ver=5325e5c5faad8bca

js ruanfujia.com collected 2026-10-08 05:50:53 UTC 7,912 bytes, 68 lines download raw bytes

1(function(){
2'use strict';
3var generation=0,pending=new Set(),drafts=new Map();
4var modes={opportunity_status:['status','status','rfj_ajax_crm_opp_update_status'],opportunity_remark:['remark','remark','rfj_ajax_crm_opp_update_remark'],opportunity_log:['log','comment','rfj_ajax_crm_log_insert'],opportunity_assign:['assign','assigned_to','rfj_ajax_crm_opp_update_assign'],contact_assign_btn:['contact_assign','assigned_to','rfj_ajax_crm_contact_update_assign']};
5function id(v){return typeof v==='string'&&/^[1-9][0-9]*$/.test(v)&&Number.isSafeInteger(Number(v))&&String(Number(v))===v?v:'';}
6function buttonContext(b){return {userID:id(b.getAttribute('data-crm-user')),companyID:id(b.getAttribute('data-crm-company')),objectID:id(b.getAttribute(b.id==='contact_assign_btn'?'data-contact-id':'data-opp-id'))};}
7function input(b){var m=b.closest('.modal');return b.id==='opportunity_remark'?document.getElementById('remark_text'):b.id==='opportunity_log'?document.getElementById('log_text'):m&&m.querySelector('#get_assign_to');}
8function value(b){if(b.id==='opportunity_status'){var m=b.closest('.modal'),s=m&&m.querySelector('[data-status].active');return s?s.getAttribute('data-status'):'';}var e=input(b);return e?e.value:'';}
9async function request(fields,c){var r=await fetch('/wp-admin/admin-ajax.php',{method:'POST',credentials:'same-origin',signal:c.signal,headers:{'Content-Type':'application/x-www-form-urlencoded;charset=UTF-8'},body:new URLSearchParams(fields).toString()});if(!r.ok)throw new Error('跟进结果暂无法确认,请核对原操作');var d=await r.json();if(!d||!d.success){var e=new Error(d&&d.data&&d.data.message||'跟进结果暂无法确认,请核对原操作');e.code=d&&d.data&&d.data.code;throw e;}return d.data;}
10function bootstrap(c){return request({action:'rfj_ajax_crm_opp_insert',mode:'bootstrap'},c);}
11function owned(o,x){return o&&String(o.userID)===x.userID&&String(o.companyID)===x.companyID&&typeof o.nonce==='string'&&o.nonce;}
12function privateClear(){['opportunity_remark','opportunity_log'].forEach(function(k){var b=document.getElementById(k),e=b&&input(b);if(b&&e){if(e.value!==''||!drafts.has(k))drafts.set(k,{context:buttonContext(b),text:e.value});e.value='';}});}
13function suspend(){generation++;pending.forEach(function(c){c.abort();});pending.clear();privateClear();}
14window.addEventListener('pagehide',suspend);
15document.addEventListener('visibilitychange',async function(){
16 if(document.hidden){suspend();return;}if(!drafts.size)return;
17 var mine=generation,c=new AbortController();pending.add(c);
18 try{var o=await bootstrap(c);if(mine!==generation||document.hidden)return;var verified=new Map();
19  for(var item of drafts.entries()){var k=item[0],d=item[1],b=document.getElementById(k),e=b&&input(b);if(!e||!owned(o,d.context))continue;
20   var fields={action:modes[k][2],mode:'context',nonce:o.nonce,expectedUserID:d.context.userID,expectedCompanyID:d.context.companyID,opp_id:d.context.objectID};
21   var access=verified.get(d.context.objectID);if(!access){access=await request(fields,c);verified.set(d.context.objectID,access);}if(mine!==generation||document.hidden)return;
22   if(access&&access.allowed===true&&String(access.opp_id)===d.context.objectID)e.value=d.text;
23  }drafts.clear();
24 }catch(e){/* Drafts remain hidden until current object access is confirmed. */}finally{pending.delete(c);}
25});
26document.addEventListener('click',async function(event){
27 var b=event.target.closest&&event.target.closest('#opportunity_status,#opportunity_remark,#opportunity_log,#opportunity_assign,#contact_assign_btn');if(!b)return;event.preventDefault();event.stopImmediatePropagation();
28 if(document.hidden||b.dataset.crmBusy==='1')return;
29 var spec=modes[b.id],ctx=buttonContext(b),desired=value(b),mine=generation,c=new AbortController(),storageKey=null,record=null,wasRecovery=false;
30 if(!ctx.userID||!ctx.companyID||!ctx.objectID||typeof desired!=='string'){window.alert('跟进目标暂不可确认,请刷新页面');return;}
31 b.dataset.crmBusy='1';pending.add(c);
32 function current(){return mine===generation&&!document.hidden&&!c.signal.aborted&&b.isConnected;}
33 try{
34  var owner=await bootstrap(c);if(!current())return;if(!owned(owner,ctx)){privateClear();throw new Error('账号或企业已变化,请重新打开');}
35  if(drafts.has(b.id)){
36   var draft=drafts.get(b.id),access=await request({action:spec[2],mode:'context',nonce:owner.nonce,expectedUserID:ctx.userID,expectedCompanyID:ctx.companyID,opp_id:ctx.objectID}
36,c);
37   if(!current())return;if(!access||access.allowed!==true||String(access.opp_id)!==ctx.objectID)throw new Error('原编辑权限暂无法确认');
38   if(owned(owner,draft.context)){var editor=input(b);if(editor)editor.value=draft.text;}drafts.delete(b.id);window.alert('原编辑已恢复,请核对后再保存');return;
39  }
40  var fields={action:spec[2],nonce:owner.nonce,expectedUserID:ctx.userID,expectedCompanyID:ctx.companyID};fields[b.id==='contact_assign_btn'?'contact_id':'opp_id']=ctx.objectID;fields[spec[1]]=desired;
41  var original=b.getAttribute('data-crm-original');if(original!==null&&spec[0]!=='log')fields.expectedValue=original;
42  if(spec[0]==='log'){
43   storageKey='rfj_crm_pending_log_v1:'+ctx.userID+':'+ctx.companyID+':'+ctx.objectID;
44   var raw=localStorage.getItem(storageKey);if(raw!==null){
45    try{record=JSON.parse(raw);}catch(e){throw new Error('原跟进记录需要核对,请勿重复提交');}
46    if(!record||record.userID!==ctx.userID||record.companyID!==ctx.companyID||record.objectID!==ctx.objectID||typeof record.text!=='string'||!record.text||typeof record.requestID!=='string'||record.requestID.length!==36||! /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(record.requestID))throw new Error('原跟进记录需要核对,请勿重复提交');
47    wasRecovery=true;
48   }else{
49    if(!desired.trim())throw new Error('请填写活动记录');
50    record={userID:ctx.userID,companyID:ctx.companyID,objectID:ctx.objectID,text:desired,requestID:crypto.randomUUID()};
51    localStorage.setItem(storageKey,JSON.stringify(record));if(localStorage.getItem(storageKey)!==JSON.stringify(record))throw new Error('原跟进记录无法安全保留,请重试');
52   }
53   fields.comment=record.text;fields.requestID=record.requestID;
54   var recovered=await request(Object.assign({},fields,{mode:'resolve'}),c);if(!current())return;
55   if(!recovered||typeof recovered.found!=='boolean'||String(recovered.opp_id)!==ctx.objectID)throw new Error('原跟进结果暂无法确认');
56   if(recovered.found){if(!id(String(recovered.logID)))throw new Error('原跟进关联需要核对');}
57   else{recovered=await request(fields,c);if(!current())return;if(!recovered||!id(String(recovered.logID))||String(recovered.opp_id)!==ctx.objectID)throw new Error('跟进关联暂无法确认');}
58  }else{
59   var saved=await request(fields,c);if(!current())return;if(!saved||String(saved.opp_id)!==ctx.objectID||String(saved.value)!==desired)throw new Error('跟进字段暂无法确认');
60  }
61  var fresh=await bootstrap(c);if(!current())return;if(!owned(fresh,ctx))throw new Error('账号或企业已变化,请重新打开');
62  if(storageKey){if(localStorage.getItem(storageKey)!==JSON.stringify(record)){window.alert('原活动记录已确认保存;本机恢复记录已变化,请核对后再操作');return;}localStorage.removeItem(storageKey);if(wasRecovery&&desired!==record.text){window.alert('原活动记录已确认保存;当前输入尚未提交,请核对历史记录后再保存');return;}}
63  if(value(b)!==desired){window.alert('原内容已确认保存,当前修改尚未提交');return;}
64  window.location.reload();
65 }catch(e){if(current()){if(['identity','login_required','forbidden'].includes(e.code))privateClear();window.alert(e.message||'跟进结果暂无法确认,请核对原操作');}}
66 finally{pending.delete(c);b.dataset.crmBusy='0';}
67},true);
68})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.