@fudanda/admin-antd 2.20261109.1-beta.1 → 2.20261109.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/chunks/ApiTokensPage-w3JGRNQC.js +2 -0
  2. package/dist/chunks/ApiTokensPage-w3JGRNQC.js.map +1 -0
  3. package/dist/chunks/{App-CfQx7jWq.js → App-nh6_qOo8.js} +5 -5
  4. package/dist/chunks/{App-CfQx7jWq.js.map → App-nh6_qOo8.js.map} +1 -1
  5. package/dist/chunks/{AppModal-CVoUC2t3.js → AppModal-CEouaU03.js} +2 -2
  6. package/dist/chunks/{AppModal-CVoUC2t3.js.map → AppModal-CEouaU03.js.map} +1 -1
  7. package/dist/chunks/{ArcBaseAdmin-DQyddbFC.js → ArcBaseAdmin-CX7fQz4x.js} +2 -2
  8. package/dist/chunks/{ArcBaseAdmin-DQyddbFC.js.map → ArcBaseAdmin-CX7fQz4x.js.map} +1 -1
  9. package/dist/chunks/{DepartmentsPage-CQx53QGE.js → DepartmentsPage-BdD9VfDI.js} +2 -2
  10. package/dist/chunks/{DepartmentsPage-CQx53QGE.js.map → DepartmentsPage-BdD9VfDI.js.map} +1 -1
  11. package/dist/chunks/{DictsPage-BGnamkWk.js → DictsPage-fxBdV1-3.js} +2 -2
  12. package/dist/chunks/{DictsPage-BGnamkWk.js.map → DictsPage-fxBdV1-3.js.map} +1 -1
  13. package/dist/chunks/{EntityContextRuntime-CbzeMNXR.js → EntityContextRuntime-CMhoj6qe.js} +2 -2
  14. package/dist/chunks/{EntityContextRuntime-CbzeMNXR.js.map → EntityContextRuntime-CMhoj6qe.js.map} +1 -1
  15. package/dist/chunks/EntityContextRuntime-Dc89ZSnM.js +1 -0
  16. package/dist/chunks/{ExportButton-sMPAV2hd.js → ExportButton-DGxB2tcu.js} +2 -2
  17. package/dist/chunks/{ExportButton-sMPAV2hd.js.map → ExportButton-DGxB2tcu.js.map} +1 -1
  18. package/dist/chunks/{FileViewerPreviewPanel-Rti7pUhU.js → FileViewerPreviewPanel-BMZ7FdKV.js} +3 -3
  19. package/dist/chunks/{FileViewerPreviewPanel-Rti7pUhU.js.map → FileViewerPreviewPanel-BMZ7FdKV.js.map} +1 -1
  20. package/dist/chunks/{FilesPage-D_iYsUwl.js → FilesPage-DrgWELKy.js} +3 -3
  21. package/dist/chunks/{FilesPage-D_iYsUwl.js.map → FilesPage-DrgWELKy.js.map} +1 -1
  22. package/dist/chunks/{IdentitySecurityPage-rDYX5ksX.js → IdentitySecurityPage-BjTKvr-O.js} +2 -2
  23. package/dist/chunks/{IdentitySecurityPage-rDYX5ksX.js.map → IdentitySecurityPage-BjTKvr-O.js.map} +1 -1
  24. package/dist/chunks/{LogStatsScaffold-CMIz4z0a.js → LogStatsScaffold-DaB0JeAN.js} +2 -2
  25. package/dist/chunks/{LogStatsScaffold-CMIz4z0a.js.map → LogStatsScaffold-DaB0JeAN.js.map} +1 -1
  26. package/dist/chunks/{LoginLogsPage-Bv5pPJb2.js → LoginLogsPage-VmYC7i6q.js} +2 -2
  27. package/dist/chunks/{LoginLogsPage-Bv5pPJb2.js.map → LoginLogsPage-VmYC7i6q.js.map} +1 -1
  28. package/dist/chunks/{LoginLogsTable-DYcTx2oR.js → LoginLogsTable-CULcc_e8.js} +2 -2
  29. package/dist/chunks/{LoginLogsTable-DYcTx2oR.js.map → LoginLogsTable-CULcc_e8.js.map} +1 -1
  30. package/dist/chunks/{MarkdownPreviewPanel-lqs6AsoN.js → MarkdownPreviewPanel-CBcPc7Wv.js} +2 -2
  31. package/dist/chunks/{MarkdownPreviewPanel-lqs6AsoN.js.map → MarkdownPreviewPanel-CBcPc7Wv.js.map} +1 -1
  32. package/dist/chunks/{MasterDetailLayout-B5uqvh98.js → MasterDetailLayout-ByIBpmkZ.js} +2 -2
  33. package/dist/chunks/{MasterDetailLayout-B5uqvh98.js.map → MasterDetailLayout-ByIBpmkZ.js.map} +1 -1
  34. package/dist/chunks/{McpSettingsPage-Cdej9Evi.js → McpSettingsPage-C5V_rj2V.js} +2 -2
  35. package/dist/chunks/{McpSettingsPage-Cdej9Evi.js.map → McpSettingsPage-C5V_rj2V.js.map} +1 -1
  36. package/dist/chunks/{MemberAssignmentSheet-AKvLbbYW.js → MemberAssignmentSheet-DL0fg1Yf.js} +2 -2
  37. package/dist/chunks/{MemberAssignmentSheet-AKvLbbYW.js.map → MemberAssignmentSheet-DL0fg1Yf.js.map} +1 -1
  38. package/dist/chunks/{MenuCommandPalette-ia0pSIFg.js → MenuCommandPalette-C2PvK3Gh.js} +2 -2
  39. package/dist/chunks/{MenuCommandPalette-ia0pSIFg.js.map → MenuCommandPalette-C2PvK3Gh.js.map} +1 -1
  40. package/dist/chunks/{MenusPage-Bh55bvKH.js → MenusPage-Bytd1HlW.js} +2 -2
  41. package/dist/chunks/{MenusPage-Bh55bvKH.js.map → MenusPage-Bytd1HlW.js.map} +1 -1
  42. package/dist/chunks/{MonacoPreviewPanel-CT_sjf7Z.js → MonacoPreviewPanel-BZrNIcFZ.js} +2 -2
  43. package/dist/chunks/{MonacoPreviewPanel-CT_sjf7Z.js.map → MonacoPreviewPanel-BZrNIcFZ.js.map} +1 -1
  44. package/dist/chunks/{NavListPanel-DMIUMh7q.js → NavListPanel-zwUDIYSP.js} +2 -2
  45. package/dist/chunks/{NavListPanel-DMIUMh7q.js.map → NavListPanel-zwUDIYSP.js.map} +1 -1
  46. package/dist/chunks/{OperationLogsPage-7XVfmhxP.js → OperationLogsPage-D2BQdGNp.js} +2 -2
  47. package/dist/chunks/{OperationLogsPage-7XVfmhxP.js.map → OperationLogsPage-D2BQdGNp.js.map} +1 -1
  48. package/dist/chunks/{OperationLogsTable-gp-WZxiF.js → OperationLogsTable-DaaON4QR.js} +3 -3
  49. package/dist/chunks/{OperationLogsTable-gp-WZxiF.js.map → OperationLogsTable-DaaON4QR.js.map} +1 -1
  50. package/dist/chunks/{PDFPreviewPanel-CyfhyWav.js → PDFPreviewPanel-CMmnWBKW.js} +3 -3
  51. package/dist/chunks/{PDFPreviewPanel-CyfhyWav.js.map → PDFPreviewPanel-CMmnWBKW.js.map} +1 -1
  52. package/dist/chunks/{PositionsPage-Bwua1vNV.js → PositionsPage-CBZITssT.js} +2 -2
  53. package/dist/chunks/{PositionsPage-Bwua1vNV.js.map → PositionsPage-CBZITssT.js.map} +1 -1
  54. package/dist/chunks/{PreferencesSheetBody-C80D3dOM.js → PreferencesSheetBody-B0poTPEA.js} +2 -2
  55. package/dist/chunks/{PreferencesSheetBody-C80D3dOM.js.map → PreferencesSheetBody-B0poTPEA.js.map} +1 -1
  56. package/dist/chunks/{ProfilePage-DeTw-59a.js → ProfilePage-DLhacdWt.js} +2 -2
  57. package/dist/chunks/{ProfilePage-DeTw-59a.js.map → ProfilePage-DLhacdWt.js.map} +1 -1
  58. package/dist/chunks/{ProjectInfoPage-Bqn3gfXa.js → ProjectInfoPage-CJpGxpu5.js} +2 -2
  59. package/dist/chunks/{ProjectInfoPage-Bqn3gfXa.js.map → ProjectInfoPage-CJpGxpu5.js.map} +1 -1
  60. package/dist/chunks/{RelationPanel-Bio-ig-l.js → RelationPanel-BsbfEDo2.js} +2 -2
  61. package/dist/chunks/{RelationPanel-Bio-ig-l.js.map → RelationPanel-BsbfEDo2.js.map} +1 -1
  62. package/dist/chunks/{RolesPage-fwUSs2HR.js → RolesPage-46dzdiah.js} +2 -2
  63. package/dist/chunks/{RolesPage-fwUSs2HR.js.map → RolesPage-46dzdiah.js.map} +1 -1
  64. package/dist/chunks/{SessionConflictModal-Dw4opoNP.js → SessionConflictModal-Bm38eExL.js} +2 -2
  65. package/dist/chunks/{SessionConflictModal-Dw4opoNP.js.map → SessionConflictModal-Bm38eExL.js.map} +1 -1
  66. package/dist/chunks/SettingModuleDetail-BjxUJeH5.js +17 -0
  67. package/dist/chunks/SettingModuleDetail-BjxUJeH5.js.map +1 -0
  68. package/dist/chunks/{SettingsPage-Cy5-q-P6.js → SettingsPage-C32Vuizm.js} +2 -2
  69. package/dist/chunks/{SettingsPage-Cy5-q-P6.js.map → SettingsPage-C32Vuizm.js.map} +1 -1
  70. package/dist/chunks/{StatCard-BROG4NSp.js → StatCard-CRPfkSBY.js} +2 -2
  71. package/dist/chunks/{StatCard-BROG4NSp.js.map → StatCard-CRPfkSBY.js.map} +1 -1
  72. package/dist/chunks/{UserGroupsPage-CsTBkUPy.js → UserGroupsPage-08G5MdGm.js} +2 -2
  73. package/dist/chunks/{UserGroupsPage-CsTBkUPy.js.map → UserGroupsPage-08G5MdGm.js.map} +1 -1
  74. package/dist/chunks/{UserLogsPanels-3u8Z93YU.js → UserLogsPanels-nbIrMym9.js} +2 -2
  75. package/dist/chunks/{UserLogsPanels-3u8Z93YU.js.map → UserLogsPanels-nbIrMym9.js.map} +1 -1
  76. package/dist/chunks/{UserTransferSelect-ChLj4EkS.js → UserTransferSelect-C8betaWG.js} +2 -2
  77. package/dist/chunks/{UserTransferSelect-ChLj4EkS.js.map → UserTransferSelect-C8betaWG.js.map} +1 -1
  78. package/dist/chunks/{UsersPage-CfNnQJi4.js → UsersPage-BjTiNEqF.js} +2 -2
  79. package/dist/chunks/{UsersPage-CfNnQJi4.js.map → UsersPage-BjTiNEqF.js.map} +1 -1
  80. package/dist/chunks/{browser-BKLM0ThC-D0TgzKTC.js → browser-BKLM0ThC-Cp7Gi4-F.js} +1 -1
  81. package/dist/chunks/{code-BkkeN2fd.js → code-LswxAr98.js} +1 -1
  82. package/dist/chunks/{direct-engine-m32m22nx.js → direct-engine-Dv_6v4sx.js} +1 -1
  83. package/dist/chunks/{html-BGNWR_Bp.js → html-BokjxlLb.js} +1 -1
  84. package/dist/chunks/{line-CsTeuEcK.js → line-Bqh6atHH.js} +1 -1
  85. package/dist/chunks/{lrc-D6gjnGZ5.js → lrc-T5e1svZG.js} +1 -1
  86. package/dist/chunks/{markdown-BglUe9j8.js → markdown-BpWokN1s.js} +1 -1
  87. package/dist/chunks/{patch-C97Yl3jr.js → patch-BYVX5C9G.js} +1 -1
  88. package/dist/chunks/{worker-engine-B7_3qQVS.js → worker-engine-CtUAeI4G.js} +1 -1
  89. package/dist/index.js +1 -1
  90. package/dist/{src-DAkFn9SG.js → src-D6UlLiVN.js} +2 -2
  91. package/dist/{src-DAkFn9SG.js.map → src-D6UlLiVN.js.map} +1 -1
  92. package/package.json +9 -9
  93. package/dist/chunks/ApiTokensPage-DiRMgViD.js +0 -2
  94. package/dist/chunks/ApiTokensPage-DiRMgViD.js.map +0 -1
  95. package/dist/chunks/EntityContextRuntime-C9q95WDf.js +0 -1
  96. package/dist/chunks/SettingModuleDetail-I9CSJ-fh.js +0 -17
  97. package/dist/chunks/SettingModuleDetail-I9CSJ-fh.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{n as e}from"./rolldown-runtime-Bruge11p.js";import{$ as t,E as n,It as r,M as i,O as a,Q as ee,R as o,T as s,Ut as te,b as ne,f as re,m as c}from"./clipboard-dpqHdCyy.js";import{i as l,r as ie}from"./feedback-B1UL2ETz.js";import{r as ae,s as u,t as oe}from"./toolbar-controls-CrM8hKZg.js";import{n as d,t as se}from"./users-BTMhbl7C.js";import{n as ce,t as f}from"./useStatusToggle-D7g3Ew35.js";import{i as p,t as m}from"./deleteAction-CNNyRUSw.js";import{n as le,t as h}from"./useCrudOperationColumn-CPAzNGXp.js";import{n as ue,t as g}from"./useRowSelection-CFCS4_38.js";import{n as de,t as _}from"./useListPage-BPw-KZCv.js";import{a as fe,i as v,n as pe,s as y,t as me}from"./table-columns-CvT6OHd2.js";import{a as b,i as he,n as ge,o as _e,r as x}from"./EditFormModal-DVQksS9R.js";import{n as S,t as ve}from"./ConfigurableTable-8sYmeul2.js";import{D as C,E as w}from"./App-CfQx7jWq.js";import{n as T,t as ye}from"./AppModal-CVoUC2t3.js";import{n as E,r as be,t as xe}from"./MemberAssignmentSheet-AKvLbbYW.js";import{n as Se,t as D}from"./webmcp-crud-D-Dz8BY4.js";import{useEffect as O,useMemo as k,useState as A}from"react";import{Fragment as j,jsx as M,jsxs as N}from"react/jsx-runtime";import{useDictItems as Ce}from"@fudanda/admin-antd/core/hooks/useDictItems";import{departmentContract as we,positionContract as Te,roleContract as Ee,userContract as De,userGroupContract as P}from"@fudanda/admin-antd/core/lib/native-operations";import{usePermission as Oe}from"@fudanda/admin-antd/core/hooks/usePermission";import{useAllUsers as ke}from"@fudanda/admin-antd/core/hooks/queries/users";import{departmentsToTreeData as Ae,useFlatDepartments as je}from"@fudanda/admin-antd/core/hooks/queries/departments";import{useAllPositions as Me}from"@fudanda/admin-antd/core/hooks/queries/positions";import{useAllRoles as Ne}from"@fudanda/admin-antd/core/hooks/queries/roles";import{useAssignUserGroupMembers as Pe,useAssignUserGroupRoles as Fe,useDeleteUserGroups as Ie,useSaveUserGroup as F,useSyncUserGroup as Le,useUserGroupDetail as Re,useUserGroupList as I,useUserGroupMembers as ze,useUserGroupRoles as Be,useUserGroupRulePreview as Ve}from"@fudanda/admin-antd/core/hooks/queries/user-groups";function He(e,t){let n=e.memberMode??t?.memberMode??`static`;if(n===`dynamic`&&`userIds`in e&&e.userIds!==void 0)throw Error(`动态用户组不接受静态成员字段,请使用动态规则`);if(n!==`dynamic`&&e.memberRule!==void 0&&e.memberRule!==null)throw Error(`静态用户组不接受动态成员规则`)}function L(){let{options:e}=Ce(`common_status`),{hasPermission:t}=Oe(),{selectedRowKeys:i,clear:u,rowSelection:d}=ue(),f=de({contract:P,useList:I,table:{empty:`暂无数据`,rowSelection:d}}),{tableProps:p}=f,h=ke(),g=je(),_=(h.data??[]).map(e=>({id:e.id,username:e.username,nickname:e.nickname,avatar:e.avatar,email:e.email,departmentId:e.departmentId,departmentName:e.departmentName})),v=k(()=>g.data??[],[g.data]),[b,x]=A(!1),[S,C]=A(null),[T,E]=A([]),D=ze(S?.id,b),[L,R]=A(null),[z,B]=A(!1),[V,H]=A(null),[U,W]=A([]),G=Be(V?.id,z),K=Ne(),Ue=F(),q=Ve(),J=Le(),We=Me(),Y=_e({entityName:`用户组`,save:he(Ue,{create:P.create.input.shape.body,update:P.update.input.shape.body}),useDetail:Re,defaults:{status:`enabled`,memberMode:`static`,roleIds:[],userIds:[]},labelPosition:`top`,toValues:e=>({name:e.name,code:e.code,description:e.description??void 0,ownerId:e.ownerId??void 0,status:e.status,memberMode:e.memberMode??`static`,memberRule:e.memberRule??void 0,roleIds:e.rolePreview?.map(e=>e.id)??[]}),beforeSave:(e,{editing:t})=>({...e,description:e.description||void 0,ownerId:e.ownerId??void 0,memberRule:e.memberMode===`dynamic`?e.memberRule??void 0:void 0,...t?{clearFields:[...e.description?[]:[`description`],...e.ownerId==null?[`ownerId`]:[],...e.memberMode!==`dynamic`||!e.memberRule?[`memberRule`]:[]]}:{}})}),Ge=F(),X=Ie(),Z=Pe(),Q=Fe();Se({resource:`user_groups`,title:`用户组`,permission:`system:user-groups`,hasPermission:t,modal:Y,list:P.list,detail:P.detail,create:P.create.input.shape.body,update:P.update.input.shape.body,createFields:[`name`,`code`,`description`,`ownerId`,`roleIds`,`userIds`,`status`,`memberMode`,`memberRule`],updateFields:[`name`,`code`,`description`,`ownerId`,`roleIds`,`status`,`memberMode`,`memberRule`,`clearFields`],updateClearFields:[`description`,`ownerId`,`memberRule`],guardValues:He,fields:[`id`,`name`,`code`,`description`,`ownerId`,`status`,`memberMode`,`memberRule`,`createdAt`,`updatedAt`],filters:f.filterQuery,selectedIds:i,remove:e=>X.mutateAsync([e]),onDeleted:u,busy:b||z,lookups:[{name:`users`,title:`用户`,operation:De.all,permission:`system:user:list`,fields:[`id`,`username`,`nickname`]},{name:`roles`,title:`角色`,operation:Ee.all,permission:`system:role:list`,fields:[`id`,`name`,`code`]},{name:`departments`,title:`部门`,operation:we.flat,permission:`system:department:list`,fields:[`id`,`name`,`parentId`]},{name:`positions`,title:`岗位`,operation:Te.all,permission:`system:position:list`,fields:[`id`,`name`,`code`]}]});let Ke=ce({toggle:(e,t)=>Ge.mutateAsync({id:e.id,values:{status:t?`enabled`:`disabled`}}),confirmDisable:e=>({danger:!0,title:`确认禁用用户组「${e.name}」?`,content:`禁用后该用户组将不可选择。`,okText:`确认禁用`}),disabled:!t(`system:user-groups:update`)}),qe=k(()=>Ae(v,{keepEmptyChildren:!0}),[v]);O(()=>{b&&E((D.data??[]).map(e=>e.id))},[b,D.data]),O(()=>{z&&W((G.data??[]).map(e=>e.id))},[z,G.data]),O(()=>{Y.modalProps.visible||R(null)},[Y.modalProps.visible]);let Je=()=>{i.length&&m({title:`确认删除选中的 ${i.length} 个用户组?`,content:`删除后无法恢复,请确认操作`,run:()=>X.mutateAsync(i),onDeleted:u})},Ye=e=>{C(e),x(!0)},$=e=>{H(e),B(!0)},Xe=async()=>{V&&(await Q.mutateAsync({params:{id:V.id},body:{roleIds:U}}),l.success(`角色已更新,组内成员即时生效`),B(!1),H(null))},Ze=async()=>{S&&(await Z.mutateAsync({params:{id:S.id},body:{userIds:T}}),x(!1))},Qe=le({permission:`system:user-groups`,edit:e=>{Y.openEdit(e)},remove:X,title:`确定要删除该用户组吗?`,extra:e=>[{key:`members`,label:`成员`,hidden:!t(`system:user-groups:assign`),onClick:()=>{Ye(e)}},{key:`roles`,label:`角色`,hidden:!t(`system:user-groups:assign`),onClick:()=>{$(e)}},{key:`sync`,label:`同步`,hidden:e.memberMode!==`dynamic`||!t(`system:user-groups:assign`),onClick:()=>{J.mutate({params:{id:e.id}},{onSuccess:()=>l.success(`成员已按规则同步`)})}}],width:240,desktopInlineKeys:[`members`,`roles`,`edit`]}),$e=[{title:`用户组名称`,dataIndex:`name`,width:200,render:y},{title:`编码`,dataIndex:`code`,width:180,render:y},{title:`描述`,dataIndex:`description`,render:y},{title:`负责人`,dataIndex:`ownerName`,width:120,render:e=>e||me},{title:`成员模式`,dataIndex:`memberMode`,width:110,render:(e,t)=>e===`dynamic`?/* @__PURE__ */ N(a,{color:`teal`,children:[`动态`,t.ruleSyncedAt?``:`(未同步)`]}):/* @__PURE__ */ M(a,{color:`grey`,children:`静态`})},be({dataIndex:`memberPreview`,width:150,getPreview:e=>e.memberPreview,getCount:e=>e.memberCount,getScope:e=>({type:`userGroup`,id:e.id,name:e.name})}),fe({title:`角色`,dataIndex:`rolePreview`,width:260,contentWidth:228,getItems:e=>(e??[]).map(e=>({key:String(e.id),label:e.name})),tagColor:`blue`,popoverWidth:240,empty:/* @__PURE__ */ M(a,{color:`grey`,children:`无角色`}),onClick:t(`system:user-groups:assign`)?$:void 0}),pe,Ke.column(),Qe];return/* @__PURE__ */ N(`div`,{className:`page-container`,children:[/* @__PURE__ */ M(ve,{searchConfig:ee({page:f,filters:[`keyword`,`status`],create:/* @__PURE__ */ M(ae,{permission:`system:user-groups:create`,onClick:Y.openCreate}),actions:i.length>0&&t(`system:user-groups:delete`)&&/* @__PURE__ */ M(oe,{count:i.length,onClick:Je}),filterTitle:`用户组筛选`,actionTitle:`用户组操作`}),columns:$e,...p}),/* @__PURE__ */ M(ge,{modal:Y,width:520,okText:`确定`,children:({formState:t})=>{let n=t.values.memberMode??`static`;return/* @__PURE__ */ N(j,{children:[/* @__PURE__ */ M(o.Input,{field:`name`,label:`名称`,placeholder:`请输入用户组名称`,rules:[{required:!0,message:`请输入用户组名称`}]}),/* @__PURE__ */ M(o.Input,{field:`code`,label:`编码`,placeholder:`字母数字下划线`,rules:[{required:!0,message:`请输入用户组编码`},{pattern:/^\w+$/,message:`编码只能包含字母、数字和下划线`}]}),/* @__PURE__ */ M(o.Select,{field:`ownerId`,label:`负责人`,placeholder:`请选择负责人(可选)`,style:{width:`100%`},filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname} (${e.username})`}))}),/* @__PURE__ */ M(o.Select,{field:`status`,label:`状态`,style:{width:`100%`},optionList:e}),/* @__PURE__ */ M(o.Select,{field:`roleIds`,label:`角色`,placeholder:`请选择角色(可选)`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:(K.data??[]).map(e=>({value:e.id,label:`${e.name}(${e.code})`}))}),!Y.editing&&n===`static`&&/* @__PURE__ */ M(o.Select,{field:`userIds`,label:`成员`,placeholder:`请选择成员(可选)`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname}(${e.username})`}))}),/* @__PURE__ */ N(o.RadioGroup,{field:`memberMode`,label:`成员模式`,type:`button`,extraText:n===`dynamic`?`成员由规则自动维护:用户的部门/岗位/状态变化后自动加入或退出,不可手工增删`:`成员由管理员手工维护`,children:[/* @__PURE__ */ M(o.Radio,{value:`static`,children:`静态(手工维护)`}),/* @__PURE__ */ M(o.Radio,{value:`dynamic`,children:`动态(按规则自动维护)`})]}),n===`dynamic`&&/* @__PURE__ */ N(`div`,{style:{border:`1px solid var(--arc-antd-color-border)`,borderRadius:`var(--arc-antd-border-radius-medium)`,padding:`4px 16px 16px`,margin:`12px 0`},children:[/* @__PURE__ */ M(o.TreeSelect,{field:`memberRule.departmentIds`,label:`命中部门`,placeholder:`选择部门(可多选)`,style:{width:`100%`},multiple:!0,filterTreeNode:!0,showClear:!0,leafOnly:!1,treeData:qe}),/* @__PURE__ */ M(o.Switch,{field:`memberRule.includeSubDepartments`,label:`包含子部门`,checkedText:`是`,uncheckedText:`否`}),/* @__PURE__ */ M(o.Select,{field:`memberRule.positionIds`,label:`命中岗位`,placeholder:`选择岗位(可多选)`,extraText:`多个岗位满足任一即可;与部门条件同时设置时须同时满足`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:(We.data??[]).map(e=>({value:e.id,label:e.name}))}),/* @__PURE__ */ M(o.Select,{field:`memberRule.includeUserIds`,label:`强制包含`,placeholder:`选择用户(可选)`,extraText:`规则之外的例外成员`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname} (${e.username})`}))}),/* @__PURE__ */ M(o.Select,{field:`memberRule.excludeUserIds`,label:`强制排除`,placeholder:`选择用户(可选)`,extraText:`优先级最高,命中规则也不会加入`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname} (${e.username})`}))}),/* @__PURE__ */ N(s,{vertical:!0,align:`start`,spacing:8,style:{width:`100%`,marginTop:12},children:[/* @__PURE__ */ M(c,{theme:`light`,loading:q.isPending,onClick:()=>{let e=t.values.memberRule??{};q.mutate({body:{groupId:Y.editing?.id,memberRule:e}},{onSuccess:R})},children:`预览成员变化`}),L&&/* @__PURE__ */ N(`div`,{style:{fontSize:12,lineHeight:`20px`,width:`100%`},children:[/* @__PURE__ */ N(r.Text,{type:`secondary`,children:[`目标成员 `,L.total,` 人:新加入 `,L.joiningCount,` 人,移除 `,L.leavingCount,` 人`]}),L.joining.length>0&&/* @__PURE__ */ N(`div`,{style:{marginTop:4},children:[/* @__PURE__ */ M(r.Text,{type:`success`,children:`加入:`}),/* @__PURE__ */ N(s,{wrap:!0,spacing:4,children:[L.joining.map(e=>/* @__PURE__ */ M(a,{size:`small`,color:`green`,children:e.nickname},e.id)),L.joiningCount>L.joining.length&&/* @__PURE__ */ N(a,{size:`small`,children:[`+`,L.joiningCount-L.joining.length]})]})]}),L.leaving.length>0&&/* @__PURE__ */ N(`div`,{style:{marginTop:4},children:[/* @__PURE__ */ M(r.Text,{type:`danger`,children:`移除:`}),/* @__PURE__ */ N(s,{wrap:!0,spacing:4,children:[L.leaving.map(e=>/* @__PURE__ */ M(a,{size:`small`,color:`red`,children:e.nickname},e.id)),L.leavingCount>L.leaving.length&&/* @__PURE__ */ N(a,{size:`small`,children:[`+`,L.leavingCount-L.leaving.length]})]})]})]})]})]}),/* @__PURE__ */ M(o.TextArea,{field:`description`,label:`描述`,placeholder:`请输入描述(可选)`,maxCount:256})]})}}),S?.memberMode===`dynamic`?/* @__PURE__ */ M(ie,{title:/* @__PURE__ */ N(s,{children:[/* @__PURE__ */ M(se,{size:16}),/* @__PURE__ */ N(`span`,{children:[`成员管理 - `,S?.name]}),/* @__PURE__ */ M(a,{color:`teal`,size:`small`,children:`动态`})]}),visible:b,onCancel:()=>x(!1),width:720,footer:/* @__PURE__ */ N(`div`,{style:{display:`flex`,justifyContent:`flex-end`,gap:8},children:[/* @__PURE__ */ M(c,{onClick:()=>x(!1),children:`关闭`}),t(`system:user-groups:assign`)&&/* @__PURE__ */ M(c,{type:`primary`,icon:/* @__PURE__ */ M(w,{size:14}),loading:J.isPending,onClick:()=>{S&&J.mutate({params:{id:S.id}},{onSuccess:()=>l.success(`成员已按规则同步`)})},children:`立即同步`})]}),children:/* @__PURE__ */ N(s,{vertical:!0,align:`start`,spacing:12,style:{width:`100%`},children:[/* @__PURE__ */ M(re,{fullMode:!1,type:`info`,closeIcon:null,description:`成员由规则自动维护,不可手工增删;需要例外时请在编辑规则中使用强制包含/排除名单。${S.ruleSyncedAt?`最近同步:`+S.ruleSyncedAt:`尚未同步`}`,style:{width:`100%`}}),/* @__PURE__ */ M(n,{spinning:D.isFetching,style:{width:`100%`},children:(D.data??[]).length===0?/* @__PURE__ */ M(ne,{title:`暂无成员`,description:`当前没有用户命中该组规则`,style:{padding:`32px 0`}}):/* @__PURE__ */ M(s,{wrap:!0,spacing:6,children:(D.data??[]).map(e=>/* @__PURE__ */ N(a,{size:`large`,children:[e.nickname,`(`,e.username,`)`]},e.id))})})]})}):/* @__PURE__ */ M(xe,{title:`成员管理 - ${S?.name??``}`,visible:b,onCancel:()=>x(!1),users:_,value:T,onChange:E,departments:v,canSave:D.isSuccess,saveLoading:Z.isPending,onSave:Ze}),/* @__PURE__ */ M(ye,{title:`分配角色 — ${V?.name??``}`,visible:z,onCancel:()=>{B(!1),H(null)},onOk:Xe,okButtonProps:{disabled:!G.isSuccess},confirmLoading:Q.isPending,width:480,children:/* @__PURE__ */ N(n,{spinning:G.isFetching||K.isFetching,children:[/* @__PURE__ */ M(`div`,{style:{marginBottom:8,fontSize:12,color:`var(--arc-antd-color-text-2)`},children:`组内成员将自动继承所选角色的菜单与数据权限(与成员直接分配的角色取并集)`}),/* @__PURE__ */ M(te,{multiple:!0,filter:!0,placeholder:`请选择角色`,style:{width:`100%`},value:U,onChange:e=>W(e??[]),optionList:(K.data??[]).map(e=>({value:e.id,label:`${e.name}(${e.code})`}))})]})})]})}function R(){return(R=e((()=>{i(),C(),d(),T(),S(),v(),b(),u(),p(),t(),g(),f(),h(),E(),_(),x(),D()})))()}R();export{L as default};
2
- //# sourceMappingURL=UserGroupsPage-CsTBkUPy.js.map
1
+ import{n as e}from"./rolldown-runtime-Bruge11p.js";import{$ as t,E as n,It as r,M as i,O as a,Q as ee,R as o,T as s,Ut as te,b as ne,f as re,m as c}from"./clipboard-dpqHdCyy.js";import{i as l,r as ie}from"./feedback-B1UL2ETz.js";import{r as ae,s as u,t as oe}from"./toolbar-controls-CrM8hKZg.js";import{n as d,t as se}from"./users-BTMhbl7C.js";import{n as ce,t as f}from"./useStatusToggle-D7g3Ew35.js";import{i as p,t as m}from"./deleteAction-CNNyRUSw.js";import{n as le,t as h}from"./useCrudOperationColumn-CPAzNGXp.js";import{n as ue,t as g}from"./useRowSelection-CFCS4_38.js";import{n as de,t as _}from"./useListPage-BPw-KZCv.js";import{a as fe,i as v,n as pe,s as y,t as me}from"./table-columns-CvT6OHd2.js";import{a as b,i as he,n as ge,o as _e,r as x}from"./EditFormModal-DVQksS9R.js";import{n as S,t as ve}from"./ConfigurableTable-8sYmeul2.js";import{D as C,E as w}from"./App-nh6_qOo8.js";import{n as T,t as ye}from"./AppModal-CEouaU03.js";import{n as E,r as be,t as xe}from"./MemberAssignmentSheet-DL0fg1Yf.js";import{n as Se,t as D}from"./webmcp-crud-D-Dz8BY4.js";import{useEffect as O,useMemo as k,useState as A}from"react";import{Fragment as j,jsx as M,jsxs as N}from"react/jsx-runtime";import{useDictItems as Ce}from"@fudanda/admin-antd/core/hooks/useDictItems";import{departmentContract as we,positionContract as Te,roleContract as Ee,userContract as De,userGroupContract as P}from"@fudanda/admin-antd/core/lib/native-operations";import{usePermission as Oe}from"@fudanda/admin-antd/core/hooks/usePermission";import{useAllUsers as ke}from"@fudanda/admin-antd/core/hooks/queries/users";import{departmentsToTreeData as Ae,useFlatDepartments as je}from"@fudanda/admin-antd/core/hooks/queries/departments";import{useAllPositions as Me}from"@fudanda/admin-antd/core/hooks/queries/positions";import{useAllRoles as Ne}from"@fudanda/admin-antd/core/hooks/queries/roles";import{useAssignUserGroupMembers as Pe,useAssignUserGroupRoles as Fe,useDeleteUserGroups as Ie,useSaveUserGroup as F,useSyncUserGroup as Le,useUserGroupDetail as Re,useUserGroupList as I,useUserGroupMembers as ze,useUserGroupRoles as Be,useUserGroupRulePreview as Ve}from"@fudanda/admin-antd/core/hooks/queries/user-groups";function He(e,t){let n=e.memberMode??t?.memberMode??`static`;if(n===`dynamic`&&`userIds`in e&&e.userIds!==void 0)throw Error(`动态用户组不接受静态成员字段,请使用动态规则`);if(n!==`dynamic`&&e.memberRule!==void 0&&e.memberRule!==null)throw Error(`静态用户组不接受动态成员规则`)}function L(){let{options:e}=Ce(`common_status`),{hasPermission:t}=Oe(),{selectedRowKeys:i,clear:u,rowSelection:d}=ue(),f=de({contract:P,useList:I,table:{empty:`暂无数据`,rowSelection:d}}),{tableProps:p}=f,h=ke(),g=je(),_=(h.data??[]).map(e=>({id:e.id,username:e.username,nickname:e.nickname,avatar:e.avatar,email:e.email,departmentId:e.departmentId,departmentName:e.departmentName})),v=k(()=>g.data??[],[g.data]),[b,x]=A(!1),[S,C]=A(null),[T,E]=A([]),D=ze(S?.id,b),[L,R]=A(null),[z,B]=A(!1),[V,H]=A(null),[U,W]=A([]),G=Be(V?.id,z),K=Ne(),Ue=F(),q=Ve(),J=Le(),We=Me(),Y=_e({entityName:`用户组`,save:he(Ue,{create:P.create.input.shape.body,update:P.update.input.shape.body}),useDetail:Re,defaults:{status:`enabled`,memberMode:`static`,roleIds:[],userIds:[]},labelPosition:`top`,toValues:e=>({name:e.name,code:e.code,description:e.description??void 0,ownerId:e.ownerId??void 0,status:e.status,memberMode:e.memberMode??`static`,memberRule:e.memberRule??void 0,roleIds:e.rolePreview?.map(e=>e.id)??[]}),beforeSave:(e,{editing:t})=>({...e,description:e.description||void 0,ownerId:e.ownerId??void 0,memberRule:e.memberMode===`dynamic`?e.memberRule??void 0:void 0,...t?{clearFields:[...e.description?[]:[`description`],...e.ownerId==null?[`ownerId`]:[],...e.memberMode!==`dynamic`||!e.memberRule?[`memberRule`]:[]]}:{}})}),Ge=F(),X=Ie(),Z=Pe(),Q=Fe();Se({resource:`user_groups`,title:`用户组`,permission:`system:user-groups`,hasPermission:t,modal:Y,list:P.list,detail:P.detail,create:P.create.input.shape.body,update:P.update.input.shape.body,createFields:[`name`,`code`,`description`,`ownerId`,`roleIds`,`userIds`,`status`,`memberMode`,`memberRule`],updateFields:[`name`,`code`,`description`,`ownerId`,`roleIds`,`status`,`memberMode`,`memberRule`,`clearFields`],updateClearFields:[`description`,`ownerId`,`memberRule`],guardValues:He,fields:[`id`,`name`,`code`,`description`,`ownerId`,`status`,`memberMode`,`memberRule`,`createdAt`,`updatedAt`],filters:f.filterQuery,selectedIds:i,remove:e=>X.mutateAsync([e]),onDeleted:u,busy:b||z,lookups:[{name:`users`,title:`用户`,operation:De.all,permission:`system:user:list`,fields:[`id`,`username`,`nickname`]},{name:`roles`,title:`角色`,operation:Ee.all,permission:`system:role:list`,fields:[`id`,`name`,`code`]},{name:`departments`,title:`部门`,operation:we.flat,permission:`system:department:list`,fields:[`id`,`name`,`parentId`]},{name:`positions`,title:`岗位`,operation:Te.all,permission:`system:position:list`,fields:[`id`,`name`,`code`]}]});let Ke=ce({toggle:(e,t)=>Ge.mutateAsync({id:e.id,values:{status:t?`enabled`:`disabled`}}),confirmDisable:e=>({danger:!0,title:`确认禁用用户组「${e.name}」?`,content:`禁用后该用户组将不可选择。`,okText:`确认禁用`}),disabled:!t(`system:user-groups:update`)}),qe=k(()=>Ae(v,{keepEmptyChildren:!0}),[v]);O(()=>{b&&E((D.data??[]).map(e=>e.id))},[b,D.data]),O(()=>{z&&W((G.data??[]).map(e=>e.id))},[z,G.data]),O(()=>{Y.modalProps.visible||R(null)},[Y.modalProps.visible]);let Je=()=>{i.length&&m({title:`确认删除选中的 ${i.length} 个用户组?`,content:`删除后无法恢复,请确认操作`,run:()=>X.mutateAsync(i),onDeleted:u})},Ye=e=>{C(e),x(!0)},$=e=>{H(e),B(!0)},Xe=async()=>{V&&(await Q.mutateAsync({params:{id:V.id},body:{roleIds:U}}),l.success(`角色已更新,组内成员即时生效`),B(!1),H(null))},Ze=async()=>{S&&(await Z.mutateAsync({params:{id:S.id},body:{userIds:T}}),x(!1))},Qe=le({permission:`system:user-groups`,edit:e=>{Y.openEdit(e)},remove:X,title:`确定要删除该用户组吗?`,extra:e=>[{key:`members`,label:`成员`,hidden:!t(`system:user-groups:assign`),onClick:()=>{Ye(e)}},{key:`roles`,label:`角色`,hidden:!t(`system:user-groups:assign`),onClick:()=>{$(e)}},{key:`sync`,label:`同步`,hidden:e.memberMode!==`dynamic`||!t(`system:user-groups:assign`),onClick:()=>{J.mutate({params:{id:e.id}},{onSuccess:()=>l.success(`成员已按规则同步`)})}}],width:240,desktopInlineKeys:[`members`,`roles`,`edit`]}),$e=[{title:`用户组名称`,dataIndex:`name`,width:200,render:y},{title:`编码`,dataIndex:`code`,width:180,render:y},{title:`描述`,dataIndex:`description`,render:y},{title:`负责人`,dataIndex:`ownerName`,width:120,render:e=>e||me},{title:`成员模式`,dataIndex:`memberMode`,width:110,render:(e,t)=>e===`dynamic`?/* @__PURE__ */ N(a,{color:`teal`,children:[`动态`,t.ruleSyncedAt?``:`(未同步)`]}):/* @__PURE__ */ M(a,{color:`grey`,children:`静态`})},be({dataIndex:`memberPreview`,width:150,getPreview:e=>e.memberPreview,getCount:e=>e.memberCount,getScope:e=>({type:`userGroup`,id:e.id,name:e.name})}),fe({title:`角色`,dataIndex:`rolePreview`,width:260,contentWidth:228,getItems:e=>(e??[]).map(e=>({key:String(e.id),label:e.name})),tagColor:`blue`,popoverWidth:240,empty:/* @__PURE__ */ M(a,{color:`grey`,children:`无角色`}),onClick:t(`system:user-groups:assign`)?$:void 0}),pe,Ke.column(),Qe];return/* @__PURE__ */ N(`div`,{className:`page-container`,children:[/* @__PURE__ */ M(ve,{searchConfig:ee({page:f,filters:[`keyword`,`status`],create:/* @__PURE__ */ M(ae,{permission:`system:user-groups:create`,onClick:Y.openCreate}),actions:i.length>0&&t(`system:user-groups:delete`)&&/* @__PURE__ */ M(oe,{count:i.length,onClick:Je}),filterTitle:`用户组筛选`,actionTitle:`用户组操作`}),columns:$e,...p}),/* @__PURE__ */ M(ge,{modal:Y,width:520,okText:`确定`,children:({formState:t})=>{let n=t.values.memberMode??`static`;return/* @__PURE__ */ N(j,{children:[/* @__PURE__ */ M(o.Input,{field:`name`,label:`名称`,placeholder:`请输入用户组名称`,rules:[{required:!0,message:`请输入用户组名称`}]}),/* @__PURE__ */ M(o.Input,{field:`code`,label:`编码`,placeholder:`字母数字下划线`,rules:[{required:!0,message:`请输入用户组编码`},{pattern:/^\w+$/,message:`编码只能包含字母、数字和下划线`}]}),/* @__PURE__ */ M(o.Select,{field:`ownerId`,label:`负责人`,placeholder:`请选择负责人(可选)`,style:{width:`100%`},filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname} (${e.username})`}))}),/* @__PURE__ */ M(o.Select,{field:`status`,label:`状态`,style:{width:`100%`},optionList:e}),/* @__PURE__ */ M(o.Select,{field:`roleIds`,label:`角色`,placeholder:`请选择角色(可选)`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:(K.data??[]).map(e=>({value:e.id,label:`${e.name}(${e.code})`}))}),!Y.editing&&n===`static`&&/* @__PURE__ */ M(o.Select,{field:`userIds`,label:`成员`,placeholder:`请选择成员(可选)`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname}(${e.username})`}))}),/* @__PURE__ */ N(o.RadioGroup,{field:`memberMode`,label:`成员模式`,type:`button`,extraText:n===`dynamic`?`成员由规则自动维护:用户的部门/岗位/状态变化后自动加入或退出,不可手工增删`:`成员由管理员手工维护`,children:[/* @__PURE__ */ M(o.Radio,{value:`static`,children:`静态(手工维护)`}),/* @__PURE__ */ M(o.Radio,{value:`dynamic`,children:`动态(按规则自动维护)`})]}),n===`dynamic`&&/* @__PURE__ */ N(`div`,{style:{border:`1px solid var(--arc-antd-color-border)`,borderRadius:`var(--arc-antd-border-radius-medium)`,padding:`4px 16px 16px`,margin:`12px 0`},children:[/* @__PURE__ */ M(o.TreeSelect,{field:`memberRule.departmentIds`,label:`命中部门`,placeholder:`选择部门(可多选)`,style:{width:`100%`},multiple:!0,filterTreeNode:!0,showClear:!0,leafOnly:!1,treeData:qe}),/* @__PURE__ */ M(o.Switch,{field:`memberRule.includeSubDepartments`,label:`包含子部门`,checkedText:`是`,uncheckedText:`否`}),/* @__PURE__ */ M(o.Select,{field:`memberRule.positionIds`,label:`命中岗位`,placeholder:`选择岗位(可多选)`,extraText:`多个岗位满足任一即可;与部门条件同时设置时须同时满足`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:(We.data??[]).map(e=>({value:e.id,label:e.name}))}),/* @__PURE__ */ M(o.Select,{field:`memberRule.includeUserIds`,label:`强制包含`,placeholder:`选择用户(可选)`,extraText:`规则之外的例外成员`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname} (${e.username})`}))}),/* @__PURE__ */ M(o.Select,{field:`memberRule.excludeUserIds`,label:`强制排除`,placeholder:`选择用户(可选)`,extraText:`优先级最高,命中规则也不会加入`,style:{width:`100%`},multiple:!0,filter:!0,showClear:!0,optionList:_.map(e=>({value:e.id,label:`${e.nickname} (${e.username})`}))}),/* @__PURE__ */ N(s,{vertical:!0,align:`start`,spacing:8,style:{width:`100%`,marginTop:12},children:[/* @__PURE__ */ M(c,{theme:`light`,loading:q.isPending,onClick:()=>{let e=t.values.memberRule??{};q.mutate({body:{groupId:Y.editing?.id,memberRule:e}},{onSuccess:R})},children:`预览成员变化`}),L&&/* @__PURE__ */ N(`div`,{style:{fontSize:12,lineHeight:`20px`,width:`100%`},children:[/* @__PURE__ */ N(r.Text,{type:`secondary`,children:[`目标成员 `,L.total,` 人:新加入 `,L.joiningCount,` 人,移除 `,L.leavingCount,` 人`]}),L.joining.length>0&&/* @__PURE__ */ N(`div`,{style:{marginTop:4},children:[/* @__PURE__ */ M(r.Text,{type:`success`,children:`加入:`}),/* @__PURE__ */ N(s,{wrap:!0,spacing:4,children:[L.joining.map(e=>/* @__PURE__ */ M(a,{size:`small`,color:`green`,children:e.nickname},e.id)),L.joiningCount>L.joining.length&&/* @__PURE__ */ N(a,{size:`small`,children:[`+`,L.joiningCount-L.joining.length]})]})]}),L.leaving.length>0&&/* @__PURE__ */ N(`div`,{style:{marginTop:4},children:[/* @__PURE__ */ M(r.Text,{type:`danger`,children:`移除:`}),/* @__PURE__ */ N(s,{wrap:!0,spacing:4,children:[L.leaving.map(e=>/* @__PURE__ */ M(a,{size:`small`,color:`red`,children:e.nickname},e.id)),L.leavingCount>L.leaving.length&&/* @__PURE__ */ N(a,{size:`small`,children:[`+`,L.leavingCount-L.leaving.length]})]})]})]})]})]}),/* @__PURE__ */ M(o.TextArea,{field:`description`,label:`描述`,placeholder:`请输入描述(可选)`,maxCount:256})]})}}),S?.memberMode===`dynamic`?/* @__PURE__ */ M(ie,{title:/* @__PURE__ */ N(s,{children:[/* @__PURE__ */ M(se,{size:16}),/* @__PURE__ */ N(`span`,{children:[`成员管理 - `,S?.name]}),/* @__PURE__ */ M(a,{color:`teal`,size:`small`,children:`动态`})]}),visible:b,onCancel:()=>x(!1),width:720,footer:/* @__PURE__ */ N(`div`,{style:{display:`flex`,justifyContent:`flex-end`,gap:8},children:[/* @__PURE__ */ M(c,{onClick:()=>x(!1),children:`关闭`}),t(`system:user-groups:assign`)&&/* @__PURE__ */ M(c,{type:`primary`,icon:/* @__PURE__ */ M(w,{size:14}),loading:J.isPending,onClick:()=>{S&&J.mutate({params:{id:S.id}},{onSuccess:()=>l.success(`成员已按规则同步`)})},children:`立即同步`})]}),children:/* @__PURE__ */ N(s,{vertical:!0,align:`start`,spacing:12,style:{width:`100%`},children:[/* @__PURE__ */ M(re,{fullMode:!1,type:`info`,closeIcon:null,description:`成员由规则自动维护,不可手工增删;需要例外时请在编辑规则中使用强制包含/排除名单。${S.ruleSyncedAt?`最近同步:`+S.ruleSyncedAt:`尚未同步`}`,style:{width:`100%`}}),/* @__PURE__ */ M(n,{spinning:D.isFetching,style:{width:`100%`},children:(D.data??[]).length===0?/* @__PURE__ */ M(ne,{title:`暂无成员`,description:`当前没有用户命中该组规则`,style:{padding:`32px 0`}}):/* @__PURE__ */ M(s,{wrap:!0,spacing:6,children:(D.data??[]).map(e=>/* @__PURE__ */ N(a,{size:`large`,children:[e.nickname,`(`,e.username,`)`]},e.id))})})]})}):/* @__PURE__ */ M(xe,{title:`成员管理 - ${S?.name??``}`,visible:b,onCancel:()=>x(!1),users:_,value:T,onChange:E,departments:v,canSave:D.isSuccess,saveLoading:Z.isPending,onSave:Ze}),/* @__PURE__ */ M(ye,{title:`分配角色 — ${V?.name??``}`,visible:z,onCancel:()=>{B(!1),H(null)},onOk:Xe,okButtonProps:{disabled:!G.isSuccess},confirmLoading:Q.isPending,width:480,children:/* @__PURE__ */ N(n,{spinning:G.isFetching||K.isFetching,children:[/* @__PURE__ */ M(`div`,{style:{marginBottom:8,fontSize:12,color:`var(--arc-antd-color-text-2)`},children:`组内成员将自动继承所选角色的菜单与数据权限(与成员直接分配的角色取并集)`}),/* @__PURE__ */ M(te,{multiple:!0,filter:!0,placeholder:`请选择角色`,style:{width:`100%`},value:U,onChange:e=>W(e??[]),optionList:(K.data??[]).map(e=>({value:e.id,label:`${e.name}(${e.code})`}))})]})})]})}function R(){return(R=e((()=>{i(),C(),d(),T(),S(),v(),b(),u(),p(),t(),g(),f(),h(),E(),_(),x(),D()})))()}R();export{L as default};
2
+ //# sourceMappingURL=UserGroupsPage-08G5MdGm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserGroupsPage-CsTBkUPy.js","mappings":"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","names":["assertWebMcpGroupMode","mode","values","record","UserGroupsPage","statusOptions","useDictItems","usePermission","clearSelection","useRowSelection","page","useListPage","userGroupContract","useUserGroupList","allUsersQuery","useAllUsers","departmentsQuery","useFlatDepartments","allUsers","u","departments","useMemo","memberSheetVisible","setMemberSheetVisible","useState","memberGroup","setMemberGroup","memberIds","setMemberIds","membersQuery","useUserGroupMembers","rulePreview","setRulePreview","roleModalVisible","setRoleModalVisible","roleGroup","setRoleGroup","roleIds","setRoleIds","groupRolesQuery","useUserGroupRoles","allRolesQuery","useAllRoles","saveMutation","useSaveUserGroup","rulePreviewMutation","useUserGroupRulePreview","syncMutation","useSyncUserGroup","positionsQuery","useAllPositions","groupModal","useEditModal","formSaveMutation","useUserGroupDetail","group","role","editing","toggleStatusMutation","deleteMutation","useDeleteUserGroups","assignMembersMutation","useAssignUserGroupMembers","assignRolesMutation","useAssignUserGroupRoles","useStandardWebMcpCrudAdapter","selectedRowKeys","id","userContract","roleContract","departmentContract","positionContract","status","useStatusToggle","enabled","hasPermission","departmentTreeData","departmentsToTreeData","useEffect","m","r","handleBatchDelete","confirmAndDelete","openMembers","openRoles","handleSaveRoles","Toast","handleSaveMembers","operationColumn","useCrudOperationColumn","columns","renderEllipsis","v","EMPTY_PLACEHOLDER","Tag","memberPreviewColumn","overflowTagColumn","roles","createdAtColumn","ConfigurableTable","buildTableSearch","CreateButton","BatchDeleteButton","tableProps","EditFormSheet","memberMode","formState","_Fragment","Form","user","p","Space","Button","memberRule","Typography","SideSheet","Users","RefreshCw","Banner","Spin","Empty","MemberAssignmentSheet","AppModal","Select"],"ignoreList":[],"sources":["../../src/pages/system/user-groups/webmcp-values.ts","../../src/pages/system/user-groups/UserGroupsPage.tsx"],"sourcesContent":["/** Keep WebMCP inputs consistent with the ordinary editor's conditional fields. */\nexport function assertWebMcpGroupMode(values: Record<string, unknown>, record?: { memberMode?: string | null }): void {\n const mode = values.memberMode ?? record?.memberMode ?? 'static';\n if (mode === 'dynamic' && 'userIds' in values && values.userIds !== undefined) throw new Error('动态用户组不接受静态成员字段,请使用动态规则');\n if (mode !== 'dynamic' && values.memberRule !== undefined && values.memberRule !== null) throw new Error('静态用户组不接受动态成员规则');\n}\n","import { useEffect, useMemo, useState } from 'react';\nimport { Banner, Button, Form, Select, Space, Toast, SideSheet, Empty, Tag, Spin, Typography } from \"@/ui\";\nimport { RefreshCw, Users } from 'lucide-react';\nimport type { ColumnProps } from \"@/ui\";\nimport type { TreeNodeData } from \"@/ui\";\nimport { type CreateUserGroupInput, type User, type UserGroup, type UserGroupMemberRule, type UserGroupRulePreview } from '@fudanda/shared/identity';\nimport { userGroupContract, userContract, roleContract, departmentContract, positionContract } from '@fudanda/react-core/lib/native-operations';\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport type { UserTransferUser } from \"@/components/UserTransferSelect\";\nimport { AppModal } from \"@/components/AppModal\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { createdAtColumn, EMPTY_PLACEHOLDER, overflowTagColumn, renderEllipsis } from \"@/utils/table-columns\";\nimport { departmentsToTreeData, useFlatDepartments } from \"@fudanda/react-core/hooks/queries/departments\";\nimport { useAllPositions } from \"@fudanda/react-core/hooks/queries/positions\";\nimport { useAssignUserGroupMembers, useAssignUserGroupRoles, useDeleteUserGroups, useSaveUserGroup, useSyncUserGroup, useUserGroupDetail, useUserGroupList, useUserGroupMembers, useUserGroupRulePreview, useUserGroupRoles } from \"@fudanda/react-core/hooks/queries/user-groups\";\nimport { useAllUsers } from \"@fudanda/react-core/hooks/queries/users\";\nimport { useAllRoles } from \"@fudanda/react-core/hooks/queries/roles\";\nimport { useDictItems } from \"@fudanda/react-core/hooks/useDictItems\";\nimport { useEditModal, formSaveMutation } from \"@/hooks/useEditModal\";\nimport { BatchDeleteButton, CreateButton } from \"@/components/toolbar-controls\";\nimport { confirmAndDelete, buildTableSearch, useRowSelection, useStatusToggle, useCrudOperationColumn } from \"@/components/list-page/index\";\nimport { MemberAssignmentSheet, memberPreviewColumn } from \"@/components/members/MemberAssignmentSheet\";\nimport { useListPage } from \"@/hooks/useListPage\";\nimport { EditFormSheet } from \"@/components/EditFormModal\";\nimport { useStandardWebMcpCrudAdapter } from '@/webmcp-crud';\nimport { assertWebMcpGroupMode } from './webmcp-values';\ntype SimpleUser = UserTransferUser & {\n email?: string | null;\n departmentId?: number | null;\n};\nexport default function UserGroupsPage() {\n const { options: statusOptions } = useDictItems('common_status');\n const { hasPermission } = usePermission();\n const { selectedRowKeys, clear: clearSelection, rowSelection } = useRowSelection();\n const page = useListPage({\n contract: userGroupContract,\n useList: useUserGroupList,\n table: { empty: '暂无数据', rowSelection },\n });\n const { tableProps } = page;\n // 选项数据\n const allUsersQuery = useAllUsers();\n const departmentsQuery = useFlatDepartments();\n const allUsers: SimpleUser[] = (allUsersQuery.data ?? []).map((u: User) => ({\n id: u.id,\n username: u.username,\n nickname: u.nickname,\n avatar: u.avatar,\n email: u.email,\n departmentId: u.departmentId,\n departmentName: u.departmentName,\n }));\n const departments = useMemo(() => departmentsQuery.data ?? [], [departmentsQuery.data]);\n // 成员管理\n const [memberSheetVisible, setMemberSheetVisible] = useState(false);\n const [memberGroup, setMemberGroup] = useState<UserGroup | null>(null);\n const [memberIds, setMemberIds] = useState<number[]>([]);\n const membersQuery = useUserGroupMembers(memberGroup?.id, memberSheetVisible);\n // 动态组规则预览\n const [rulePreview, setRulePreview] = useState<UserGroupRulePreview | null>(null);\n // 角色分配\n const [roleModalVisible, setRoleModalVisible] = useState(false);\n const [roleGroup, setRoleGroup] = useState<UserGroup | null>(null);\n const [roleIds, setRoleIds] = useState<number[]>([]);\n const groupRolesQuery = useUserGroupRoles(roleGroup?.id, roleModalVisible);\n const allRolesQuery = useAllRoles();\n const saveMutation = useSaveUserGroup();\n const rulePreviewMutation = useUserGroupRulePreview();\n const syncMutation = useSyncUserGroup();\n const positionsQuery = useAllPositions();\n const groupModal = useEditModal<UserGroup, Partial<CreateUserGroupInput>>({\n entityName: '用户组',\n save: formSaveMutation(saveMutation, { create: userGroupContract.create.input.shape.body, update: userGroupContract.update.input.shape.body }),\n useDetail: useUserGroupDetail,\n defaults: { status: 'enabled', memberMode: 'static', roleIds: [], userIds: [] },\n labelPosition: 'top',\n toValues: (group) => ({\n name: group.name,\n code: group.code,\n description: group.description ?? undefined,\n ownerId: group.ownerId ?? undefined,\n status: group.status,\n memberMode: group.memberMode ?? 'static',\n memberRule: group.memberRule ?? undefined,\n roleIds: group.rolePreview?.map((role) => role.id) ?? [],\n }),\n beforeSave: (values, { editing }) => ({ ...values, description: values.description || undefined, ownerId: values.ownerId ?? undefined, memberRule: values.memberMode === 'dynamic' ? values.memberRule ?? undefined : undefined,\n ...(editing ? { clearFields: [...(!values.description ? ['description'] : []), ...(values.ownerId == null ? ['ownerId'] : []), ...(values.memberMode !== 'dynamic' || !values.memberRule ? ['memberRule'] : [])] } : {}) }),\n });\n const toggleStatusMutation = useSaveUserGroup();\n const deleteMutation = useDeleteUserGroups();\n const assignMembersMutation = useAssignUserGroupMembers();\n const assignRolesMutation = useAssignUserGroupRoles();\n useStandardWebMcpCrudAdapter<UserGroup>({ resource: 'user_groups', title: '用户组', permission: 'system:user-groups', hasPermission, modal: groupModal,\n list: userGroupContract.list, detail: userGroupContract.detail, create: userGroupContract.create.input.shape.body, update: userGroupContract.update.input.shape.body,\n createFields: ['name', 'code', 'description', 'ownerId', 'roleIds', 'userIds', 'status', 'memberMode', 'memberRule'], updateFields: ['name', 'code', 'description', 'ownerId', 'roleIds', 'status', 'memberMode', 'memberRule', 'clearFields'], updateClearFields: ['description', 'ownerId', 'memberRule'],\n guardValues: assertWebMcpGroupMode,\n fields: ['id', 'name', 'code', 'description', 'ownerId', 'status', 'memberMode', 'memberRule', 'createdAt', 'updatedAt'], filters: page.filterQuery, selectedIds: selectedRowKeys,\n remove: id => deleteMutation.mutateAsync([id]), onDeleted: clearSelection, busy: memberSheetVisible || roleModalVisible,\n lookups: [{ name: 'users', title: '用户', operation: userContract.all, permission: 'system:user:list', fields: ['id', 'username', 'nickname'] },\n { name: 'roles', title: '角色', operation: roleContract.all, permission: 'system:role:list', fields: ['id', 'name', 'code'] },\n { name: 'departments', title: '部门', operation: departmentContract.flat, permission: 'system:department:list', fields: ['id', 'name', 'parentId'] },\n { name: 'positions', title: '岗位', operation: positionContract.all, permission: 'system:position:list', fields: ['id', 'name', 'code'] }] });\n const status = useStatusToggle<UserGroup>({\n toggle: (group, enabled) => toggleStatusMutation.mutateAsync({ id: group.id, values: { status: enabled ? 'enabled' : 'disabled' } }),\n confirmDisable: (group) => ({ danger: true, title: `确认禁用用户组「${group.name}」?`, content: '禁用后该用户组将不可选择。', okText: '确认禁用' }),\n disabled: !hasPermission('system:user-groups:update'),\n });\n const departmentTreeData = useMemo<TreeNodeData[]>(() => departmentsToTreeData(departments, { keepEmptyChildren: true }), [departments]);\n useEffect(() => {\n if (memberSheetVisible)\n setMemberIds((membersQuery.data ?? []).map((m) => m.id));\n }, [memberSheetVisible, membersQuery.data]);\n useEffect(() => {\n if (roleModalVisible)\n setRoleIds((groupRolesQuery.data ?? []).map((r) => r.id));\n }, [roleModalVisible, groupRolesQuery.data]);\n // 编辑弹窗关闭时清空规则预览,避免下次打开残留上一个组的结果\n useEffect(() => {\n if (!groupModal.modalProps.visible)\n setRulePreview(null);\n }, [groupModal.modalProps.visible]);\n const handleBatchDelete = () => {\n if (!selectedRowKeys.length)\n return;\n confirmAndDelete({\n title: `确认删除选中的 ${selectedRowKeys.length} 个用户组?`,\n content: '删除后无法恢复,请确认操作',\n run: () => deleteMutation.mutateAsync(selectedRowKeys),\n onDeleted: clearSelection,\n });\n };\n const openMembers = (group: UserGroup) => {\n setMemberGroup(group);\n setMemberSheetVisible(true);\n };\n const openRoles = (group: UserGroup) => {\n setRoleGroup(group);\n setRoleModalVisible(true);\n };\n const handleSaveRoles = async () => {\n if (!roleGroup)\n return;\n await assignRolesMutation.mutateAsync({ params: { id: roleGroup.id }, body: { roleIds } });\n Toast.success('角色已更新,组内成员即时生效');\n setRoleModalVisible(false);\n setRoleGroup(null);\n };\n const handleSaveMembers = async () => {\n if (!memberGroup)\n return;\n await assignMembersMutation.mutateAsync({ params: { id: memberGroup.id }, body: { userIds: memberIds } });\n setMemberSheetVisible(false);\n };\n const operationColumn = useCrudOperationColumn<UserGroup>({\n permission: 'system:user-groups',\n edit: (record) => { groupModal.openEdit(record); },\n remove: deleteMutation,\n title: '确定要删除该用户组吗?',\n extra: (record) => [\n {\n key: 'members',\n label: '成员',\n hidden: !hasPermission('system:user-groups:assign'),\n onClick: () => { openMembers(record); },\n },\n {\n key: 'roles',\n label: '角色',\n hidden: !hasPermission('system:user-groups:assign'),\n onClick: () => { openRoles(record); },\n },\n {\n key: 'sync',\n label: '同步',\n hidden: record.memberMode !== 'dynamic' || !hasPermission('system:user-groups:assign'),\n onClick: () => {\n syncMutation.mutate({ params: { id: record.id } }, { onSuccess: () => Toast.success('成员已按规则同步') });\n },\n },\n ],\n width: 240,\n desktopInlineKeys: ['members', 'roles', 'edit'],\n });\n const columns: ColumnProps<UserGroup>[] = [\n { title: '用户组名称', dataIndex: 'name', width: 200, render: renderEllipsis },\n { title: '编码', dataIndex: 'code', width: 180, render: renderEllipsis },\n {\n title: '描述', dataIndex: 'description',\n render: renderEllipsis,\n },\n {\n title: '负责人', dataIndex: 'ownerName', width: 120,\n render: (v: string | null | undefined) => v || EMPTY_PLACEHOLDER,\n },\n {\n title: '成员模式', dataIndex: 'memberMode', width: 110,\n render: (v: string, record: UserGroup) => v === 'dynamic'\n ? <Tag color=\"teal\">动态{record.ruleSyncedAt ? '' : '(未同步)'}</Tag>\n : <Tag color=\"grey\">静态</Tag>,\n },\n memberPreviewColumn<UserGroup>({\n dataIndex: 'memberPreview',\n width: 150,\n getPreview: (record) => record.memberPreview,\n getCount: (record) => record.memberCount,\n getScope: (record) => ({ type: 'userGroup', id: record.id, name: record.name }),\n }),\n overflowTagColumn<UserGroup>({\n title: '角色',\n dataIndex: 'rolePreview',\n width: 260,\n contentWidth: 228,\n getItems: (roles) => (roles as UserGroup['rolePreview'] ?? []).map((role) => ({ key: String(role.id), label: role.name })),\n tagColor: 'blue',\n popoverWidth: 240,\n empty: <Tag color=\"grey\">无角色</Tag>,\n onClick: hasPermission('system:user-groups:assign') ? openRoles : undefined,\n }),\n createdAtColumn,\n status.column(),\n operationColumn,\n ];\n return (<div className=\"page-container\">\n\n\n <ConfigurableTable<UserGroup> searchConfig={buildTableSearch({\n page: (page),\n filters: (['keyword', 'status']),\n create: (<CreateButton permission=\"system:user-groups:create\" onClick={groupModal.openCreate}/>),\n actions: (selectedRowKeys.length > 0 && hasPermission('system:user-groups:delete') && <BatchDeleteButton count={selectedRowKeys.length} onClick={handleBatchDelete}/>),\n filterTitle: \"用户组筛选\",\n actionTitle: \"用户组操作\"\n })} columns={columns} {...tableProps}/>\n\n <EditFormSheet modal={groupModal} width={520} okText=\"确定\">\n {({ formState }) => {\n const memberMode = (formState.values as {\n memberMode?: string;\n }).memberMode ?? 'static';\n return (<>\n <Form.Input field=\"name\" label=\"名称\" placeholder=\"请输入用户组名称\" rules={[{ required: true, message: '请输入用户组名称' }]}/>\n <Form.Input field=\"code\" label=\"编码\" placeholder=\"字母数字下划线\" rules={[\n { required: true, message: '请输入用户组编码' },\n { pattern: /^\\w+$/, message: '编码只能包含字母、数字和下划线' },\n ]}/>\n <Form.Select field=\"ownerId\" label=\"负责人\" placeholder=\"请选择负责人(可选)\" style={{ width: '100%' }} filter showClear optionList={allUsers.map(u => ({ value: u.id, label: `${u.nickname} (${u.username})` }))}/>\n <Form.Select field=\"status\" label=\"状态\" style={{ width: '100%' }} optionList={statusOptions}/>\n <Form.Select field=\"roleIds\" label=\"角色\" placeholder=\"请选择角色(可选)\" style={{ width: '100%' }} multiple filter showClear optionList={(allRolesQuery.data ?? []).map((role) => ({ value: role.id, label: `${role.name}(${role.code})` }))}/>\n {!groupModal.editing && memberMode === 'static' && (<Form.Select field=\"userIds\" label=\"成员\" placeholder=\"请选择成员(可选)\" style={{ width: '100%' }} multiple filter showClear optionList={allUsers.map((user) => ({ value: user.id, label: `${user.nickname}(${user.username})` }))}/>)}\n <Form.RadioGroup field=\"memberMode\" label=\"成员模式\" type=\"button\" extraText={memberMode === 'dynamic'\n ? '成员由规则自动维护:用户的部门/岗位/状态变化后自动加入或退出,不可手工增删'\n : '成员由管理员手工维护'}>\n <Form.Radio value=\"static\">静态(手工维护)</Form.Radio>\n <Form.Radio value=\"dynamic\">动态(按规则自动维护)</Form.Radio>\n </Form.RadioGroup>\n {memberMode === 'dynamic' && (<div style={{\n border: '1px solid var(--arc-antd-color-border)',\n borderRadius: 'var(--arc-antd-border-radius-medium)',\n padding: '4px 16px 16px',\n margin: '12px 0',\n }}>\n <Form.TreeSelect field=\"memberRule.departmentIds\" label=\"命中部门\" placeholder=\"选择部门(可多选)\" style={{ width: '100%' }} multiple filterTreeNode showClear leafOnly={false} treeData={departmentTreeData}/>\n <Form.Switch field=\"memberRule.includeSubDepartments\" label=\"包含子部门\" checkedText=\"是\" uncheckedText=\"否\"/>\n <Form.Select field=\"memberRule.positionIds\" label=\"命中岗位\" placeholder=\"选择岗位(可多选)\" extraText=\"多个岗位满足任一即可;与部门条件同时设置时须同时满足\" style={{ width: '100%' }} multiple filter showClear optionList={(positionsQuery.data ?? []).map((p) => ({ value: p.id, label: p.name }))}/>\n <Form.Select field=\"memberRule.includeUserIds\" label=\"强制包含\" placeholder=\"选择用户(可选)\" extraText=\"规则之外的例外成员\" style={{ width: '100%' }} multiple filter showClear optionList={allUsers.map(u => ({ value: u.id, label: `${u.nickname} (${u.username})` }))}/>\n <Form.Select field=\"memberRule.excludeUserIds\" label=\"强制排除\" placeholder=\"选择用户(可选)\" extraText=\"优先级最高,命中规则也不会加入\" style={{ width: '100%' }} multiple filter showClear optionList={allUsers.map(u => ({ value: u.id, label: `${u.nickname} (${u.username})` }))}/>\n <Space vertical align=\"start\" spacing={8} style={{ width: '100%', marginTop: 12 }}>\n <Button theme=\"light\" loading={rulePreviewMutation.isPending} onClick={() => {\n const memberRule = ((formState.values as {\n memberRule?: UserGroupMemberRule;\n }).memberRule ?? {});\n rulePreviewMutation.mutate({ body: { groupId: groupModal.editing?.id, memberRule } }, { onSuccess: setRulePreview });\n }}>\n 预览成员变化\n </Button>\n {rulePreview && (<div style={{ fontSize: 12, lineHeight: '20px', width: '100%' }}>\n <Typography.Text type=\"secondary\">\n 目标成员 {rulePreview.total} 人:新加入 {rulePreview.joiningCount} 人,移除 {rulePreview.leavingCount} 人\n </Typography.Text>\n {rulePreview.joining.length > 0 && (<div style={{ marginTop: 4 }}>\n <Typography.Text type=\"success\">加入:</Typography.Text>\n <Space wrap spacing={4}>\n {rulePreview.joining.map((u) => <Tag key={u.id} size=\"small\" color=\"green\">{u.nickname}</Tag>)}\n {rulePreview.joiningCount > rulePreview.joining.length && <Tag size=\"small\">+{rulePreview.joiningCount - rulePreview.joining.length}</Tag>}\n </Space>\n </div>)}\n {rulePreview.leaving.length > 0 && (<div style={{ marginTop: 4 }}>\n <Typography.Text type=\"danger\">移除:</Typography.Text>\n <Space wrap spacing={4}>\n {rulePreview.leaving.map((u) => <Tag key={u.id} size=\"small\" color=\"red\">{u.nickname}</Tag>)}\n {rulePreview.leavingCount > rulePreview.leaving.length && <Tag size=\"small\">+{rulePreview.leavingCount - rulePreview.leaving.length}</Tag>}\n </Space>\n </div>)}\n </div>)}\n </Space>\n </div>)}\n <Form.TextArea field=\"description\" label=\"描述\" placeholder=\"请输入描述(可选)\" maxCount={256}/>\n </>);\n }}\n </EditFormSheet>\n\n {memberGroup?.memberMode === 'dynamic' ? (<SideSheet title={<Space>\n <Users size={16}/>\n <span>成员管理 - {memberGroup?.name}</span>\n <Tag color=\"teal\" size=\"small\">动态</Tag>\n </Space>} visible={memberSheetVisible} onCancel={() => setMemberSheetVisible(false)} width={720} footer={(<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>\n <Button onClick={() => setMemberSheetVisible(false)}>关闭</Button>\n {hasPermission('system:user-groups:assign') && (<Button type=\"primary\" icon={<RefreshCw size={14}/>} loading={syncMutation.isPending} onClick={() => {\n if (!memberGroup)\n return;\n syncMutation.mutate({ params: { id: memberGroup.id } }, { onSuccess: () => Toast.success('成员已按规则同步') });\n }}>\n 立即同步\n </Button>)}\n </div>)}>\n <Space vertical align=\"start\" spacing={12} style={{ width: '100%' }}>\n <Banner fullMode={false} type=\"info\" closeIcon={null} description={`成员由规则自动维护,不可手工增删;需要例外时请在编辑规则中使用强制包含/排除名单。${memberGroup.ruleSyncedAt ? '最近同步:' + memberGroup.ruleSyncedAt : '尚未同步'}`} style={{ width: '100%' }}/>\n <Spin spinning={membersQuery.isFetching} style={{ width: '100%' }}>\n {(membersQuery.data ?? []).length === 0 ? (<Empty title=\"暂无成员\" description=\"当前没有用户命中该组规则\" style={{ padding: '32px 0' }}/>) : (<Space wrap spacing={6}>\n {(membersQuery.data ?? []).map((m) => (<Tag key={m.id} size=\"large\">{m.nickname}({m.username})</Tag>))}\n </Space>)}\n </Spin>\n </Space>\n </SideSheet>) : (<MemberAssignmentSheet title={`成员管理 - ${memberGroup?.name ?? ''}`} visible={memberSheetVisible} onCancel={() => setMemberSheetVisible(false)} users={allUsers} value={memberIds} onChange={setMemberIds} departments={departments} canSave={membersQuery.isSuccess} saveLoading={assignMembersMutation.isPending} onSave={handleSaveMembers}/>)}\n\n <AppModal title={`分配角色 — ${roleGroup?.name ?? ''}`} visible={roleModalVisible} onCancel={() => { setRoleModalVisible(false); setRoleGroup(null); }} onOk={handleSaveRoles} okButtonProps={{ disabled: !groupRolesQuery.isSuccess }} confirmLoading={assignRolesMutation.isPending} width={480}>\n <Spin spinning={groupRolesQuery.isFetching || allRolesQuery.isFetching}>\n <div style={{ marginBottom: 8, fontSize: 12, color: 'var(--arc-antd-color-text-2)' }}>\n 组内成员将自动继承所选角色的菜单与数据权限(与成员直接分配的角色取并集)\n </div>\n <Select multiple filter placeholder=\"请选择角色\" style={{ width: '100%' }} value={roleIds} onChange={(v) => setRoleIds((v as number[]) ?? [])} optionList={(allRolesQuery.data ?? []).map((r) => ({ value: r.id, label: `${r.name}(${r.code})` }))}/>\n </Spin>\n </AppModal>\n </div>);\n}\n"]}
1
+ {"version":3,"file":"UserGroupsPage-08G5MdGm.js","mappings":"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","names":["assertWebMcpGroupMode","mode","values","record","UserGroupsPage","statusOptions","useDictItems","usePermission","clearSelection","useRowSelection","page","useListPage","userGroupContract","useUserGroupList","allUsersQuery","useAllUsers","departmentsQuery","useFlatDepartments","allUsers","u","departments","useMemo","memberSheetVisible","setMemberSheetVisible","useState","memberGroup","setMemberGroup","memberIds","setMemberIds","membersQuery","useUserGroupMembers","rulePreview","setRulePreview","roleModalVisible","setRoleModalVisible","roleGroup","setRoleGroup","roleIds","setRoleIds","groupRolesQuery","useUserGroupRoles","allRolesQuery","useAllRoles","saveMutation","useSaveUserGroup","rulePreviewMutation","useUserGroupRulePreview","syncMutation","useSyncUserGroup","positionsQuery","useAllPositions","groupModal","useEditModal","formSaveMutation","useUserGroupDetail","group","role","editing","toggleStatusMutation","deleteMutation","useDeleteUserGroups","assignMembersMutation","useAssignUserGroupMembers","assignRolesMutation","useAssignUserGroupRoles","useStandardWebMcpCrudAdapter","selectedRowKeys","id","userContract","roleContract","departmentContract","positionContract","status","useStatusToggle","enabled","hasPermission","departmentTreeData","departmentsToTreeData","useEffect","m","r","handleBatchDelete","confirmAndDelete","openMembers","openRoles","handleSaveRoles","Toast","handleSaveMembers","operationColumn","useCrudOperationColumn","columns","renderEllipsis","v","EMPTY_PLACEHOLDER","Tag","memberPreviewColumn","overflowTagColumn","roles","createdAtColumn","ConfigurableTable","buildTableSearch","CreateButton","BatchDeleteButton","tableProps","EditFormSheet","memberMode","formState","_Fragment","Form","user","p","Space","Button","memberRule","Typography","SideSheet","Users","RefreshCw","Banner","Spin","Empty","MemberAssignmentSheet","AppModal","Select"],"ignoreList":[],"sources":["../../src/pages/system/user-groups/webmcp-values.ts","../../src/pages/system/user-groups/UserGroupsPage.tsx"],"sourcesContent":["/** Keep WebMCP inputs consistent with the ordinary editor's conditional fields. */\nexport function assertWebMcpGroupMode(values: Record<string, unknown>, record?: { memberMode?: string | null }): void {\n const mode = values.memberMode ?? record?.memberMode ?? 'static';\n if (mode === 'dynamic' && 'userIds' in values && values.userIds !== undefined) throw new Error('动态用户组不接受静态成员字段,请使用动态规则');\n if (mode !== 'dynamic' && values.memberRule !== undefined && values.memberRule !== null) throw new Error('静态用户组不接受动态成员规则');\n}\n","import { useEffect, useMemo, useState } from 'react';\nimport { Banner, Button, Form, Select, Space, Toast, SideSheet, Empty, Tag, Spin, Typography } from \"@/ui\";\nimport { RefreshCw, Users } from 'lucide-react';\nimport type { ColumnProps } from \"@/ui\";\nimport type { TreeNodeData } from \"@/ui\";\nimport { type CreateUserGroupInput, type User, type UserGroup, type UserGroupMemberRule, type UserGroupRulePreview } from '@fudanda/shared/identity';\nimport { userGroupContract, userContract, roleContract, departmentContract, positionContract } from '@fudanda/react-core/lib/native-operations';\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport type { UserTransferUser } from \"@/components/UserTransferSelect\";\nimport { AppModal } from \"@/components/AppModal\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { createdAtColumn, EMPTY_PLACEHOLDER, overflowTagColumn, renderEllipsis } from \"@/utils/table-columns\";\nimport { departmentsToTreeData, useFlatDepartments } from \"@fudanda/react-core/hooks/queries/departments\";\nimport { useAllPositions } from \"@fudanda/react-core/hooks/queries/positions\";\nimport { useAssignUserGroupMembers, useAssignUserGroupRoles, useDeleteUserGroups, useSaveUserGroup, useSyncUserGroup, useUserGroupDetail, useUserGroupList, useUserGroupMembers, useUserGroupRulePreview, useUserGroupRoles } from \"@fudanda/react-core/hooks/queries/user-groups\";\nimport { useAllUsers } from \"@fudanda/react-core/hooks/queries/users\";\nimport { useAllRoles } from \"@fudanda/react-core/hooks/queries/roles\";\nimport { useDictItems } from \"@fudanda/react-core/hooks/useDictItems\";\nimport { useEditModal, formSaveMutation } from \"@/hooks/useEditModal\";\nimport { BatchDeleteButton, CreateButton } from \"@/components/toolbar-controls\";\nimport { confirmAndDelete, buildTableSearch, useRowSelection, useStatusToggle, useCrudOperationColumn } from \"@/components/list-page/index\";\nimport { MemberAssignmentSheet, memberPreviewColumn } from \"@/components/members/MemberAssignmentSheet\";\nimport { useListPage } from \"@/hooks/useListPage\";\nimport { EditFormSheet } from \"@/components/EditFormModal\";\nimport { useStandardWebMcpCrudAdapter } from '@/webmcp-crud';\nimport { assertWebMcpGroupMode } from './webmcp-values';\ntype SimpleUser = UserTransferUser & {\n email?: string | null;\n departmentId?: number | null;\n};\nexport default function UserGroupsPage() {\n const { options: statusOptions } = useDictItems('common_status');\n const { hasPermission } = usePermission();\n const { selectedRowKeys, clear: clearSelection, rowSelection } = useRowSelection();\n const page = useListPage({\n contract: userGroupContract,\n useList: useUserGroupList,\n table: { empty: '暂无数据', rowSelection },\n });\n const { tableProps } = page;\n // 选项数据\n const allUsersQuery = useAllUsers();\n const departmentsQuery = useFlatDepartments();\n const allUsers: SimpleUser[] = (allUsersQuery.data ?? []).map((u: User) => ({\n id: u.id,\n username: u.username,\n nickname: u.nickname,\n avatar: u.avatar,\n email: u.email,\n departmentId: u.departmentId,\n departmentName: u.departmentName,\n }));\n const departments = useMemo(() => departmentsQuery.data ?? [], [departmentsQuery.data]);\n // 成员管理\n const [memberSheetVisible, setMemberSheetVisible] = useState(false);\n const [memberGroup, setMemberGroup] = useState<UserGroup | null>(null);\n const [memberIds, setMemberIds] = useState<number[]>([]);\n const membersQuery = useUserGroupMembers(memberGroup?.id, memberSheetVisible);\n // 动态组规则预览\n const [rulePreview, setRulePreview] = useState<UserGroupRulePreview | null>(null);\n // 角色分配\n const [roleModalVisible, setRoleModalVisible] = useState(false);\n const [roleGroup, setRoleGroup] = useState<UserGroup | null>(null);\n const [roleIds, setRoleIds] = useState<number[]>([]);\n const groupRolesQuery = useUserGroupRoles(roleGroup?.id, roleModalVisible);\n const allRolesQuery = useAllRoles();\n const saveMutation = useSaveUserGroup();\n const rulePreviewMutation = useUserGroupRulePreview();\n const syncMutation = useSyncUserGroup();\n const positionsQuery = useAllPositions();\n const groupModal = useEditModal<UserGroup, Partial<CreateUserGroupInput>>({\n entityName: '用户组',\n save: formSaveMutation(saveMutation, { create: userGroupContract.create.input.shape.body, update: userGroupContract.update.input.shape.body }),\n useDetail: useUserGroupDetail,\n defaults: { status: 'enabled', memberMode: 'static', roleIds: [], userIds: [] },\n labelPosition: 'top',\n toValues: (group) => ({\n name: group.name,\n code: group.code,\n description: group.description ?? undefined,\n ownerId: group.ownerId ?? undefined,\n status: group.status,\n memberMode: group.memberMode ?? 'static',\n memberRule: group.memberRule ?? undefined,\n roleIds: group.rolePreview?.map((role) => role.id) ?? [],\n }),\n beforeSave: (values, { editing }) => ({ ...values, description: values.description || undefined, ownerId: values.ownerId ?? undefined, memberRule: values.memberMode === 'dynamic' ? values.memberRule ?? undefined : undefined,\n ...(editing ? { clearFields: [...(!values.description ? ['description'] : []), ...(values.ownerId == null ? ['ownerId'] : []), ...(values.memberMode !== 'dynamic' || !values.memberRule ? ['memberRule'] : [])] } : {}) }),\n });\n const toggleStatusMutation = useSaveUserGroup();\n const deleteMutation = useDeleteUserGroups();\n const assignMembersMutation = useAssignUserGroupMembers();\n const assignRolesMutation = useAssignUserGroupRoles();\n useStandardWebMcpCrudAdapter<UserGroup>({ resource: 'user_groups', title: '用户组', permission: 'system:user-groups', hasPermission, modal: groupModal,\n list: userGroupContract.list, detail: userGroupContract.detail, create: userGroupContract.create.input.shape.body, update: userGroupContract.update.input.shape.body,\n createFields: ['name', 'code', 'description', 'ownerId', 'roleIds', 'userIds', 'status', 'memberMode', 'memberRule'], updateFields: ['name', 'code', 'description', 'ownerId', 'roleIds', 'status', 'memberMode', 'memberRule', 'clearFields'], updateClearFields: ['description', 'ownerId', 'memberRule'],\n guardValues: assertWebMcpGroupMode,\n fields: ['id', 'name', 'code', 'description', 'ownerId', 'status', 'memberMode', 'memberRule', 'createdAt', 'updatedAt'], filters: page.filterQuery, selectedIds: selectedRowKeys,\n remove: id => deleteMutation.mutateAsync([id]), onDeleted: clearSelection, busy: memberSheetVisible || roleModalVisible,\n lookups: [{ name: 'users', title: '用户', operation: userContract.all, permission: 'system:user:list', fields: ['id', 'username', 'nickname'] },\n { name: 'roles', title: '角色', operation: roleContract.all, permission: 'system:role:list', fields: ['id', 'name', 'code'] },\n { name: 'departments', title: '部门', operation: departmentContract.flat, permission: 'system:department:list', fields: ['id', 'name', 'parentId'] },\n { name: 'positions', title: '岗位', operation: positionContract.all, permission: 'system:position:list', fields: ['id', 'name', 'code'] }] });\n const status = useStatusToggle<UserGroup>({\n toggle: (group, enabled) => toggleStatusMutation.mutateAsync({ id: group.id, values: { status: enabled ? 'enabled' : 'disabled' } }),\n confirmDisable: (group) => ({ danger: true, title: `确认禁用用户组「${group.name}」?`, content: '禁用后该用户组将不可选择。', okText: '确认禁用' }),\n disabled: !hasPermission('system:user-groups:update'),\n });\n const departmentTreeData = useMemo<TreeNodeData[]>(() => departmentsToTreeData(departments, { keepEmptyChildren: true }), [departments]);\n useEffect(() => {\n if (memberSheetVisible)\n setMemberIds((membersQuery.data ?? []).map((m) => m.id));\n }, [memberSheetVisible, membersQuery.data]);\n useEffect(() => {\n if (roleModalVisible)\n setRoleIds((groupRolesQuery.data ?? []).map((r) => r.id));\n }, [roleModalVisible, groupRolesQuery.data]);\n // 编辑弹窗关闭时清空规则预览,避免下次打开残留上一个组的结果\n useEffect(() => {\n if (!groupModal.modalProps.visible)\n setRulePreview(null);\n }, [groupModal.modalProps.visible]);\n const handleBatchDelete = () => {\n if (!selectedRowKeys.length)\n return;\n confirmAndDelete({\n title: `确认删除选中的 ${selectedRowKeys.length} 个用户组?`,\n content: '删除后无法恢复,请确认操作',\n run: () => deleteMutation.mutateAsync(selectedRowKeys),\n onDeleted: clearSelection,\n });\n };\n const openMembers = (group: UserGroup) => {\n setMemberGroup(group);\n setMemberSheetVisible(true);\n };\n const openRoles = (group: UserGroup) => {\n setRoleGroup(group);\n setRoleModalVisible(true);\n };\n const handleSaveRoles = async () => {\n if (!roleGroup)\n return;\n await assignRolesMutation.mutateAsync({ params: { id: roleGroup.id }, body: { roleIds } });\n Toast.success('角色已更新,组内成员即时生效');\n setRoleModalVisible(false);\n setRoleGroup(null);\n };\n const handleSaveMembers = async () => {\n if (!memberGroup)\n return;\n await assignMembersMutation.mutateAsync({ params: { id: memberGroup.id }, body: { userIds: memberIds } });\n setMemberSheetVisible(false);\n };\n const operationColumn = useCrudOperationColumn<UserGroup>({\n permission: 'system:user-groups',\n edit: (record) => { groupModal.openEdit(record); },\n remove: deleteMutation,\n title: '确定要删除该用户组吗?',\n extra: (record) => [\n {\n key: 'members',\n label: '成员',\n hidden: !hasPermission('system:user-groups:assign'),\n onClick: () => { openMembers(record); },\n },\n {\n key: 'roles',\n label: '角色',\n hidden: !hasPermission('system:user-groups:assign'),\n onClick: () => { openRoles(record); },\n },\n {\n key: 'sync',\n label: '同步',\n hidden: record.memberMode !== 'dynamic' || !hasPermission('system:user-groups:assign'),\n onClick: () => {\n syncMutation.mutate({ params: { id: record.id } }, { onSuccess: () => Toast.success('成员已按规则同步') });\n },\n },\n ],\n width: 240,\n desktopInlineKeys: ['members', 'roles', 'edit'],\n });\n const columns: ColumnProps<UserGroup>[] = [\n { title: '用户组名称', dataIndex: 'name', width: 200, render: renderEllipsis },\n { title: '编码', dataIndex: 'code', width: 180, render: renderEllipsis },\n {\n title: '描述', dataIndex: 'description',\n render: renderEllipsis,\n },\n {\n title: '负责人', dataIndex: 'ownerName', width: 120,\n render: (v: string | null | undefined) => v || EMPTY_PLACEHOLDER,\n },\n {\n title: '成员模式', dataIndex: 'memberMode', width: 110,\n render: (v: string, record: UserGroup) => v === 'dynamic'\n ? <Tag color=\"teal\">动态{record.ruleSyncedAt ? '' : '(未同步)'}</Tag>\n : <Tag color=\"grey\">静态</Tag>,\n },\n memberPreviewColumn<UserGroup>({\n dataIndex: 'memberPreview',\n width: 150,\n getPreview: (record) => record.memberPreview,\n getCount: (record) => record.memberCount,\n getScope: (record) => ({ type: 'userGroup', id: record.id, name: record.name }),\n }),\n overflowTagColumn<UserGroup>({\n title: '角色',\n dataIndex: 'rolePreview',\n width: 260,\n contentWidth: 228,\n getItems: (roles) => (roles as UserGroup['rolePreview'] ?? []).map((role) => ({ key: String(role.id), label: role.name })),\n tagColor: 'blue',\n popoverWidth: 240,\n empty: <Tag color=\"grey\">无角色</Tag>,\n onClick: hasPermission('system:user-groups:assign') ? openRoles : undefined,\n }),\n createdAtColumn,\n status.column(),\n operationColumn,\n ];\n return (<div className=\"page-container\">\n\n\n <ConfigurableTable<UserGroup> searchConfig={buildTableSearch({\n page: (page),\n filters: (['keyword', 'status']),\n create: (<CreateButton permission=\"system:user-groups:create\" onClick={groupModal.openCreate}/>),\n actions: (selectedRowKeys.length > 0 && hasPermission('system:user-groups:delete') && <BatchDeleteButton count={selectedRowKeys.length} onClick={handleBatchDelete}/>),\n filterTitle: \"用户组筛选\",\n actionTitle: \"用户组操作\"\n })} columns={columns} {...tableProps}/>\n\n <EditFormSheet modal={groupModal} width={520} okText=\"确定\">\n {({ formState }) => {\n const memberMode = (formState.values as {\n memberMode?: string;\n }).memberMode ?? 'static';\n return (<>\n <Form.Input field=\"name\" label=\"名称\" placeholder=\"请输入用户组名称\" rules={[{ required: true, message: '请输入用户组名称' }]}/>\n <Form.Input field=\"code\" label=\"编码\" placeholder=\"字母数字下划线\" rules={[\n { required: true, message: '请输入用户组编码' },\n { pattern: /^\\w+$/, message: '编码只能包含字母、数字和下划线' },\n ]}/>\n <Form.Select field=\"ownerId\" label=\"负责人\" placeholder=\"请选择负责人(可选)\" style={{ width: '100%' }} filter showClear optionList={allUsers.map(u => ({ value: u.id, label: `${u.nickname} (${u.username})` }))}/>\n <Form.Select field=\"status\" label=\"状态\" style={{ width: '100%' }} optionList={statusOptions}/>\n <Form.Select field=\"roleIds\" label=\"角色\" placeholder=\"请选择角色(可选)\" style={{ width: '100%' }} multiple filter showClear optionList={(allRolesQuery.data ?? []).map((role) => ({ value: role.id, label: `${role.name}(${role.code})` }))}/>\n {!groupModal.editing && memberMode === 'static' && (<Form.Select field=\"userIds\" label=\"成员\" placeholder=\"请选择成员(可选)\" style={{ width: '100%' }} multiple filter showClear optionList={allUsers.map((user) => ({ value: user.id, label: `${user.nickname}(${user.username})` }))}/>)}\n <Form.RadioGroup field=\"memberMode\" label=\"成员模式\" type=\"button\" extraText={memberMode === 'dynamic'\n ? '成员由规则自动维护:用户的部门/岗位/状态变化后自动加入或退出,不可手工增删'\n : '成员由管理员手工维护'}>\n <Form.Radio value=\"static\">静态(手工维护)</Form.Radio>\n <Form.Radio value=\"dynamic\">动态(按规则自动维护)</Form.Radio>\n </Form.RadioGroup>\n {memberMode === 'dynamic' && (<div style={{\n border: '1px solid var(--arc-antd-color-border)',\n borderRadius: 'var(--arc-antd-border-radius-medium)',\n padding: '4px 16px 16px',\n margin: '12px 0',\n }}>\n <Form.TreeSelect field=\"memberRule.departmentIds\" label=\"命中部门\" placeholder=\"选择部门(可多选)\" style={{ width: '100%' }} multiple filterTreeNode showClear leafOnly={false} treeData={departmentTreeData}/>\n <Form.Switch field=\"memberRule.includeSubDepartments\" label=\"包含子部门\" checkedText=\"是\" uncheckedText=\"否\"/>\n <Form.Select field=\"memberRule.positionIds\" label=\"命中岗位\" placeholder=\"选择岗位(可多选)\" extraText=\"多个岗位满足任一即可;与部门条件同时设置时须同时满足\" style={{ width: '100%' }} multiple filter showClear optionList={(positionsQuery.data ?? []).map((p) => ({ value: p.id, label: p.name }))}/>\n <Form.Select field=\"memberRule.includeUserIds\" label=\"强制包含\" placeholder=\"选择用户(可选)\" extraText=\"规则之外的例外成员\" style={{ width: '100%' }} multiple filter showClear optionList={allUsers.map(u => ({ value: u.id, label: `${u.nickname} (${u.username})` }))}/>\n <Form.Select field=\"memberRule.excludeUserIds\" label=\"强制排除\" placeholder=\"选择用户(可选)\" extraText=\"优先级最高,命中规则也不会加入\" style={{ width: '100%' }} multiple filter showClear optionList={allUsers.map(u => ({ value: u.id, label: `${u.nickname} (${u.username})` }))}/>\n <Space vertical align=\"start\" spacing={8} style={{ width: '100%', marginTop: 12 }}>\n <Button theme=\"light\" loading={rulePreviewMutation.isPending} onClick={() => {\n const memberRule = ((formState.values as {\n memberRule?: UserGroupMemberRule;\n }).memberRule ?? {});\n rulePreviewMutation.mutate({ body: { groupId: groupModal.editing?.id, memberRule } }, { onSuccess: setRulePreview });\n }}>\n 预览成员变化\n </Button>\n {rulePreview && (<div style={{ fontSize: 12, lineHeight: '20px', width: '100%' }}>\n <Typography.Text type=\"secondary\">\n 目标成员 {rulePreview.total} 人:新加入 {rulePreview.joiningCount} 人,移除 {rulePreview.leavingCount} 人\n </Typography.Text>\n {rulePreview.joining.length > 0 && (<div style={{ marginTop: 4 }}>\n <Typography.Text type=\"success\">加入:</Typography.Text>\n <Space wrap spacing={4}>\n {rulePreview.joining.map((u) => <Tag key={u.id} size=\"small\" color=\"green\">{u.nickname}</Tag>)}\n {rulePreview.joiningCount > rulePreview.joining.length && <Tag size=\"small\">+{rulePreview.joiningCount - rulePreview.joining.length}</Tag>}\n </Space>\n </div>)}\n {rulePreview.leaving.length > 0 && (<div style={{ marginTop: 4 }}>\n <Typography.Text type=\"danger\">移除:</Typography.Text>\n <Space wrap spacing={4}>\n {rulePreview.leaving.map((u) => <Tag key={u.id} size=\"small\" color=\"red\">{u.nickname}</Tag>)}\n {rulePreview.leavingCount > rulePreview.leaving.length && <Tag size=\"small\">+{rulePreview.leavingCount - rulePreview.leaving.length}</Tag>}\n </Space>\n </div>)}\n </div>)}\n </Space>\n </div>)}\n <Form.TextArea field=\"description\" label=\"描述\" placeholder=\"请输入描述(可选)\" maxCount={256}/>\n </>);\n }}\n </EditFormSheet>\n\n {memberGroup?.memberMode === 'dynamic' ? (<SideSheet title={<Space>\n <Users size={16}/>\n <span>成员管理 - {memberGroup?.name}</span>\n <Tag color=\"teal\" size=\"small\">动态</Tag>\n </Space>} visible={memberSheetVisible} onCancel={() => setMemberSheetVisible(false)} width={720} footer={(<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>\n <Button onClick={() => setMemberSheetVisible(false)}>关闭</Button>\n {hasPermission('system:user-groups:assign') && (<Button type=\"primary\" icon={<RefreshCw size={14}/>} loading={syncMutation.isPending} onClick={() => {\n if (!memberGroup)\n return;\n syncMutation.mutate({ params: { id: memberGroup.id } }, { onSuccess: () => Toast.success('成员已按规则同步') });\n }}>\n 立即同步\n </Button>)}\n </div>)}>\n <Space vertical align=\"start\" spacing={12} style={{ width: '100%' }}>\n <Banner fullMode={false} type=\"info\" closeIcon={null} description={`成员由规则自动维护,不可手工增删;需要例外时请在编辑规则中使用强制包含/排除名单。${memberGroup.ruleSyncedAt ? '最近同步:' + memberGroup.ruleSyncedAt : '尚未同步'}`} style={{ width: '100%' }}/>\n <Spin spinning={membersQuery.isFetching} style={{ width: '100%' }}>\n {(membersQuery.data ?? []).length === 0 ? (<Empty title=\"暂无成员\" description=\"当前没有用户命中该组规则\" style={{ padding: '32px 0' }}/>) : (<Space wrap spacing={6}>\n {(membersQuery.data ?? []).map((m) => (<Tag key={m.id} size=\"large\">{m.nickname}({m.username})</Tag>))}\n </Space>)}\n </Spin>\n </Space>\n </SideSheet>) : (<MemberAssignmentSheet title={`成员管理 - ${memberGroup?.name ?? ''}`} visible={memberSheetVisible} onCancel={() => setMemberSheetVisible(false)} users={allUsers} value={memberIds} onChange={setMemberIds} departments={departments} canSave={membersQuery.isSuccess} saveLoading={assignMembersMutation.isPending} onSave={handleSaveMembers}/>)}\n\n <AppModal title={`分配角色 — ${roleGroup?.name ?? ''}`} visible={roleModalVisible} onCancel={() => { setRoleModalVisible(false); setRoleGroup(null); }} onOk={handleSaveRoles} okButtonProps={{ disabled: !groupRolesQuery.isSuccess }} confirmLoading={assignRolesMutation.isPending} width={480}>\n <Spin spinning={groupRolesQuery.isFetching || allRolesQuery.isFetching}>\n <div style={{ marginBottom: 8, fontSize: 12, color: 'var(--arc-antd-color-text-2)' }}>\n 组内成员将自动继承所选角色的菜单与数据权限(与成员直接分配的角色取并集)\n </div>\n <Select multiple filter placeholder=\"请选择角色\" style={{ width: '100%' }} value={roleIds} onChange={(v) => setRoleIds((v as number[]) ?? [])} optionList={(allRolesQuery.data ?? []).map((r) => ({ value: r.id, label: `${r.name}(${r.code})` }))}/>\n </Spin>\n </AppModal>\n </div>);\n}\n"]}
@@ -1,4 +1,4 @@
1
- import{n as e}from"./rolldown-runtime-Bruge11p.js";import{Ct as t,M as n,T as r,bt as i,dt as a,et as o,it as s,m as c,nt as l,ot as u,rt as d,st as f,tt as p,ut as m,xt as h}from"./clipboard-dpqHdCyy.js";import{i as g}from"./feedback-B1UL2ETz.js";import{a as _,o as v,s as y}from"./toolbar-controls-CrM8hKZg.js";import{n as b,t as x}from"./circle-check-big-BacE3WpZ.js";import{n as ee,t as te}from"./OperationLogsTable-gp-WZxiF.js";import{n as S,t as ne}from"./rotate-cw-BBq-LUa4.js";import{n as re,t as ie}from"./rendered-slot-G6nvF0nR.js";import{n as ae,t as oe}from"./SearchToolbar-KtXD_Wgr.js";import{n as C,t as w}from"./AppModal-CVoUC2t3.js";import{n as se,t as ce}from"./LoginLogsTable-DYcTx2oR.js";import{useEffect as le,useMemo as ue,useRef as T,useState as E}from"react";import{Fragment as D,jsx as O,jsxs as k}from"react/jsx-runtime";import{enumValueOf as A}from"@fudanda/client/core";import{useRenderedSlot as de}from"@fudanda/admin-antd/core/hooks/useRenderedSlot";import{useFilterQuery as j}from"@fudanda/admin-antd/core/hooks/useFilterQuery";import{useListSearch as M}from"@fudanda/admin-antd/core/hooks/useListSearch";import{formatDateTimeRangeForApi as N}from"@fudanda/admin-antd/core/utils/date";import{LOGIN_EVENT_TYPES as fe,LOGIN_EVENT_TYPE_OPTIONS as pe,LOGIN_STATUSES as me,LOGIN_STATUS_OPTIONS as he}from"@fudanda/client/identity";import{profileKeys as P,useProfileLoginLogs as ge,useProfileOperationLogs as _e}from"@fudanda/admin-antd/core/hooks/queries/profile";import{operationLogKeys as ve,useOperationLogList as ye}from"@fudanda/admin-antd/core/hooks/queries/operation-logs";import{loginLogKeys as be,useLoginLogList as xe}from"@fudanda/admin-antd/core/hooks/queries/login-logs";import{OPERATION_LOG_RESULTS as Se,OPERATION_LOG_RESULT_OPTIONS as Ce}from"@fudanda/client/platform";var F,I;function L(){return(L=e((()=>{
1
+ import{n as e}from"./rolldown-runtime-Bruge11p.js";import{Ct as t,M as n,T as r,bt as i,dt as a,et as o,it as s,m as c,nt as l,ot as u,rt as d,st as f,tt as p,ut as m,xt as h}from"./clipboard-dpqHdCyy.js";import{i as g}from"./feedback-B1UL2ETz.js";import{a as _,o as v,s as y}from"./toolbar-controls-CrM8hKZg.js";import{n as b,t as x}from"./circle-check-big-BacE3WpZ.js";import{n as ee,t as te}from"./OperationLogsTable-DaaON4QR.js";import{n as S,t as ne}from"./rotate-cw-BBq-LUa4.js";import{n as re,t as ie}from"./rendered-slot-G6nvF0nR.js";import{n as ae,t as oe}from"./SearchToolbar-KtXD_Wgr.js";import{n as C,t as w}from"./AppModal-CEouaU03.js";import{n as se,t as ce}from"./LoginLogsTable-CULcc_e8.js";import{useEffect as le,useMemo as ue,useRef as T,useState as E}from"react";import{Fragment as D,jsx as O,jsxs as k}from"react/jsx-runtime";import{enumValueOf as A}from"@fudanda/client/core";import{useRenderedSlot as de}from"@fudanda/admin-antd/core/hooks/useRenderedSlot";import{useFilterQuery as j}from"@fudanda/admin-antd/core/hooks/useFilterQuery";import{useListSearch as M}from"@fudanda/admin-antd/core/hooks/useListSearch";import{formatDateTimeRangeForApi as N}from"@fudanda/admin-antd/core/utils/date";import{LOGIN_EVENT_TYPES as fe,LOGIN_EVENT_TYPE_OPTIONS as pe,LOGIN_STATUSES as me,LOGIN_STATUS_OPTIONS as he}from"@fudanda/client/identity";import{profileKeys as P,useProfileLoginLogs as ge,useProfileOperationLogs as _e}from"@fudanda/admin-antd/core/hooks/queries/profile";import{operationLogKeys as ve,useOperationLogList as ye}from"@fudanda/admin-antd/core/hooks/queries/operation-logs";import{loginLogKeys as be,useLoginLogList as xe}from"@fudanda/admin-antd/core/hooks/queries/login-logs";import{OPERATION_LOG_RESULTS as Se,OPERATION_LOG_RESULT_OPTIONS as Ce}from"@fudanda/client/platform";var F,I;function L(){return(L=e((()=>{
2
2
  /**
3
3
  * @license lucide-react v1.67.0 - ISC
4
4
  *
@@ -27,4 +27,4 @@ h(),V={name:`zoom-in`,size:24,node:[[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u
27
27
  * See the LICENSE file in the root directory of this source tree.
28
28
  */
29
29
  h(),U={name:`zoom-out`,size:24,node:[[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u`}],[`line`,{x1:`21`,x2:`16.65`,y1:`21`,y2:`16.65`,key:`13gj7c`}],[`line`,{x1:`8`,x2:`14`,y1:`11`,y2:`11`,key:`durymu`}]]},U.node,W=i(U)})))()}function K(e){let{create:t,actions:n,mobileActions:r,filterTitle:i,actionTitle:a,className:s}=e,c,l,u,d;if(e.page){let t=o({page:e.page,specs:e.filters,overrides:e.overrides});c=t.keyword.length?t.keyword:void 0,l=t.filters.length||e.extraFilters?/* @__PURE__ */ k(D,{children:[t.filters,e.extraFilters]}):void 0,{onSearch:u,onReset:d}=e.page.toolbarProps}else({keyword:c,filters:l,onSearch:u,onReset:d}=e);let f=de(),p=q(t)||q(n)?/* @__PURE__ */ k(D,{children:[t,/* @__PURE__ */ O(ie,{ref:f.probeRef,children:n})]}):void 0;return/* @__PURE__ */ O(oe,{className:s,primary:/* @__PURE__ */ k(D,{children:[c,l,/* @__PURE__ */ O(v,{onClick:u}),/* @__PURE__ */ O(_,{onClick:d})]}),actions:p,mobilePrimary:/* @__PURE__ */ k(D,{children:[c,/* @__PURE__ */ O(v,{onClick:u}),t]}),mobileFilters:q(l)?l:void 0,mobileActions:r??(f.rendered?n:!1),filterTitle:i,actionTitle:a,onFilterApply:u,onFilterReset:d})}var q;function J(){return(J=e((()=>{ae(),y(),re(),p(),q=e=>!!e&&!(Array.isArray(e)&&e.length===0)})))()}function Te(e){if(!Number.isFinite(e))return null;let t=Number((Math.round(e/Z)*Z).toFixed(2));return Math.min(X,Math.max(Y,t))}function Ee({file:e,confirmLoading:n,onCancel:i,onConfirm:a}){let o=T(null),s=T(``),[l,u]=E(``),[d,f]=E(0),[p,h]=E(1),g=e=>{s.current.startsWith(`blob:`)&&URL.revokeObjectURL(s.current),s.current=e,u(e)};le(()=>{if(f(0),h(1),!e)g(``);else return g(URL.createObjectURL(e)),()=>{g(``)}},[e]);function _(e){f(t=>((t+e)%360+360)%360)}function v(e){if(e===void 0)return;let t=Te(typeof e==`number`?e:e[0]);t!==null&&h(t)}function y(e){h(t=>Te(Number((t+e).toFixed(2)))??t)}function b(){let e=o.current?.getCropperCanvas();e&&e.toBlob(e=>{e&&a(e)},`image/jpeg`,.85)}return/* @__PURE__ */ k(w,{title:`裁剪头像`,visible:e!==null,onCancel:i,footer:/* @__PURE__ */ k(r,{children:[/* @__PURE__ */ O(c,{onClick:i,children:`取消`}),/* @__PURE__ */ O(c,{type:`primary`,loading:n,onClick:b,children:`确认并上传`})]}),width:520,centered:!0,children:[/* @__PURE__ */ O(`div`,{style:{width:`100%`,height:380},children:l&&/* @__PURE__ */ O(t,{ref:o,src:l,shape:`round`,aspectRatio:1,showResizeBox:!0,rotate:d,zoom:p,minZoom:Y,maxZoom:X,zoomStep:Z,onZoomChange:v,style:{width:`100%`,height:`100%`}})}),/* @__PURE__ */ k(`div`,{style:{display:`flex`,justifyContent:`center`,gap:12,marginTop:12},children:[/* @__PURE__ */ O(c,{icon:/* @__PURE__ */ O(m,{size:14}),size:`small`,theme:`borderless`,onClick:()=>_(-90),children:`向左旋转`}),/* @__PURE__ */ O(c,{icon:/* @__PURE__ */ O(ne,{size:14}),size:`small`,theme:`borderless`,onClick:()=>_(90),children:`向右旋转`})]}),/* @__PURE__ */ k(`div`,{style:{display:`flex`,alignItems:`center`,justifyContent:`center`,gap:12,marginTop:8},children:[/* @__PURE__ */ O(c,{icon:/* @__PURE__ */ O(W,{size:14}),size:`small`,theme:`borderless`,"aria-label":`缩小`,disabled:p<=Y,onClick:()=>y(-.1),children:`缩小`}),/* @__PURE__ */ k(`span`,{style:{fontSize:12,color:`var(--arc-antd-color-text-2)`,minWidth:44,textAlign:`center`},children:[p.toFixed(1),`x`]}),/* @__PURE__ */ O(c,{icon:/* @__PURE__ */ O(we,{size:14}),size:`small`,theme:`borderless`,"aria-label":`放大`,disabled:p>=X,onClick:()=>y(Z),children:`放大`})]})]})}var Y,X,Z;function De(){return(De=e((()=>{n(),a(),S(),H(),G(),C(),Y=.1,X=3,Z=.1})))()}function Oe({onUploaded:e,uploadBlob:t,maxSizeMB:n=2}){let r=T(null),[i,a]=E(null),[o,s]=E(!1);function c(e){let t=e.target.files?.[0];t&&(e.target.value=``,t.type.startsWith(`image/`)?t.size>n*1024*1024?g.warning(`图片不能超过 ${n}MB`):a(t):g.warning(`请选择图片文件`))}async function l(n){let r;s(!0);try{r=await t(n)}catch(e){g.error(e instanceof Error&&e.message?e.message:`上传失败`);return}finally{s(!1)}await e(r)!==!1&&a(null)}return{cropFile:i,uploading:o,openFilePicker:()=>r.current?.click(),fileInputProps:{ref:r,type:`file`,accept:`image/*`,style:{display:`none`},onChange:c},cropperProps:{file:i,onCancel:()=>a(null),onConfirm:e=>{l(e)}}}}function Q(){return(Q=e((()=>{n()})))()}function ke({visible:e,currentAvatar:t,confirmLoading:n,uploadBlob:r,onCancel:i,onSelect:a,onRemove:o}){let s=Oe({uploadBlob:r,onUploaded:e=>{a(e)}}),l=s.uploading||(n??!1);return/* @__PURE__ */ k(D,{children:[/* @__PURE__ */ O(w,{title:`更换头像`,visible:e,onCancel:i,footer:null,width:480,centered:!0,children:/* @__PURE__ */ k(`div`,{children:[/* @__PURE__ */ O(`div`,{style:{fontSize:13,color:`var(--arc-antd-color-text-2)`,marginBottom:8},children:`本地上传(≤ 2MB,上传后可裁剪、旋转、缩放)`}),/* @__PURE__ */ k(`div`,{style:{display:`flex`,gap:8},children:[/* @__PURE__ */ O(c,{icon:/* @__PURE__ */ O(I,{size:14}),loading:s.uploading,onClick:s.openFilePicker,children:`选择图片`}),t&&o&&/* @__PURE__ */ O(c,{theme:`borderless`,type:`danger`,onClick:o,children:`移除头像`})]}),/* @__PURE__ */ O(`input`,{...s.fileInputProps})]})}),/* @__PURE__ */ O(Ee,{...s.cropperProps,confirmLoading:l})]})}function Ae(){return(Ae=e((()=>{n(),L(),C(),De(),Q()})))()}function je(e){if(!e)return 0;if(e.length<6)return 1;let t=0;return/[a-z]/.test(e)&&t++,/[A-Z]/.test(e)&&t++,/\d/.test(e)&&t++,/[^a-zA-Z0-9]/.test(e)&&t++,e.length>=10&&t++,t<=1?1:t===2?2:t===3?3:4}function Me({password:e,policy:t}){let n=ue(()=>je(e),[e]);if(!e)return null;let{label:r,color:i}=Ne[n],a=t?[...t.requireUppercase?[{label:`含大写字母`,ok:/[A-Z]/.test(e)}]:[],...t.requireSpecialChar?[{label:`含特殊字符`,ok:/[^a-zA-Z0-9]/.test(e)}]:[]]:null,o=t?e.length>=t.minLength:null,s=t?`至少 ${t.minLength} 位`:null;return/* @__PURE__ */ k(`div`,{style:{padding:`4px 0`},children:[/* @__PURE__ */ k(`div`,{style:{display:`flex`,alignItems:`center`,gap:8},children:[/* @__PURE__ */ O(`div`,{style:{display:`flex`,gap:3,flex:1},children:[1,2,3,4].map(e=>/* @__PURE__ */ O(`div`,{style:{flex:1,height:4,borderRadius:`var(--arc-antd-border-radius-small)`,background:e<=n?i:`var(--arc-antd-color-fill-2)`,transition:`background 0.25s ease`}},e))}),/* @__PURE__ */ O(`span`,{style:{fontSize:12,fontWeight:600,color:i,minWidth:28,textAlign:`right`},children:r}),s!==null&&/* @__PURE__ */ k(`div`,{style:{display:`flex`,alignItems:`center`,gap:3,flexShrink:0},children:[o?/* @__PURE__ */ O(x,{size:11,style:{flexShrink:0,color:`var(--arc-antd-color-success)`}}):/* @__PURE__ */ O(z,{size:11,style:{flexShrink:0,color:`var(--arc-antd-color-text-3)`}}),/* @__PURE__ */ O(`span`,{style:{fontSize:12,color:o?`var(--arc-antd-color-success)`:`var(--arc-antd-color-text-3)`,transition:`color 0.2s`},children:s})]})]}),a&&a.length>0&&/* @__PURE__ */ O(`div`,{style:{display:`flex`,flexWrap:`wrap`,gap:`3px 14px`,marginTop:5},children:a.map(({label:e,ok:t})=>/* @__PURE__ */ k(`div`,{style:{display:`flex`,alignItems:`center`,gap:4,fontSize:12,color:t?`var(--arc-antd-color-success)`:`var(--arc-antd-color-text-3)`,transition:`color 0.2s`},children:[O(t?x:z,{size:11,style:{flexShrink:0}}),/* @__PURE__ */ O(`span`,{children:e})]},e))})]})}var Ne;function Pe(){return(Pe=e((()=>{b(),B(),Ne={0:{label:``,color:`transparent`},1:{label:`弱`,color:`var(--arc-antd-color-danger)`},2:{label:`一般`,color:`var(--arc-antd-color-warning)`},3:{label:`良好`,color:`var(--arc-antd-color-info)`},4:{label:`强`,color:`var(--arc-antd-color-success)`}}})))()}function Fe(e,t,n){let r=n.kind===`user`?n.userId:void 0;return ue(()=>({listKey:r===void 0?e:[...t,`user`,r],extraKeys:r===void 0?void 0:[t]}),[e,t,r])}function Ie({scope:e,enabled:t=!0,columnSettingsKey:n}){let r=e.kind===`self`,{listKey:i,extraKeys:a}=Fe(P.loginLogs,be.lists,e),{page:o,pageSize:s,buildPagination:c,bind:f,submittedParams:p,handleSearch:m,handleReset:h}=M({defaults:{eventType:void 0,status:void 0,timeRange:null},listKey:i,extraKeys:a}),g={page:o,pageSize:s,...j({eventType:A(fe,p.eventType),status:A(me,p.status),...N(p.timeRange)})},_=ge(g,t&&r),v=xe({...g,userId:e.kind===`user`?e.userId:void 0},t&&!r),y=r?_:v,b=y.data?.list??[],x=y.data?.total??0;return/* @__PURE__ */ k(D,{children:[/* @__PURE__ */ O(K,{filters:/* @__PURE__ */ k(D,{children:[/* @__PURE__ */ O(d,{placeholder:`全部事件`,items:pe,...f(`eventType`)}),/* @__PURE__ */ O(u,{items:he,...f(`status`)}),/* @__PURE__ */ O(l,{...f(`timeRange`)})]}),onSearch:m,onReset:h,filterTitle:`登录记录筛选`}),/* @__PURE__ */ O(ce,{loading:y.isFetching,dataSource:b,onRefresh:()=>void y.refetch(),columnSettingsKey:n,pagination:c(x)})]})}function Le({scope:e,enabled:t=!0,columnSettingsKey:n}){let r=e.kind===`self`,{listKey:i,extraKeys:a}=Fe(P.operationLogs,ve.lists,e),{page:o,pageSize:c,buildPagination:f,bind:p,bindKeyword:m,submittedParams:h,handleSearch:g,handleReset:_}=M({defaults:{module:``,description:``,method:void 0,path:``,ip:``,status:void 0,content:``,impersonated:void 0,timeRange:null},listKey:i,extraKeys:a}),v={page:o,pageSize:c,...j({module:h.module,description:h.description,method:h.method,path:h.path,ip:h.ip,status:A(Se,h.status),content:h.content,impersonated:h.impersonated===`true`||h.impersonated!==`false`&&void 0,...N(h.timeRange)})},y=_e(v,t&&r),b=ye({...v,userId:e.kind===`user`?e.userId:void 0},t&&!r),x=r?y:b,ee=x.data?.list??[],S=x.data?.total??0;return/* @__PURE__ */ k(D,{children:[/* @__PURE__ */ O(K,{filters:/* @__PURE__ */ k(D,{children:[/* @__PURE__ */ O(s,{placeholder:`请输入功能模块`,...m(`module`),width:160}),/* @__PURE__ */ O(s,{placeholder:`请输入操作描述`,...m(`description`),width:160}),/* @__PURE__ */ O(d,{placeholder:`全部请求方法`,items:Re,...p(`method`),width:140}),/* @__PURE__ */ O(s,{placeholder:`请输入请求路径`,...m(`path`),width:180}),/* @__PURE__ */ O(s,{placeholder:`请输入 IP 地址`,...m(`ip`),width:160}),/* @__PURE__ */ O(s,{placeholder:`请求/变更内容包含…`,...m(`content`),width:180}),/* @__PURE__ */ O(u,{items:Ce,...p(`status`)}),/* @__PURE__ */ O(d,{placeholder:`全部操作来源`,items:ze,...p(`impersonated`),width:140}),/* @__PURE__ */ O(l,{...p(`timeRange`)})]}),onSearch:g,onReset:_,filterTitle:`操作记录筛选`}),/* @__PURE__ */ O(te,{loading:x.isFetching,dataSource:ee,onRefresh:()=>void x.refetch(),columnSettingsKey:n,pagination:f(S)})]})}var Re,ze;function $(){return($=e((()=>{f(),J(),se(),ee(),Re=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`].map(e=>({value:e,label:e})),ze=[{value:`true`,label:`仅模拟操作`},{value:`false`,label:`仅本人操作`}]})))()}export{Pe as a,Me as i,Le as n,ke as o,$ as r,Ae as s,Ie as t};
30
- //# sourceMappingURL=UserLogsPanels-3u8Z93YU.js.map
30
+ //# sourceMappingURL=UserLogsPanels-nbIrMym9.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserLogsPanels-3u8Z93YU.js","mappings":";;;;;;;IASMA,EAAa,CACjB,KAAM,SACN,KAAM,GACN,KAAM,CACJ,CACE,OACA,CACE,EAAG,qLACH,IAAK,QACP,CACF,EACA,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,QAAS,CAAC,CAC1D,CACF,EACAC,EAAW,KACL,EAASC,EAAiBF,CAAU,C;;;;;;;ICfpCA,EAAa,CACjB,KAAM,SACN,KAAM,GACN,KAAM,CAAC,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,KAAM,IAAK,QAAS,CAAC,CAAC,CACnE,EACAG,EAAW,KACL,EAASD,EAAiBF,CAAU,C;;;;;;;ICNpCA,EAAa,CACjB,KAAM,UACN,KAAM,GACN,KAAM,CACJ,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,QAAS,CAAC,EACxD,CAAC,OAAQ,CAAE,GAAI,KAAM,GAAI,QAAS,GAAI,KAAM,GAAI,QAAS,IAAK,QAAS,CAAC,EACxE,CAAC,OAAQ,CAAE,GAAI,KAAM,GAAI,KAAM,GAAI,IAAK,GAAI,KAAM,IAAK,QAAS,CAAC,EACjE,CAAC,OAAQ,CAAE,GAAI,IAAK,GAAI,KAAM,GAAI,KAAM,GAAI,KAAM,IAAK,QAAS,CAAC,CACnE,CACF,EACAI,EAAW,KACL,GAASF,EAAiBF,CAAU,C;;;;;;;ICXpC,EAAa,CACjB,KAAM,WACN,KAAM,GACN,KAAM,CACJ,CAAC,SAAU,CAAE,GAAI,KAAM,GAAI,KAAM,EAAG,IAAK,IAAK,QAAS,CAAC,EACxD,CAAC,OAAQ,CAAE,GAAI,KAAM,GAAI,QAAS,GAAI,KAAM,GAAI,QAAS,IAAK,QAAS,CAAC,EACxE,CAAC,OAAQ,CAAE,GAAI,IAAK,GAAI,KAAM,GAAI,KAAM,GAAI,KAAM,IAAK,QAAS,CAAC,CACnE,CACF,EACAA,EAAW,KACL,EAAUE,EAAiBF,CAAU,C,OCoC3C,SAAgBK,EAAqD,EAAwC,CACzG,GAAM,CAAE,SAAQ,UAAS,gBAAe,cAAa,cAAa,aAAcC,EAC5EC,EACAC,EACAC,EACAC,EACJ,GAAIJ,EAAM,KAAM,CACZ,IAAMK,EAAUC,EAAqB,CAAE,KAAMN,EAAM,KAAM,MAAOA,EAAM,QAAS,UAAWA,EAAM,SAAU,CAAC,EAC3G,EAAUK,EAAQ,QAAQ,OAASA,EAAQ,QAAU,OACrD,EAAUA,EAAQ,QAAQ,QAAUL,EAAM,aAAe,kBAAAO,EAAA,WAAGF,EAAQ,QAASL,EAAM,YAAe,IAAI,OACrG,uBAAwBA,EAAM,KAAK,YACxC,MAEK,2CAA0CA,GAG/C,IAAMQ,EAAcC,GAAgB,EAC9BC,EAAiBC,EAAQC,CAAM,GAAKD,EAAQE,CAAO,EACnD,kBAAAN,EAAA,WAAGK,EAAO,kBAACE,GAAD,CAAW,IAAKN,EAAY,SAAW,SAAAK,CAAmB,EAAG,IACvE,OACN,MAAQ,kBAACE,GAAD,CAA0B,YAAW,QAAU,kBAAAR,EAAA,WAChDN,EACAC,EACD,kBAACc,EAAD,CAAc,QAASb,CAAU,GACjC,kBAACc,EAAD,CAAa,QAASb,CAAS,EAC/B,IAAI,QAASM,EAAgB,cAAgB,kBAAAH,EAAA,WAC5CN,EACD,kBAACe,EAAD,CAAc,QAASb,CAAU,GAChCS,CACD,IAAI,cAAeD,EAAQT,CAAO,EAAIA,EAAU,OAGtD,cAAegB,IAAkBV,EAAY,SAAWK,EAAU,IAAqB,cAA0B,cAAa,cAAeV,EAAU,cAAeC,CAAS,EACnL,C,sDAxCM,EAAW,GAAoB,EAAQe,GAAS,EAAE,MAAM,QAAQA,CAAI,GAAKA,EAAK,SAAW,E,OCrC/F,SAASC,GAAc,EAA8B,CACjD,GAAI,CAAC,OAAO,SAASC,CAAK,EACtB,OAAO,KACX,IAAMC,EAAU,QAAQ,KAAK,MAAMD,EAAQE,CAAS,EAAIA,EAAA,CAAW,QAAQ,CAAC,CAAC,EAC7E,OAAO,KAAK,IAAIC,EAAU,KAAK,IAAIC,EAAUH,CAAO,CAAC,CACzD,CAcA,SAAgBI,GAAmB,CAAE,OAAM,iBAAgB,WAAU,aAAsC,CACvG,IAAMC,EAAaC,EAAgB,IAAI,EACjCC,EAASD,EAAO,EAAE,EAClB,CAACE,EAAKC,GAAUC,EAAS,EAAE,EAC3B,CAACC,EAAQC,GAAaF,EAAS,CAAC,EAChC,CAACG,EAAMC,GAAWJ,EAAS,CAAC,EAE5BK,EAAa,GAAiB,CAC5BR,EAAO,QAAQ,WAAW,OAAO,GACjC,IAAI,gBAAgBA,EAAO,OAAO,EACtC,EAAO,QAAUS,EACjBP,EAAOO,CAAI,CACf,EACAC,OAAgB,CAGZ,GAFAL,EAAU,CAAC,EACXE,EAAQ,CAAC,EACL,CAACI,EACDH,EAAU,EAAE,OAIhB,OADAA,EAAU,IAAI,gBAAgBG,CAAI,CAAC,MACtB,CACTH,EAAU,EAAE,CAChB,CACJ,EAAG,CAACG,CAAI,CAAC,EACT,SAASC,EAAa,EAAe,CACjCP,EAAW,KAAYQ,EAAOC,GAAS,IAAO,KAAO,GAAG,CAC5D,CACA,SAASC,EAAiB,EAAsC,CAC5D,GAAIvB,IAAU,OACV,OACJ,IAAMiB,EAAOlB,GAAc,OAAOC,GAAU,SAAWA,EAAQA,EAAM,EAAE,EACnEiB,IAAS,MACTF,EAAQE,CAAI,CACpB,CACA,SAASO,EAAe,EAAe,CACnCT,EAAS,GAAShB,GAAc,QAAQsB,EAAOC,EAAA,CAAO,QAAQ,CAAC,CAAC,CAAC,GAAKD,CAAI,CAC9E,CACA,SAASI,GAAgB,CACrB,IAAMC,EAASpB,EAAW,SAAS,iBAAiB,EAC/CoB,GAELA,EAAO,OAAQ,GAAS,CACfC,GAELC,EAAUD,CAAI,CAClB,EAAG,aAAc,GAAI,CACzB,CACA,MAAQ,kBAACE,EAAD,CAAU,MAAM,OAAO,QAASV,IAAS,KAAgB,WAAU,OAAQ,kBAACW,EAAD,WAC7E,kBAACC,EAAD,CAAQ,QAASC,EAAU,aAAU,GACrC,kBAACD,EAAD,CAAQ,KAAK,UAAU,QAASE,EAAgB,QAASR,EAAe,gBAAa,EAChF,IAAG,MAAO,IAAK,YAHlB,UAIN,kBAAC,MAAD,CAAK,MAAO,CAAE,MAAO,OAAQ,OAAQ,GAAI,EACtC,SAAAhB,GAAQ,kBAACyB,EAAD,CAAS,IAAK5B,EAAiB,MAAK,MAAM,QAAQ,YAAa,EAAG,iBAAsB,SAAc,OAAM,QAASF,EAAU,QAASD,EAAU,SAAUD,EAAW,aAAcqB,EAAkB,MAAO,CAAE,MAAO,OAAQ,OAAQ,MAAO,CAAG,EACvP,GACL,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,eAAgB,SAAU,IAAK,GAAI,UAAW,EAAG,EAAhF,UACE,kBAACQ,EAAD,CAAQ,KAAM,kBAACI,EAAD,CAAW,KAAM,EAAI,GAAG,KAAK,QAAQ,MAAM,aAAa,YAAef,EAAa,GAAG,EAAG,eAAY,GACpH,kBAACW,EAAD,CAAQ,KAAM,kBAACK,GAAD,CAAU,KAAM,EAAI,GAAG,KAAK,QAAQ,MAAM,aAAa,YAAehB,EAAa,EAAE,EAAG,eAAY,EAC/G,C,GACL,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,WAAY,SAAU,eAAgB,SAAU,IAAK,GAAI,UAAW,CAAE,EAArG,UACE,kBAACW,EAAD,CAAQ,KAAM,kBAACM,EAAD,CAAS,KAAM,EAAI,GAAG,KAAK,QAAQ,MAAM,aAAa,aAAW,KAAK,SAAUvB,GAAQV,EAAU,YAAeoB,EAAe,GAAU,EAAG,aAAU,GACrK,kBAAC,OAAD,CAAM,MAAO,CAAE,SAAU,GAAI,MAAO,+BAAgC,SAAU,GAAI,UAAW,QAAS,EAAtG,UAA0GV,EAAK,QAAQ,CAAC,EAAE,GAAO,C,GACjI,kBAACiB,EAAD,CAAQ,KAAM,kBAACO,GAAD,CAAQ,KAAM,EAAI,GAAG,KAAK,QAAQ,MAAM,aAAa,aAAW,KAAK,SAAUxB,GAAQX,EAAU,YAAeqB,EAAetB,CAAS,EAAG,aAAU,EAChK,C,EACG,C,EACd,C,kEA3FM,EAAW,GACX,EAAW,EACX,EAAY,E,OCmBlB,SAAgBqC,GAAoB,CAAE,aAAY,aAAY,YAAY,GAAiC,CACvG,IAAMC,EAAWjC,EAAyB,IAAI,EACxC,CAACkC,EAAUC,GAAe/B,EAAsB,IAAI,EACpD,CAACgC,EAAWC,GAAgBjC,EAAS,EAAK,EAChD,SAASkC,EAAiB,EAAkC,CACxD,IAAM1B,EAAO,EAAE,OAAO,QAAQ,GACzBA,IAEL,EAAE,OAAO,MAAQ,GACZA,EAAK,KAAK,WAAW,QAAQ,EAI9BA,EAAK,KAAO2B,EAAY,KAAO,KAC/BC,EAAM,QAAQ,UAAUD,EAAU,GAAG,EAGzCJ,EAAYvB,CAAI,EAPZ4B,EAAM,QAAQ,SAAS,EAQ/B,CACA,eAAeC,EAAkB,EAAY,CACzC,IAAIC,EACJL,EAAa,EAAI,EACjB,GAAI,CACA,EAAc,MAAMM,EAAWvB,CAAI,CACvC,OACOwB,EAAK,CACRJ,EAAM,MAAMI,aAAe,OAASA,EAAI,QAAUA,EAAI,QAAU,MAAM,EACtE,MACJ,QACQ,CACJP,EAAa,EAAK,CACtB,CACK,MAAMQ,EAAWH,CAAW,IAAO,IAExCP,EAAY,IAAI,CACpB,CACA,MAAO,CAEH,WACA,YACA,mBAAsBF,EAAS,SAAS,MAAM,EAE9C,eAAgB,CACZ,IAAKA,EACL,KAAM,OACN,OAAQ,UACR,MAAO,CAAE,QAAS,MAAO,EACzB,SAAUK,CACd,EAEA,aAAc,CACV,KAAMJ,EACN,aAAgBC,EAAY,IAAI,EAChC,UAAY,GAAe,CAAEM,EAAuBrB,CAAI,CAAG,CAC/D,CACJ,CACJ,C,wCCtDA,SAAgB0B,GAAkB,CAAE,UAAS,gBAAe,iBAAgB,aAAY,WAAU,WAAU,YAAoC,CAC5I,IAAMC,EAAef,GAAoB,CACrC,aACA,WAAa,GAAQ,CACjBgB,EAASC,CAAG,CAChB,CACJ,CAAC,EACKC,EAAUH,EAAa,YAAcrB,GAAkB,IAC7D,MAAQ,kBAAA/C,EAAA,WACN,kBAAC2C,EAAD,CAAU,MAAM,OAAgB,UAAmB,WAAU,OAAQ,KAAM,MAAO,IAAK,YACrF,2BAAC,MAAD,WACE,kBAAC,MAAD,CAAK,MAAO,CAAE,SAAU,GAAI,MAAO,+BAAgC,aAAc,CAAE,EAAG,mCAA6B,GACnH,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,IAAK,CAAE,EAAtC,UACE,kBAACE,EAAD,CAAQ,KAAM,kBAAC2B,EAAD,CAAQ,KAAM,EAAI,GAAG,QAASJ,EAAa,UAAW,QAASA,EAAa,eAAgB,eAElG,GACPK,GAAiBC,GAAa,kBAAC7B,EAAD,CAAQ,MAAM,aAAa,KAAK,SAAS,QAAS6B,EAAU,eAEjF,EACP,C,GACL,kBAAC,QAAD,CAAO,GAAIN,EAAa,cAAgB,EACrC,GACG,GAEV,kBAACjD,GAAD,CAAoB,GAAIiD,EAAa,aAAc,eAAgBG,CAAS,EAC5E,GACN,C,2DCjCA,SAASI,GAAa,EAAoB,CACtC,GAAI,CAACC,EACD,MAAO,GACX,GAAIA,EAAI,OAAS,EACb,MAAO,GACX,IAAIC,EAAQ,EAiBZ,MAhBI,QAAQ,KAAKD,CAAG,GAChB,IACA,QAAQ,KAAKA,CAAG,GAChB,IACA,KAAK,KAAKA,CAAG,GACb,IACA,eAAe,KAAKA,CAAG,GACvB,IACAA,EAAI,QAAU,IACd,IACAC,GAAS,EACF,EACPA,IAAU,EACH,EACPA,IAAU,EACH,EACJ,CACX,CAOA,SAAgBC,GAAsB,CAAE,WAAU,UAAsC,CACpF,IAAMC,EAAWC,OAAcL,GAAaM,CAAQ,EAAG,CAACA,CAAQ,CAAC,EACjE,GAAI,CAACA,EACD,OAAO,KACX,GAAM,CAAE,QAAO,SAAUC,GAAaH,GAChCI,EAAeC,EACf,CACE,GAAIA,EAAO,iBACL,CAAC,CAAE,MAAO,QAAS,GAAI,QAAQ,KAAKH,CAAQ,CAAE,CAAC,EAC/C,CAAC,EACP,GAAIG,EAAO,mBACL,CAAC,CAAE,MAAO,QAAS,GAAI,eAAe,KAAKH,CAAQ,CAAE,CAAC,EACtD,CAAC,CACX,EACE,KAEAI,EAAcD,EAASH,EAAS,QAAUG,EAAO,UAAY,KAC7DE,EAAiBF,EAAS,MAAMA,EAAO,UAAU,IAAM,KAC7D,MAAQ,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,EAA/B,UAEN,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,WAAY,SAAU,IAAK,CAAE,EAA5D,UACE,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,IAAK,EAAG,KAAM,CAAE,EAC3C,UAAC,EAAG,EAAG,EAAG,CAAC,CAAC,CAAa,IAAK,GAAO,kBAAC,MAAD,CAAa,MAAO,CACrD,KAAM,EACN,OAAQ,EACR,aAAc,sCACd,WAAYG,GAAKR,EAAWS,EAAQ,+BACpC,WAAY,uBAChB,CAAG,EAN4CD,CAM5C,CAAE,CACJ,GACL,kBAAC,OAAD,CAAM,MAAO,CAAE,SAAU,GAAI,WAAY,IAAK,QAAO,SAAU,GAAI,UAAW,OAAQ,EACnF,SAAAE,CACG,GACLH,IAAmB,MAAS,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,WAAY,SAAU,IAAK,EAAG,WAAY,CAAE,EAA3E,UACxBD,EACK,kBAACK,EAAD,CAAa,KAAM,GAAI,MAAO,CAAE,WAAY,EAAG,MAAO,+BAAgC,CAAG,GACzF,kBAACC,EAAD,CAAQ,KAAM,GAAI,MAAO,CAAE,WAAY,EAAG,MAAO,8BAA+B,CAAG,GACzF,kBAAC,OAAD,CAAM,MAAO,CACT,SAAU,GACV,MAAON,EAAc,gCAAkC,+BACvD,WAAY,YAChB,EACG,SAAAC,CACG,EACH,C,EACJ,CAGJ,GAAAH,GAAgBA,EAAa,OAAS,GAAM,kBAAC,MAAD,CAAK,MAAO,CAAE,QAAS,OAAQ,SAAU,OAAQ,IAAK,WAAY,UAAW,CAAE,EACvH,SAAAA,EAAa,KAAK,CAAE,MAAOS,EAAG,QAAU,kBAAC,MAAD,CAAa,MAAO,CACnD,QAAS,OACT,WAAY,SACZ,IAAK,EACL,SAAU,GACV,MAAOC,EAAK,gCAAkC,+BAC9C,WAAY,YAChB,EAPmC,UAS7BC,EADPD,EACQH,EACAC,EADD,CAAa,KAAM,GAAI,MAAO,CAAE,WAAY,CAAE,CAAG,CACL,EACpD,kBAAC,OAAD,UAAOC,CAAQ,EACZ,CAZ4C,EAAAA,CAY5C,CAAE,CACN,EACJ,C,EACT,C,+CAxGM,GAGD,CACD,EAAG,CAAE,MAAO,GAAI,MAAO,aAAc,EACrC,EAAG,CAAE,MAAO,IAAK,MAAO,8BAA+B,EACvD,EAAG,CAAE,MAAO,KAAM,MAAO,+BAAgC,EACzD,EAAG,CAAE,MAAO,KAAM,MAAO,4BAA6B,EACtD,EAAG,CAAE,MAAO,IAAK,MAAO,+BAAgC,CAC5D,C,OCyDA,SAASG,GAAgB,EAAmB,EAAyB,EAAsB,CACvF,IAAMC,EAASC,EAAM,OAAS,OAASA,EAAM,OAAS,OACtD,OAAOjB,QAAe,CAClB,QAASgB,IAAW,OAAYE,EAAU,CAAC,GAAGC,EAAe,OAAQH,CAAM,EAC3E,UAAWA,IAAW,OAAY,OAAY,CAACG,CAAa,CAChE,GAAI,CAACD,EAASC,EAAeH,CAAM,CAAC,CACxC,CACA,SAAgBI,GAAmB,CAAE,QAAO,UAAU,GAAM,qBAAyC,CACjG,IAAMC,EAASJ,EAAM,OAAS,OACxB,CAAE,UAAS,aAAcF,GAAgBO,EAAY,UAAWC,GAAa,MAAON,CAAK,EACzF,CAAE,OAAM,WAAU,kBAAiB,OAAM,kBAAiB,eAAc,eAAiBO,EAAoC,CAC/H,SAAU,CAAE,UAAW,OAAW,OAAQ,OAAW,UAAW,IAAK,EACrE,UACA,WACJ,CAAC,EAMKC,EAAS,CAAE,OAAM,WAAU,GALbC,EAAe,CAC/B,UAAWC,EAAYC,GAAmBC,EAAgB,SAAS,EACnE,OAAQF,EAAYG,GAAgBD,EAAgB,MAAM,EAC1D,GAAGE,EAA0BF,EAAgB,SAAS,CAC1D,CACoC,CAAY,EAE1CG,EAAYC,GAAoBR,EAAQS,GAAWb,CAAM,EACzDc,EAAcC,GAAgB,CAAE,GAAGX,EAAQ,OAAQR,EAAM,OAAS,OAASA,EAAM,OAAS,MAAU,EAAGiB,GAAW,CAACb,CAAM,EACzHgB,EAAQhB,EAASW,EAAYG,EAC7BG,EAAOD,EAAM,MAAM,MAAQ,CAAC,EAC5BE,EAAQF,EAAM,MAAM,OAAS,EACnC,MAAQ,kBAAArH,EAAA,WACN,kBAACR,EAAD,CAAmB,QAAU,kBAAAQ,EAAA,WACvB,kBAACwH,EAAD,CAAc,YAAY,OAAO,MAAOC,GAA0B,GAAIC,EAAK,WAAW,CAAG,GACzF,kBAACC,EAAD,CAAc,MAAOC,GAAsB,GAAIF,EAAK,QAAQ,CAAG,GAC/D,kBAACG,EAAD,CAAiB,GAAIH,EAAK,WAAW,CAAG,EACxC,IAAI,SAAUI,EAAc,QAASC,EAAa,YAAY,QAAS,GAC7E,kBAACC,GAAD,CAAgB,QAASX,EAAM,WAAY,WAAYC,EAAM,cAAiB,KAAKD,EAAM,QAAQ,EAAsB,oBAAmB,WAAYY,EAAgBV,CAAK,CAAG,EAC9K,GACN,CACA,SAAgBW,GAAuB,CAAE,QAAO,UAAU,GAAM,qBAAyC,CACrG,IAAM7B,EAASJ,EAAM,OAAS,OACxB,CAAE,UAAS,aAAcF,GAAgBO,EAAY,cAAe6B,GAAiB,MAAOlC,CAAK,EACjG,CAAE,OAAM,WAAU,kBAAiB,OAAM,cAAa,kBAAiB,eAAc,eAAiBO,EAAwC,CAChJ,SAAU,CACN,OAAQ,GAAI,YAAa,GAAI,OAAQ,OAAW,KAAM,GAAI,GAAI,GAC9D,OAAQ,OAAW,QAAS,GAAI,aAAc,OAAW,UAAW,IACxE,EACA,UACA,WACJ,CAAC,EAYKC,EAAS,CAAE,OAAM,WAAU,GAXbC,EAAe,CAC/B,OAAQG,EAAgB,OACxB,YAAaA,EAAgB,YAC7B,OAAQA,EAAgB,OACxB,KAAMA,EAAgB,KACtB,GAAIA,EAAgB,GACpB,OAAQF,EAAYyB,GAAuBvB,EAAgB,MAAM,EACjE,QAASA,EAAgB,QACzB,aAAcA,EAAgB,eAAiB,QAAgBA,EAAgB,eAAiB,SAAkB,OAClH,GAAGE,EAA0BF,EAAgB,SAAS,CAC1D,CACoC,CAAY,EAC1CG,EAAYqB,GAAwB5B,EAAQS,GAAWb,CAAM,EAC7Dc,EAAcmB,GAAoB,CAAE,GAAG7B,EAAQ,OAAQR,EAAM,OAAS,OAASA,EAAM,OAAS,MAAU,EAAGiB,GAAW,CAACb,CAAM,EAC7HgB,EAAQhB,EAASW,EAAYG,EAC7BG,GAAOD,EAAM,MAAM,MAAQ,CAAC,EAC5BE,EAAQF,EAAM,MAAM,OAAS,EACnC,MAAQ,kBAAArH,EAAA,WACN,kBAACR,EAAD,CAAmB,QAAU,kBAAAQ,EAAA,WACvB,kBAACuI,EAAD,CAAc,YAAY,UAAU,GAAIC,EAAY,QAAQ,EAAG,MAAO,GAAK,GAC3E,kBAACD,EAAD,CAAc,YAAY,UAAU,GAAIC,EAAY,aAAa,EAAG,MAAO,GAAK,GAChF,kBAAChB,EAAD,CAAc,YAAY,SAAS,MAAOiB,GAA0B,GAAIf,EAAK,QAAQ,EAAG,MAAO,GAAK,GACpG,kBAACa,EAAD,CAAc,YAAY,UAAU,GAAIC,EAAY,MAAM,EAAG,MAAO,GAAK,GACzE,kBAACD,EAAD,CAAc,YAAY,YAAY,GAAIC,EAAY,IAAI,EAAG,MAAO,GAAK,GACzE,kBAACD,EAAD,CAAc,YAAY,aAAa,GAAIC,EAAY,SAAS,EAAG,MAAO,GAAK,GAC/E,kBAACb,EAAD,CAAc,MAAOe,GAA8B,GAAIhB,EAAK,QAAQ,CAAG,GACvE,kBAACF,EAAD,CAAc,YAAY,SAAS,MAAOmB,GAAgC,GAAIjB,EAAK,cAAc,EAAG,MAAO,GAAK,GAChH,kBAACG,EAAD,CAAiB,GAAIH,EAAK,WAAW,CAAG,EACxC,IAAI,SAAUI,EAAc,QAASC,EAAa,YAAY,QAAS,GAC7E,kBAACa,GAAD,CAAoB,QAASvB,EAAM,WAAY,WAAYC,GAAM,cAAiB,KAAKD,EAAM,QAAQ,EAAsB,oBAAmB,WAAYY,EAAgBV,CAAK,CAAG,EAClL,GACN,C,0DA/GM,GAA2B,CAAC,MAAO,OAAQ,MAAO,QAAS,QAAQ,CAAC,CAAC,IAAK,IAAW,CAAE,QAAO,MAAOzG,CAAM,EAAE,EAE7G,GAAiC,CACnC,CAAE,MAAO,OAAQ,MAAO,OAAQ,EAChC,CAAE,MAAO,QAAS,MAAO,OAAQ,CACrC,C","names":["__iconData","__iconData$3","createLucideIcon","__iconData$2","__iconData$1","ListSearchToolbar","props","keyword","filters","onSearch","onReset","derived","deriveFilterControls","_Fragment","actionsSlot","useRenderedSlot","desktopActions","present","create","actions","SlotProbe","SearchToolbar","SearchButton","ResetButton","mobileActions","node","normalizeZoom","value","rounded","ZOOM_STEP","MAX_ZOOM","MIN_ZOOM","AvatarCropperModal","cropperRef","useRef","srcRef","src","setSrc","useState","rotate","setRotate","zoom","setZoom","updateSrc","next","useEffect","file","handleRotate","prev","delta","handleZoomChange","handleZoomStep","handleConfirm","canvas","blob","onConfirm","AppModal","Space","Button","onCancel","confirmLoading","Cropper","RotateCcw","RotateCw","ZoomOut","ZoomIn","useAvatarCropUpload","inputRef","cropFile","setCropFile","uploading","setUploading","handleFileSelect","maxSizeMB","Toast","handleCropConfirm","uploadedUrl","uploadBlob","err","onUploaded","AvatarSelectModal","avatarUpload","onSelect","url","loading","Camera","currentAvatar","onRemove","calcStrength","pwd","score","PasswordStrengthMeter","strength","useMemo","password","LEVEL_CONFIG","policyChecks","policy","minLengthOk","minLengthLabel","i","color","label","CheckCircle","Circle","l","ok","jsx","useScopeListKey","userId","scope","selfKey","adminListsKey","UserLoginLogsPanel","isSelf","profileKeys","loginLogKeys","useListSearch","params","useFilterQuery","enumValueOf","LOGIN_EVENT_TYPES","submittedParams","LOGIN_STATUSES","formatDateTimeRangeForApi","selfQuery","useProfileLoginLogs","enabled","scopedQuery","useLoginLogList","query","list","total","FilterSelect","LOGIN_EVENT_TYPE_OPTIONS","bind","StatusSelect","LOGIN_STATUS_OPTIONS","DateRangeFilter","handleSearch","handleReset","LoginLogsTable","buildPagination","UserOperationLogsPanel","operationLogKeys","OPERATION_LOG_RESULTS","useProfileOperationLogs","useOperationLogList","KeywordInput","bindKeyword","OPERATION_METHOD_OPTIONS","OPERATION_LOG_RESULT_OPTIONS","OPERATION_IMPERSONATED_OPTIONS","OperationLogsTable"],"ignoreList":[0,1,2,3],"sources":["../../../../node_modules/lucide-react/dist/esm/icons/camera.mjs","../../../../node_modules/lucide-react/dist/esm/icons/circle.mjs","../../../../node_modules/lucide-react/dist/esm/icons/zoom-in.mjs","../../../../node_modules/lucide-react/dist/esm/icons/zoom-out.mjs","../../src/components/list-page/ListSearchToolbar.tsx","../../src/components/AvatarCropperModal.tsx","../../src/hooks/useAvatarCropUpload.ts","../../src/components/AvatarSelectModal.tsx","../../src/components/PasswordStrengthMeter.tsx","../../src/components/logs/UserLogsPanels.tsx"],"sourcesContent":[null,null,null,null,"import type { ReactNode } from 'react';\nimport { SearchToolbar } from \"@/components/SearchToolbar\";\nimport { ResetButton, SearchButton } from \"@/components/toolbar-controls\";\nimport { SlotProbe } from \"@/components/rendered-slot\";\nimport { useRenderedSlot } from \"@fudanda/react-core/hooks/useRenderedSlot\";\nimport { deriveFilterControls, type FilterOverrides, type FilterPageLike, type FilterSpec } from \"@/components/list-page/ContractFilters\";\ninterface ListSearchToolbarBaseProps {\n /** 新增按钮:桌面端排在查询 / 重置之后,移动端保留在主区 */\n create?: ReactNode;\n /** 低频操作(导出 / 批量 / 同步…):桌面端跟在新增之后,移动端收进「更多操作」菜单 */\n actions?: ReactNode;\n /** 覆盖移动端更多菜单内容;缺省与 `actions` 相同 */\n mobileActions?: ReactNode;\n filterTitle?: ReactNode;\n actionTitle?: string;\n className?: string;\n}\n/** 槽位写法:控件由页面创建(非契约类型的筛选状态、树形 / 客户端过滤等非标准形态) */\nexport interface ListSearchToolbarSlotProps extends ListSearchToolbarBaseProps {\n page?: undefined;\n /** 关键词输入(`KeywordInput`),桌面与移动端主区都展示 */\n keyword?: ReactNode;\n /** 其余筛选项(状态 / 类型 / 时间范围…):桌面端内联,移动端收进筛选抽屉 */\n filters?: ReactNode;\n onSearch: () => void;\n onReset: () => void;\n}\n/**\n * 契约写法:传 `useListPage({ contract })` 的返回,`filters` 只声明键(与成对的时间范围端点),\n * 控件按契约 query 的 `x-filter` 语义派生(见 `ContractFilters.tsx`);关键字类自动进主区。\n * 查询 / 重置从 `page` 取,专用控件在 `overrides` 里给出,额外的手写控件仍可放 `extraFilters`。\n */\nexport interface ListSearchToolbarContractProps<TSearch> extends ListSearchToolbarBaseProps {\n page: FilterPageLike<TSearch> & {\n readonly toolbarProps: {\n readonly onSearch: () => void;\n readonly onReset: () => void;\n };\n };\n filters: ReadonlyArray<FilterSpec<TSearch>>;\n overrides?: FilterOverrides<TSearch>;\n /** 契约之外的手写筛选控件(`{...page.bind('x')}` 绑定),排在派生控件之后 */\n extraFilters?: ReactNode;\n keyword?: undefined;\n onSearch?: undefined;\n onReset?: undefined;\n}\nexport type ListSearchToolbarProps<TSearch = Record<string, unknown>> = ListSearchToolbarSlotProps | ListSearchToolbarContractProps<TSearch>;\nconst present = (node: ReactNode) => Boolean(node) && !(Array.isArray(node) && node.length === 0);\n/**\n * 标准列表页工具栏:把 `SearchToolbar` 的桌面 / 移动槽位排布规则写在一处——\n * 桌面:关键词 → 筛选项 → 查询 / 重置 → 新增 → 低频操作;\n * 移动:主区 关键词 + 查询 + 新增,筛选项进抽屉(应用 / 重置即查询 / 重置),低频操作进更多菜单。\n * 契约写法下控件由契约 query 的语义派生;槽位写法下控件仍由页面创建(占位文案、字典项、权限门控都在页面里可见)。\n */\nexport function ListSearchToolbar<TSearch = Record<string, unknown>>(props: ListSearchToolbarProps<TSearch>) {\n const { create, actions, mobileActions, filterTitle, actionTitle, className } = props;\n let keyword: ReactNode;\n let filters: ReactNode;\n let onSearch: () => void;\n let onReset: () => void;\n if (props.page) {\n const derived = deriveFilterControls({ page: props.page, specs: props.filters, overrides: props.overrides });\n keyword = derived.keyword.length ? derived.keyword : undefined;\n filters = derived.filters.length || props.extraFilters ? <>{derived.filters}{props.extraFilters}</> : undefined;\n ({ onSearch, onReset } = props.page.toolbarProps);\n }\n else {\n ({ keyword, filters, onSearch, onReset } = props);\n }\n // 低频操作可能整体渲染为空(带 permission 的导出 / 按选中数出现的批量按钮):按实际渲染结果决定移动端是否出现更多菜单\n const actionsSlot = useRenderedSlot();\n const desktopActions = present(create) || present(actions)\n ? <>{create}<SlotProbe ref={actionsSlot.probeRef}>{actions}</SlotProbe></>\n : undefined;\n return (<SearchToolbar className={className} primary={(<>\n {keyword}\n {filters}\n <SearchButton onClick={onSearch}/>\n <ResetButton onClick={onReset}/>\n </>)} actions={desktopActions} mobilePrimary={(<>\n {keyword}\n <SearchButton onClick={onSearch}/>\n {create}\n </>)} mobileFilters={present(filters) ? filters : undefined}\n // SearchToolbar 对 mobileActions 缺省回落到 actions(含新增按钮,而新增已在移动主区),这里显式给出:\n // 页面覆盖优先;否则只有低频操作真的渲染出了元素才复用,用 false 明确「没有更多操作」\n mobileActions={mobileActions ?? (actionsSlot.rendered ? actions : false)} filterTitle={filterTitle} actionTitle={actionTitle} onFilterApply={onSearch} onFilterReset={onReset}/>);\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { Button, Cropper, Space } from \"@/ui\";\nimport { RotateCcw, RotateCw, ZoomIn, ZoomOut } from 'lucide-react';\nimport { AppModal } from \"@/components/AppModal\";\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 3;\nconst ZOOM_STEP = 0.1;\n/**\n * 将缩放值收敛到步长精度并夹紧到允许区间;非有限值返回 null。\n * Semi Slider 在轨道尚无布局宽度时可能算出 NaN,一律在此挡掉,避免污染受控的 zoom 状态。\n */\nfunction normalizeZoom(value: number): number | null {\n if (!Number.isFinite(value))\n return null;\n const rounded = Number((Math.round(value / ZOOM_STEP) * ZOOM_STEP).toFixed(2));\n return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, rounded));\n}\nexport interface AvatarCropperModalProps {\n /** 待裁剪的原始图片文件;为 null 时关闭弹窗 */\n readonly file: File | null;\n /** 确认按钮 loading(上传/保存中) */\n readonly confirmLoading?: boolean;\n readonly onCancel: () => void;\n /** 用户确认裁剪后回调裁剪结果(JPEG Blob,质量 0.85) */\n readonly onConfirm: (blob: Blob) => void;\n}\n/**\n * 头像裁剪弹窗:圆形裁剪框 + 受控旋转 / 缩放(Semi Cropper `rotate` / `zoom`)。\n * 内部管理旋转角度、缩放倍数与图片预览地址(blob URL)的创建与释放。\n */\nexport function AvatarCropperModal({ file, confirmLoading, onCancel, onConfirm }: AvatarCropperModalProps) {\n const cropperRef = useRef<Cropper>(null);\n const srcRef = useRef('');\n const [src, setSrc] = useState('');\n const [rotate, setRotate] = useState(0);\n const [zoom, setZoom] = useState(1);\n // 更新预览地址并释放上一个 blob URL\n const updateSrc = (next: string) => {\n if (srcRef.current.startsWith('blob:'))\n URL.revokeObjectURL(srcRef.current);\n srcRef.current = next;\n setSrc(next);\n };\n useEffect(() => {\n setRotate(0);\n setZoom(1);\n if (!file) {\n updateSrc('');\n return;\n }\n updateSrc(URL.createObjectURL(file));\n return () => {\n updateSrc('');\n };\n }, [file]);\n function handleRotate(delta: number) {\n setRotate((prev) => (((prev + delta) % 360) + 360) % 360);\n }\n function handleZoomChange(value: number | number[] | undefined) {\n if (value === undefined)\n return;\n const next = normalizeZoom(typeof value === 'number' ? value : value[0]);\n if (next !== null)\n setZoom(next);\n }\n function handleZoomStep(delta: number) {\n setZoom((prev) => normalizeZoom(Number((prev + delta).toFixed(2))) ?? prev);\n }\n function handleConfirm() {\n const canvas = cropperRef.current?.getCropperCanvas();\n if (!canvas)\n return;\n canvas.toBlob((blob) => {\n if (!blob)\n return;\n onConfirm(blob);\n }, 'image/jpeg', 0.85);\n }\n return (<AppModal title=\"裁剪头像\" visible={file !== null} onCancel={onCancel} footer={<Space>\n <Button onClick={onCancel}>取消</Button>\n <Button type=\"primary\" loading={confirmLoading} onClick={handleConfirm}>确认并上传</Button>\n </Space>} width={520} centered>\n <div style={{ width: '100%', height: 380 }}>\n {src && (<Cropper ref={cropperRef} src={src} shape=\"round\" aspectRatio={1} showResizeBox rotate={rotate} zoom={zoom} minZoom={MIN_ZOOM} maxZoom={MAX_ZOOM} zoomStep={ZOOM_STEP} onZoomChange={handleZoomChange} style={{ width: '100%', height: '100%' }}/>)}\n </div>\n <div style={{ display: 'flex', justifyContent: 'center', gap: 12, marginTop: 12 }}>\n <Button icon={<RotateCcw size={14}/>} size=\"small\" theme=\"borderless\" onClick={() => handleRotate(-90)}>向左旋转</Button>\n <Button icon={<RotateCw size={14}/>} size=\"small\" theme=\"borderless\" onClick={() => handleRotate(90)}>向右旋转</Button>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12, marginTop: 8 }}>\n <Button icon={<ZoomOut size={14}/>} size=\"small\" theme=\"borderless\" aria-label=\"缩小\" disabled={zoom <= MIN_ZOOM} onClick={() => handleZoomStep(-ZOOM_STEP)}>缩小</Button>\n <span style={{ fontSize: 12, color: 'var(--arc-antd-color-text-2)', minWidth: 44, textAlign: 'center' }}>{zoom.toFixed(1)}x</span>\n <Button icon={<ZoomIn size={14}/>} size=\"small\" theme=\"borderless\" aria-label=\"放大\" disabled={zoom >= MAX_ZOOM} onClick={() => handleZoomStep(ZOOM_STEP)}>放大</Button>\n </div>\n </AppModal>);\n}\n","import { useRef, useState, type ChangeEvent, type RefObject } from 'react';\nimport { Toast } from \"@/ui\";\nexport interface UseAvatarCropUploadOptions {\n /**\n * 裁剪结果上传成功后的落库动作(改本人资料 / 管理员改他人)。\n * 返回 `false` 表示落库失败且已自行提示,裁剪弹窗保持打开供重试;其余返回值视为成功并关闭裁剪弹窗。\n */\n readonly onUploaded: (url: string) => Promise<boolean | void> | boolean | void;\n /**\n * 裁剪 Blob 的上传实现(必填):管理后台传文件中心通道\n *(`utils/avatar-upload.ts` 的 `uploadAvatarBlobToFileCenter`),\n * 会员前台等走独立上传通道的调用方传入自己的实现。\n * 上传实现内部不要弹失败提示——失败由本 hook 统一提示。\n */\n readonly uploadBlob: (blob: Blob) => Promise<string>;\n /** 本地图片大小上限(MB),默认 2;与会员端编辑资料页保持一致 */\n readonly maxSizeMB?: number;\n}\n/**\n * 头像「选文件 → 裁剪 → 上传文件中心 → 落库」的公共流程。\n *\n * 页面只需:把 `fileInputProps` 展开到隐藏的 `<input>`、用 `openFilePicker` 触发选择、\n * 把 `cropperProps` 展开到 `<AvatarCropperModal>`,并在 `onUploaded` 里完成各自的落库动作。\n * 上传失败的提示由本 hook 统一给出(请求层静默),落库失败的提示由调用方按场景给出。\n */\nexport function useAvatarCropUpload({ onUploaded, uploadBlob, maxSizeMB = 2 }: UseAvatarCropUploadOptions) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [cropFile, setCropFile] = useState<File | null>(null);\n const [uploading, setUploading] = useState(false);\n function handleFileSelect(e: ChangeEvent<HTMLInputElement>) {\n const file = e.target.files?.[0];\n if (!file)\n return;\n e.target.value = '';\n if (!file.type.startsWith('image/')) {\n Toast.warning('请选择图片文件');\n return;\n }\n if (file.size > maxSizeMB * 1024 * 1024) {\n Toast.warning(`图片不能超过 ${maxSizeMB}MB`);\n return;\n }\n setCropFile(file);\n }\n async function handleCropConfirm(blob: Blob) {\n let uploadedUrl: string;\n setUploading(true);\n try {\n uploadedUrl = await uploadBlob(blob);\n }\n catch (err) {\n Toast.error(err instanceof Error && err.message ? err.message : '上传失败');\n return;\n }\n finally {\n setUploading(false);\n }\n if ((await onUploaded(uploadedUrl)) === false)\n return;\n setCropFile(null);\n }\n return {\n /** 当前待裁剪的文件;为 null 时裁剪弹窗关闭 */\n cropFile,\n uploading,\n openFilePicker: () => inputRef.current?.click(),\n /** 展开到隐藏的 `<input>`;需要额外属性(如 `id`)时在展开后补充 */\n fileInputProps: {\n ref: inputRef as RefObject<HTMLInputElement>,\n type: 'file' as const,\n accept: 'image/*',\n style: { display: 'none' } as const,\n onChange: handleFileSelect,\n },\n /** 展开到 `<AvatarCropperModal>`;`confirmLoading` 由调用方按自身落库状态合并后传入 */\n cropperProps: {\n file: cropFile,\n onCancel: () => setCropFile(null),\n onConfirm: (blob: Blob) => { void handleCropConfirm(blob); },\n },\n };\n}\n","import { Button } from \"@/ui\";\nimport { Camera } from 'lucide-react';\nimport { AppModal } from \"@/components/AppModal\";\nimport { AvatarCropperModal } from \"@/components/AvatarCropperModal\";\nimport { useAvatarCropUpload } from \"@/hooks/useAvatarCropUpload\";\nexport interface AvatarSelectModalProps {\n readonly visible: boolean;\n /** 当前头像地址;有值且传了 onRemove 时展示移除入口 */\n readonly currentAvatar?: string | null;\n /** 调用方落库中的 loading(与上传 loading 合并后传给裁剪确认按钮) */\n readonly confirmLoading?: boolean;\n /**\n * 裁剪 Blob 的上传实现(必填):管理后台传文件中心通道\n *(`utils/avatar-upload.ts` 的 `uploadAvatarBlobToFileCenter`),\n * 会员前台传入自己的上传实现(如会员头像上传接口)。\n */\n readonly uploadBlob: (blob: Blob) => Promise<string>;\n readonly onCancel: () => void;\n /** 裁剪上传成功;落库与关闭弹窗由调用方负责 */\n readonly onSelect: (url: string) => void;\n /** 移除头像;不传则不展示移除入口(移除前的确认由调用方负责) */\n readonly onRemove?: () => void;\n}\n/**\n * 统一头像选择弹窗:本地上传(选中后进裁剪)与移除。\n * 管理后台个人中心、用户管理、会员前台编辑资料三处共用。\n */\nexport function AvatarSelectModal({ visible, currentAvatar, confirmLoading, uploadBlob, onCancel, onSelect, onRemove }: AvatarSelectModalProps) {\n const avatarUpload = useAvatarCropUpload({\n uploadBlob,\n onUploaded: (url) => {\n onSelect(url);\n },\n });\n const loading = avatarUpload.uploading || (confirmLoading ?? false);\n return (<>\n <AppModal title=\"更换头像\" visible={visible} onCancel={onCancel} footer={null} width={480} centered>\n <div>\n <div style={{ fontSize: 13, color: 'var(--arc-antd-color-text-2)', marginBottom: 8 }}>本地上传(≤ 2MB,上传后可裁剪、旋转、缩放)</div>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button icon={<Camera size={14}/>} loading={avatarUpload.uploading} onClick={avatarUpload.openFilePicker}>\n 选择图片\n </Button>\n {currentAvatar && onRemove && (<Button theme=\"borderless\" type=\"danger\" onClick={onRemove}>\n 移除头像\n </Button>)}\n </div>\n <input {...avatarUpload.fileInputProps}/>\n </div>\n </AppModal>\n\n <AvatarCropperModal {...avatarUpload.cropperProps} confirmLoading={loading}/>\n </>);\n}\n","import { useMemo } from 'react';\nimport { CheckCircle, Circle } from 'lucide-react';\nimport type { PasswordPolicy } from \"@fudanda/react-core/utils/password-policy\";\ninterface PasswordStrengthMeterProps {\n /** 当前密码输入值;为空时组件不渲染任何内容 */\n readonly password: string;\n /** 密码策略(可选);提供时在强度条下方展示策略达标清单 */\n readonly policy?: PasswordPolicy | null;\n}\ntype Level = 0 | 1 | 2 | 3 | 4;\nconst LEVEL_CONFIG: Record<Level, {\n label: string;\n color: string;\n}> = {\n 0: { label: '', color: 'transparent' },\n 1: { label: '弱', color: 'var(--arc-antd-color-danger)' },\n 2: { label: '一般', color: 'var(--arc-antd-color-warning)' },\n 3: { label: '良好', color: 'var(--arc-antd-color-info)' },\n 4: { label: '强', color: 'var(--arc-antd-color-success)' },\n};\nfunction calcStrength(pwd: string): Level {\n if (!pwd)\n return 0;\n if (pwd.length < 6)\n return 1;\n let score = 0;\n if (/[a-z]/.test(pwd))\n score++;\n if (/[A-Z]/.test(pwd))\n score++;\n if (/\\d/.test(pwd))\n score++;\n if (/[^a-zA-Z0-9]/.test(pwd))\n score++;\n if (pwd.length >= 10)\n score++;\n if (score <= 1)\n return 1;\n if (score === 2)\n return 2;\n if (score === 3)\n return 3;\n return 4;\n}\n/**\n * 密码强度指示器。\n *\n * - `password` 为空时不渲染任何内容,可作为 `helpText` 直接传入 Semi `Form.Input`。\n * - 提供 `policy` 时,在强度条下方展示策略达标清单(✓ / ○)。\n */\nexport function PasswordStrengthMeter({ password, policy }: PasswordStrengthMeterProps) {\n const strength = useMemo(() => calcStrength(password), [password]);\n if (!password)\n return null;\n const { label, color } = LEVEL_CONFIG[strength];\n const policyChecks = policy\n ? [\n ...(policy.requireUppercase\n ? [{ label: '含大写字母', ok: /[A-Z]/.test(password) }]\n : []),\n ...(policy.requireSpecialChar\n ? [{ label: '含特殊字符', ok: /[^a-zA-Z0-9]/.test(password) }]\n : []),\n ]\n : null;\n // 最小长度单独提出,与强度条同行显示\n const minLengthOk = policy ? password.length >= policy.minLength : null;\n const minLengthLabel = policy ? `至少 ${policy.minLength} 位` : null;\n return (<div style={{ padding: '4px 0' }}>\n {/* 强度分段条 + 标签 + 最小位数(同行) */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <div style={{ display: 'flex', gap: 3, flex: 1 }}>\n {([1, 2, 3, 4] as Level[]).map((i) => (<div key={i} style={{\n flex: 1,\n height: 4,\n borderRadius: 'var(--arc-antd-border-radius-small)',\n background: i <= strength ? color : 'var(--arc-antd-color-fill-2)',\n transition: 'background 0.25s ease',\n }}/>))}\n </div>\n <span style={{ fontSize: 12, fontWeight: 600, color, minWidth: 28, textAlign: 'right' }}>\n {label}\n </span>\n {minLengthLabel !== null && (<div style={{ display: 'flex', alignItems: 'center', gap: 3, flexShrink: 0 }}>\n {minLengthOk\n ? <CheckCircle size={11} style={{ flexShrink: 0, color: 'var(--arc-antd-color-success)' }}/>\n : <Circle size={11} style={{ flexShrink: 0, color: 'var(--arc-antd-color-text-3)' }}/>}\n <span style={{\n fontSize: 12,\n color: minLengthOk ? 'var(--arc-antd-color-success)' : 'var(--arc-antd-color-text-3)',\n transition: 'color 0.2s',\n }}>\n {minLengthLabel}\n </span>\n </div>)}\n </div>\n\n {/* 其他策略达标项(大写/特殊字符等),仍在下方 */}\n {policyChecks && policyChecks.length > 0 && (<div style={{ display: 'flex', flexWrap: 'wrap', gap: '3px 14px', marginTop: 5 }}>\n {policyChecks.map(({ label: l, ok }) => (<div key={l} style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n fontSize: 12,\n color: ok ? 'var(--arc-antd-color-success)' : 'var(--arc-antd-color-text-3)',\n transition: 'color 0.2s',\n }}>\n {ok\n ? <CheckCircle size={11} style={{ flexShrink: 0 }}/>\n : <Circle size={11} style={{ flexShrink: 0 }}/>}\n <span>{l}</span>\n </div>))}\n </div>)}\n </div>);\n}\n","/**\n * 「某个人」的登录记录 / 操作记录面板:个人中心与用户管理(记录抽屉)共用同一份筛选条、分页与表格接线,\n * 差异只在取数来源:\n *\n * - `scope = { kind: 'self' }`:走 `authContract.myLoginLogs` / `myOperationLogs`,服务端按当前登录用户过滤;\n * - `scope = { kind: 'user', userId }`:走管理侧 `loginLogContract.list` / `operationLogContract.list` 的 `userId` 精确筛选\n * (管理侧的 `username` 是模糊关键字,不能用来定位到某个人)。\n *\n * 两个 scope 的分页 / 筛选状态是同一份(同一行控件服务两条链路),因此两条查询都无条件调用,\n * 只把当前生效的那条 `enabled`;未激活的一条不会发请求(抽屉里切 tab 才拉取)。\n *\n * 表格本身仍是既有的 `LoginLogsTable` / `OperationLogsTable`,这里只负责取数接线与筛选条。\n */\nimport { useMemo } from 'react';\nimport { DateRangeFilter, FilterSelect, KeywordInput, StatusSelect } from \"@/components/search-filters\";\nimport { ListSearchToolbar } from \"@/components/list-page/index\";\nimport { LoginLogsTable } from \"@/components/logs/LoginLogsTable\";\nimport { OperationLogsTable } from \"@/components/logs/OperationLogsTable\";\nimport { useListSearch } from \"@fudanda/react-core/hooks/useListSearch\";\nimport { useFilterQuery } from \"@fudanda/react-core/hooks/useFilterQuery\";\nimport { profileKeys, useProfileLoginLogs, useProfileOperationLogs } from \"@fudanda/react-core/hooks/queries/profile\";\nimport { loginLogKeys, useLoginLogList } from \"@fudanda/react-core/hooks/queries/login-logs\";\nimport { operationLogKeys, useOperationLogList } from \"@fudanda/react-core/hooks/queries/operation-logs\";\nimport { enumValueOf } from '@fudanda/shared/core';\nimport { formatDateTimeRangeForApi } from \"@fudanda/react-core/utils/date\";\nimport { LOGIN_EVENT_TYPE_OPTIONS, LOGIN_EVENT_TYPES, LOGIN_STATUS_OPTIONS, LOGIN_STATUSES } from '@fudanda/shared/identity';\nimport { OPERATION_LOG_RESULT_OPTIONS, OPERATION_LOG_RESULTS } from '@fudanda/shared/platform';\nimport type { QueryKey } from '@tanstack/react-query';\n/** 记录归属:本人(个人中心)或指定用户(用户管理) */\nexport type UserLogsScope = {\n readonly kind: 'self';\n} | {\n readonly kind: 'user';\n readonly userId: number;\n};\nexport interface UserLogsPanelProps {\n readonly scope: UserLogsScope;\n /** 面板未激活时不发请求(抽屉 / 个人中心按当前 tab 传) */\n readonly enabled?: boolean;\n /** 列设置持久化 key:两个入口各自记住列宽 */\n readonly columnSettingsKey: string;\n}\n/** 操作记录筛选选项:与管理侧操作日志页保持一致(操作人除外,面板本身已限定到某个人) */\nconst OPERATION_METHOD_OPTIONS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].map((value) => ({ value, label: value }));\n/** 操作来源筛选:与契约 `impersonated` 的 queryBool 文案一致 */\nconst OPERATION_IMPERSONATED_OPTIONS = [\n { value: 'true', label: '仅模拟操作' },\n { value: 'false', label: '仅本人操作' },\n];\ninterface LoginLogSearchParams {\n eventType?: string;\n status?: string;\n timeRange: [\n Date,\n Date\n ] | null;\n}\ninterface OperationLogSearchParams {\n module: string;\n description: string;\n method?: string;\n path: string;\n ip: string;\n status?: string;\n content: string;\n /** 'true' 仅模拟登录期间的操作 / 'false' 仅本人操作;未选为 undefined */\n impersonated?: string;\n timeRange: [\n Date,\n Date\n ] | null;\n}\n/**\n * 列表查询的筛选键:指定用户时按 userId 分片,让「记住列表筛选条件」不跨用户串味;\n * 同时把整个管理侧列表键放进 extraKeys——「查询」要失效的正是该用户那份列表(前缀匹配同域全部列表查询)。\n */\nfunction useScopeListKey(selfKey: QueryKey, adminListsKey: QueryKey, scope: UserLogsScope) {\n const userId = scope.kind === 'user' ? scope.userId : undefined;\n return useMemo(() => ({\n listKey: userId === undefined ? selfKey : [...adminListsKey, 'user', userId],\n extraKeys: userId === undefined ? undefined : [adminListsKey],\n }), [selfKey, adminListsKey, userId]);\n}\nexport function UserLoginLogsPanel({ scope, enabled = true, columnSettingsKey }: UserLogsPanelProps) {\n const isSelf = scope.kind === 'self';\n const { listKey, extraKeys } = useScopeListKey(profileKeys.loginLogs, loginLogKeys.lists, scope);\n const { page, pageSize, buildPagination, bind, submittedParams, handleSearch, handleReset, } = useListSearch<LoginLogSearchParams>({\n defaults: { eventType: undefined, status: undefined, timeRange: null },\n listKey,\n extraKeys,\n });\n const filterQuery = useFilterQuery({\n eventType: enumValueOf(LOGIN_EVENT_TYPES, submittedParams.eventType),\n status: enumValueOf(LOGIN_STATUSES, submittedParams.status),\n ...formatDateTimeRangeForApi(submittedParams.timeRange),\n });\n const params = { page, pageSize, ...filterQuery };\n // 两条链路都调用、只启用生效的一条(React 不允许条件调用 hook)\n const selfQuery = useProfileLoginLogs(params, enabled && isSelf);\n const scopedQuery = useLoginLogList({ ...params, userId: scope.kind === 'user' ? scope.userId : undefined }, enabled && !isSelf);\n const query = isSelf ? selfQuery : scopedQuery;\n const list = query.data?.list ?? [];\n const total = query.data?.total ?? 0;\n return (<>\n <ListSearchToolbar filters={(<>\n <FilterSelect placeholder=\"全部事件\" items={LOGIN_EVENT_TYPE_OPTIONS} {...bind('eventType')}/>\n <StatusSelect items={LOGIN_STATUS_OPTIONS} {...bind('status')}/>\n <DateRangeFilter {...bind('timeRange')}/>\n </>)} onSearch={handleSearch} onReset={handleReset} filterTitle=\"登录记录筛选\"/>\n <LoginLogsTable loading={query.isFetching} dataSource={list} onRefresh={() => void query.refetch()} columnSettingsKey={columnSettingsKey} pagination={buildPagination(total)}/>\n </>);\n}\nexport function UserOperationLogsPanel({ scope, enabled = true, columnSettingsKey }: UserLogsPanelProps) {\n const isSelf = scope.kind === 'self';\n const { listKey, extraKeys } = useScopeListKey(profileKeys.operationLogs, operationLogKeys.lists, scope);\n const { page, pageSize, buildPagination, bind, bindKeyword, submittedParams, handleSearch, handleReset, } = useListSearch<OperationLogSearchParams>({\n defaults: {\n module: '', description: '', method: undefined, path: '', ip: '',\n status: undefined, content: '', impersonated: undefined, timeRange: null,\n },\n listKey,\n extraKeys,\n });\n const filterQuery = useFilterQuery({\n module: submittedParams.module,\n description: submittedParams.description,\n method: submittedParams.method,\n path: submittedParams.path,\n ip: submittedParams.ip,\n status: enumValueOf(OPERATION_LOG_RESULTS, submittedParams.status),\n content: submittedParams.content,\n impersonated: submittedParams.impersonated === 'true' ? true : submittedParams.impersonated === 'false' ? false : undefined,\n ...formatDateTimeRangeForApi(submittedParams.timeRange),\n });\n const params = { page, pageSize, ...filterQuery };\n const selfQuery = useProfileOperationLogs(params, enabled && isSelf);\n const scopedQuery = useOperationLogList({ ...params, userId: scope.kind === 'user' ? scope.userId : undefined }, enabled && !isSelf);\n const query = isSelf ? selfQuery : scopedQuery;\n const list = query.data?.list ?? [];\n const total = query.data?.total ?? 0;\n return (<>\n <ListSearchToolbar filters={(<>\n <KeywordInput placeholder=\"请输入功能模块\" {...bindKeyword('module')} width={160}/>\n <KeywordInput placeholder=\"请输入操作描述\" {...bindKeyword('description')} width={160}/>\n <FilterSelect placeholder=\"全部请求方法\" items={OPERATION_METHOD_OPTIONS} {...bind('method')} width={140}/>\n <KeywordInput placeholder=\"请输入请求路径\" {...bindKeyword('path')} width={180}/>\n <KeywordInput placeholder=\"请输入 IP 地址\" {...bindKeyword('ip')} width={160}/>\n <KeywordInput placeholder=\"请求/变更内容包含…\" {...bindKeyword('content')} width={180}/>\n <StatusSelect items={OPERATION_LOG_RESULT_OPTIONS} {...bind('status')}/>\n <FilterSelect placeholder=\"全部操作来源\" items={OPERATION_IMPERSONATED_OPTIONS} {...bind('impersonated')} width={140}/>\n <DateRangeFilter {...bind('timeRange')}/>\n </>)} onSearch={handleSearch} onReset={handleReset} filterTitle=\"操作记录筛选\"/>\n <OperationLogsTable loading={query.isFetching} dataSource={list} onRefresh={() => void query.refetch()} columnSettingsKey={columnSettingsKey} pagination={buildPagination(total)}/>\n </>);\n}\n"]}
1
+ {"version":3,"file":"UserLogsPanels-nbIrMym9.js","mappings":"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","names":["__iconData","__iconData$3","createLucideIcon","__iconData$2","__iconData$1","ListSearchToolbar","props","keyword","filters","onSearch","onReset","derived","deriveFilterControls","_Fragment","actionsSlot","useRenderedSlot","desktopActions","present","create","actions","SlotProbe","SearchToolbar","SearchButton","ResetButton","mobileActions","node","normalizeZoom","value","rounded","ZOOM_STEP","MAX_ZOOM","MIN_ZOOM","AvatarCropperModal","cropperRef","useRef","srcRef","src","setSrc","useState","rotate","setRotate","zoom","setZoom","updateSrc","next","useEffect","file","handleRotate","prev","delta","handleZoomChange","handleZoomStep","handleConfirm","canvas","blob","onConfirm","AppModal","Space","Button","onCancel","confirmLoading","Cropper","RotateCcw","RotateCw","ZoomOut","ZoomIn","useAvatarCropUpload","inputRef","cropFile","setCropFile","uploading","setUploading","handleFileSelect","maxSizeMB","Toast","handleCropConfirm","uploadedUrl","uploadBlob","err","onUploaded","AvatarSelectModal","avatarUpload","onSelect","url","loading","Camera","currentAvatar","onRemove","calcStrength","pwd","score","PasswordStrengthMeter","strength","useMemo","password","LEVEL_CONFIG","policyChecks","policy","minLengthOk","minLengthLabel","i","color","label","CheckCircle","Circle","l","ok","jsx","useScopeListKey","userId","scope","selfKey","adminListsKey","UserLoginLogsPanel","isSelf","profileKeys","loginLogKeys","useListSearch","params","useFilterQuery","enumValueOf","LOGIN_EVENT_TYPES","submittedParams","LOGIN_STATUSES","formatDateTimeRangeForApi","selfQuery","useProfileLoginLogs","enabled","scopedQuery","useLoginLogList","query","list","total","FilterSelect","LOGIN_EVENT_TYPE_OPTIONS","bind","StatusSelect","LOGIN_STATUS_OPTIONS","DateRangeFilter","handleSearch","handleReset","LoginLogsTable","buildPagination","UserOperationLogsPanel","operationLogKeys","OPERATION_LOG_RESULTS","useProfileOperationLogs","useOperationLogList","KeywordInput","bindKeyword","OPERATION_METHOD_OPTIONS","OPERATION_LOG_RESULT_OPTIONS","OPERATION_IMPERSONATED_OPTIONS","OperationLogsTable"],"ignoreList":[0,1,2,3],"sources":["../../../../node_modules/lucide-react/dist/esm/icons/camera.mjs","../../../../node_modules/lucide-react/dist/esm/icons/circle.mjs","../../../../node_modules/lucide-react/dist/esm/icons/zoom-in.mjs","../../../../node_modules/lucide-react/dist/esm/icons/zoom-out.mjs","../../src/components/list-page/ListSearchToolbar.tsx","../../src/components/AvatarCropperModal.tsx","../../src/hooks/useAvatarCropUpload.ts","../../src/components/AvatarSelectModal.tsx","../../src/components/PasswordStrengthMeter.tsx","../../src/components/logs/UserLogsPanels.tsx"],"sourcesContent":[null,null,null,null,"import type { ReactNode } from 'react';\nimport { SearchToolbar } from \"@/components/SearchToolbar\";\nimport { ResetButton, SearchButton } from \"@/components/toolbar-controls\";\nimport { SlotProbe } from \"@/components/rendered-slot\";\nimport { useRenderedSlot } from \"@fudanda/react-core/hooks/useRenderedSlot\";\nimport { deriveFilterControls, type FilterOverrides, type FilterPageLike, type FilterSpec } from \"@/components/list-page/ContractFilters\";\ninterface ListSearchToolbarBaseProps {\n /** 新增按钮:桌面端排在查询 / 重置之后,移动端保留在主区 */\n create?: ReactNode;\n /** 低频操作(导出 / 批量 / 同步…):桌面端跟在新增之后,移动端收进「更多操作」菜单 */\n actions?: ReactNode;\n /** 覆盖移动端更多菜单内容;缺省与 `actions` 相同 */\n mobileActions?: ReactNode;\n filterTitle?: ReactNode;\n actionTitle?: string;\n className?: string;\n}\n/** 槽位写法:控件由页面创建(非契约类型的筛选状态、树形 / 客户端过滤等非标准形态) */\nexport interface ListSearchToolbarSlotProps extends ListSearchToolbarBaseProps {\n page?: undefined;\n /** 关键词输入(`KeywordInput`),桌面与移动端主区都展示 */\n keyword?: ReactNode;\n /** 其余筛选项(状态 / 类型 / 时间范围…):桌面端内联,移动端收进筛选抽屉 */\n filters?: ReactNode;\n onSearch: () => void;\n onReset: () => void;\n}\n/**\n * 契约写法:传 `useListPage({ contract })` 的返回,`filters` 只声明键(与成对的时间范围端点),\n * 控件按契约 query 的 `x-filter` 语义派生(见 `ContractFilters.tsx`);关键字类自动进主区。\n * 查询 / 重置从 `page` 取,专用控件在 `overrides` 里给出,额外的手写控件仍可放 `extraFilters`。\n */\nexport interface ListSearchToolbarContractProps<TSearch> extends ListSearchToolbarBaseProps {\n page: FilterPageLike<TSearch> & {\n readonly toolbarProps: {\n readonly onSearch: () => void;\n readonly onReset: () => void;\n };\n };\n filters: ReadonlyArray<FilterSpec<TSearch>>;\n overrides?: FilterOverrides<TSearch>;\n /** 契约之外的手写筛选控件(`{...page.bind('x')}` 绑定),排在派生控件之后 */\n extraFilters?: ReactNode;\n keyword?: undefined;\n onSearch?: undefined;\n onReset?: undefined;\n}\nexport type ListSearchToolbarProps<TSearch = Record<string, unknown>> = ListSearchToolbarSlotProps | ListSearchToolbarContractProps<TSearch>;\nconst present = (node: ReactNode) => Boolean(node) && !(Array.isArray(node) && node.length === 0);\n/**\n * 标准列表页工具栏:把 `SearchToolbar` 的桌面 / 移动槽位排布规则写在一处——\n * 桌面:关键词 → 筛选项 → 查询 / 重置 → 新增 → 低频操作;\n * 移动:主区 关键词 + 查询 + 新增,筛选项进抽屉(应用 / 重置即查询 / 重置),低频操作进更多菜单。\n * 契约写法下控件由契约 query 的语义派生;槽位写法下控件仍由页面创建(占位文案、字典项、权限门控都在页面里可见)。\n */\nexport function ListSearchToolbar<TSearch = Record<string, unknown>>(props: ListSearchToolbarProps<TSearch>) {\n const { create, actions, mobileActions, filterTitle, actionTitle, className } = props;\n let keyword: ReactNode;\n let filters: ReactNode;\n let onSearch: () => void;\n let onReset: () => void;\n if (props.page) {\n const derived = deriveFilterControls({ page: props.page, specs: props.filters, overrides: props.overrides });\n keyword = derived.keyword.length ? derived.keyword : undefined;\n filters = derived.filters.length || props.extraFilters ? <>{derived.filters}{props.extraFilters}</> : undefined;\n ({ onSearch, onReset } = props.page.toolbarProps);\n }\n else {\n ({ keyword, filters, onSearch, onReset } = props);\n }\n // 低频操作可能整体渲染为空(带 permission 的导出 / 按选中数出现的批量按钮):按实际渲染结果决定移动端是否出现更多菜单\n const actionsSlot = useRenderedSlot();\n const desktopActions = present(create) || present(actions)\n ? <>{create}<SlotProbe ref={actionsSlot.probeRef}>{actions}</SlotProbe></>\n : undefined;\n return (<SearchToolbar className={className} primary={(<>\n {keyword}\n {filters}\n <SearchButton onClick={onSearch}/>\n <ResetButton onClick={onReset}/>\n </>)} actions={desktopActions} mobilePrimary={(<>\n {keyword}\n <SearchButton onClick={onSearch}/>\n {create}\n </>)} mobileFilters={present(filters) ? filters : undefined}\n // SearchToolbar 对 mobileActions 缺省回落到 actions(含新增按钮,而新增已在移动主区),这里显式给出:\n // 页面覆盖优先;否则只有低频操作真的渲染出了元素才复用,用 false 明确「没有更多操作」\n mobileActions={mobileActions ?? (actionsSlot.rendered ? actions : false)} filterTitle={filterTitle} actionTitle={actionTitle} onFilterApply={onSearch} onFilterReset={onReset}/>);\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { Button, Cropper, Space } from \"@/ui\";\nimport { RotateCcw, RotateCw, ZoomIn, ZoomOut } from 'lucide-react';\nimport { AppModal } from \"@/components/AppModal\";\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 3;\nconst ZOOM_STEP = 0.1;\n/**\n * 将缩放值收敛到步长精度并夹紧到允许区间;非有限值返回 null。\n * Semi Slider 在轨道尚无布局宽度时可能算出 NaN,一律在此挡掉,避免污染受控的 zoom 状态。\n */\nfunction normalizeZoom(value: number): number | null {\n if (!Number.isFinite(value))\n return null;\n const rounded = Number((Math.round(value / ZOOM_STEP) * ZOOM_STEP).toFixed(2));\n return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, rounded));\n}\nexport interface AvatarCropperModalProps {\n /** 待裁剪的原始图片文件;为 null 时关闭弹窗 */\n readonly file: File | null;\n /** 确认按钮 loading(上传/保存中) */\n readonly confirmLoading?: boolean;\n readonly onCancel: () => void;\n /** 用户确认裁剪后回调裁剪结果(JPEG Blob,质量 0.85) */\n readonly onConfirm: (blob: Blob) => void;\n}\n/**\n * 头像裁剪弹窗:圆形裁剪框 + 受控旋转 / 缩放(Semi Cropper `rotate` / `zoom`)。\n * 内部管理旋转角度、缩放倍数与图片预览地址(blob URL)的创建与释放。\n */\nexport function AvatarCropperModal({ file, confirmLoading, onCancel, onConfirm }: AvatarCropperModalProps) {\n const cropperRef = useRef<Cropper>(null);\n const srcRef = useRef('');\n const [src, setSrc] = useState('');\n const [rotate, setRotate] = useState(0);\n const [zoom, setZoom] = useState(1);\n // 更新预览地址并释放上一个 blob URL\n const updateSrc = (next: string) => {\n if (srcRef.current.startsWith('blob:'))\n URL.revokeObjectURL(srcRef.current);\n srcRef.current = next;\n setSrc(next);\n };\n useEffect(() => {\n setRotate(0);\n setZoom(1);\n if (!file) {\n updateSrc('');\n return;\n }\n updateSrc(URL.createObjectURL(file));\n return () => {\n updateSrc('');\n };\n }, [file]);\n function handleRotate(delta: number) {\n setRotate((prev) => (((prev + delta) % 360) + 360) % 360);\n }\n function handleZoomChange(value: number | number[] | undefined) {\n if (value === undefined)\n return;\n const next = normalizeZoom(typeof value === 'number' ? value : value[0]);\n if (next !== null)\n setZoom(next);\n }\n function handleZoomStep(delta: number) {\n setZoom((prev) => normalizeZoom(Number((prev + delta).toFixed(2))) ?? prev);\n }\n function handleConfirm() {\n const canvas = cropperRef.current?.getCropperCanvas();\n if (!canvas)\n return;\n canvas.toBlob((blob) => {\n if (!blob)\n return;\n onConfirm(blob);\n }, 'image/jpeg', 0.85);\n }\n return (<AppModal title=\"裁剪头像\" visible={file !== null} onCancel={onCancel} footer={<Space>\n <Button onClick={onCancel}>取消</Button>\n <Button type=\"primary\" loading={confirmLoading} onClick={handleConfirm}>确认并上传</Button>\n </Space>} width={520} centered>\n <div style={{ width: '100%', height: 380 }}>\n {src && (<Cropper ref={cropperRef} src={src} shape=\"round\" aspectRatio={1} showResizeBox rotate={rotate} zoom={zoom} minZoom={MIN_ZOOM} maxZoom={MAX_ZOOM} zoomStep={ZOOM_STEP} onZoomChange={handleZoomChange} style={{ width: '100%', height: '100%' }}/>)}\n </div>\n <div style={{ display: 'flex', justifyContent: 'center', gap: 12, marginTop: 12 }}>\n <Button icon={<RotateCcw size={14}/>} size=\"small\" theme=\"borderless\" onClick={() => handleRotate(-90)}>向左旋转</Button>\n <Button icon={<RotateCw size={14}/>} size=\"small\" theme=\"borderless\" onClick={() => handleRotate(90)}>向右旋转</Button>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12, marginTop: 8 }}>\n <Button icon={<ZoomOut size={14}/>} size=\"small\" theme=\"borderless\" aria-label=\"缩小\" disabled={zoom <= MIN_ZOOM} onClick={() => handleZoomStep(-ZOOM_STEP)}>缩小</Button>\n <span style={{ fontSize: 12, color: 'var(--arc-antd-color-text-2)', minWidth: 44, textAlign: 'center' }}>{zoom.toFixed(1)}x</span>\n <Button icon={<ZoomIn size={14}/>} size=\"small\" theme=\"borderless\" aria-label=\"放大\" disabled={zoom >= MAX_ZOOM} onClick={() => handleZoomStep(ZOOM_STEP)}>放大</Button>\n </div>\n </AppModal>);\n}\n","import { useRef, useState, type ChangeEvent, type RefObject } from 'react';\nimport { Toast } from \"@/ui\";\nexport interface UseAvatarCropUploadOptions {\n /**\n * 裁剪结果上传成功后的落库动作(改本人资料 / 管理员改他人)。\n * 返回 `false` 表示落库失败且已自行提示,裁剪弹窗保持打开供重试;其余返回值视为成功并关闭裁剪弹窗。\n */\n readonly onUploaded: (url: string) => Promise<boolean | void> | boolean | void;\n /**\n * 裁剪 Blob 的上传实现(必填):管理后台传文件中心通道\n *(`utils/avatar-upload.ts` 的 `uploadAvatarBlobToFileCenter`),\n * 会员前台等走独立上传通道的调用方传入自己的实现。\n * 上传实现内部不要弹失败提示——失败由本 hook 统一提示。\n */\n readonly uploadBlob: (blob: Blob) => Promise<string>;\n /** 本地图片大小上限(MB),默认 2;与会员端编辑资料页保持一致 */\n readonly maxSizeMB?: number;\n}\n/**\n * 头像「选文件 → 裁剪 → 上传文件中心 → 落库」的公共流程。\n *\n * 页面只需:把 `fileInputProps` 展开到隐藏的 `<input>`、用 `openFilePicker` 触发选择、\n * 把 `cropperProps` 展开到 `<AvatarCropperModal>`,并在 `onUploaded` 里完成各自的落库动作。\n * 上传失败的提示由本 hook 统一给出(请求层静默),落库失败的提示由调用方按场景给出。\n */\nexport function useAvatarCropUpload({ onUploaded, uploadBlob, maxSizeMB = 2 }: UseAvatarCropUploadOptions) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [cropFile, setCropFile] = useState<File | null>(null);\n const [uploading, setUploading] = useState(false);\n function handleFileSelect(e: ChangeEvent<HTMLInputElement>) {\n const file = e.target.files?.[0];\n if (!file)\n return;\n e.target.value = '';\n if (!file.type.startsWith('image/')) {\n Toast.warning('请选择图片文件');\n return;\n }\n if (file.size > maxSizeMB * 1024 * 1024) {\n Toast.warning(`图片不能超过 ${maxSizeMB}MB`);\n return;\n }\n setCropFile(file);\n }\n async function handleCropConfirm(blob: Blob) {\n let uploadedUrl: string;\n setUploading(true);\n try {\n uploadedUrl = await uploadBlob(blob);\n }\n catch (err) {\n Toast.error(err instanceof Error && err.message ? err.message : '上传失败');\n return;\n }\n finally {\n setUploading(false);\n }\n if ((await onUploaded(uploadedUrl)) === false)\n return;\n setCropFile(null);\n }\n return {\n /** 当前待裁剪的文件;为 null 时裁剪弹窗关闭 */\n cropFile,\n uploading,\n openFilePicker: () => inputRef.current?.click(),\n /** 展开到隐藏的 `<input>`;需要额外属性(如 `id`)时在展开后补充 */\n fileInputProps: {\n ref: inputRef as RefObject<HTMLInputElement>,\n type: 'file' as const,\n accept: 'image/*',\n style: { display: 'none' } as const,\n onChange: handleFileSelect,\n },\n /** 展开到 `<AvatarCropperModal>`;`confirmLoading` 由调用方按自身落库状态合并后传入 */\n cropperProps: {\n file: cropFile,\n onCancel: () => setCropFile(null),\n onConfirm: (blob: Blob) => { void handleCropConfirm(blob); },\n },\n };\n}\n","import { Button } from \"@/ui\";\nimport { Camera } from 'lucide-react';\nimport { AppModal } from \"@/components/AppModal\";\nimport { AvatarCropperModal } from \"@/components/AvatarCropperModal\";\nimport { useAvatarCropUpload } from \"@/hooks/useAvatarCropUpload\";\nexport interface AvatarSelectModalProps {\n readonly visible: boolean;\n /** 当前头像地址;有值且传了 onRemove 时展示移除入口 */\n readonly currentAvatar?: string | null;\n /** 调用方落库中的 loading(与上传 loading 合并后传给裁剪确认按钮) */\n readonly confirmLoading?: boolean;\n /**\n * 裁剪 Blob 的上传实现(必填):管理后台传文件中心通道\n *(`utils/avatar-upload.ts` 的 `uploadAvatarBlobToFileCenter`),\n * 会员前台传入自己的上传实现(如会员头像上传接口)。\n */\n readonly uploadBlob: (blob: Blob) => Promise<string>;\n readonly onCancel: () => void;\n /** 裁剪上传成功;落库与关闭弹窗由调用方负责 */\n readonly onSelect: (url: string) => void;\n /** 移除头像;不传则不展示移除入口(移除前的确认由调用方负责) */\n readonly onRemove?: () => void;\n}\n/**\n * 统一头像选择弹窗:本地上传(选中后进裁剪)与移除。\n * 管理后台个人中心、用户管理、会员前台编辑资料三处共用。\n */\nexport function AvatarSelectModal({ visible, currentAvatar, confirmLoading, uploadBlob, onCancel, onSelect, onRemove }: AvatarSelectModalProps) {\n const avatarUpload = useAvatarCropUpload({\n uploadBlob,\n onUploaded: (url) => {\n onSelect(url);\n },\n });\n const loading = avatarUpload.uploading || (confirmLoading ?? false);\n return (<>\n <AppModal title=\"更换头像\" visible={visible} onCancel={onCancel} footer={null} width={480} centered>\n <div>\n <div style={{ fontSize: 13, color: 'var(--arc-antd-color-text-2)', marginBottom: 8 }}>本地上传(≤ 2MB,上传后可裁剪、旋转、缩放)</div>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button icon={<Camera size={14}/>} loading={avatarUpload.uploading} onClick={avatarUpload.openFilePicker}>\n 选择图片\n </Button>\n {currentAvatar && onRemove && (<Button theme=\"borderless\" type=\"danger\" onClick={onRemove}>\n 移除头像\n </Button>)}\n </div>\n <input {...avatarUpload.fileInputProps}/>\n </div>\n </AppModal>\n\n <AvatarCropperModal {...avatarUpload.cropperProps} confirmLoading={loading}/>\n </>);\n}\n","import { useMemo } from 'react';\nimport { CheckCircle, Circle } from 'lucide-react';\nimport type { PasswordPolicy } from \"@fudanda/react-core/utils/password-policy\";\ninterface PasswordStrengthMeterProps {\n /** 当前密码输入值;为空时组件不渲染任何内容 */\n readonly password: string;\n /** 密码策略(可选);提供时在强度条下方展示策略达标清单 */\n readonly policy?: PasswordPolicy | null;\n}\ntype Level = 0 | 1 | 2 | 3 | 4;\nconst LEVEL_CONFIG: Record<Level, {\n label: string;\n color: string;\n}> = {\n 0: { label: '', color: 'transparent' },\n 1: { label: '弱', color: 'var(--arc-antd-color-danger)' },\n 2: { label: '一般', color: 'var(--arc-antd-color-warning)' },\n 3: { label: '良好', color: 'var(--arc-antd-color-info)' },\n 4: { label: '强', color: 'var(--arc-antd-color-success)' },\n};\nfunction calcStrength(pwd: string): Level {\n if (!pwd)\n return 0;\n if (pwd.length < 6)\n return 1;\n let score = 0;\n if (/[a-z]/.test(pwd))\n score++;\n if (/[A-Z]/.test(pwd))\n score++;\n if (/\\d/.test(pwd))\n score++;\n if (/[^a-zA-Z0-9]/.test(pwd))\n score++;\n if (pwd.length >= 10)\n score++;\n if (score <= 1)\n return 1;\n if (score === 2)\n return 2;\n if (score === 3)\n return 3;\n return 4;\n}\n/**\n * 密码强度指示器。\n *\n * - `password` 为空时不渲染任何内容,可作为 `helpText` 直接传入 Semi `Form.Input`。\n * - 提供 `policy` 时,在强度条下方展示策略达标清单(✓ / ○)。\n */\nexport function PasswordStrengthMeter({ password, policy }: PasswordStrengthMeterProps) {\n const strength = useMemo(() => calcStrength(password), [password]);\n if (!password)\n return null;\n const { label, color } = LEVEL_CONFIG[strength];\n const policyChecks = policy\n ? [\n ...(policy.requireUppercase\n ? [{ label: '含大写字母', ok: /[A-Z]/.test(password) }]\n : []),\n ...(policy.requireSpecialChar\n ? [{ label: '含特殊字符', ok: /[^a-zA-Z0-9]/.test(password) }]\n : []),\n ]\n : null;\n // 最小长度单独提出,与强度条同行显示\n const minLengthOk = policy ? password.length >= policy.minLength : null;\n const minLengthLabel = policy ? `至少 ${policy.minLength} 位` : null;\n return (<div style={{ padding: '4px 0' }}>\n {/* 强度分段条 + 标签 + 最小位数(同行) */}\n <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <div style={{ display: 'flex', gap: 3, flex: 1 }}>\n {([1, 2, 3, 4] as Level[]).map((i) => (<div key={i} style={{\n flex: 1,\n height: 4,\n borderRadius: 'var(--arc-antd-border-radius-small)',\n background: i <= strength ? color : 'var(--arc-antd-color-fill-2)',\n transition: 'background 0.25s ease',\n }}/>))}\n </div>\n <span style={{ fontSize: 12, fontWeight: 600, color, minWidth: 28, textAlign: 'right' }}>\n {label}\n </span>\n {minLengthLabel !== null && (<div style={{ display: 'flex', alignItems: 'center', gap: 3, flexShrink: 0 }}>\n {minLengthOk\n ? <CheckCircle size={11} style={{ flexShrink: 0, color: 'var(--arc-antd-color-success)' }}/>\n : <Circle size={11} style={{ flexShrink: 0, color: 'var(--arc-antd-color-text-3)' }}/>}\n <span style={{\n fontSize: 12,\n color: minLengthOk ? 'var(--arc-antd-color-success)' : 'var(--arc-antd-color-text-3)',\n transition: 'color 0.2s',\n }}>\n {minLengthLabel}\n </span>\n </div>)}\n </div>\n\n {/* 其他策略达标项(大写/特殊字符等),仍在下方 */}\n {policyChecks && policyChecks.length > 0 && (<div style={{ display: 'flex', flexWrap: 'wrap', gap: '3px 14px', marginTop: 5 }}>\n {policyChecks.map(({ label: l, ok }) => (<div key={l} style={{\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n fontSize: 12,\n color: ok ? 'var(--arc-antd-color-success)' : 'var(--arc-antd-color-text-3)',\n transition: 'color 0.2s',\n }}>\n {ok\n ? <CheckCircle size={11} style={{ flexShrink: 0 }}/>\n : <Circle size={11} style={{ flexShrink: 0 }}/>}\n <span>{l}</span>\n </div>))}\n </div>)}\n </div>);\n}\n","/**\n * 「某个人」的登录记录 / 操作记录面板:个人中心与用户管理(记录抽屉)共用同一份筛选条、分页与表格接线,\n * 差异只在取数来源:\n *\n * - `scope = { kind: 'self' }`:走 `authContract.myLoginLogs` / `myOperationLogs`,服务端按当前登录用户过滤;\n * - `scope = { kind: 'user', userId }`:走管理侧 `loginLogContract.list` / `operationLogContract.list` 的 `userId` 精确筛选\n * (管理侧的 `username` 是模糊关键字,不能用来定位到某个人)。\n *\n * 两个 scope 的分页 / 筛选状态是同一份(同一行控件服务两条链路),因此两条查询都无条件调用,\n * 只把当前生效的那条 `enabled`;未激活的一条不会发请求(抽屉里切 tab 才拉取)。\n *\n * 表格本身仍是既有的 `LoginLogsTable` / `OperationLogsTable`,这里只负责取数接线与筛选条。\n */\nimport { useMemo } from 'react';\nimport { DateRangeFilter, FilterSelect, KeywordInput, StatusSelect } from \"@/components/search-filters\";\nimport { ListSearchToolbar } from \"@/components/list-page/index\";\nimport { LoginLogsTable } from \"@/components/logs/LoginLogsTable\";\nimport { OperationLogsTable } from \"@/components/logs/OperationLogsTable\";\nimport { useListSearch } from \"@fudanda/react-core/hooks/useListSearch\";\nimport { useFilterQuery } from \"@fudanda/react-core/hooks/useFilterQuery\";\nimport { profileKeys, useProfileLoginLogs, useProfileOperationLogs } from \"@fudanda/react-core/hooks/queries/profile\";\nimport { loginLogKeys, useLoginLogList } from \"@fudanda/react-core/hooks/queries/login-logs\";\nimport { operationLogKeys, useOperationLogList } from \"@fudanda/react-core/hooks/queries/operation-logs\";\nimport { enumValueOf } from '@fudanda/shared/core';\nimport { formatDateTimeRangeForApi } from \"@fudanda/react-core/utils/date\";\nimport { LOGIN_EVENT_TYPE_OPTIONS, LOGIN_EVENT_TYPES, LOGIN_STATUS_OPTIONS, LOGIN_STATUSES } from '@fudanda/shared/identity';\nimport { OPERATION_LOG_RESULT_OPTIONS, OPERATION_LOG_RESULTS } from '@fudanda/shared/platform';\nimport type { QueryKey } from '@tanstack/react-query';\n/** 记录归属:本人(个人中心)或指定用户(用户管理) */\nexport type UserLogsScope = {\n readonly kind: 'self';\n} | {\n readonly kind: 'user';\n readonly userId: number;\n};\nexport interface UserLogsPanelProps {\n readonly scope: UserLogsScope;\n /** 面板未激活时不发请求(抽屉 / 个人中心按当前 tab 传) */\n readonly enabled?: boolean;\n /** 列设置持久化 key:两个入口各自记住列宽 */\n readonly columnSettingsKey: string;\n}\n/** 操作记录筛选选项:与管理侧操作日志页保持一致(操作人除外,面板本身已限定到某个人) */\nconst OPERATION_METHOD_OPTIONS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].map((value) => ({ value, label: value }));\n/** 操作来源筛选:与契约 `impersonated` 的 queryBool 文案一致 */\nconst OPERATION_IMPERSONATED_OPTIONS = [\n { value: 'true', label: '仅模拟操作' },\n { value: 'false', label: '仅本人操作' },\n];\ninterface LoginLogSearchParams {\n eventType?: string;\n status?: string;\n timeRange: [\n Date,\n Date\n ] | null;\n}\ninterface OperationLogSearchParams {\n module: string;\n description: string;\n method?: string;\n path: string;\n ip: string;\n status?: string;\n content: string;\n /** 'true' 仅模拟登录期间的操作 / 'false' 仅本人操作;未选为 undefined */\n impersonated?: string;\n timeRange: [\n Date,\n Date\n ] | null;\n}\n/**\n * 列表查询的筛选键:指定用户时按 userId 分片,让「记住列表筛选条件」不跨用户串味;\n * 同时把整个管理侧列表键放进 extraKeys——「查询」要失效的正是该用户那份列表(前缀匹配同域全部列表查询)。\n */\nfunction useScopeListKey(selfKey: QueryKey, adminListsKey: QueryKey, scope: UserLogsScope) {\n const userId = scope.kind === 'user' ? scope.userId : undefined;\n return useMemo(() => ({\n listKey: userId === undefined ? selfKey : [...adminListsKey, 'user', userId],\n extraKeys: userId === undefined ? undefined : [adminListsKey],\n }), [selfKey, adminListsKey, userId]);\n}\nexport function UserLoginLogsPanel({ scope, enabled = true, columnSettingsKey }: UserLogsPanelProps) {\n const isSelf = scope.kind === 'self';\n const { listKey, extraKeys } = useScopeListKey(profileKeys.loginLogs, loginLogKeys.lists, scope);\n const { page, pageSize, buildPagination, bind, submittedParams, handleSearch, handleReset, } = useListSearch<LoginLogSearchParams>({\n defaults: { eventType: undefined, status: undefined, timeRange: null },\n listKey,\n extraKeys,\n });\n const filterQuery = useFilterQuery({\n eventType: enumValueOf(LOGIN_EVENT_TYPES, submittedParams.eventType),\n status: enumValueOf(LOGIN_STATUSES, submittedParams.status),\n ...formatDateTimeRangeForApi(submittedParams.timeRange),\n });\n const params = { page, pageSize, ...filterQuery };\n // 两条链路都调用、只启用生效的一条(React 不允许条件调用 hook)\n const selfQuery = useProfileLoginLogs(params, enabled && isSelf);\n const scopedQuery = useLoginLogList({ ...params, userId: scope.kind === 'user' ? scope.userId : undefined }, enabled && !isSelf);\n const query = isSelf ? selfQuery : scopedQuery;\n const list = query.data?.list ?? [];\n const total = query.data?.total ?? 0;\n return (<>\n <ListSearchToolbar filters={(<>\n <FilterSelect placeholder=\"全部事件\" items={LOGIN_EVENT_TYPE_OPTIONS} {...bind('eventType')}/>\n <StatusSelect items={LOGIN_STATUS_OPTIONS} {...bind('status')}/>\n <DateRangeFilter {...bind('timeRange')}/>\n </>)} onSearch={handleSearch} onReset={handleReset} filterTitle=\"登录记录筛选\"/>\n <LoginLogsTable loading={query.isFetching} dataSource={list} onRefresh={() => void query.refetch()} columnSettingsKey={columnSettingsKey} pagination={buildPagination(total)}/>\n </>);\n}\nexport function UserOperationLogsPanel({ scope, enabled = true, columnSettingsKey }: UserLogsPanelProps) {\n const isSelf = scope.kind === 'self';\n const { listKey, extraKeys } = useScopeListKey(profileKeys.operationLogs, operationLogKeys.lists, scope);\n const { page, pageSize, buildPagination, bind, bindKeyword, submittedParams, handleSearch, handleReset, } = useListSearch<OperationLogSearchParams>({\n defaults: {\n module: '', description: '', method: undefined, path: '', ip: '',\n status: undefined, content: '', impersonated: undefined, timeRange: null,\n },\n listKey,\n extraKeys,\n });\n const filterQuery = useFilterQuery({\n module: submittedParams.module,\n description: submittedParams.description,\n method: submittedParams.method,\n path: submittedParams.path,\n ip: submittedParams.ip,\n status: enumValueOf(OPERATION_LOG_RESULTS, submittedParams.status),\n content: submittedParams.content,\n impersonated: submittedParams.impersonated === 'true' ? true : submittedParams.impersonated === 'false' ? false : undefined,\n ...formatDateTimeRangeForApi(submittedParams.timeRange),\n });\n const params = { page, pageSize, ...filterQuery };\n const selfQuery = useProfileOperationLogs(params, enabled && isSelf);\n const scopedQuery = useOperationLogList({ ...params, userId: scope.kind === 'user' ? scope.userId : undefined }, enabled && !isSelf);\n const query = isSelf ? selfQuery : scopedQuery;\n const list = query.data?.list ?? [];\n const total = query.data?.total ?? 0;\n return (<>\n <ListSearchToolbar filters={(<>\n <KeywordInput placeholder=\"请输入功能模块\" {...bindKeyword('module')} width={160}/>\n <KeywordInput placeholder=\"请输入操作描述\" {...bindKeyword('description')} width={160}/>\n <FilterSelect placeholder=\"全部请求方法\" items={OPERATION_METHOD_OPTIONS} {...bind('method')} width={140}/>\n <KeywordInput placeholder=\"请输入请求路径\" {...bindKeyword('path')} width={180}/>\n <KeywordInput placeholder=\"请输入 IP 地址\" {...bindKeyword('ip')} width={160}/>\n <KeywordInput placeholder=\"请求/变更内容包含…\" {...bindKeyword('content')} width={180}/>\n <StatusSelect items={OPERATION_LOG_RESULT_OPTIONS} {...bind('status')}/>\n <FilterSelect placeholder=\"全部操作来源\" items={OPERATION_IMPERSONATED_OPTIONS} {...bind('impersonated')} width={140}/>\n <DateRangeFilter {...bind('timeRange')}/>\n </>)} onSearch={handleSearch} onReset={handleReset} filterTitle=\"操作记录筛选\"/>\n <OperationLogsTable loading={query.isFetching} dataSource={list} onRefresh={() => void query.refetch()} columnSettingsKey={columnSettingsKey} pagination={buildPagination(total)}/>\n </>);\n}\n"]}