mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-11-03 03:32:08 +08:00
feat(alarm): rename event views to alarm, add alarm routes and i18n, extend login and settings nav
This commit is contained in:
+4
-13
@@ -42,8 +42,8 @@ export const revokeMcpConnection = (id: string) =>
|
||||
|
||||
export const replaceMcpConnectionTools = (connectionId: string, toolIds: string[]) =>
|
||||
httpPost<R<boolean>>(`${API_MCP_BASE}/connection/tools/replace`, {
|
||||
connection_id: connectionId,
|
||||
tool_ids: toolIds,
|
||||
connectionId,
|
||||
toolIds,
|
||||
});
|
||||
|
||||
export const listMcpConnectionTool = (id: string) =>
|
||||
@@ -51,7 +51,7 @@ export const listMcpConnectionTool = (id: string) =>
|
||||
|
||||
export const refreshMcpToolCatalog = () => httpPost<R<number>>(`${API_MCP_BASE}/tool/catalog/refresh`);
|
||||
|
||||
export const listMcpTool = (query: { keyword?: string; risk_level?: string; limit?: number } = {}) =>
|
||||
export const listMcpTool = (query: { keyword?: string; riskLevel?: string; limit?: number } = {}) =>
|
||||
httpPost<R<McpToolRecord[]>>(`${API_MCP_BASE}/tool/list`, query);
|
||||
|
||||
export const listMcpAudit = (
|
||||
@@ -62,13 +62,4 @@ export const listMcpAudit = (
|
||||
riskLevel?: string;
|
||||
limit?: number;
|
||||
} = {}
|
||||
) =>
|
||||
httpPost<R<McpAuditRecord[]>>(`${API_MCP_BASE}/audit/list`, undefined, {
|
||||
params: {
|
||||
principal_id: params.principalId,
|
||||
tool_id: params.toolId,
|
||||
status: params.status,
|
||||
risk_level: params.riskLevel,
|
||||
limit: params.limit,
|
||||
},
|
||||
});
|
||||
) => httpPost<R<McpAuditRecord[]>>(`${API_MCP_BASE}/audit/list`, undefined, { params });
|
||||
|
||||
@@ -22,6 +22,8 @@ export const generateSalt = (login: Login) => httpPost(`${API_AUTH_BASE}/token/s
|
||||
|
||||
export const generateToken = (login: Login) => httpPost(`${API_AUTH_BASE}/token/generate`, login);
|
||||
|
||||
export const changePassword = (login: Login) => httpPost(`${API_AUTH_BASE}/token/change_password`, login);
|
||||
|
||||
export const cancelToken = (login: Login) => httpPost(`${API_AUTH_BASE}/token/cancel`, login);
|
||||
|
||||
export const checkTokenValid = (login: Login) => httpPost(`${API_AUTH_BASE}/token/check`, login);
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
profileDetail: 'nav.profileDetail',
|
||||
profileEdit: 'nav.profileEdit',
|
||||
settings: 'nav.settings',
|
||||
settingsIdentity: 'nav.settingsIdentity',
|
||||
settingsAccess: 'nav.settingsAccess',
|
||||
settingsEventCommand: 'nav.settingsEventCommand',
|
||||
settingsAudit: 'nav.settingsAudit',
|
||||
settingsIntegration: 'nav.settingsIntegration',
|
||||
settingsSystem: 'nav.settingsSystem',
|
||||
settingsUser: 'nav.settingsUser',
|
||||
settingsPrincipal: 'nav.settingsPrincipal',
|
||||
settingsTenantMembership: 'nav.settingsTenantMembership',
|
||||
@@ -222,6 +228,12 @@
|
||||
device: 'Management',
|
||||
pointValue: 'TrendCharts',
|
||||
settings: 'Setting',
|
||||
settingsIdentity: 'User',
|
||||
settingsAccess: 'Stamp',
|
||||
settingsEventCommand: 'Operation',
|
||||
settingsAudit: 'Files',
|
||||
settingsIntegration: 'Share',
|
||||
settingsSystem: 'Tools',
|
||||
settingsUser: 'User',
|
||||
settingsPrincipal: 'Avatar',
|
||||
settingsTenantMembership: 'OfficeBuilding',
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { AxiosInstance } from 'axios';
|
||||
import axios, { type AxiosError, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios';
|
||||
import { ElNotification } from 'element-plus';
|
||||
|
||||
import { AXIOS_CONFIG, AXIOS_ERROR_MESSAGES } from '@/config/constant/axios';
|
||||
import { AXIOS_CONFIG, AXIOS_ERROR_MESSAGES, PASSWORD_CHANGE_CODES } from '@/config/constant/axios';
|
||||
import { AUTH_HEADERS } from '@/config/constant/common';
|
||||
import { failMessage, warnMessage } from '@/utils/notificationUtil';
|
||||
import { getStorage, removeStorage } from '@/utils/storageUtil';
|
||||
@@ -111,6 +111,12 @@ request.interceptors.response.use(
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// Password change / expiry: a business outcome, not an error. Pass the payload
|
||||
// through silently so the login flow can open the password change dialog.
|
||||
if (PASSWORD_CHANGE_CODES.includes(response.data?.code)) {
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
|
||||
// Handle unauthorized access
|
||||
if (status === AXIOS_CONFIG.UNAUTHORIZED_STATUS) {
|
||||
warnMessage(AXIOS_ERROR_MESSAGES.UNAUTHORIZED, AXIOS_ERROR_MESSAGES.UNAUTHORIZED_TITLE);
|
||||
|
||||
@@ -32,3 +32,10 @@ export const AXIOS_ERROR_MESSAGES = {
|
||||
UNAUTHORIZED_TITLE: 'Login credentials expired',
|
||||
REQUEST_ERROR: 'API request error. Please contact the system administrator.',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Business response codes the interceptor must pass through silently so the calling
|
||||
* flow can react (e.g. drive the user to the password change dialog) instead of the
|
||||
* generic error toast. Mirrors backend ErrorCode (R4031 change required, R4032 expired).
|
||||
*/
|
||||
export const PASSWORD_CHANGE_CODES = ['R4031', 'R4032'] as const;
|
||||
|
||||
@@ -118,6 +118,12 @@ export default {
|
||||
pointDetail: 'Point Detail',
|
||||
pointEdit: 'Point Edit',
|
||||
settings: 'Settings',
|
||||
settingsIdentity: 'Identity',
|
||||
settingsAccess: 'Access Control',
|
||||
settingsEventCommand: 'Event & Command',
|
||||
settingsAudit: 'Audit',
|
||||
settingsIntegration: 'Integration',
|
||||
settingsSystem: 'System',
|
||||
settingsUser: 'User',
|
||||
settingsRole: 'Role',
|
||||
settingsResource: 'Resource',
|
||||
@@ -207,6 +213,17 @@ export default {
|
||||
passwordMin: 'Password must be at least 6 characters',
|
||||
loading: 'Logging in, please wait...',
|
||||
failed: 'Login failed. Please check your credentials and try again.',
|
||||
changePasswordTitle: 'Change Password',
|
||||
changePasswordRequired: 'Your password must be changed before you can log in',
|
||||
passwordExpired: 'Your password has expired. Please change it and log in again',
|
||||
newPassword: 'New Password',
|
||||
newPasswordPlaceholder: 'Please enter the new password',
|
||||
confirmPassword: 'Confirm New Password',
|
||||
confirmPasswordPlaceholder: 'Please re-enter the new password',
|
||||
confirmPasswordMismatch: 'The two passwords do not match',
|
||||
changePasswordSubmit: 'Submit and log in again',
|
||||
changePasswordSuccess: 'Password changed. Please log in with the new password',
|
||||
changePasswordFailed: 'Failed to change password. Please try again',
|
||||
},
|
||||
error: {
|
||||
forbidden: 'Sorry, you do not have permission to access this page',
|
||||
|
||||
@@ -116,6 +116,12 @@ export default {
|
||||
pointDetail: '位号详情',
|
||||
pointEdit: '位号编辑',
|
||||
settings: '设置',
|
||||
settingsIdentity: '身份',
|
||||
settingsAccess: '权限',
|
||||
settingsEventCommand: '事件与指令',
|
||||
settingsAudit: '审计',
|
||||
settingsIntegration: '集成',
|
||||
settingsSystem: '系统',
|
||||
settingsUser: '用户管理',
|
||||
settingsRole: '角色管理',
|
||||
settingsResource: '资源管理',
|
||||
@@ -205,6 +211,17 @@ export default {
|
||||
passwordMin: '密码长度最少为6位',
|
||||
loading: '登录中,请稍后...',
|
||||
failed: '登录失败,请检查您的账号信息后重试。',
|
||||
changePasswordTitle: '修改密码',
|
||||
changePasswordRequired: '当前密码需要修改后才能登录',
|
||||
passwordExpired: '密码已过期,请修改后重新登录',
|
||||
newPassword: '新密码',
|
||||
newPasswordPlaceholder: '请输入新密码',
|
||||
confirmPassword: '确认新密码',
|
||||
confirmPasswordPlaceholder: '请再次输入新密码',
|
||||
confirmPasswordMismatch: '两次输入的密码不一致',
|
||||
changePasswordSubmit: '提交并重新登录',
|
||||
changePasswordSuccess: '密码修改成功,请使用新密码登录',
|
||||
changePasswordFailed: '密码修改失败,请重试',
|
||||
},
|
||||
error: {
|
||||
forbidden: '抱歉, 你无权访问该页面',
|
||||
|
||||
@@ -123,6 +123,42 @@ const settingsRouter: RouteRecordRaw = {
|
||||
redirect: '/settings/command/history',
|
||||
meta: { title: 'Command' },
|
||||
},
|
||||
{
|
||||
name: 'settingsIdentity',
|
||||
path: 'identity',
|
||||
redirect: '/settings/user',
|
||||
meta: { title: 'Identity' },
|
||||
},
|
||||
{
|
||||
name: 'settingsAccess',
|
||||
path: 'access',
|
||||
redirect: '/settings/role',
|
||||
meta: { title: 'Access Control' },
|
||||
},
|
||||
{
|
||||
name: 'settingsEventCommand',
|
||||
path: 'event-command',
|
||||
redirect: '/settings/event/history',
|
||||
meta: { title: 'Event & Command' },
|
||||
},
|
||||
{
|
||||
name: 'settingsAudit',
|
||||
path: 'audit',
|
||||
redirect: '/settings/identity_audit',
|
||||
meta: { title: 'Audit' },
|
||||
},
|
||||
{
|
||||
name: 'settingsIntegration',
|
||||
path: 'integration',
|
||||
redirect: '/settings/mcp',
|
||||
meta: { title: 'Integration' },
|
||||
},
|
||||
{
|
||||
name: 'settingsSystem',
|
||||
path: 'system',
|
||||
redirect: '/settings/group',
|
||||
meta: { title: 'System' },
|
||||
},
|
||||
{
|
||||
name: 'settingsAlarmRule',
|
||||
path: 'alarm/rule',
|
||||
@@ -176,26 +212,26 @@ const settingsRouter: RouteRecordRaw = {
|
||||
name: 'settingsAlarmOverview',
|
||||
path: 'alarm/overview',
|
||||
meta: { title: 'Event Overview' },
|
||||
component: () => import('@/views/settings/event/Overview.vue'),
|
||||
component: () => import('@/views/settings/alarm/Overview.vue'),
|
||||
},
|
||||
{
|
||||
name: 'settingsDeviceAlarm',
|
||||
path: 'alarm/device',
|
||||
meta: { title: 'Device Alarm' },
|
||||
component: () => import('@/views/settings/event/DeviceEvent.vue'),
|
||||
component: () => import('@/views/settings/alarm/DeviceEvent.vue'),
|
||||
},
|
||||
{
|
||||
name: 'settingsDriverAlarm',
|
||||
path: 'alarm/driver',
|
||||
meta: { title: 'Driver Alarm' },
|
||||
component: () => import('@/views/settings/event/DriverEvent.vue'),
|
||||
component: () => import('@/views/settings/alarm/DriverEvent.vue'),
|
||||
},
|
||||
{
|
||||
name: 'settingsPointAlarm',
|
||||
path: 'alarm/point',
|
||||
meta: { title: 'Point Events' },
|
||||
|
||||
component: () => import('@/views/settings/event/PointEvent.vue'),
|
||||
component: () => import('@/views/settings/alarm/PointEvent.vue'),
|
||||
},
|
||||
{
|
||||
name: 'settingsEventHistory',
|
||||
|
||||
+245
-85
@@ -28,41 +28,55 @@ export interface SettingsBreadcrumbParent {
|
||||
}
|
||||
|
||||
export const SETTINGS_TITLE_KEYS: Record<string, string> = {
|
||||
// group containers
|
||||
settingsIdentity: 'nav.settingsIdentity',
|
||||
settingsAccess: 'nav.settingsAccess',
|
||||
settingsModel: 'nav.settingsModel',
|
||||
settingsAlarm: 'nav.settingsAlarm',
|
||||
settingsEventCommand: 'nav.settingsEventCommand',
|
||||
settingsAudit: 'nav.settingsAudit',
|
||||
settingsIntegration: 'nav.settingsIntegration',
|
||||
settingsSystem: 'nav.settingsSystem',
|
||||
// identity
|
||||
settingsUser: 'nav.settingsUser',
|
||||
settingsPrincipal: 'nav.settingsPrincipal',
|
||||
settingsTenantMembership: 'nav.settingsTenantMembership',
|
||||
settingsLocalCredential: 'nav.settingsLocalCredential',
|
||||
settingsIdentityAudit: 'nav.settingsIdentityAudit',
|
||||
settingsServiceAccount: 'nav.settingsServiceAccount',
|
||||
// access control
|
||||
settingsRole: 'nav.settingsRole',
|
||||
settingsRolePrincipalBind: 'nav.settingsRolePrincipalBind',
|
||||
settingsResource: 'nav.settingsResource',
|
||||
settingsApi: 'nav.settingsApi',
|
||||
settingsMenu: 'nav.settingsMenu',
|
||||
settingsGroup: 'nav.settingsGroup',
|
||||
settingsLabel: 'nav.settingsLabel',
|
||||
settingsAlarm: 'nav.settingsAlarm',
|
||||
// model
|
||||
settingsModelConfig: 'nav.settingsModelConfig',
|
||||
settingsModelProvider: 'nav.settingsModelProvider',
|
||||
// alarm
|
||||
settingsAlarmRule: 'nav.settingsAlarmRule',
|
||||
settingsAlarmNotify: 'nav.settingsAlarmNotify',
|
||||
settingsAlarmMessage: 'nav.settingsAlarmMessage',
|
||||
settingsAlarmChannel: 'nav.settingsAlarmChannel',
|
||||
settingsAlarmBind: 'nav.settingsAlarmBind',
|
||||
settingsAlarmOverview: 'nav.settingsAlarmOverview',
|
||||
settingsAlarmState: 'nav.settingsAlarmState',
|
||||
settingsAlarmHistory: 'nav.settingsAlarmHistory',
|
||||
settingsDeviceAlarm: 'nav.settingsDeviceAlarm',
|
||||
settingsDriverAlarm: 'nav.settingsDriverAlarm',
|
||||
settingsModel: 'nav.settingsModel',
|
||||
settingsModelConfig: 'nav.settingsModelConfig',
|
||||
settingsModelProvider: 'nav.settingsModelProvider',
|
||||
settingsEvent: 'nav.settingsEvent',
|
||||
settingsAlarmOverview: 'nav.settingsAlarmOverview',
|
||||
settingsDeviceAlarm: 'nav.settingsDeviceAlarm',
|
||||
settingsPointAlarm: 'nav.settingsPointAlarm',
|
||||
settingsCommand: 'nav.settingsCommand',
|
||||
settingsCommandHistory: 'nav.settingsCommandHistory',
|
||||
// event & command
|
||||
settingsEventHistory: 'nav.settingsEventHistory',
|
||||
settingsServiceAccount: 'nav.settingsServiceAccount',
|
||||
settingsMcpServer: 'nav.settingsMcpServer',
|
||||
settingsCommandHistory: 'nav.settingsCommandHistory',
|
||||
// audit
|
||||
settingsIdentityAudit: 'nav.settingsIdentityAudit',
|
||||
settingsMcpAudit: 'nav.settingsMcpAudit',
|
||||
// integration
|
||||
settingsMcpServer: 'nav.settingsMcpServer',
|
||||
// system
|
||||
settingsGroup: 'nav.settingsGroup',
|
||||
settingsLabel: 'nav.settingsLabel',
|
||||
settingsAbout: 'nav.settingsAbout',
|
||||
// detail pages
|
||||
settingsUserDetail: 'nav.settingsUserDetail',
|
||||
settingsRoleDetail: 'nav.settingsRoleDetail',
|
||||
settingsResourceDetail: 'nav.settingsResourceDetail',
|
||||
@@ -83,41 +97,55 @@ export const SETTINGS_TITLE_KEYS: Record<string, string> = {
|
||||
|
||||
export const SETTINGS_FALLBACK_ICON: Record<string, string> = {
|
||||
settings: 'Setting',
|
||||
// group containers
|
||||
settingsIdentity: 'User',
|
||||
settingsAccess: 'Stamp',
|
||||
settingsModel: 'Cpu',
|
||||
settingsAlarm: 'AlarmClock',
|
||||
settingsEventCommand: 'Operation',
|
||||
settingsAudit: 'Files',
|
||||
settingsIntegration: 'Share',
|
||||
settingsSystem: 'Tools',
|
||||
// identity
|
||||
settingsUser: 'User',
|
||||
settingsPrincipal: 'Avatar',
|
||||
settingsTenantMembership: 'OfficeBuilding',
|
||||
settingsLocalCredential: 'Lock',
|
||||
settingsIdentityAudit: 'DocumentChecked',
|
||||
settingsServiceAccount: 'Ticket',
|
||||
// access control
|
||||
settingsRole: 'UserFilled',
|
||||
settingsRolePrincipalBind: 'Link',
|
||||
settingsResource: 'Key',
|
||||
settingsApi: 'Link',
|
||||
settingsMenu: 'Menu',
|
||||
settingsGroup: 'Grid',
|
||||
settingsLabel: 'CollectionTag',
|
||||
settingsAlarm: 'AlarmClock',
|
||||
// model
|
||||
settingsModelConfig: 'ChatDotRound',
|
||||
settingsModelProvider: 'ChatLineSquare',
|
||||
// alarm
|
||||
settingsAlarmRule: 'SetUp',
|
||||
settingsAlarmNotify: 'Bell',
|
||||
settingsAlarmMessage: 'Message',
|
||||
settingsAlarmChannel: 'Connection',
|
||||
settingsAlarmBind: 'Link',
|
||||
settingsAlarmOverview: 'DataLine',
|
||||
settingsAlarmState: 'Monitor',
|
||||
settingsAlarmHistory: 'DocumentChecked',
|
||||
settingsDeviceAlarm: 'Management',
|
||||
settingsDriverAlarm: 'Promotion',
|
||||
settingsModel: 'Cpu',
|
||||
settingsModelConfig: 'ChatDotRound',
|
||||
settingsModelProvider: 'ChatLineSquare',
|
||||
settingsEvent: 'Bell',
|
||||
settingsAlarmOverview: 'DataLine',
|
||||
settingsDeviceAlarm: 'Management',
|
||||
settingsPointAlarm: 'TrendCharts',
|
||||
settingsCommand: 'Operation',
|
||||
settingsCommandHistory: 'Document',
|
||||
// event & command
|
||||
settingsEventHistory: 'Document',
|
||||
settingsServiceAccount: 'Key',
|
||||
settingsMcpServer: 'Connection',
|
||||
settingsCommandHistory: 'Document',
|
||||
// audit
|
||||
settingsIdentityAudit: 'DocumentChecked',
|
||||
settingsMcpAudit: 'Document',
|
||||
// integration
|
||||
settingsMcpServer: 'Connection',
|
||||
// system
|
||||
settingsGroup: 'Grid',
|
||||
settingsLabel: 'CollectionTag',
|
||||
settingsAbout: 'InfoFilled',
|
||||
// detail pages
|
||||
settingsUserDetail: 'User',
|
||||
settingsRoleDetail: 'UserFilled',
|
||||
settingsResourceDetail: 'Key',
|
||||
@@ -136,18 +164,36 @@ export const SETTINGS_FALLBACK_ICON: Record<string, string> = {
|
||||
settingsModelProviderDetail: 'ChatLineSquare',
|
||||
};
|
||||
|
||||
export const SETTINGS_IDENTITY_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsUser', titleKey: 'nav.settingsUser', icon: 'User' },
|
||||
{ name: 'settingsPrincipal', titleKey: 'nav.settingsPrincipal', icon: 'Avatar' },
|
||||
{ name: 'settingsTenantMembership', titleKey: 'nav.settingsTenantMembership', icon: 'OfficeBuilding' },
|
||||
{ name: 'settingsLocalCredential', titleKey: 'nav.settingsLocalCredential', icon: 'Lock' },
|
||||
{ name: 'settingsServiceAccount', titleKey: 'nav.settingsServiceAccount', icon: 'Ticket' },
|
||||
];
|
||||
|
||||
export const SETTINGS_ACCESS_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsRole', titleKey: 'nav.settingsRole', icon: 'UserFilled' },
|
||||
{ name: 'settingsRolePrincipalBind', titleKey: 'nav.settingsRolePrincipalBind', icon: 'Link' },
|
||||
{ name: 'settingsResource', titleKey: 'nav.settingsResource', icon: 'Key' },
|
||||
{ name: 'settingsApi', titleKey: 'nav.settingsApi', icon: 'Link' },
|
||||
{ name: 'settingsMenu', titleKey: 'nav.settingsMenu', icon: 'Menu' },
|
||||
];
|
||||
|
||||
export const SETTINGS_MODEL_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsModelConfig', titleKey: 'nav.settingsModelConfig', icon: 'ChatDotRound' },
|
||||
{ name: 'settingsModelProvider', titleKey: 'nav.settingsModelProvider', icon: 'ChatLineSquare' },
|
||||
];
|
||||
|
||||
// Order reflects two intent groups: configuration first (rule/notify/message/channel/bind),
|
||||
// then monitoring (overview/state/history + driver/device/point alarm views).
|
||||
export const SETTINGS_ALARM_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsAlarmOverview', titleKey: 'nav.settingsAlarmOverview', icon: 'DataLine' },
|
||||
{ name: 'settingsAlarmRule', titleKey: 'nav.settingsAlarmRule', icon: 'SetUp' },
|
||||
{ name: 'settingsAlarmNotify', titleKey: 'nav.settingsAlarmNotify', icon: 'Bell' },
|
||||
{ name: 'settingsAlarmMessage', titleKey: 'nav.settingsAlarmMessage', icon: 'Message' },
|
||||
{ name: 'settingsAlarmChannel', titleKey: 'nav.settingsAlarmChannel', icon: 'Connection' },
|
||||
{ name: 'settingsAlarmBind', titleKey: 'nav.settingsAlarmBind', icon: 'Link' },
|
||||
{ name: 'settingsAlarmOverview', titleKey: 'nav.settingsAlarmOverview', icon: 'DataLine' },
|
||||
{ name: 'settingsAlarmState', titleKey: 'nav.settingsAlarmState', icon: 'Monitor' },
|
||||
{ name: 'settingsAlarmHistory', titleKey: 'nav.settingsAlarmHistory', icon: 'DocumentChecked' },
|
||||
{ name: 'settingsDriverAlarm', titleKey: 'nav.settingsDriverAlarm', icon: 'Promotion' },
|
||||
@@ -155,42 +201,63 @@ export const SETTINGS_ALARM_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsPointAlarm', titleKey: 'nav.settingsPointAlarm', icon: 'TrendCharts' },
|
||||
];
|
||||
|
||||
export const SETTINGS_EVENT_CHILDREN: SettingsNavNode[] = [
|
||||
export const SETTINGS_EVENT_COMMAND_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsEventHistory', titleKey: 'nav.settingsEventHistory', icon: 'Document' },
|
||||
];
|
||||
|
||||
export const SETTINGS_COMMAND_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsCommandHistory', titleKey: 'nav.settingsCommandHistory', icon: 'Document' },
|
||||
];
|
||||
|
||||
export const SETTINGS_FALLBACK_SIDEBAR: SettingsNavNode[] = [
|
||||
{ name: 'settingsUser', titleKey: 'nav.settingsUser', icon: 'User' },
|
||||
{ name: 'settingsPrincipal', titleKey: 'nav.settingsPrincipal', icon: 'Avatar' },
|
||||
{ name: 'settingsTenantMembership', titleKey: 'nav.settingsTenantMembership', icon: 'OfficeBuilding' },
|
||||
{ name: 'settingsLocalCredential', titleKey: 'nav.settingsLocalCredential', icon: 'Lock' },
|
||||
export const SETTINGS_AUDIT_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsIdentityAudit', titleKey: 'nav.settingsIdentityAudit', icon: 'DocumentChecked' },
|
||||
{ name: 'settingsRole', titleKey: 'nav.settingsRole', icon: 'UserFilled' },
|
||||
{ name: 'settingsRolePrincipalBind', titleKey: 'nav.settingsRolePrincipalBind', icon: 'Link' },
|
||||
{ name: 'settingsModel', titleKey: 'nav.settingsModel', icon: 'Cpu', children: SETTINGS_MODEL_CHILDREN },
|
||||
{ name: 'settingsCommand', titleKey: 'nav.settingsCommand', icon: 'Operation', children: SETTINGS_COMMAND_CHILDREN },
|
||||
{ name: 'settingsEvent', titleKey: 'nav.settingsEvent', icon: 'Bell', children: SETTINGS_EVENT_CHILDREN },
|
||||
{ name: 'settingsAlarm', titleKey: 'nav.settingsAlarm', icon: 'AlarmClock', children: SETTINGS_ALARM_CHILDREN },
|
||||
{ name: 'settingsApi', titleKey: 'nav.settingsApi', icon: 'Link' },
|
||||
{ name: 'settingsResource', titleKey: 'nav.settingsResource', icon: 'Key' },
|
||||
{ name: 'settingsMenu', titleKey: 'nav.settingsMenu', icon: 'Menu' },
|
||||
{ name: 'settingsMcpAudit', titleKey: 'nav.settingsMcpAudit', icon: 'Document' },
|
||||
];
|
||||
|
||||
export const SETTINGS_INTEGRATION_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsMcpServer', titleKey: 'nav.settingsMcpServer', icon: 'Connection' },
|
||||
];
|
||||
|
||||
export const SETTINGS_SYSTEM_CHILDREN: SettingsNavNode[] = [
|
||||
{ name: 'settingsGroup', titleKey: 'nav.settingsGroup', icon: 'Grid' },
|
||||
{ name: 'settingsLabel', titleKey: 'nav.settingsLabel', icon: 'CollectionTag' },
|
||||
{ name: 'settingsServiceAccount', titleKey: 'nav.settingsServiceAccount', icon: 'Key' },
|
||||
{ name: 'settingsMcpServer', titleKey: 'nav.settingsMcpServer', icon: 'Connection' },
|
||||
{ name: 'settingsMcpAudit', titleKey: 'nav.settingsMcpAudit', icon: 'Document' },
|
||||
{ name: 'settingsAbout', titleKey: 'nav.settingsAbout', icon: 'InfoFilled' },
|
||||
];
|
||||
|
||||
export const SETTINGS_FALLBACK_SIDEBAR: SettingsNavNode[] = [
|
||||
{ name: 'settingsIdentity', titleKey: 'nav.settingsIdentity', icon: 'User', children: SETTINGS_IDENTITY_CHILDREN },
|
||||
{ name: 'settingsAccess', titleKey: 'nav.settingsAccess', icon: 'Stamp', children: SETTINGS_ACCESS_CHILDREN },
|
||||
{ name: 'settingsModel', titleKey: 'nav.settingsModel', icon: 'Cpu', children: SETTINGS_MODEL_CHILDREN },
|
||||
{ name: 'settingsAlarm', titleKey: 'nav.settingsAlarm', icon: 'AlarmClock', children: SETTINGS_ALARM_CHILDREN },
|
||||
{
|
||||
name: 'settingsEventCommand',
|
||||
titleKey: 'nav.settingsEventCommand',
|
||||
icon: 'Operation',
|
||||
children: SETTINGS_EVENT_COMMAND_CHILDREN,
|
||||
},
|
||||
{ name: 'settingsAudit', titleKey: 'nav.settingsAudit', icon: 'Files', children: SETTINGS_AUDIT_CHILDREN },
|
||||
{
|
||||
name: 'settingsIntegration',
|
||||
titleKey: 'nav.settingsIntegration',
|
||||
icon: 'Share',
|
||||
children: SETTINGS_INTEGRATION_CHILDREN,
|
||||
},
|
||||
{ name: 'settingsSystem', titleKey: 'nav.settingsSystem', icon: 'Tools', children: SETTINGS_SYSTEM_CHILDREN },
|
||||
];
|
||||
|
||||
export const SETTINGS_ACTIVE_ALIAS: Record<string, string> = {
|
||||
// group container → its first child (used when a route resolves to a container name)
|
||||
settingsIdentity: 'settingsUser',
|
||||
settingsAccess: 'settingsRole',
|
||||
settingsModel: 'settingsModelConfig',
|
||||
settingsAlarm: 'settingsAlarmRule',
|
||||
settingsEventCommand: 'settingsEventHistory',
|
||||
settingsAudit: 'settingsIdentityAudit',
|
||||
settingsIntegration: 'settingsMcpServer',
|
||||
settingsSystem: 'settingsGroup',
|
||||
// legacy route containers kept for redirect compatibility
|
||||
settingsCommand: 'settingsCommandHistory',
|
||||
settingsEvent: 'settingsEventHistory',
|
||||
// detail pages highlight their list sibling
|
||||
settingsModelConfigDetail: 'settingsModelConfig',
|
||||
settingsModelProviderDetail: 'settingsModelProvider',
|
||||
settingsAlarm: 'settingsAlarmRule',
|
||||
settingsAlarmRuleDetail: 'settingsAlarmRule',
|
||||
settingsAlarmNotifyDetail: 'settingsAlarmNotify',
|
||||
settingsAlarmMessageDetail: 'settingsAlarmMessage',
|
||||
@@ -198,33 +265,68 @@ export const SETTINGS_ACTIVE_ALIAS: Record<string, string> = {
|
||||
settingsAlarmBindDetail: 'settingsAlarmBind',
|
||||
settingsAlarmStateDetail: 'settingsAlarmState',
|
||||
settingsAlarmHistoryDetail: 'settingsAlarmHistory',
|
||||
settingsCommand: 'settingsCommandHistory',
|
||||
settingsUserDetail: 'settingsUser',
|
||||
settingsRoleDetail: 'settingsRole',
|
||||
settingsResourceDetail: 'settingsResource',
|
||||
settingsApiDetail: 'settingsApi',
|
||||
settingsMenuDetail: 'settingsMenu',
|
||||
settingsGroupDetail: 'settingsGroup',
|
||||
settingsLabelDetail: 'settingsLabel',
|
||||
};
|
||||
|
||||
export const SETTINGS_ROUTE_ALIAS: Record<string, string> = {
|
||||
settingsAlarm: 'settingsAlarmRule',
|
||||
settingsIdentity: 'settingsUser',
|
||||
settingsAccess: 'settingsRole',
|
||||
settingsModel: 'settingsModelConfig',
|
||||
settingsAlarm: 'settingsAlarmRule',
|
||||
settingsEventCommand: 'settingsEventHistory',
|
||||
settingsAudit: 'settingsIdentityAudit',
|
||||
settingsIntegration: 'settingsMcpServer',
|
||||
settingsSystem: 'settingsGroup',
|
||||
settingsCommand: 'settingsCommandHistory',
|
||||
settingsEvent: 'settingsEventHistory',
|
||||
};
|
||||
|
||||
export const SETTINGS_GROUP_OPENERS: Record<string, string> = {
|
||||
// identity
|
||||
settingsUser: 'settingsIdentity',
|
||||
settingsPrincipal: 'settingsIdentity',
|
||||
settingsTenantMembership: 'settingsIdentity',
|
||||
settingsLocalCredential: 'settingsIdentity',
|
||||
settingsServiceAccount: 'settingsIdentity',
|
||||
// access control
|
||||
settingsRole: 'settingsAccess',
|
||||
settingsRolePrincipalBind: 'settingsAccess',
|
||||
settingsResource: 'settingsAccess',
|
||||
settingsApi: 'settingsAccess',
|
||||
settingsMenu: 'settingsAccess',
|
||||
// model
|
||||
settingsModelConfig: 'settingsModel',
|
||||
settingsModelProvider: 'settingsModel',
|
||||
// alarm
|
||||
settingsAlarmRule: 'settingsAlarm',
|
||||
settingsAlarmNotify: 'settingsAlarm',
|
||||
settingsAlarmMessage: 'settingsAlarm',
|
||||
settingsAlarmChannel: 'settingsAlarm',
|
||||
settingsAlarmBind: 'settingsAlarm',
|
||||
settingsAlarmOverview: 'settingsAlarm',
|
||||
settingsAlarmState: 'settingsAlarm',
|
||||
settingsAlarmHistory: 'settingsAlarm',
|
||||
settingsDeviceAlarm: 'settingsAlarm',
|
||||
settingsDriverAlarm: 'settingsAlarm',
|
||||
settingsAlarmOverview: 'settingsAlarm',
|
||||
settingsDeviceAlarm: 'settingsAlarm',
|
||||
settingsPointAlarm: 'settingsAlarm',
|
||||
settingsModelConfig: 'settingsModel',
|
||||
settingsModelProvider: 'settingsModel',
|
||||
settingsCommandHistory: 'settingsCommand',
|
||||
settingsEventHistory: 'settingsEvent',
|
||||
// event & command
|
||||
settingsEventHistory: 'settingsEventCommand',
|
||||
settingsCommandHistory: 'settingsEventCommand',
|
||||
// audit
|
||||
settingsIdentityAudit: 'settingsAudit',
|
||||
settingsMcpAudit: 'settingsAudit',
|
||||
// integration
|
||||
settingsMcpServer: 'settingsIntegration',
|
||||
// system
|
||||
settingsGroup: 'settingsSystem',
|
||||
settingsLabel: 'settingsSystem',
|
||||
settingsAbout: 'settingsSystem',
|
||||
};
|
||||
|
||||
export const getSettingsRouteName = (name: string): string => SETTINGS_ROUTE_ALIAS[name] || name;
|
||||
@@ -240,16 +342,16 @@ export const getSettingsTitleKey = (name: string): string | undefined => SETTING
|
||||
|
||||
export const getSettingsLeafIconCode = (name: string): string => name;
|
||||
|
||||
const commandParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/command/history',
|
||||
titleKey: 'nav.settingsCommand',
|
||||
code: 'settingsCommand',
|
||||
const identityParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/identity',
|
||||
titleKey: 'nav.settingsIdentity',
|
||||
code: 'settingsIdentity',
|
||||
};
|
||||
|
||||
const alarmParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/alarm',
|
||||
titleKey: 'nav.settingsAlarm',
|
||||
code: 'settingsAlarm',
|
||||
const accessParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/access',
|
||||
titleKey: 'nav.settingsAccess',
|
||||
code: 'settingsAccess',
|
||||
};
|
||||
|
||||
const modelParent: SettingsBreadcrumbParent = {
|
||||
@@ -258,29 +360,93 @@ const modelParent: SettingsBreadcrumbParent = {
|
||||
code: 'settingsModel',
|
||||
};
|
||||
|
||||
const eventParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/event',
|
||||
titleKey: 'nav.settingsEvent',
|
||||
code: 'settingsEvent',
|
||||
const alarmParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/alarm',
|
||||
titleKey: 'nav.settingsAlarm',
|
||||
code: 'settingsAlarm',
|
||||
};
|
||||
|
||||
const eventCommandParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/event-command',
|
||||
titleKey: 'nav.settingsEventCommand',
|
||||
code: 'settingsEventCommand',
|
||||
};
|
||||
|
||||
const auditParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/audit',
|
||||
titleKey: 'nav.settingsAudit',
|
||||
code: 'settingsAudit',
|
||||
};
|
||||
|
||||
const integrationParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/integration',
|
||||
titleKey: 'nav.settingsIntegration',
|
||||
code: 'settingsIntegration',
|
||||
};
|
||||
|
||||
const systemParent: SettingsBreadcrumbParent = {
|
||||
path: '/settings/system',
|
||||
titleKey: 'nav.settingsSystem',
|
||||
code: 'settingsSystem',
|
||||
};
|
||||
|
||||
export const SETTINGS_BREADCRUMB_PARENTS: Record<string, SettingsBreadcrumbParent[]> = {
|
||||
settingsUserDetail: [{ path: '/settings/user', titleKey: 'nav.settingsUser', code: 'settingsUser' }],
|
||||
settingsRoleDetail: [{ path: '/settings/role', titleKey: 'nav.settingsRole', code: 'settingsRole' }],
|
||||
settingsResourceDetail: [{ path: '/settings/resource', titleKey: 'nav.settingsResource', code: 'settingsResource' }],
|
||||
settingsApiDetail: [{ path: '/settings/api', titleKey: 'nav.settingsApi', code: 'settingsApi' }],
|
||||
settingsMenuDetail: [{ path: '/settings/menu', titleKey: 'nav.settingsMenu', code: 'settingsMenu' }],
|
||||
settingsGroupDetail: [{ path: '/settings/group', titleKey: 'nav.settingsGroup', code: 'settingsGroup' }],
|
||||
settingsLabelDetail: [{ path: '/settings/label', titleKey: 'nav.settingsLabel', code: 'settingsLabel' }],
|
||||
// identity leaves
|
||||
settingsUser: [identityParent],
|
||||
settingsPrincipal: [identityParent],
|
||||
settingsTenantMembership: [identityParent],
|
||||
settingsLocalCredential: [identityParent],
|
||||
settingsServiceAccount: [identityParent],
|
||||
// access control leaves
|
||||
settingsRole: [accessParent],
|
||||
settingsRolePrincipalBind: [accessParent],
|
||||
settingsResource: [accessParent],
|
||||
settingsApi: [accessParent],
|
||||
settingsMenu: [accessParent],
|
||||
// model leaves
|
||||
settingsModelConfig: [modelParent],
|
||||
settingsModelProvider: [modelParent],
|
||||
// alarm leaves
|
||||
settingsAlarmRule: [alarmParent],
|
||||
settingsAlarmNotify: [alarmParent],
|
||||
settingsAlarmMessage: [alarmParent],
|
||||
settingsAlarmChannel: [alarmParent],
|
||||
settingsAlarmBind: [alarmParent],
|
||||
settingsAlarmOverview: [alarmParent],
|
||||
settingsAlarmState: [alarmParent],
|
||||
settingsAlarmHistory: [alarmParent],
|
||||
settingsDeviceAlarm: [alarmParent],
|
||||
settingsDriverAlarm: [alarmParent],
|
||||
settingsDeviceAlarm: [alarmParent],
|
||||
settingsPointAlarm: [alarmParent],
|
||||
// event & command leaves
|
||||
settingsEventHistory: [eventCommandParent],
|
||||
settingsCommandHistory: [eventCommandParent],
|
||||
// audit leaves
|
||||
settingsIdentityAudit: [auditParent],
|
||||
settingsMcpAudit: [auditParent],
|
||||
// integration leaves
|
||||
settingsMcpServer: [integrationParent],
|
||||
// system leaves
|
||||
settingsGroup: [systemParent],
|
||||
settingsLabel: [systemParent],
|
||||
settingsAbout: [systemParent],
|
||||
// detail pages: group → list → detail
|
||||
settingsUserDetail: [identityParent, { path: '/settings/user', titleKey: 'nav.settingsUser', code: 'settingsUser' }],
|
||||
settingsRoleDetail: [accessParent, { path: '/settings/role', titleKey: 'nav.settingsRole', code: 'settingsRole' }],
|
||||
settingsResourceDetail: [
|
||||
accessParent,
|
||||
{ path: '/settings/resource', titleKey: 'nav.settingsResource', code: 'settingsResource' },
|
||||
],
|
||||
settingsApiDetail: [accessParent, { path: '/settings/api', titleKey: 'nav.settingsApi', code: 'settingsApi' }],
|
||||
settingsMenuDetail: [accessParent, { path: '/settings/menu', titleKey: 'nav.settingsMenu', code: 'settingsMenu' }],
|
||||
settingsGroupDetail: [
|
||||
systemParent,
|
||||
{ path: '/settings/group', titleKey: 'nav.settingsGroup', code: 'settingsGroup' },
|
||||
],
|
||||
settingsLabelDetail: [
|
||||
systemParent,
|
||||
{ path: '/settings/label', titleKey: 'nav.settingsLabel', code: 'settingsLabel' },
|
||||
],
|
||||
settingsAlarmRuleDetail: [
|
||||
alarmParent,
|
||||
{ path: '/settings/alarm/rule', titleKey: 'nav.settingsAlarmRule', code: 'settingsAlarmRule' },
|
||||
@@ -309,8 +475,6 @@ export const SETTINGS_BREADCRUMB_PARENTS: Record<string, SettingsBreadcrumbParen
|
||||
alarmParent,
|
||||
{ path: '/settings/alarm/history', titleKey: 'nav.settingsAlarmHistory', code: 'settingsAlarmHistory' },
|
||||
],
|
||||
settingsModelConfig: [modelParent],
|
||||
settingsModelProvider: [modelParent],
|
||||
settingsModelConfigDetail: [
|
||||
modelParent,
|
||||
{ path: '/settings/model/config', titleKey: 'nav.settingsModelConfig', code: 'settingsModelConfig' },
|
||||
@@ -319,8 +483,4 @@ export const SETTINGS_BREADCRUMB_PARENTS: Record<string, SettingsBreadcrumbParen
|
||||
modelParent,
|
||||
{ path: '/settings/model/provider', titleKey: 'nav.settingsModelProvider', code: 'settingsModelProvider' },
|
||||
],
|
||||
settingsAlarmOverview: [alarmParent],
|
||||
settingsPointAlarm: [alarmParent],
|
||||
settingsCommandHistory: [commandParent],
|
||||
settingsEventHistory: [eventParent],
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface Login {
|
||||
salt?: string;
|
||||
password?: string;
|
||||
token?: string;
|
||||
newPassword?: string;
|
||||
}
|
||||
|
||||
/** Attribute information */
|
||||
|
||||
@@ -20,9 +20,10 @@ import router from '@/config/router';
|
||||
import i18n from '@/config/i18n';
|
||||
import { ElLoading } from 'element-plus';
|
||||
|
||||
import { cancelToken, generateSalt, generateToken } from '@/api/token';
|
||||
import { cancelToken, changePassword, generateSalt, generateToken } from '@/api/token';
|
||||
|
||||
import { AUTH_HEADERS } from '@/config/constant/common';
|
||||
import { PASSWORD_CHANGE_CODES } from '@/config/constant/axios';
|
||||
import type { Login } from '@/config/types';
|
||||
import { getStorage, removeStorage, setStorage } from '@/utils/storageUtil';
|
||||
import { failMessage } from '@/utils/notificationUtil';
|
||||
@@ -91,13 +92,28 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
token: tokenRes.data,
|
||||
});
|
||||
await router.push({ name: 'home' });
|
||||
} catch {
|
||||
} catch (error) {
|
||||
const code = (error as { code?: string })?.code;
|
||||
if (code && PASSWORD_CHANGE_CODES.includes(code as never)) {
|
||||
// Password change required / expired: surface the code so the view can
|
||||
// open the change-password dialog instead of reporting a failed login.
|
||||
throw error;
|
||||
}
|
||||
failMessage(i18n.global.t('login.failed'));
|
||||
} finally {
|
||||
loading.close();
|
||||
}
|
||||
};
|
||||
|
||||
const changeLoginPassword = async (form: LoginForm & { newPassword: string }) => {
|
||||
await changePassword({
|
||||
tenant: form.tenant,
|
||||
name: form.name,
|
||||
password: form.password,
|
||||
newPassword: form.newPassword,
|
||||
});
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
const tenantValue = getStorage(AUTH_HEADERS.TENANT);
|
||||
const userValue = getStorage(AUTH_HEADERS.LOGIN);
|
||||
@@ -120,6 +136,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
getName,
|
||||
// Actions
|
||||
login,
|
||||
changeLoginPassword,
|
||||
logout,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -69,16 +69,45 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="changePasswordVisible" :title="t('login.changePasswordTitle')" width="420px">
|
||||
<el-alert :title="changePasswordHint" :closable="false" type="warning" show-icon class="mb-4" />
|
||||
<el-form ref="changePasswordRef" :model="changePasswordData" :rules="changePasswordRule" label-width="0">
|
||||
<el-form-item prop="newPassword">
|
||||
<el-input
|
||||
v-model="changePasswordData.newPassword"
|
||||
:placeholder="t('login.newPasswordPlaceholder')"
|
||||
type="password"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="confirmPassword">
|
||||
<el-input
|
||||
v-model="changePasswordData.confirmPassword"
|
||||
:placeholder="t('login.confirmPasswordPlaceholder')"
|
||||
type="password"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :loading="changePasswordLoading" type="primary" @click="handleChangePassword">
|
||||
{{ t('login.changePasswordSubmit') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, unref } from 'vue';
|
||||
import { computed, reactive, ref, unref } from 'vue';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import { Box, Lock, User } from '@element-plus/icons-vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAuthStore } from '@/store';
|
||||
import { PASSWORD_CHANGE_CODES } from '@/config/constant/axios';
|
||||
import { successMessage, failMessage } from '@/utils/notificationUtil';
|
||||
|
||||
import Particles from '@/components/particles/Particles.vue';
|
||||
|
||||
@@ -146,12 +175,79 @@
|
||||
loading.value = true;
|
||||
try {
|
||||
await authStore.login(reactiveData.formData);
|
||||
} catch {
|
||||
// authStore.login already shows failMessage
|
||||
} catch (error) {
|
||||
const code = (error as { code?: string })?.code;
|
||||
if (code && PASSWORD_CHANGE_CODES.includes(code as never)) {
|
||||
openChangePassword(code);
|
||||
}
|
||||
// other failures already surface failMessage inside authStore.login
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Password change flow (mandatory change / expired password)
|
||||
const changePasswordVisible = ref(false);
|
||||
const changePasswordLoading = ref(false);
|
||||
const changePasswordCode = ref('');
|
||||
const changePasswordRef = ref<FormInstance>();
|
||||
const changePasswordData = reactive({ newPassword: '', confirmPassword: '' });
|
||||
|
||||
const changePasswordHint = computed(() =>
|
||||
changePasswordCode.value === 'R4032' ? t('login.passwordExpired') : t('login.changePasswordRequired')
|
||||
);
|
||||
|
||||
const changePasswordRule = reactive<FormRules>({
|
||||
newPassword: [
|
||||
{ required: true, message: t('login.passwordRequired'), trigger: 'blur' },
|
||||
{ min: 6, message: t('login.passwordMin'), trigger: 'blur' },
|
||||
],
|
||||
confirmPassword: [
|
||||
{
|
||||
validator: (_rule, value, callback) =>
|
||||
value === changePasswordData.newPassword
|
||||
? callback()
|
||||
: callback(new Error(t('login.confirmPasswordMismatch'))),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const openChangePassword = (code: string) => {
|
||||
changePasswordCode.value = code;
|
||||
changePasswordData.newPassword = '';
|
||||
changePasswordData.confirmPassword = '';
|
||||
changePasswordVisible.value = true;
|
||||
};
|
||||
|
||||
const handleChangePassword = async () => {
|
||||
const form = unref(changePasswordRef);
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await form.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
changePasswordLoading.value = true;
|
||||
try {
|
||||
await authStore.changeLoginPassword({
|
||||
tenant: reactiveData.formData.tenant,
|
||||
name: reactiveData.formData.name,
|
||||
password: reactiveData.formData.password,
|
||||
newPassword: changePasswordData.newPassword,
|
||||
});
|
||||
changePasswordVisible.value = false;
|
||||
successMessage(t('login.changePasswordSuccess'));
|
||||
reactiveData.formData.password = changePasswordData.newPassword;
|
||||
await handleLogin();
|
||||
} catch {
|
||||
failMessage(t('login.changePasswordFailed'));
|
||||
} finally {
|
||||
changePasswordLoading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
+10
-225
@@ -61,12 +61,7 @@
|
||||
getSettingsActiveName,
|
||||
getSettingsDefaultOpeneds,
|
||||
getSettingsRouteName,
|
||||
SETTINGS_ALARM_CHILDREN,
|
||||
SETTINGS_COMMAND_CHILDREN,
|
||||
SETTINGS_EVENT_CHILDREN,
|
||||
SETTINGS_FALLBACK_ICON,
|
||||
SETTINGS_FALLBACK_SIDEBAR,
|
||||
SETTINGS_MODEL_CHILDREN,
|
||||
SETTINGS_TITLE_KEYS,
|
||||
type SettingsNavNode,
|
||||
} from '@/config/settingsNav';
|
||||
@@ -98,40 +93,12 @@
|
||||
children: node.children?.map(toSidebarItem),
|
||||
});
|
||||
|
||||
const fallbackItems = (): SidebarItem[] => SETTINGS_FALLBACK_SIDEBAR.map(toSidebarItem);
|
||||
|
||||
// Static fallback shown when the menu API is unreachable or still loading.
|
||||
// `icon` holds the globally-registered element-plus icon component name.
|
||||
const modelGroup = (children?: SidebarItem[]): SidebarItem => ({
|
||||
name: 'settingsModel',
|
||||
title: t('nav.settingsModel'),
|
||||
icon: SETTINGS_FALLBACK_ICON.settingsModel,
|
||||
children: children?.length ? children : SETTINGS_MODEL_CHILDREN.map(toSidebarItem),
|
||||
});
|
||||
|
||||
const alarmGroup = (children?: SidebarItem[]): SidebarItem => ({
|
||||
name: 'settingsAlarm',
|
||||
title: t('nav.settingsAlarm'),
|
||||
icon: SETTINGS_FALLBACK_ICON.settingsAlarm,
|
||||
children: children?.length ? children : SETTINGS_ALARM_CHILDREN.map(toSidebarItem),
|
||||
});
|
||||
|
||||
const eventGroup = (children?: SidebarItem[]): SidebarItem => ({
|
||||
name: 'settingsEvent',
|
||||
title: t('nav.settingsEvent'),
|
||||
icon: SETTINGS_FALLBACK_ICON.settingsEvent,
|
||||
children: children?.length ? children : SETTINGS_EVENT_CHILDREN.map(toSidebarItem),
|
||||
});
|
||||
|
||||
const commandGroup = (children?: SidebarItem[]): SidebarItem => ({
|
||||
name: 'settingsCommand',
|
||||
title: t('nav.settingsCommand'),
|
||||
icon: SETTINGS_FALLBACK_ICON.settingsCommand,
|
||||
children: children?.length ? children : SETTINGS_COMMAND_CHILDREN.map(toSidebarItem),
|
||||
});
|
||||
const fallbackItems = (): SidebarItem[] => SETTINGS_FALLBACK_SIDEBAR.map(toSidebarItem);
|
||||
|
||||
const menuTitle = (node: any) => {
|
||||
const titleKey = node.menuCode === 'settingsEvent' ? 'nav.settingsEvent' : SETTINGS_TITLE_KEYS[node.menuCode];
|
||||
const titleKey = SETTINGS_TITLE_KEYS[node.menuCode];
|
||||
return titleKey ? t(titleKey) : resolveMenuTitle(node);
|
||||
};
|
||||
|
||||
@@ -150,200 +117,18 @@
|
||||
const sidebarItems = computed<SidebarItem[]>(() => {
|
||||
const settings = menuStore.findByCode('settings');
|
||||
const children = settings?.children || [];
|
||||
if (!children.length) return menuStore.loaded ? [] : fallbackItems();
|
||||
const items = children
|
||||
.slice()
|
||||
.sort((a, b) => (a.menuIndex ?? 0) - (b.menuIndex ?? 0))
|
||||
.map(mapMenuNode);
|
||||
if (menuStore.loaded) {
|
||||
return items;
|
||||
// The menu API is authoritative once loaded; the DB already encodes the group tree.
|
||||
return children.length
|
||||
? children
|
||||
.slice()
|
||||
.sort((a, b) => (a.menuIndex ?? 0) - (b.menuIndex ?? 0))
|
||||
.map(mapMenuNode)
|
||||
: [];
|
||||
}
|
||||
ensureDirectItem(items, { name: 'settingsGroup', title: t('nav.settingsGroup'), icon: 'Grid' }, [
|
||||
'settingsLabel',
|
||||
'settingsModel',
|
||||
'settingsEvent',
|
||||
'settingsAbout',
|
||||
]);
|
||||
ensureDirectItem(items, { name: 'settingsPrincipal', title: t('nav.settingsPrincipal'), icon: 'Avatar' }, [
|
||||
'settingsTenantMembership',
|
||||
'settingsRole',
|
||||
'settingsRolePrincipalBind',
|
||||
'settingsResource',
|
||||
'settingsApi',
|
||||
'settingsMenu',
|
||||
'settingsAbout',
|
||||
]);
|
||||
ensureDirectItem(
|
||||
items,
|
||||
{ name: 'settingsTenantMembership', title: t('nav.settingsTenantMembership'), icon: 'OfficeBuilding' },
|
||||
[
|
||||
'settingsLocalCredential',
|
||||
'settingsRole',
|
||||
'settingsRolePrincipalBind',
|
||||
'settingsResource',
|
||||
'settingsApi',
|
||||
'settingsAbout',
|
||||
]
|
||||
);
|
||||
ensureDirectItem(
|
||||
items,
|
||||
{ name: 'settingsLocalCredential', title: t('nav.settingsLocalCredential'), icon: 'Lock' },
|
||||
[
|
||||
'settingsIdentityAudit',
|
||||
'settingsRole',
|
||||
'settingsRolePrincipalBind',
|
||||
'settingsResource',
|
||||
'settingsApi',
|
||||
'settingsAbout',
|
||||
]
|
||||
);
|
||||
ensureDirectItem(
|
||||
items,
|
||||
{ name: 'settingsIdentityAudit', title: t('nav.settingsIdentityAudit'), icon: 'DocumentChecked' },
|
||||
['settingsRole', 'settingsRolePrincipalBind', 'settingsResource', 'settingsApi', 'settingsAbout']
|
||||
);
|
||||
ensureDirectItem(
|
||||
items,
|
||||
{ name: 'settingsRolePrincipalBind', title: t('nav.settingsRolePrincipalBind'), icon: 'Link' },
|
||||
['settingsResource', 'settingsApi', 'settingsMenu', 'settingsAbout']
|
||||
);
|
||||
ensureDirectItem(items, { name: 'settingsLabel', title: t('nav.settingsLabel'), icon: 'CollectionTag' }, [
|
||||
'settingsAbout',
|
||||
]);
|
||||
ensureDirectItem(items, { name: 'settingsServiceAccount', title: t('nav.settingsServiceAccount'), icon: 'Key' }, [
|
||||
'settingsMcpServer',
|
||||
'settingsAbout',
|
||||
]);
|
||||
ensureDirectItem(items, { name: 'settingsMcpServer', title: t('nav.settingsMcpServer'), icon: 'Connection' }, [
|
||||
'settingsMcpAudit',
|
||||
'settingsAbout',
|
||||
]);
|
||||
ensureDirectItem(items, { name: 'settingsMcpAudit', title: t('nav.settingsMcpAudit'), icon: 'Document' }, [
|
||||
'settingsAbout',
|
||||
]);
|
||||
ensureDirectItem(items, { name: 'settingsAbout', title: t('nav.settingsAbout'), icon: 'InfoFilled' }, []);
|
||||
ensureAlarmGroup(items);
|
||||
ensureModelGroup(items);
|
||||
ensureCommandGroup(items);
|
||||
ensureEventGroup(items);
|
||||
return items;
|
||||
return fallbackItems();
|
||||
});
|
||||
|
||||
const hasItem = (items: SidebarItem[], name: string): boolean => {
|
||||
return items.some((item) => item.name === name || hasItem(item.children || [], name));
|
||||
};
|
||||
|
||||
const insertBefore = (items: SidebarItem[], item: SidebarItem, beforeNames: string[]) => {
|
||||
const index = items.findIndex((candidate) => beforeNames.includes(candidate.name));
|
||||
if (index >= 0) {
|
||||
items.splice(index, 0, item);
|
||||
} else {
|
||||
items.push(item);
|
||||
}
|
||||
};
|
||||
|
||||
const ensureDirectItem = (items: SidebarItem[], item: SidebarItem, beforeNames: string[]) => {
|
||||
if (hasItem(items, item.name)) return;
|
||||
insertBefore(items, item, beforeNames);
|
||||
};
|
||||
|
||||
const ensureModelGroup = (items: SidebarItem[]) => {
|
||||
const extracted: SidebarItem[] = [];
|
||||
for (const name of SETTINGS_MODEL_CHILDREN.map((item) => item.name)) {
|
||||
const index = items.findIndex((item) => item.name === name);
|
||||
if (index >= 0) {
|
||||
const removed = items.splice(index, 1)[0];
|
||||
if (removed) extracted.push(removed);
|
||||
}
|
||||
}
|
||||
|
||||
const model = items.find((item) => item.name === 'settingsModel');
|
||||
if (model) {
|
||||
model.children = model.children || [];
|
||||
if (!hasItem(model.children, 'settingsModelConfig')) {
|
||||
model.children.push(
|
||||
extracted.find((item) => item.name === 'settingsModelConfig') || {
|
||||
name: 'settingsModelConfig',
|
||||
title: t('nav.settingsModelConfig'),
|
||||
icon: SETTINGS_FALLBACK_ICON.settingsModelConfig,
|
||||
}
|
||||
);
|
||||
}
|
||||
if (!hasItem(model.children, 'settingsModelProvider')) {
|
||||
model.children.push(
|
||||
extracted.find((item) => item.name === 'settingsModelProvider') || {
|
||||
name: 'settingsModelProvider',
|
||||
title: t('nav.settingsModelProvider'),
|
||||
icon: SETTINGS_FALLBACK_ICON.settingsModelProvider,
|
||||
}
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
insertBefore(items, modelGroup(extracted), ['settingsEvent', 'settingsAbout']);
|
||||
};
|
||||
|
||||
const ensureAlarmGroup = (items: SidebarItem[]) => {
|
||||
const alarmChildNames = SETTINGS_ALARM_CHILDREN.map((item) => item.name);
|
||||
const extracted: SidebarItem[] = [];
|
||||
for (const name of alarmChildNames) {
|
||||
const index = items.findIndex((item) => item.name === name);
|
||||
if (index >= 0) {
|
||||
const removed = items.splice(index, 1)[0];
|
||||
if (removed) extracted.push(removed);
|
||||
}
|
||||
}
|
||||
|
||||
const alarm = items.find((item) => item.name === 'settingsAlarm');
|
||||
if (alarm) {
|
||||
alarm.children = alarm.children?.length ? alarm.children : alarmGroup(extracted).children;
|
||||
return;
|
||||
}
|
||||
|
||||
insertBefore(items, alarmGroup(extracted), ['settingsModel', 'settingsEvent', 'settingsAbout']);
|
||||
};
|
||||
|
||||
const ensureCommandGroup = (items: SidebarItem[]) => {
|
||||
const commandChildNames = SETTINGS_COMMAND_CHILDREN.map((item) => item.name);
|
||||
const extracted: SidebarItem[] = [];
|
||||
for (const name of commandChildNames) {
|
||||
const index = items.findIndex((item) => item.name === name);
|
||||
if (index >= 0) {
|
||||
const removed = items.splice(index, 1)[0];
|
||||
if (removed) extracted.push(removed);
|
||||
}
|
||||
}
|
||||
|
||||
const command = items.find((item) => item.name === 'settingsCommand');
|
||||
if (command) {
|
||||
command.children = command.children?.length ? command.children : commandGroup(extracted).children;
|
||||
return;
|
||||
}
|
||||
|
||||
insertBefore(items, commandGroup(extracted), ['settingsAbout']);
|
||||
};
|
||||
|
||||
const ensureEventGroup = (items: SidebarItem[]) => {
|
||||
const eventChildNames = SETTINGS_EVENT_CHILDREN.map((item) => item.name);
|
||||
const extracted: SidebarItem[] = [];
|
||||
for (const name of eventChildNames) {
|
||||
const index = items.findIndex((item) => item.name === name);
|
||||
if (index >= 0) {
|
||||
const removed = items.splice(index, 1)[0];
|
||||
if (removed) extracted.push(removed);
|
||||
}
|
||||
}
|
||||
|
||||
const event = items.find((item) => item.name === 'settingsEvent');
|
||||
if (event) {
|
||||
event.children = event.children?.length ? event.children : eventGroup(extracted).children;
|
||||
return;
|
||||
}
|
||||
|
||||
insertBefore(items, eventGroup(extracted), ['settingsAbout']);
|
||||
};
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const name = String(route.name || 'settingsUser');
|
||||
return getSettingsActiveName(name);
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
try {
|
||||
const res = await listMcpTool({
|
||||
keyword: reactiveData.toolKeyword,
|
||||
risk_level: reactiveData.toolRisk,
|
||||
riskLevel: reactiveData.toolRisk,
|
||||
limit: 500,
|
||||
});
|
||||
reactiveData.tools = res.data || [];
|
||||
|
||||
@@ -3847,6 +3847,16 @@ exports[`API wrapper contracts > token > cancelToken maps to its transport contr
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`API wrapper contracts > token > changePassword maps to its transport contract 1`] = `
|
||||
{
|
||||
"args": [
|
||||
"api/v3/auth/token/change_password",
|
||||
undefined,
|
||||
],
|
||||
"transport": "httpPost",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`API wrapper contracts > token > checkTokenValid maps to its transport contract 1`] = `
|
||||
{
|
||||
"args": [
|
||||
@@ -3880,6 +3890,7 @@ exports[`API wrapper contracts > token > generateToken maps to its transport con
|
||||
exports[`API wrapper contracts > token > has every export covered by the contract matrix 1`] = `
|
||||
[
|
||||
"cancelToken",
|
||||
"changePassword",
|
||||
"checkTokenValid",
|
||||
"generateSalt",
|
||||
"generateToken",
|
||||
|
||||
Reference in New Issue
Block a user