// VL_VERSION:4.2.13
<App-UserCenter "root">

# SysConfig
DEVICE_TARGET:"PC"
SCREEN_RESOLUTION:"1440x900"

# Frontend Global Vars
$currentPage(STRING) = "login"
$isLogin(BOOL) = false
$currentUserId(STRING) = ""
$currentUserNickname(STRING) = ""
$currentUserEmail(STRING) = ""
$selectedUserId(STRING) = ""
$returnUrl(STRING) = ""
$userMenuOpen(BOOL) = false
$pcInitChecked(BOOL) = false
$pcInitialized(BOOL) = false
$isAdmin(BOOL) = false
$isSuperAdmin(BOOL) = false
$isPcAdmin(BOOL) = false
$adminActions([STRING]) = []

# Frontend Tree
<Page-Main "mainPage"> path:"home"
-<Section-SessionGate "sessionGate"> width:"0" height:"0"
-<Section-PcInitGate "pcInitGate"> width:"0" height:"0"
-<Col-ViewportShell> min-width:"100vw" min-height:"100dvh"
--<If-Public> conditions:($currentPage == "login" || $currentPage == "register" || $currentPage == "forgotPassword" || $currentPage == "setup")
---<Col-PublicShell> width:"100%" flex:"1" min-height:"0" align-items:"center" justify-content:"center" padding:"24px" sk.bg:"#f5f6f8"
----<If-PageSetup> conditions:($currentPage == "setup")
-----<Section-SetupPage "setupPage"> width:"460px" max-width:"100%"
----<If-PageLogin> conditions:($currentPage == "login")
-----<Section-LoginPage "loginPage"> width:"400px" max-width:"100%"
----<If-PageRegister> conditions:($currentPage == "register")
-----<Section-RegisterPage "registerPage"> width:"460px" max-width:"100%"
----<If-PageForgotPassword> conditions:($currentPage == "forgotPassword")
-----<Section-ForgotPasswordPage "forgotPasswordPage"> width:"400px" max-width:"100%"
--<If-Private> conditions:($currentPage != "login" && $currentPage != "register" && $currentPage != "forgotPassword" && $currentPage != "setup")
---<Col-AppShell> width:"100%" flex:"1" min-height:"0" gap:"0" sk.bg:"#ffffff"
----<Row-TopBar> width:"100%" height:"56px" align-items:"center" padding:"0 20px" gap:"10px" sk.bg:"#ffffff" sk.borderBottom:"1px solid #ebedf1"
-----<Col-BrandMark> width:"32px" height:"32px" radius:"md" align-items:"center" justify-content:"center" sk.bg:"#2549d6"
------<Icon-BrandMark> svgCode:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'><path d='M12 2.2L4.5 5.3v5.4c0 4.6 3.2 8.9 7.5 10 4.3-1.1 7.5-5.4 7.5-10V5.3L12 2.2z' fill='#ffffff'/><path d='M8.6 11.9l2.3 2.3 4.4-4.6' fill='none' stroke='#2549d6' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/></svg>" width:"20px" height:"20px"
-----<Col-BrandLockup> gap:"1px"
------<Text-Brand> value:"用户权限中心" style:"caption" size:"sm"
------<Text-SubBrand> value:"统一身份与权限管理" style:"muted" size:"xs"
-----<Col-SpacerFill> flex:"1"
-----<Row-UserChip> align-items:"center" gap:"8px" padding:"3px 6px 3px 3px" sk.radius:"9999px" sk.bg:"#f4f6f9"
------<Col-Avatar> width:"26px" height:"26px" sk.radius:"9999px" align-items:"center" justify-content:"center" sk.bg:"#2549d6"
-------<Text-AvatarT> value:($currentUserNickname != "" ? $currentUserNickname.slice(0,1) : "U") style:"body" size:"xs" sk.fg:"#ffffff"
------<Button-UserMenu> value:(($currentUserNickname != "" ? $currentUserNickname : "未登录") + "  ▾") style:"neutral|ghost|default|sm|actionable" min-height:"26px"
----<If-UserMenuOpen> conditions:($userMenuOpen == true)
-----<Col-UserDrop> position:"absolute" top:"58px" right:"20px" min-width:"212px" sk.bg:"#ffffff" border:"1px solid #ebedf1" radius:"md" sk.shadow:"0 10px 30px rgba(17,24,39,0.12)" z-index:"100"
------<Col-UserDropHeader> width:"100%" padding:"12px 16px" gap:"2px"
-------<Text-DropName> value:($currentUserNickname != "" ? $currentUserNickname : "未登录") style:"caption" size:"sm"
-------<Text-DropEmail> value:$currentUserEmail style:"muted" size:"xs"
------<Divider> width:"100%"
------<Button-MenuProfile> value:"个人资料" style:"neutral|ghost|default|sm|actionable" width:"100%" justify-content:"flex-start" padding:"0 16px" min-height:"34px" border:"none"
------<Button-MenuSecurity> value:"安全设置" style:"neutral|ghost|default|sm|actionable" width:"100%" justify-content:"flex-start" padding:"0 16px" min-height:"34px" border:"none"
------<Divider> width:"100%"
------<Button-MenuLogout> value:"登出" style:"danger|ghost|default|sm|actionable" width:"100%" justify-content:"flex-start" padding:"0 16px" min-height:"34px" border:"none"
----<Row-Body> width:"100%" flex:"1" min-height:"0" gap:"0"
-----<Col-Sidebar> width:"220px" height:"100%" padding:"12px 12px" gap:"2px" sk.bg:"#fafafa" sk.borderRight:"1px solid #ebedf1"
------<Text-NavGroupUser> value:"个人" padding:"8px 12px 4px 12px" style:"hint" size:"xs"
------<Button-NavHome> value:"概览" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" border:"none"
-------<StateStyle-Active> conditions:($currentPage == "home") style:"primary|filled|default|sm|actionable"
------<Button-NavProfile> value:"个人资料" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" border:"none"
-------<StateStyle-ActProfile> conditions:($currentPage == "profile") style:"primary|filled|default|sm|actionable"
------<Button-NavSecurity> value:"安全" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" border:"none"
-------<StateStyle-ActSecurity> conditions:($currentPage == "security") style:"primary|filled|default|sm|actionable"
------<Divider> width:"100%" margin:"10px 0" show:($isAdmin == true)
------<Text-NavGroupAdmin> value:"管理" padding:"8px 12px 4px 12px" style:"hint" size:"xs" show:($isAdmin == true)
------<Button-NavAdminDash> value:"管理概览" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isAdmin == true) border:"none"
-------<StateStyle-ActAdminDash> conditions:($currentPage == "adminDash") style:"primary|filled|default|sm|actionable"
------<Button-NavAdminUsers> value:"用户" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isAdmin == true) border:"none"
-------<StateStyle-ActAdminUsers> conditions:($currentPage == "adminUsers" || $currentPage == "adminUserDetail") style:"primary|filled|default|sm|actionable"
------<Button-NavAdminRoles> value:"角色" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isAdmin == true) border:"none"
-------<StateStyle-ActAdminRoles> conditions:($currentPage == "adminRoles") style:"primary|filled|default|sm|actionable"
------<Button-NavAdminDepts> value:"部门" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isAdmin == true) border:"none"
-------<StateStyle-ActAdminDepts> conditions:($currentPage == "adminDepartments") style:"primary|filled|default|sm|actionable"
------<Button-NavAdminAudit> value:"审计日志" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isAdmin == true) border:"none"
-------<StateStyle-ActAdminAudit> conditions:($currentPage == "adminAudit") style:"primary|filled|default|sm|actionable"
------<Button-NavAdminSettings> value:"系统配置" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isAdmin == true) border:"none"
-------<StateStyle-ActAdminSettings> conditions:($currentPage == "adminSettings") style:"primary|filled|default|sm|actionable"
------<Button-NavAdminBindings> value:"实例与绑定" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isAdmin == true) border:"none"
-------<StateStyle-ActAdminBindings> conditions:($currentPage == "adminBindings") style:"primary|filled|default|sm|actionable"
------<Divider> width:"100%" margin:"10px 0" show:($isPcAdmin == true || $pcInitialized == false)
------<Text-NavGroupPc> value:"权限中心" padding:"8px 12px 4px 12px" style:"hint" size:"xs" show:($isPcAdmin == true || $pcInitialized == false)
------<Button-NavPcDash> value:"权限概览" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isPcAdmin == true || $pcInitialized == false) border:"none"
-------<StateStyle-ActPcDash> conditions:($currentPage == "pcDash") style:"primary|filled|default|sm|actionable"
------<Button-NavPcScope> value:"管理范围" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isPcAdmin == true || $pcInitialized == false) border:"none"
-------<StateStyle-ActPcScope> conditions:($currentPage == "pcScope") style:"primary|filled|default|sm|actionable"
------<Button-NavPcProjects> value:"项目权限" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isPcAdmin == true || $pcInitialized == false) border:"none"
-------<StateStyle-ActPcProjects> conditions:($currentPage == "pcProjects") style:"primary|filled|default|sm|actionable"
------<Button-NavPcAdmins> value:"管理员授权" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isPcAdmin == true || $pcInitialized == false) border:"none"
-------<StateStyle-ActPcAdmins> conditions:($currentPage == "pcAdmins") style:"primary|filled|default|sm|actionable"
------<Button-NavPcRecords> value:"发布记录" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isPcAdmin == true || $pcInitialized == false) border:"none"
-------<StateStyle-ActPcRecords> conditions:($currentPage == "pcRecords") style:"primary|filled|default|sm|actionable"
------<Button-NavPcSettings> value:"权限设置" style:"neutral|ghost|default|sm|actionable" justify-content:"flex-start" padding:"0 12px" min-height:"34px" width:"100%" radius:"sm" show:($isPcAdmin == true || $pcInitialized == false) border:"none"
-------<StateStyle-ActPcSettings> conditions:($currentPage == "pcSettings") style:"primary|filled|default|sm|actionable"
-----<Col-Main> flex:"1" min-height:"0" overflow:"auto" sk.bg:"#ffffff"
------<If-PageHome> conditions:($currentPage == "home")
-------<Section-HomePage "homePage"> width:"100%" flex:"1" min-height:"0"
------<If-PageProfile> conditions:($currentPage == "profile")
-------<Section-ProfilePage "profilePage"> width:"100%" flex:"1" min-height:"0"
------<If-PageSecurity> conditions:($currentPage == "security")
-------<Section-SecurityPage "securityPage"> width:"100%" flex:"1" min-height:"0"
------<If-NoAccess> conditions:(($currentPage.startsWith("admin") && $isAdmin == false) || ($currentPage.startsWith("pc") && $pcInitialized == true && $isPcAdmin == false))
-------<Col-NoAccess> width:"100%" flex:"1" min-height:"0" align-items:"center" justify-content:"center" gap:"8px" padding:"48px"
--------<Text-NoAccessT> value:"无权限访问" style:"caption" size:"md"
--------<Text-NoAccessD> value:"你的账号没有管理权限。如需使用该功能，请联系系统管理员开通。" style:"muted" size:"sm"
------<If-PageAdminDash> conditions:($currentPage == "adminDash" && $isAdmin == true)
-------<Section-AdminDashboardPage "adminDashPage"> width:"100%" flex:"1" min-height:"0"
------<If-PageAdminUsers> conditions:($currentPage == "adminUsers" && $isAdmin == true)
-------<Section-AdminUsersPage "adminUsersPage"> width:"100%" flex:"1" min-height:"0"
------<If-PageAdminUserDetail> conditions:($currentPage == "adminUserDetail" && $isAdmin == true)
-------<Section-AdminUserDetailPage "adminUserDetailPage"> width:"100%" flex:"1" min-height:"0" userId:$selectedUserId
------<If-PageAdminRoles> conditions:($currentPage == "adminRoles" && $isAdmin == true)
-------<Section-AdminRolesPage "adminRolesPage"> width:"100%" flex:"1" min-height:"0"
------<If-PageAdminDepts> conditions:($currentPage == "adminDepartments" && $isAdmin == true)
-------<Section-AdminDepartmentsPage "adminDepartmentsPage"> width:"100%" flex:"1" min-height:"0"
------<If-PageAdminAudit> conditions:($currentPage == "adminAudit" && $isAdmin == true)
-------<Section-AdminAuditPage "adminAuditPage"> width:"100%" flex:"1" min-height:"0"
------<If-PageAdminSettings> conditions:($currentPage == "adminSettings" && $isAdmin == true)
-------<Section-AdminSettingsPage "adminSettingsPage"> width:"100%" flex:"1" min-height:"0"
------<If-PageAdminBindings> conditions:($currentPage == "adminBindings" && $isAdmin == true)
-------<Section-AdminBindingsPage "adminBindingsPage"> width:"100%" flex:"1" min-height:"0"
------<If-PcNeedInit> conditions:(($currentPage == "pcDash" || $currentPage == "pcScope" || $currentPage == "pcProjects" || $currentPage == "pcAdmins" || $currentPage == "pcRecords" || $currentPage == "pcSettings") && $pcInitialized == false)
-------<Section-BootstrapPage "bootstrapPage"> width:"100%" flex:"1" min-height:"0"
------<If-PagePcDash> conditions:($currentPage == "pcDash" && $pcInitialized == true && $isPcAdmin == true)
-------<Section-PcDashboardPage "pcDashboardPage"> width:"100%" flex:"1" min-height:"0"
------<If-PagePcScope> conditions:($currentPage == "pcScope" && $pcInitialized == true && $isPcAdmin == true)
-------<Section-ScopeTreePage "scopeTreePage"> width:"100%" flex:"1" min-height:"0"
------<If-PagePcProjects> conditions:($currentPage == "pcProjects" && $pcInitialized == true && $isPcAdmin == true)
-------<Section-ProjectPermissionPage "projectPermissionPage"> width:"100%" flex:"1" min-height:"0" adminActions:$adminActions isSuperAdmin:$isSuperAdmin
------<If-PagePcAdmins> conditions:($currentPage == "pcAdmins" && $pcInitialized == true && $isPcAdmin == true)
-------<Section-AdminGrantPage "adminGrantPage"> width:"100%" flex:"1" min-height:"0" adminActions:$adminActions isSuperAdmin:$isSuperAdmin
------<If-PagePcRecords> conditions:($currentPage == "pcRecords" && $pcInitialized == true && $isPcAdmin == true)
-------<Section-PolicyRecordPage "policyRecordPage"> width:"100%" flex:"1" min-height:"0"
------<If-PagePcSettings> conditions:($currentPage == "pcSettings" && $pcInitialized == true && $isPcAdmin == true)
-------<Section-PcSettingsPage "pcSettingsPage"> width:"100%" flex:"1" min-height:"0"

# Frontend Event Handlers
<Page-Main "mainPage">.@init()
-// UC-A-CONFIG-20260616-001：PM 验证 getQueryParams() 在预览/线上预览取空、不支撑回跳；
-// 改为从 getCurrentFullUrl() 解析 returnUrl 与 mode，并做最小 URL decode（VL 无 decodeURIComponent）。
-<SysRoute>.getCurrentFullUrl() -> _full
-_fullUrl(STRING) = (_full != NULL) ? ("" + _full) : ""
-IF _fullUrl.includes("returnUrl=")
--_ruRaw(STRING) = _fullUrl.split("returnUrl=")[1]
--_ruCut(STRING) = _ruRaw.split("&")[0]
--_ru(STRING) = _ruCut.replaceAll("%3A", ":").replaceAll("%2F", "/").replaceAll("%3F", "?").replaceAll("%3D", "=").replaceAll("%26", "&").replaceAll("%23", "#").replaceAll("%25", "%")
--IF _ru != ""
---$returnUrl = _ru
-<Section-SessionGate "sessionGate">.FetchSession() -> _cur
-IF _cur.isLogin
--$isLogin = true
--$currentUserId = _cur.userId
--$currentUserNickname = _cur.nickname
--$currentUserEmail = _cur.email
--$isAdmin = _cur.isAdmin
--$isSuperAdmin = _cur.isSuperAdmin
--$isPcAdmin = _cur.isPcAdmin
--$adminActions = _cur.adminActions
--$currentPage = "home"
-IF _cur.isLogin == false
--<Section-SessionGate "sessionGate">.GetSetupNeeded() -> _setup
--IF _setup.needsSetup == true
---$currentPage = "setup"
-IF _fullUrl.includes("mode=login") && $currentPage != "setup"
--$currentPage = "login"

<Section-SetupPage "setupPage">.@setupDone(userId, tokenId)
-$currentUserId = userId
-$isLogin = true
-<Section-SessionGate "sessionGate">.FetchSession() -> _cur
-IF _cur.isLogin
--$currentUserNickname = _cur.nickname
--$currentUserEmail = _cur.email
--$isAdmin = _cur.isAdmin
--$isSuperAdmin = _cur.isSuperAdmin
--$isPcAdmin = _cur.isPcAdmin
--$adminActions = _cur.adminActions
-$pcInitialized = true
-IF $returnUrl != ""
--buildReturnUrlWithToken($returnUrl, tokenId) -> _tokRet
--<ClientUtils>.openLink(_tokRet.url, true)
--RETURN
-$currentPage = "home"

<Button-UserMenu>.@click()
-$userMenuOpen = !$userMenuOpen

<Button-MenuProfile>.@click()
-$userMenuOpen = false
-$currentPage = "profile"

<Button-MenuSecurity>.@click()
-$userMenuOpen = false
-$currentPage = "security"

<Button-MenuLogout>.@click()
-$userMenuOpen = false
-<Section-SessionGate "sessionGate">.DoLogout() -> _r
-<ClientUtils>.refreshCurrentUser()
-$isLogin = false
-$currentUserId = ""
-$currentUserNickname = ""
-$currentUserEmail = ""
-$selectedUserId = ""
-$isAdmin = false
-$isSuperAdmin = false
-$currentPage = "login"

<Button-NavHome>.@click()
-$currentPage = "home"

<Button-NavProfile>.@click()
-$currentPage = "profile"

<Button-NavSecurity>.@click()
-$currentPage = "security"

<Button-NavAdminDash>.@click()
-$currentPage = "adminDash"

<Button-NavAdminUsers>.@click()
-$currentPage = "adminUsers"

<Button-NavAdminRoles>.@click()
-$currentPage = "adminRoles"

<Button-NavAdminDepts>.@click()
-$currentPage = "adminDepartments"

<Button-NavAdminAudit>.@click()
-$currentPage = "adminAudit"

<Button-NavAdminSettings>.@click()
-$currentPage = "adminSettings"

<Button-NavAdminBindings>.@click()
-$currentPage = "adminBindings"

<Button-NavPcDash>.@click()
-$currentPage = "pcDash"

<Button-NavPcScope>.@click()
-$currentPage = "pcScope"

<Button-NavPcProjects>.@click()
-$currentPage = "pcProjects"

<Button-NavPcAdmins>.@click()
-$currentPage = "pcAdmins"

<Button-NavPcRecords>.@click()
-$currentPage = "pcRecords"

<Button-NavPcSettings>.@click()
-$currentPage = "pcSettings"

<Section-PcInitGate "pcInitGate">.@ready(initialized)
-$pcInitialized = initialized
-$pcInitChecked = true

<Section-BootstrapPage "bootstrapPage">.@initialized()
-$pcInitialized = true
-<Section-SessionGate "sessionGate">.FetchSession() -> _cur
-$isAdmin = _cur.isAdmin
-$isSuperAdmin = _cur.isSuperAdmin
-$isPcAdmin = _cur.isPcAdmin
-$adminActions = _cur.adminActions
-$currentPage = "pcDash"

<Section-LoginPage "loginPage">.@loginSuccess(userId, tokenId)
-$currentUserId = userId
-$isLogin = true
-<Section-SessionGate "sessionGate">.FetchSession() -> _cur
-IF _cur.isLogin
--$currentUserNickname = _cur.nickname
--$currentUserEmail = _cur.email
--$isAdmin = _cur.isAdmin
--$isSuperAdmin = _cur.isSuperAdmin
--$isPcAdmin = _cur.isPcAdmin
--$adminActions = _cur.adminActions
-IF $returnUrl != ""
--buildReturnUrlWithToken($returnUrl, tokenId) -> _tokRet
--<ClientUtils>.openLink(_tokRet.url, true)
--RETURN
-$currentPage = "home"

<Section-RegisterPage "registerPage">.@registerSuccess(userId,needApproval,tokenId)
-$currentUserId = userId
-IF needApproval == false
--$isLogin = true
--<Section-SessionGate "sessionGate">.FetchSession() -> _cur
--IF _cur.isLogin
---$currentUserNickname = _cur.nickname
---$currentUserEmail = _cur.email
---$isAdmin = _cur.isAdmin
---$isSuperAdmin = _cur.isSuperAdmin
---$isPcAdmin = _cur.isPcAdmin
---$adminActions = _cur.adminActions
--IF $returnUrl != ""
---buildReturnUrlWithToken($returnUrl, tokenId) -> _tokRet
---<ClientUtils>.openLink(_tokRet.url, true)
---RETURN
--$currentPage = "home"

<Section-LoginPage "loginPage">.@gotoRegister()
-$currentPage = "register"

<Section-LoginPage "loginPage">.@gotoForgotPassword()
-$currentPage = "forgotPassword"

<Section-RegisterPage "registerPage">.@gotoLogin()
-$currentPage = "login"

<Section-ForgotPasswordPage "forgotPasswordPage">.@gotoLogin()
-$currentPage = "login"

<Section-AdminUsersPage "adminUsersPage">.@userSelected(userId)
-$selectedUserId = userId
-$currentPage = "adminUserDetail"

<Section-AdminUserDetailPage "adminUserDetailPage">.@back()
-$currentPage = "adminUsers"
-$selectedUserId = ""

# Frontend Internal Methods

METHOD buildReturnUrlWithToken(returnUrlArg(STRING), tokenId(STRING));RETURN {url:STRING}
-IF tokenId == "" || tokenId == "null"
--RETURN {url:returnUrlArg}
-_sep(STRING) = returnUrlArg.includes("?") ? "&" : "?"
-RETURN {url:(returnUrlArg + _sep + "ucToken=" + tokenId._encodeUrlComponent())}

# Frontend Pipeline Funcs

PIPE _encodeUrlComponent(s(STRING));RETURN STRING
-RETURN s.replaceAll("%", "%25").replaceAll(" ", "%20").replaceAll("#", "%23").replaceAll("&", "%26").replaceAll("=", "%3D").replaceAll("?", "%3F").replaceAll("+", "%2B")

</App-UserCenter>
