inspection_flow.ejs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <div class="safe-inspection-flow mt-3">
  2. <% const mobileFlow = ctx.url.indexOf('/wap/') === 0;
  3. const showTimeline = !mobileFlow && !inspection.flowDraft;
  4. const flowStatusText = { 1: '待处理', 2: '审批中', 3: '已完成', 4: '已关闭', 5: '审批退回', 6: '审批退回', 8: '已跳过', 12: '整改中' }; %>
  5. <% if (mobileFlow) { %>
  6. <div class="card mb-3">
  7. <div class="card-header d-flex justify-content-between">
  8. <span>审批流程 · <%= inspection.flowLabel %></span>
  9. <% if (!inspection.readOnly) { %>
  10. <button type="button" class="btn btn-sm btn-primary flow-action" data-action="start-inspection">提交上报</button>
  11. <% } %>
  12. </div>
  13. <div class="card-body">
  14. <span class="mr-3">是否整改</span>
  15. <% [1, 0].forEach(value => { %>
  16. <label class="mr-3 mb-0"><input type="radio" name="safe-need-rectification" value="<%= value %>" <%= inspection.need_rectification === value ? 'checked' : '' %> <%= inspection.readOnly ? 'disabled' : '' %>> <%= value ? '是' : '否' %></label>
  17. <% }); %>
  18. <small class="text-warning d-block mt-2">选择“是”时,上报结束后进入整改,整改人按顺序通过后进入批复。</small>
  19. </div>
  20. </div>
  21. <% } else { %>
  22. <div class="flow-title text-center mb-1">审批流程</div>
  23. <% } %>
  24. <div class="row flow-layout<%= showTimeline ? ' flow-progress-layout' : '' %>">
  25. <div class="col-md-4 flow-summary mb-3">
  26. <table class="table table-bordered table-sm flow-summary-table mb-0" aria-label="审批流程人员">
  27. <colgroup><col class="flow-phase-label"><col></colgroup>
  28. <% inspection.flowStages.forEach(phase => { %>
  29. <% if (phase.stage === 1 || inspection.need_rectification || !inspection.readOnly) { %>
  30. <% const people = phase.auditors.map((audit, index) => ({
  31. name: audit.name,
  32. icon: phase.stage === 3 && index === phase.auditors.length - 1 ? 'fa-stop-circle' : 'fa-chevron-circle-down',
  33. title: phase.stage === 2 ? '第' + audit.audit_order + '位整改人' : audit.audit_order + '审',
  34. placeholder: false,
  35. }));
  36. if (phase.stage === 1) people.unshift({ name: inspection.user.name, icon: 'fa-play-circle fa-rotate-90', title: '上报人', placeholder: false });
  37. if (!phase.auditors.length) people.push({ name: phase.stage === 2 ? '整改人由上报审批人指派' : '尚未设置审批人', icon: 'fa-circle-o', title: '', placeholder: true }); %>
  38. <tbody class="<%= phase.stage !== 1 && !inspection.need_rectification ? 'd-none' : '' %>" data-flow-stage="<%= phase.stage %>">
  39. <% people.forEach((person, index) => { %>
  40. <tr>
  41. <% if (index === 0) { %><th class="text-center align-middle" scope="rowgroup" rowspan="<%= people.length %>"><%= phase.name %></th><% } %>
  42. <td title="<%= person.title %>"><i class="fa <%= person.icon %> mr-1" aria-hidden="true"></i><span class="<%= person.placeholder ? 'text-muted' : '' %>"><%= person.name %></span></td>
  43. </tr>
  44. <% }); %>
  45. </tbody>
  46. <% } %>
  47. <% }); %>
  48. </table>
  49. </div>
  50. <div class="col-md-8 flow-details">
  51. <% inspection.flowStages.forEach(phase => { %>
  52. <% if (phase.stage === 1 || inspection.need_rectification || !inspection.readOnly) { %>
  53. <% const reportMode = ctx.tender.info.shenpi.safe_inspection;
  54. const editable = phase.stage === 2 ? inspection.canAssignRectifiers : !inspection.readOnly && (phase.stage !== 1 || reportMode !== shenpiConst.sp_status.gdspl);
  55. const maxOrder = phase.auditors.length ? Math.max(...phase.auditors.map(x => x.order)) : 0; %>
  56. <% if (showTimeline) { %>
  57. <section class="flow-phase-section" data-flow-stage="<%= phase.stage %>">
  58. <div class="flow-phase-title text-center text-muted">巡检<%= phase.name %></div>
  59. <% } else { %>
  60. <div class="mb-3<%= phase.stage !== 1 && !inspection.need_rectification ? ' d-none' : '' %>" data-flow-stage="<%= phase.stage %>">
  61. <% } %>
  62. <% if (editable) { %>
  63. <div class="dropdown flow-picker mb-2 text-right" data-stage="<%= phase.stage %>">
  64. <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" data-toggle="dropdown">添加<%= phase.name %><%= phase.stage === 2 ? '人' : '流程' %></button>
  65. <div class="dropdown-menu dropdown-menu-right">
  66. <div class="mb-2 p-2"><input class="form-control form-control-sm flow-user-search" placeholder="姓名/手机 检索" aria-label="检索<%= phase.name %>用户" autocomplete="off"></div>
  67. <dl class="list-unstyled book-list text-left">
  68. <% accountGroup.forEach((group, idx) => { %>
  69. <div class="flow-user-group">
  70. <dt><a href="javascript:void(0);" class="acc-btn" data-groupid="<%= idx %>" data-type="hide" aria-expanded="false" aria-label="展开或收起<%= group.groupName %>"><i class="fa fa-plus-square"></i></a> <%= group.groupName %></dt>
  71. <div class="dd-content" data-toggleid="<%= idx %>">
  72. <% group.groupList.forEach(user => { %>
  73. <% if (phase.stage !== 1 || user.id !== inspection.uid) { %>
  74. <% const selected = phase.auditors.some(x => x.aid === user.id); %>
  75. <dd class="flow-add-user border-bottom p-2 mb-0" role="button" tabindex="<%= selected ? '-1' : '0' %>" aria-disabled="<%= selected ? 'true' : 'false' %>" data-id="<%= user.id %>" data-name="<%= user.name || '' %>" data-mobile="<%= user.mobile || '' %>">
  76. <p class="mb-0 d-flex"><span class="<%= selected ? 'text-muted' : 'text-primary' %>"><%= user.name %></span><span class="ml-auto"><%= user.mobile || '' %></span></p>
  77. <span class="text-muted"><%= user.role || '' %><%= selected ? '(已添加)' : '' %></span>
  78. </dd>
  79. <% } %>
  80. <% }); %>
  81. </div>
  82. </div>
  83. <% }); %>
  84. <dd class="flow-no-user text-muted p-2 mb-0 d-none">未找到匹配的用户</dd>
  85. </dl>
  86. </div>
  87. </div>
  88. <% } %>
  89. <% if (showTimeline) { %>
  90. <% if (phase.stage === 2 && phase.auditors.length) { %>
  91. <div class="flow-stage-progress d-flex justify-content-between flex-wrap text-muted">
  92. <span>按顺序整改</span><span>已通过 <%= inspection.rectificationDone %>/<%= inspection.rectificationTotal %></span>
  93. </div>
  94. <% } %>
  95. <ul class="timeline-list list-unstyled flow-timeline">
  96. <% phase.nodes.forEach((node, index) => { %>
  97. <% const tone = node.status === 3 ? 'success' : [2, 5, 6, 12].includes(node.status) ? 'warning' : node.status === 4 ? 'danger' : 'secondary';
  98. const icon = node.origin ? 'caret-down' : node.status === 3 ? 'check' : [5, 6].includes(node.status) ? 'level-up' : [2, 12].includes(node.status) ? 'ellipsis-h' : node.status === 4 ? 'stop' : ''; %>
  99. <li class="timeline-list-item pb-3">
  100. <% if (node.time) { %>
  101. <div class="timeline-item-date"><%= moment(node.time).format('YYYY') %><span><%= moment(node.time).format('MM-DD') %></span><span><%= moment(node.time).format('HH:mm:ss') %></span></div>
  102. <% } %>
  103. <% if (index < phase.nodes.length - 1) { %><div class="timeline-item-tail"></div><% } %>
  104. <div class="timeline-item-icon bg-<%= tone %> text-light"><% if (icon) { %><i class="fa fa-<%= icon %>" aria-hidden="true"></i><% } %></div>
  105. <div class="timeline-item-content">
  106. <div class="d-flex justify-content-between py-1">
  107. <span class="text-black-50"><%= node.label %></span>
  108. <span class="text-<%= tone %>"><%= node.origin ? '上报审批' : node.status === 3 ? (phase.stage === 2 ? '整改通过' : '审批通过') : phase.stage === 2 && [5, 6].includes(node.status) ? '整改退回' : flowStatusText[node.status] %></span>
  109. </div>
  110. <div class="card">
  111. <div class="card-body px-3 py-2">
  112. <% node.auditors.forEach((audit, personIndex) => { %>
  113. <div class="flow-timeline-person py-2<%= personIndex ? ' border-top' : '' %>">
  114. <div class="d-flex align-items-start">
  115. <div class="flow-person-names"><span class="h6 mr-1"><%= audit.name %></span><small class="text-muted"><%= audit.company || '' %><%= audit.company && audit.role ? '-' : '' %><%= audit.role || '' %></small></div>
  116. <% const personTone = node.origin || audit.status === 3 ? 'success' : [2, 5, 6, 12].includes(audit.status) ? 'warning' : audit.status === 4 ? 'danger' : 'secondary'; %>
  117. <% if (phase.stage === 2 && editable) { %>
  118. <button class="btn btn-link btn-sm text-danger flow-remove-user p-0 ml-auto" data-stage="<%= phase.stage %>" data-id="<%= audit.aid %>" type="button">移除</button>
  119. <% } else { %>
  120. <span class="ml-auto pl-2 text-<%= personTone %>"><% if (!node.origin && node.auditors.length > 1) { %><small class="mr-1"><%= flowStatusText[audit.status] %></small><% } %><i class="fa <%= node.origin || audit.status === 3 ? 'fa-check-circle' : [2, 12].includes(audit.status) ? 'fa-commenting' : [5, 6].includes(audit.status) ? 'fa-reply' : audit.status === 4 ? 'fa-stop-circle' : 'fa-circle-o' %>" aria-hidden="true"></i></span>
  121. <% } %>
  122. </div>
  123. <div class="bg-light flow-opinion mt-1" aria-label="<%= phase.stage === 2 ? '整改意见' : '处理意见' %>"><i class="fa fa-commenting-o mr-1" aria-hidden="true"></i><%= audit.opinion || '' %></div>
  124. </div>
  125. <% }); %>
  126. </div>
  127. </div>
  128. </div>
  129. </li>
  130. <% }); %>
  131. <% if (!phase.nodes.length) { %><li class="flow-timeline-empty text-muted py-2"><%= phase.stage === 2 ? '整改人由上报审批人按先后顺序指派,依次处理。' : '尚未设置审批人' %></li><% } %>
  132. </ul>
  133. <% } else { %>
  134. <div class="card">
  135. <div class="card-header">
  136. <%= phase.name %>流程
  137. <% if (phase.stage === 2 && phase.auditors.length) { %><span class="float-right">第 <%= inspection.stage_round %> 轮 · 已完成 <%= inspection.rectificationDone %>/<%= inspection.rectificationTotal %></span><% } %>
  138. </div>
  139. <ul class="list-group list-group-flush">
  140. <% if (!phase.auditors.length) { %><li class="list-group-item text-muted"><%= phase.stage === 2 ? '整改人由上报审批人按先后顺序指派,依次处理。' : '请设置' + phase.name + '审批人' %></li><% } %>
  141. <% phase.auditors.forEach(audit => { %>
  142. <li class="list-group-item">
  143. <div class="d-flex justify-content-between">
  144. <span><%= audit.name %> <small class="text-muted"><%= audit.role || '' %><%= phase.stage !== 2 && audit.audit_type !== 1 ? ' · ' + auditType.info[audit.audit_type].name : '' %></small></span>
  145. <% if (editable && !(phase.stage === 1 && reportMode === shenpiConst.sp_status.gdzs && audit.order === maxOrder)) { %>
  146. <button class="btn btn-link btn-sm text-danger flow-remove-user p-0" data-stage="<%= phase.stage %>" data-id="<%= audit.aid %>" type="button">移除</button>
  147. <% } else { %>
  148. <span class="<%= auditConst.statusClass[audit.status] || '' %>"><%= audit.status === 1 ? '待处理' : audit.status === 3 ? '已完成' : audit.status === 8 ? '已跳过' : auditConst.statusString[audit.status] %></span>
  149. <% } %>
  150. </div>
  151. <small class="text-muted"><%= audit.company || '' %></small>
  152. <% if (mobileFlow && phase.stage === 2 && inspection.rectificationPower && audit.aid === ctx.session.sessionUser.accountId && audit.status === 12) { %>
  153. <div class="flow-rectification mt-2" data-id="<%= audit.id %>">
  154. <label class="d-block">整改内容<b class="text-danger">*</b><textarea class="form-control form-control-sm flow-rectification-item" rows="3" maxlength="1000"><%= audit.rectification_item || '' %></textarea></label>
  155. <label class="d-block">整改日期<b class="text-danger">*</b><input class="form-control form-control-sm flow-rectification-date" type="date" value="<%= audit.rectification_date ? moment(audit.rectification_date).format('YYYY-MM-DD') : '' %>"></label>
  156. <label class="d-block">说明<textarea class="form-control form-control-sm flow-rectification-opinion" rows="2" maxlength="1000"><%= audit.opinion || '' %></textarea></label>
  157. <% if (mobileFlow) { %>
  158. <button type="button" class="btn btn-outline-primary btn-sm flow-save-rectification" data-id="<%= audit.id %>">保存</button>
  159. <button type="button" class="btn btn-success btn-sm flow-action" data-action="flow-complete-rectification" data-id="<%= audit.id %>">提交整改</button>
  160. <% } %>
  161. </div>
  162. <% } else if (mobileFlow && phase.stage === 2 && (audit.rectification_item || audit.rectification_date)) { %>
  163. <div class="mt-2"><%= audit.rectification_item %></div>
  164. <small class="text-muted">整改日期:<%= audit.rectification_date ? moment(audit.rectification_date).format('YYYY-MM-DD') : '' %></small>
  165. <% } %>
  166. <% if (!mobileFlow && phase.stage === 2) { %>
  167. <div class="bg-light flow-opinion mt-1" aria-label="整改意见"><i class="fa fa-commenting-o mr-1" aria-hidden="true"></i><%= audit.opinion || '' %></div>
  168. <% } else if (audit.opinion && !(phase.stage === 2 && inspection.rectificationPower && audit.aid === ctx.session.sessionUser.accountId && audit.status === 12)) { %><p class="mb-0 mt-2"><%= audit.opinion %></p><% } %>
  169. <% if (mobileFlow && phase.stage !== 2 && inspection.shenpiPower && inspection.flow_stage === phase.stage && audit.aid === ctx.session.sessionUser.accountId && audit.status === 2) { %>
  170. <div class="mt-2">
  171. <button type="button" class="btn btn-success btn-sm flow-action" data-action="flow-check" data-result="3" data-id="<%= audit.id %>"><%= phase.stage === 1 ? '审批' : phase.name %>通过</button>
  172. <% if (phase.stage === 1) { %>
  173. <button type="button" class="btn btn-warning btn-sm flow-action" data-action="flow-check" data-result="5" data-id="<%= audit.id %>">退回上报人</button>
  174. <% if (phase.auditors.some(x => x.audit_order < audit.audit_order)) { %><button type="button" class="btn btn-outline-warning btn-sm flow-action" data-action="flow-check" data-result="6" data-id="<%= audit.id %>">退回上一审批节点</button><% } %>
  175. <% } else { %>
  176. <button type="button" class="btn btn-warning btn-sm flow-action" data-action="flow-check" data-result="5" data-id="<%= audit.id %>">整改退回</button>
  177. <% } %>
  178. <button type="button" class="btn btn-outline-danger btn-sm flow-action" data-action="flow-check" data-result="4" data-id="<%= audit.id %>">关闭巡检</button>
  179. </div>
  180. <% } %>
  181. </li>
  182. <% }); %>
  183. </ul>
  184. </div>
  185. <% } %>
  186. <% if (showTimeline) { %>
  187. </section>
  188. <% } else { %>
  189. </div>
  190. <% } %>
  191. <% } %>
  192. <% }); %>
  193. <div class="alert alert-warning flow-no-rectification<%= inspection.need_rectification ? ' d-none' : '' %>">本次巡检无整改/批复流程</div>
  194. </div>
  195. </div>
  196. <% if (inspection.flowHistory.length) { %>
  197. <details class="card mb-3 d-none">
  198. <summary class="card-header">查看处理记录</summary>
  199. <div class="list-group list-group-flush">
  200. <% inspection.flowHistory.forEach(audit => { %>
  201. <div class="list-group-item">
  202. <div>第 <%= audit.times %> 次上报 / 第 <%= audit.stage_round %> 轮 · <%= auditConst.stageString[audit.flow_stage] %> · <%= audit.name %><span class="float-right"><%= audit.status === 3 ? '已完成' : audit.status === 8 ? '已跳过' : auditConst.statusString[audit.status] %></span></div>
  203. <small class="text-muted"><%= moment(audit.begin_time).format('YYYY-MM-DD HH:mm:ss') %><%= audit.end_time ? ' 至 ' + moment(audit.end_time).format('YYYY-MM-DD HH:mm:ss') : '' %></small>
  204. <% if (audit.flow_stage === 2) { %><p class="mb-0"><%= audit.rectification_item || '' %> <%= audit.rectification_date ? moment(audit.rectification_date).format('YYYY-MM-DD') : '' %></p><% } %>
  205. <% if (audit.opinion) { %><p class="mb-0"><%= audit.opinion %></p><% } %>
  206. </div>
  207. <% }); %>
  208. </div>
  209. </details>
  210. <% } %>
  211. </div>