Audit.cshtml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. @using VberAdmin.Web.Models.Table
  2. @using Microsoft.AspNetCore.Mvc.Rendering
  3. @using VberAdmin.Web.Models.Search
  4. @using VberZero
  5. @{
  6. //ViewBag.Title = L("StateTitle");
  7. ViewBag.Title = "我的工作流程审批";
  8. string activeMenu = PermissionNames.VberMyWorkflowAuditMg; //The menu item will be active for this page.
  9. ViewBag.ActiveMenu = activeMenu;
  10. var table = new VmTable(VzConsts.ApiAppUrl + "WorkflowAudit/GetAll", activeMenu, new VmSearch(new List<VmSearchItem>()
  11. {
  12. //new SearchItem("dd", L("stateName")).SetSearchIcon("q_u","ddd"),
  13. //new SearchItem("ff", L("stateName")).SetSelectItem("<option value=\"\">000</option><option value=\"S10001\">111</option><option value=\"S10002\">222</option>").SetSearchIcon("q_u","fff"),
  14. //new SearchItem("name1", L("stateName"),FieldType.D),
  15. //new VmSearchItem("id", "流程编号"),
  16. new VmSearchItem("title", "流程名称")
  17. })).AddItems(new List<VmTableItem>()
  18. {
  19. //new("id", "流程编号"),
  20. new("title", "流程名称"),
  21. new("userName", "提交人"),
  22. new VmTableItem("creationTime", "提交时间","DateTimeFormatter").WithSort(),
  23. new("status", "审核状态","AuditStatusFormatter"),
  24. new("auditTime", "审核时间","DateTimeFormatter"),
  25. new("", "操作","ActionsFormatter"),
  26. });
  27. //var modalBody = new VmModalBody()
  28. // .AddInputs(new List<VmInputBase>()
  29. // {
  30. // new VmInputHidden("id"),
  31. // })
  32. // .AddInput(new VmInput("displayValue", L("displayValue")))
  33. // .AddGroup(new List<VmInputBase>()
  34. // {
  35. // new VmInput("name", L("stateName")).WithDisabled(),
  36. // new VmInput("codeKey", L("codeKey")).WithDisabled(),
  37. // new VmInput("codeValue", L("codeValue")).WithDisabled(),
  38. // }, 3);
  39. //var modal = new VmModal().WithHeaderAndFooter(L("state"), "").WithBody(modalBody);
  40. }
  41. @await Html.PartialAsync("_Table", table)
  42. <div class="bg-white"
  43. id="audit_flow_detail"
  44. data-kt-drawer="true"
  45. data-kt-drawer-activate="true"
  46. @*data-kt-drawer-toggle="#"*@
  47. data-kt-drawer-close="#detail_close"
  48. data-kt-drawer-name="audit_flow_detail"
  49. data-kt-drawer-overlay="true"
  50. data-kt-drawer-width="{default:'300px', 'md': '600px'}"
  51. data-kt-drawer-direction="end">
  52. <div class="card rounded-0 w-100">
  53. <div class="card-header pe-5">
  54. <div class="card-title">
  55. <div class="d-flex justify-content-center flex-column me-3">
  56. <a href="#" class="fs-4 me-1 text-gray-800 lh-1"><span class="px-2 fw-bolder text-primary " id="flow-title"></span> <span id="flow-status"></span></a>
  57. </div>
  58. </div>
  59. <div class="card-toolbar">
  60. <div class="btn btn-sm btn-icon btn-active-light-primary" id="detail_close">
  61. <span class="svg-icon svg-icon-2">
  62. <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  63. <rect opacity="0.5" x="6" y="17.3137" width="16" height="2" rx="1" transform="rotate(-45 6 17.3137)" fill="currentColor"></rect>
  64. <rect x="7.41422" y="6" width="16" height="2" rx="1" transform="rotate(45 7.41422 6)" fill="currentColor"></rect>
  65. </svg>
  66. </span>
  67. </div>
  68. </div>
  69. </div>
  70. <div class="card-body hover-scroll-overlay-y pt-5">
  71. <div class="fs-4 me-1 lh-1 mb-3 fw-bolder">申请详情:</div>
  72. <div class="px-5"><dl class="d-flex dl-line"><dt class="pe-2 fw-bolder">申请人:</dt><dd class="px-2 text-primary" id="flow-user-name"></dd></dl></div>
  73. <div id="flow-detail" class="px-5"></div>
  74. <div class="separator mb-3"></div>
  75. <div class="fs-4 me-1 lh-1 mb-3 fw-bolder">审批详情:</div>
  76. <div class="timeline-box" id="audit-records">
  77. <div>
  78. </div>
  79. </div>
  80. </div>
  81. </div>
  82. </div>
  83. @section scripts
  84. {
  85. <script src="~/js/workflowHelper.js" asp-append-version="false"></script>
  86. <script type="text/javascript">
  87. let $table = $('#table'), curWorkflowId = '', curExecutionRecord = [];
  88. $(function() {
  89. var funs = window.LoadTable();
  90. funs["btnCreate"] = function() {
  91. };
  92. funs["btnUpdate"] = function() {
  93. let row = $table.VbTable("getSelection");
  94. if (row) {
  95. }
  96. };
  97. });
  98. function Detail(id) {
  99. const flow_detail_drawer = KTDrawer.getInstance($('#audit_flow_detail')[0]);
  100. curWorkflowId = id;
  101. $.vbAjax4({
  102. url: abp.appUrl + "workflow/getDetails?id=" + id,
  103. success: (res) => {
  104. curExecutionRecord = res.executionRecords;
  105. $('#flow-user-name').html(res.userName);
  106. $('#flow-title').html(res.title);
  107. $('#flow-status').html(WorkflowStatusFormatter(res.status));
  108. ApplyDetail(res.data, res.inputs);
  109. RecordDetail(id, res.executionRecords);
  110. flow_detail_drawer.show();
  111. }
  112. });
  113. function ApplyDetail(data, inputs) {
  114. if (inputs && inputs.length) {
  115. let str = '';
  116. inputs.forEach(v => {
  117. const col = v.length ? 12 / v.length : 0;
  118. str += `<div class="row">`;
  119. v.forEach(vv => {
  120. str += `<dl class="col-${col} d-flex dl-line"><dt class="pe-2 fw-bolder">${vv.label}:</dt><dd >${GetInputValueText(vv)}</dd></dl>`;
  121. });
  122. str += `</div>`;
  123. });
  124. $('#flow-detail').html(str);
  125. }
  126. function GetInputValueText(input) {
  127. if (input.type == "uploadImages") {
  128. return `<img class="w-50 h-50" src="${data[input.name]}"/>`;
  129. } else if (input.type == "uploadFiles") {
  130. return `<a href="${data[input.name]}">查看附件</a>`;
  131. } else if (input.items && input.items.length) {
  132. const val = data[input.name];
  133. if (typeof val == 'string') {
  134. const item = input.items.find(v => v.value == val);
  135. return item ? item.label : "";
  136. } else if (val && val.length) {
  137. let valueStr = '';
  138. val.forEach(v => {
  139. const item = input.items.find(vv => vv.value == v);
  140. valueStr += item ? `${valueStr ? "," : ""}${item.label}` : "";
  141. });
  142. return valueStr;
  143. }
  144. }
  145. return data[input.name] || "";
  146. }
  147. }
  148. }
  149. function RecordDetail(id, executionRecords) {
  150. $.vbAjax4({
  151. url: abp.appUrl + "WorkflowAudit/GetAuditRecords",
  152. data: { id: id },
  153. success: r => { AuditRecordsFormatter(executionRecords, r) }
  154. });
  155. function AuditRecordsFormatter(data, auditData) {
  156. $('#audit-records').html('');
  157. //console.log("=======>", data, auditData);
  158. if (data && data.length) {
  159. let i = 0;
  160. data.forEach(v => {
  161. i++;
  162. let records = auditData.auditRecords[v.executionPointerId];
  163. if (records && records.length) {
  164. let str = ``, styleStr = "bg-primary";
  165. str += `<div class="timeline-label"><span class="{0} text-white px-3">${v.stepTitle}</span></div>`;
  166. str += `<div>`;
  167. records.forEach(vv => {
  168. str += `<div class="timeline-item">`;
  169. //str += `<div class="d-flex flex-stack align-items-cente mx-3">
  170. // <div>
  171. // <div class="symbol symbol-25px"><img class="" src="${vv.userHeadPhoto}" /></div>
  172. // <span class="mx-2 fw-bolder">${vv.userIdentityName}</span>
  173. // <span class="">${AuditStatusFormatter(vv.status)}</span>
  174. // </div>
  175. // <div class="fs-6"><span class="text-muted">${DateTimeFormatter(vv.auditTime) || ""}</span></div>
  176. // </div>`;
  177. if (vv.status == 0 && i==data.length ) {
  178. str += `<div class="timeline-header">`;
  179. str += HeadFormatter(vv);
  180. if (auditData.needAudit && vv.userId == @(AbpSession.UserId)) {
  181. str += `<form class="form mt-3">
  182. <textarea class="form-control form-control-sm form-control-solid" style="" id="audit-remark" placeholder="请输入备注..."></textarea>
  183. <div class="separator my-2"></div>
  184. <div>
  185. <button type="button" class="btn btn-danger btn-sm" onclick="SubmitAudit('${v.executionPointerId}',false)">拒绝</button>
  186. <button type="button" class="btn btn-primary btn-sm" onclick="SubmitAudit('${v.executionPointerId}',true)">同意</button>
  187. </div>
  188. </form>`;
  189. }
  190. str += `</div>`;
  191. } else if (vv.status !== 0 && vv.remark) {
  192. styleStr = vv.status == 1 ? "bg-success" : "bg-danger";
  193. str += `<div class="timeline-header">`;
  194. str += HeadFormatter(vv);
  195. str += `<div class="my-2"><span class="ps-5 fw-bolder"> 备注:</span>${vv.remark || ""}</div>`;
  196. str += `</div>`;
  197. }
  198. str += `</div>`;
  199. });
  200. str += `</div>`;
  201. $('#audit-records').append(str.format(styleStr));
  202. }
  203. });
  204. }
  205. }
  206. function HeadFormatter(data) {
  207. var str = `<div class="d-flex flex-stack align-items-cente mx-3">
  208. <div>
  209. <div class="symbol symbol-25px"><img class="" src="${data.userHeadPhoto}" /></div>
  210. <span class="mx-2 fw-bolder">${data.userIdentityName}</span>
  211. <span class="">${AuditStatusFormatter(data.status)}</span>
  212. </div>
  213. <div class="fs-6"><span class="text-muted">${DateTimeFormatter(data.auditTime) || ""}</span></div>
  214. </div>`;
  215. return str;
  216. }
  217. }
  218. function SubmitAudit(id, pass) {
  219. const remark = $('#audit-remark').val();
  220. if (!pass && remark.length <= 0) {
  221. abp.message.warn("请填写拒绝理由!");
  222. return;
  223. }
  224. $.vbAjax1({
  225. url: abp.appUrl + "WorkflowAudit/Audit",
  226. data: { executionPointerId: id, pass: pass, remark: remark },
  227. success: () => {
  228. RecordDetail(curWorkflowId, curExecutionRecord);
  229. RefreshTable($table);
  230. }
  231. });
  232. }
  233. </script>
  234. <script id="formatter" type="text/javascript">
  235. function AuditStatusFormatter(v) {
  236. var name = $('#hid-auditStatus option[value="' + v + '"]').text();
  237. if (v === 0) {
  238. return '<span class="badge badge-light-primary">' + name + '</span>';
  239. } else if (v === 1) {
  240. return '<span class="badge badge-light-success">' + name + '</span>';
  241. } else if (v === 2) {
  242. return '<span class="badge badge-light-danger">' + name + '</span>';
  243. } else if (v === 3) {
  244. return '<span class="badge badge-light-warning">' + name + '</span>';
  245. }
  246. return v;
  247. }
  248. function WorkflowStatusFormatter(v) {
  249. var name = $('#hid-wfStatus option[value="' + v + '"]').text();
  250. if (v === 0) {
  251. return '<span class="badge badge-primary">' + name + '</span>';
  252. } else if (v === 1) {
  253. return '<span class="badge badge-success">' + name + '</span>';
  254. } else if (v === 2) {
  255. return '<span class="badge badge-danger">' + name + '</span>';
  256. } else if (v === 3) {
  257. return '<span class="badge badge-warning">' + name + '</span>';
  258. }
  259. return v;
  260. }
  261. function ActionsFormatter(v, r) {
  262. let str = '';
  263. str += `<span class="table-action" onclick="Detail('${r.workflowId}')">流程详情${icon}</span>`;
  264. return str;
  265. }
  266. </script>
  267. }
  268. <section style="display: none">
  269. <select id="hid-auditStatus">
  270. @Html.Raw(ViewBag.AuditStatus)
  271. </select>
  272. <select id="hid-wfStatus">
  273. @Html.Raw(ViewBag.WfStatus)
  274. </select>
  275. </section>