This commit is contained in:
李鹏宇
2026-06-10 20:09:03 +08:00
parent 3c3d831bd8
commit a9db7db49f
4 changed files with 82 additions and 43 deletions
@@ -94,6 +94,7 @@ class SoongWorkflowCommentWizard(models.TransientModel):
employee_ids1 = fields.Many2many('hr.employee', 'employee_ids1_rel', 'custom_id1', 'employee_id1', string="人员范围", store=True)
employee_ids2 = fields.Many2many('hr.employee', 'employee_ids2_rel', 'custom_id2', 'employee_id2', string="加签传阅人员过滤")
@api.onchange('is_selection')
def _onchange_is_selection(self):
if not self.employee_ids1:
@@ -145,7 +145,8 @@
<field name="is_avoid" invisible="res_model not in ['issue.party.approval', 'issue.boss.approval', 'issue.class.approval']"/>
<field name="allowed_issue_ids" invisible="1"/>
<field name="avoid_issue_ids" widget="many2many_tags"
invisible="not is_avoid" options="{'no_create': True}"/>
invisible="not is_avoid" options="{'no_create': True}"
domain="[('id', 'in', allowed_issue_ids)]"/>
<field name="avoid_comment" invisible="not is_avoid" placeholder="请输入回避意见..."/>
<field name="employee_trans_domain_ids" invisible="1"/>
<!-- 退回意见退回类型 -->
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>{{REPORT_TITLE}} · 动态报表</title>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<!-- Chart.js loaded dynamically below -->
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:"Inter","PingFang SC","Microsoft YaHei",sans-serif;font-size:14px;min-height:100vh;transition:background .3s,color .3s}
@@ -190,7 +190,6 @@ tr.ch.h{display:none}
<div class="summary" id="sumArea"></div>
<div class="report-desc" id="descArea">📊 加载中...</div>
<div class="charts-wrap" id="chartsArea"></div>
<div class="flt" id="fltArea">
<div class="flt-g wide"><label>🔍</label><input id="fG" placeholder="全局搜索..." oninput="rf()"></div>
@@ -213,7 +212,6 @@ tr.ch.h{display:none}
</div>
<div class="tb-r" id="tbRight" style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<div id="chartCtrls" style="display:none;gap:6px;align-items:center;flex-wrap:wrap">
<span style="font-size:0.7rem;color:var(--fg4);white-space:nowrap">📊 图表</span>
<select id="qcDimSel" onchange="updateQuickChart()" title="按此字段分组" style="padding:4px 8px;background:var(--bg2);border:1px solid var(--bd);border-radius:6px;color:var(--fg);font-size:0.75rem;cursor:pointer;font-family:inherit;outline:none">
<option value="">维度</option>
</select>
@@ -226,16 +224,17 @@ tr.ch.h{display:none}
<button class="btn chart-type-btn" onclick="setChartType('pie')" id="btnPie" title="饼图">🥧</button>
<button class="btn chart-type-btn" onclick="setChartType('doughnut')" id="btnDoughnut" title="环形图">🍩</button>
<button class="btn chart-type-btn" onclick="setChartType('line')" id="btnLine" title="折线图">📈</button>
<button class="btn chart-type-btn" id="btnList" title="关闭图表" onclick="toggleChartView()" style="font-size:0.7rem">📋</button>
<button class="btn chart-type-btn act" id="btnList" title="列表视图" onclick="showTableView()" style="font-size:0.7rem">📋</button>
</div>
</div>
<button class="btn-excel" onclick="expXls()">⬇ 导出 Excel</button>
</div>
</div>
<div class="tw"><div class="ts" id="ts">
<div class="tw" id="tw"><div class="ts" id="ts">
<table><thead id="thd"></thead><tbody id="tbd"></tbody></table>
</div><div class="no-result" id="nr"><p>没有匹配的数据</p></div></div>
<div class="charts-wrap" id="chartsArea"></div>
<div class="ft">
<div class="ft-info" id="ftInfo"></div>
@@ -298,15 +297,24 @@ let grpExp={};
let fzCount=0;
let dark=true;
let qcState={dim:'',val:'',type:'bar'};
let chartVisible=true;
let chartVisible=false; // false=table view, true=chart view
function toggleChartView(){
chartVisible=!chartVisible;
const area=$('chartsArea');
const btn=$('btnList');
if(area)area.style.display=chartVisible?'':'none';
if(btn)btn.classList.toggle('act',!chartVisible);
if(chartVisible)renderCharts();
function showChartView(){
chartVisible=true;
$('chartsArea').style.display='';
$('tw').style.display='none';
$('nr').style.display='none';
updateChartTypeBtns();
renderCharts();
}
function showTableView(){
chartVisible=false;
$('chartsArea').style.display='none';
$('tw').style.display='';
// sync active button state
['btnBar','btnPie','btnDoughnut','btnLine','btnList'].forEach(id=>{const b=$(id);if(b)b.classList.remove('act')});
$('btnList').classList.add('act');
}
function init(){
@@ -319,8 +327,25 @@ function init(){
initChartCtrls();
buildChartsUI();
r();
showTableView(); // default: table view
}
init();
// Load Chart.js dynamically, then init — no race conditions
function loadChartJS(onReady){
if(typeof Chart!=='undefined'){onReady();return}
var s=document.createElement('script');
s.src='https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js';
s.onload=onReady;
s.onerror=function(){
var s2=document.createElement('script');
s2.src='https://cdn.bootcdn.net/ajax/libs/Chart.js/4.4.7/chart.umd.min.js';
s2.onload=onReady;
s2.onerror=function(){console.warn('Chart.js failed to load from both CDNs, running without charts');onReady()};
document.head.appendChild(s2);
};
document.head.appendChild(s);
}
loadChartJS(init);
function r(){
const fd=flt();
@@ -355,6 +380,17 @@ function initChartCtrls(){
ctrls.style.display='flex';
const dimSel=$('qcDimSel');
const valSel=$('qcValSel');
// Auto-select defaults: pick best categorical dim (string, few unique vals, not first col)
if(!qcState.dim){
const catCols=COLS.filter((c,i)=>{
if(i===0)return false; // skip ID column
const vals=data.map(r=>String(r[c.k]||''));
const uniq=new Set(vals);
return uniq.size>1&&uniq.size<=15;
});
qcState.dim=catCols.length?catCols[0].k:(COLS[2]?COLS[2].k:COLS[0].k);
}
if(!qcState.val){qcState.val='__count__'}
if(dimSel){
dimSel.innerHTML='<option value="">维度</option>'+COLS.map(c=>`<option value="${c.k}">${c.l}</option>`).join('');
dimSel.value=qcState.dim;
@@ -371,14 +407,11 @@ function updateChartTypeBtns(){
const btn=$(`btn${t==='bar'?'Bar':t==='pie'?'Pie':t==='doughnut'?'Doughnut':'Line'}`);
if(btn)btn.classList.toggle('act',qcState.type===t);
});
['btnList'].forEach(id=>{const b=$(id);if(b)b.classList.remove('act')});
}
function setChartType(type){
qcState.type=type;
if(!chartVisible){chartVisible=true;$('chartsArea').style.display='';$('btnList').classList.remove('act')}
updateChartTypeBtns();
renderCharts();
showChartView();
}
function updateQuickChart(){
@@ -460,6 +493,7 @@ function renderCharts(){
// Quick chart mode
const dim=qcState.dim, val=qcState.val, tp=qcState.type;
if(!dim||!val)return;
if(typeof Chart==='undefined'){console.warn('Chart.js not loaded');return}
const groups={};
fd.forEach(r=>{
const k=String(r[dim]||'(空)');
@@ -471,33 +505,35 @@ function renderCharts(){
const wrap=$('qcChartWrap');
if(!wrap)return;
wrap.style.display='block';
wrap.innerHTML='<div class="chart-box"><canvas id="qcCanvas" height="260"></canvas></div>';
wrap.innerHTML='<div class="chart-box"><div class="chart-title">'+(COLS.find(c=>c.k===dim)?.l||dim)+' × '+(val==='__count__'?'计数':COLS.find(c=>c.k===val)?.l||val)+'</div><div style="position:relative;height:260px"><canvas id="qcCanvas"></canvas></div></div>';
const canvas=$('qcCanvas');
if(!canvas)return;
const isPie=tp==='pie'||tp==='doughnut'||tp==='polarArea';
const cfg={
type:tp,
data:{
labels,
datasets:[{
label:val==='__count__'?'计数':(COLS.find(c=>c.k===val)?.l||val),
data:values,
backgroundColor:labels.map((_,j)=>chartColors()[j%chartColors().length]),
borderColor:isPie?'transparent':chartColors(),
borderWidth:isPie?0:1,
borderRadius:isPie?0:4,
}]
},
options:{
responsive:true,
maintainAspectRatio:false,
plugins:{
legend:isPie?{position:'right',labels:{color:chartTextColor(),padding:12,font:{size:11}}}:{display:false}
const isPie=tp==='pie'||tp==='doughnut';
try{
const cfg={
type:tp,
data:{
labels,
datasets:[{
label:val==='__count__'?'计数':(COLS.find(c=>c.k===val)?.l||val),
data:values,
backgroundColor:labels.map((_,j)=>chartColors()[j%chartColors().length]),
borderColor:isPie?'transparent':chartColors(),
borderWidth:isPie?0:1,
borderRadius:isPie?0:4,
}]
},
scales:isPie?{}:{x:{grid:{color:chartGridColor()},ticks:{color:chartTextColor(),font:{size:10}}},y:{grid:{color:chartGridColor()},ticks:{color:chartTextColor(),font:{size:10}}}}
}
};
chartInstances.push(new Chart(canvas,cfg));
options:{
responsive:true,
maintainAspectRatio:false,
plugins:{
legend:isPie?{position:'right',labels:{color:chartTextColor(),padding:12,font:{size:11}}}:{display:false}
},
scales:isPie?{}:{x:{grid:{color:chartGridColor()},ticks:{color:chartTextColor(),font:{size:10}}},y:{grid:{color:chartGridColor()},ticks:{color:chartTextColor(),font:{size:10}}}}
}
};
chartInstances.push(new Chart(canvas,cfg));
}catch(e){console.error('Chart render error:',e)}
}
}
@@ -22,3 +22,4 @@ access_issue_boss_approval,issue_boss_approval,model_issue_boss_approval,base.gr
access_issue_boss_approval_line,issue_boss_approval_line,model_issue_boss_approval_line,base.group_user,1,1,1,1
access_issue_library_attendee_line,issue_library_attendee_line,model_issue_library_attendee_line,base.group_user,1,1,1,1
access_issue_library_external_person_line,issue_library_external_person_line,model_issue_library_external_person_line,base.group_user,1,1,1,1
access_issue_library_avoid_line,issue_library_avoid_line,model_issue_library_avoid_line,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
22 access_issue_library_external_person_line issue_library_external_person_line model_issue_library_external_person_line base.group_user 1 1 1 1
23 access_issue_library_avoid_line issue_library_avoid_line model_issue_library_avoid_line base.group_user 1 1 1 1
24
25