feat: add alarm/command/event types, i18n entries, shared styles, and thingModel format utilities

This commit is contained in:
pnoker
2026-05-25 21:52:42 +08:00
parent 7b4f530505
commit ebb229e404
96 changed files with 1544 additions and 975 deletions
+33 -37
View File
@@ -14,9 +14,9 @@
* limitations under the License.
*/
import { httpGet, httpPost } from '@/api/common';
import { crudAdd, crudDelete, crudGetById, crudList, crudUpdate } from '@/api/common';
import { API_DATA_BASE } from '@/config/constant/api';
import type { PageQuery, PageResult } from '@/config/types';
import type { PageQuery } from '@/config/types';
import type {
MessageRecord,
NotifyChannelBindRecord,
@@ -37,45 +37,41 @@ const endpoints = {
history: `${API_DATA_BASE}/notify/history`,
} as const;
const add = <T>(base: string, payload: T) => httpPost(`${base}/add`, payload);
const update = <T>(base: string, payload: T) => httpPost(`${base}/update`, payload);
const remove = (base: string, id: string) => httpPost(`${base}/delete`, undefined, { params: { id } });
const selectById = <T>(base: string, id: string) => httpGet<R<T>>(`${base}/get_by_id`, { params: { id } });
const list = <T>(base: string, query: PageQuery) => httpPost<R<PageResult<T>>>(`${base}/list`, query);
export const addRule = (payload: Partial<RuleRecord>) => crudAdd(endpoints.rule, payload);
export const updateRule = (payload: Partial<RuleRecord>) => crudUpdate(endpoints.rule, payload);
export const deleteRule = (id: string) => crudDelete(endpoints.rule, id);
export const getRuleById = (id: string) => crudGetById<RuleRecord>(endpoints.rule, id);
export const listRule = (query: PageQuery) => crudList<RuleRecord>(endpoints.rule, query);
export const addRule = (payload: Partial<RuleRecord>) => add(endpoints.rule, payload);
export const updateRule = (payload: Partial<RuleRecord>) => update(endpoints.rule, payload);
export const deleteRule = (id: string) => remove(endpoints.rule, id);
export const getRuleById = (id: string) => selectById<RuleRecord>(endpoints.rule, id);
export const listRule = (query: PageQuery) => list<RuleRecord>(endpoints.rule, query);
export const addNotify = (payload: Partial<NotifyRecord>) => crudAdd(endpoints.notify, payload);
export const updateNotify = (payload: Partial<NotifyRecord>) => crudUpdate(endpoints.notify, payload);
export const deleteNotify = (id: string) => crudDelete(endpoints.notify, id);
export const getNotifyById = (id: string) => crudGetById<NotifyRecord>(endpoints.notify, id);
export const listNotify = (query: PageQuery) => crudList<NotifyRecord>(endpoints.notify, query);
export const addNotify = (payload: Partial<NotifyRecord>) => add(endpoints.notify, payload);
export const updateNotify = (payload: Partial<NotifyRecord>) => update(endpoints.notify, payload);
export const deleteNotify = (id: string) => remove(endpoints.notify, id);
export const getNotifyById = (id: string) => selectById<NotifyRecord>(endpoints.notify, id);
export const listNotify = (query: PageQuery) => list<NotifyRecord>(endpoints.notify, query);
export const addMessage = (payload: Partial<MessageRecord>) => crudAdd(endpoints.message, payload);
export const updateMessage = (payload: Partial<MessageRecord>) => crudUpdate(endpoints.message, payload);
export const deleteMessage = (id: string) => crudDelete(endpoints.message, id);
export const getMessageById = (id: string) => crudGetById<MessageRecord>(endpoints.message, id);
export const listMessage = (query: PageQuery) => crudList<MessageRecord>(endpoints.message, query);
export const addMessage = (payload: Partial<MessageRecord>) => add(endpoints.message, payload);
export const updateMessage = (payload: Partial<MessageRecord>) => update(endpoints.message, payload);
export const deleteMessage = (id: string) => remove(endpoints.message, id);
export const getMessageById = (id: string) => selectById<MessageRecord>(endpoints.message, id);
export const listMessage = (query: PageQuery) => list<MessageRecord>(endpoints.message, query);
export const addNotifyChannel = (payload: Partial<NotifyChannelRecord>) => crudAdd(endpoints.channel, payload);
export const updateNotifyChannel = (payload: Partial<NotifyChannelRecord>) => crudUpdate(endpoints.channel, payload);
export const deleteNotifyChannel = (id: string) => crudDelete(endpoints.channel, id);
export const getNotifyChannelById = (id: string) => crudGetById<NotifyChannelRecord>(endpoints.channel, id);
export const listNotifyChannel = (query: PageQuery) => crudList<NotifyChannelRecord>(endpoints.channel, query);
export const addNotifyChannel = (payload: Partial<NotifyChannelRecord>) => add(endpoints.channel, payload);
export const updateNotifyChannel = (payload: Partial<NotifyChannelRecord>) => update(endpoints.channel, payload);
export const deleteNotifyChannel = (id: string) => remove(endpoints.channel, id);
export const getNotifyChannelById = (id: string) => selectById<NotifyChannelRecord>(endpoints.channel, id);
export const listNotifyChannel = (query: PageQuery) => list<NotifyChannelRecord>(endpoints.channel, query);
export const addNotifyChannelBind = (payload: Partial<NotifyChannelBindRecord>) => add(endpoints.channelBind, payload);
export const addNotifyChannelBind = (payload: Partial<NotifyChannelBindRecord>) =>
crudAdd(endpoints.channelBind, payload);
export const updateNotifyChannelBind = (payload: Partial<NotifyChannelBindRecord>) =>
update(endpoints.channelBind, payload);
export const deleteNotifyChannelBind = (id: string) => remove(endpoints.channelBind, id);
export const getNotifyChannelBindById = (id: string) => selectById<NotifyChannelBindRecord>(endpoints.channelBind, id);
export const listNotifyChannelBind = (query: PageQuery) => list<NotifyChannelBindRecord>(endpoints.channelBind, query);
crudUpdate(endpoints.channelBind, payload);
export const deleteNotifyChannelBind = (id: string) => crudDelete(endpoints.channelBind, id);
export const getNotifyChannelBindById = (id: string) => crudGetById<NotifyChannelBindRecord>(endpoints.channelBind, id);
export const listNotifyChannelBind = (query: PageQuery) =>
crudList<NotifyChannelBindRecord>(endpoints.channelBind, query);
export const getRuleStateById = (id: string) => selectById<RuleStateRecord>(endpoints.state, id);
export const listRuleState = (query: PageQuery) => list<RuleStateRecord>(endpoints.state, query);
export const getRuleStateById = (id: string) => crudGetById<RuleStateRecord>(endpoints.state, id);
export const listRuleState = (query: PageQuery) => crudList<RuleStateRecord>(endpoints.state, query);
export const getNotifyHistoryById = (id: string) => selectById<NotifyHistoryRecord>(endpoints.history, id);
export const listNotifyHistory = (query: PageQuery) => list<NotifyHistoryRecord>(endpoints.history, query);
export const getNotifyHistoryById = (id: string) => crudGetById<NotifyHistoryRecord>(endpoints.history, id);
export const listNotifyHistory = (query: PageQuery) => crudList<NotifyHistoryRecord>(endpoints.history, query);
+20 -15
View File
@@ -14,33 +14,38 @@
* limitations under the License.
*/
import { httpGet, httpPost } from '@/api/common';
import { crudAdd, crudDelete, crudGetById, crudList, crudUpdate, httpGet, httpPost } from '@/api/common';
import { API_DATA_BASE, API_MANAGER_BASE } from '@/config/constant/api';
import type { PageQuery, PageResult } from '@/config/types';
import type { CommandHistory, CommandRecord } from '@/config/types/command';
import type { CommandHistory, CommandParamRecord, CommandRecord } from '@/config/types/command';
const endpoints = {
command: `${API_MANAGER_BASE}/command`,
commandParam: `${API_MANAGER_BASE}/command_param`,
commandHistory: `${API_DATA_BASE}/command_history`,
} as const;
const add = <T>(base: string, payload: T) => httpPost(`${base}/add`, payload);
const update = <T>(base: string, payload: T) => httpPost(`${base}/update`, payload);
const remove = (base: string, id: string) => httpPost(`${base}/delete`, undefined, { params: { id } });
const selectById = <T>(base: string, id: string) => httpGet<R<T>>(`${base}/get_by_id`, { params: { id } });
const list = <T>(base: string, query: PageQuery) => httpPost<R<PageResult<T>>>(`${base}/list`, query);
// Command Definition CRUD
// ─── Command Definition CRUD ─────────────────────────────────────────
export const addCommand = (payload: Partial<CommandRecord>) => add(endpoints.command, payload);
export const updateCommand = (payload: Partial<CommandRecord>) => update(endpoints.command, payload);
export const deleteCommand = (id: string) => remove(endpoints.command, id);
export const getCommandById = (id: string) => selectById<CommandRecord>(endpoints.command, id);
export const listCommand = (query: PageQuery) => list<CommandRecord>(endpoints.command, query);
export const addCommand = (payload: Partial<CommandRecord>) => crudAdd(endpoints.command, payload);
export const updateCommand = (payload: Partial<CommandRecord>) => crudUpdate(endpoints.command, payload);
export const deleteCommand = (id: string) => crudDelete(endpoints.command, id);
export const getCommandById = (id: string) => crudGetById<CommandRecord>(endpoints.command, id);
export const listCommand = (query: PageQuery) => crudList<CommandRecord>(endpoints.command, query);
export const listCommandByProfileId = (profileId: string) =>
httpGet<R<CommandRecord[]>>(`${endpoints.command}/list_by_profile_id`, { params: { profile_id: profileId } });
// ─── Command History Queries ──────────────────────────────────────────
// Command Param CRUD
export const addCommandParam = (payload: Partial<CommandParamRecord>) => crudAdd(endpoints.commandParam, payload);
export const updateCommandParam = (payload: Partial<CommandParamRecord>) => crudUpdate(endpoints.commandParam, payload);
export const deleteCommandParam = (id: string) => crudDelete(endpoints.commandParam, id);
export const listCommandParamByCommandId = (commandId: string) =>
httpGet<R<CommandParamRecord[]>>(`${endpoints.commandParam}/list_by_command_id`, {
params: { command_id: commandId },
});
// Command History Queries
export const getCommandHistoryById = (recordId: string) =>
httpGet<R<CommandHistory>>(`${endpoints.commandHistory}/${recordId}`);
+16
View File
@@ -16,6 +16,7 @@
import request from '@/config/axios';
import type { AxiosRequestConfig } from 'axios';
import type { PageQuery, PageResult } from '@/config/types';
/**
* Shared HTTP helpers so every `src/api/*.ts` module stays a one-liner per
@@ -28,3 +29,18 @@ export const httpGet = <T = R>(url: string, config?: AxiosRequestConfig) =>
export const httpPost = <T = R, D = unknown>(url: string, data?: D, config?: AxiosRequestConfig) =>
request<T>({ ...config, url, method: 'post', data });
export const crudAdd = <TPayload, TResponse = string>(base: string, payload: TPayload) =>
httpPost<R<TResponse>, TPayload>(`${base}/add`, payload);
export const crudUpdate = <TPayload, TResponse = string>(base: string, payload: TPayload) =>
httpPost<R<TResponse>, TPayload>(`${base}/update`, payload);
export const crudDelete = (base: string, id: string) =>
httpPost<R<string>>(`${base}/delete`, undefined, { params: { id } });
export const crudGetById = <TRecord>(base: string, id: string) =>
httpGet<R<TRecord>>(`${base}/get_by_id`, { params: { id } });
export const crudList = <TRecord>(base: string, query: PageQuery) =>
httpPost<R<PageResult<TRecord>>, PageQuery>(`${base}/list`, query);
+18 -15
View File
@@ -14,33 +14,36 @@
* limitations under the License.
*/
import { httpGet, httpPost } from '@/api/common';
import { crudAdd, crudDelete, crudGetById, crudList, crudUpdate, httpGet, httpPost } from '@/api/common';
import { API_DATA_BASE, API_MANAGER_BASE } from '@/config/constant/api';
import type { PageQuery, PageResult } from '@/config/types';
import type { EventHistory, EventRecord } from '@/config/types/event';
import type { EventHistory, EventParamRecord, EventRecord } from '@/config/types/event';
const endpoints = {
event: `${API_MANAGER_BASE}/event`,
eventParam: `${API_MANAGER_BASE}/event_param`,
eventHistory: `${API_DATA_BASE}/event_history`,
} as const;
const add = <T>(base: string, payload: T) => httpPost(`${base}/add`, payload);
const update = <T>(base: string, payload: T) => httpPost(`${base}/update`, payload);
const remove = (base: string, id: string) => httpPost(`${base}/delete`, undefined, { params: { id } });
const selectById = <T>(base: string, id: string) => httpGet<R<T>>(`${base}/get_by_id`, { params: { id } });
const list = <T>(base: string, query: PageQuery) => httpPost<R<PageResult<T>>>(`${base}/list`, query);
// Event Definition CRUD
// ─── Event Definition CRUD ────────────────────────────────────────────
export const addEvent = (payload: Partial<EventRecord>) => add(endpoints.event, payload);
export const updateEvent = (payload: Partial<EventRecord>) => update(endpoints.event, payload);
export const deleteEvent = (id: string) => remove(endpoints.event, id);
export const getEventById = (id: string) => selectById<EventRecord>(endpoints.event, id);
export const listEvent = (query: PageQuery) => list<EventRecord>(endpoints.event, query);
export const addEvent = (payload: Partial<EventRecord>) => crudAdd(endpoints.event, payload);
export const updateEvent = (payload: Partial<EventRecord>) => crudUpdate(endpoints.event, payload);
export const deleteEvent = (id: string) => crudDelete(endpoints.event, id);
export const getEventById = (id: string) => crudGetById<EventRecord>(endpoints.event, id);
export const listEvent = (query: PageQuery) => crudList<EventRecord>(endpoints.event, query);
export const listEventByProfileId = (profileId: string) =>
httpGet<R<EventRecord[]>>(`${endpoints.event}/list_by_profile_id`, { params: { profile_id: profileId } });
// ─── Event History Queries ────────────────────────────────────────────
// Event Param CRUD
export const addEventParam = (payload: Partial<EventParamRecord>) => crudAdd(endpoints.eventParam, payload);
export const updateEventParam = (payload: Partial<EventParamRecord>) => crudUpdate(endpoints.eventParam, payload);
export const deleteEventParam = (id: string) => crudDelete(endpoints.eventParam, id);
export const listEventParamByEventId = (eventId: string) =>
httpGet<R<EventParamRecord[]>>(`${endpoints.eventParam}/list_by_event_id`, { params: { event_id: eventId } });
// Event History Queries
export const getEventHistoryById = (recordId: string) =>
httpGet<R<EventHistory>>(`${endpoints.eventHistory}/${recordId}`);
+8 -4
View File
@@ -19,16 +19,20 @@ import { API_AUTH_BASE } from '@/config/constant/api';
import type { PageQuery, PageResult } from '@/config/types';
import type { ResourceForm, ResourceRecord } from '@/config/types/auth';
export const addResource = (resource: ResourceForm) => httpPost(`${API_AUTH_BASE}/resource/add`, resource);
export const addResource = (resource: ResourceForm) =>
httpPost<R<ResourceRecord>>(`${API_AUTH_BASE}/resource/add`, resource);
export const deleteResource = (id: string) =>
httpPost(`${API_AUTH_BASE}/resource/delete`, undefined, { params: { id } });
export const updateResource = (resource: ResourceForm) => httpPost(`${API_AUTH_BASE}/resource/update`, resource);
export const updateResource = (resource: ResourceForm) =>
httpPost<R<ResourceRecord>>(`${API_AUTH_BASE}/resource/update`, resource);
export const getResourceById = (id: string) => httpGet(`${API_AUTH_BASE}/resource/get_by_id`, { params: { id } });
export const getResourceById = (id: string) =>
httpGet<R<ResourceRecord>>(`${API_AUTH_BASE}/resource/get_by_id`, { params: { id } });
export const listResource = <T = R<PageResult<ResourceRecord>>>(query: PageQuery) =>
httpPost<T>(`${API_AUTH_BASE}/resource/list`, query);
export const listResourceTree = (query: PageQuery = {}) => httpPost(`${API_AUTH_BASE}/resource/list_tree`, query);
export const listResourceTree = (query: PageQuery = {}) =>
httpPost<R<ResourceRecord[]>>(`${API_AUTH_BASE}/resource/list_tree`, query);
+3 -3
View File
@@ -745,7 +745,7 @@
try {
await agenticStore.uploadAttachment(file);
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : 'Upload failed');
ElMessage.error(error instanceof Error ? error.message : t('agentic.uploadFailed'));
}
}
};
@@ -757,12 +757,12 @@
cancelButtonText: t('agentic.dialogCancel'),
});
await agenticStore.confirmAction(actionId);
ElMessage.success('Action confirmed');
ElMessage.success(t('agentic.actionConfirmed'));
};
const handleRejectAction = async (actionId: string) => {
await agenticStore.rejectAction(actionId);
ElMessage.success('Action rejected');
ElMessage.success(t('agentic.actionRejected'));
};
const handleCopyMessage = async (message: AgenticMessage) => {
@@ -87,7 +87,7 @@
margin-top: 2px;
display: flex;
justify-content: flex-end;
border-top: 1px solid #dcdfe6;
border-top: 1px solid var(--el-border-color);
}
.things-card-footer-operation {
@@ -247,13 +247,13 @@
.dashboard-card__title-text {
font-weight: 600;
color: #303133;
color: var(--el-text-color-primary);
}
.dashboard-card__subtitle {
font-size: 12px;
font-weight: normal;
color: #909399;
color: var(--el-text-color-secondary);
}
// Badge sits inline with the title — override the default floating
@@ -332,7 +332,7 @@
// ---- footer ----------------------------------------------------------
// Opt-in bar beneath the body. Matches LiveFeed's "updated at + rows"
// look: 8/16 padding, 12px/#909399 text, light top border, #fafafa bg.
// look: 8/16 padding, secondary text, light top border and subtle bg.
// Consumers lay out content with a pair of <span>s — flex space-between
// handles the rest.
.dashboard-card__footer {
@@ -342,9 +342,9 @@
gap: 8px;
padding: 8px 16px;
font-size: 12px;
color: #909399;
color: var(--el-text-color-secondary);
border-top: 1px solid var(--el-border-color-lighter);
background: #fafafa;
background: var(--el-fill-color-lighter);
flex-shrink: 0;
}
@@ -63,7 +63,7 @@
line-height: 48px;
font-size: 14px;
font-weight: bold;
color: rgba(0, 0, 0, 0.85);
color: var(--el-text-color-primary);
cursor: pointer;
display: block;
overflow: hidden;
@@ -72,7 +72,7 @@
width: 200px;
&:hover {
color: #1890ff;
color: var(--el-color-primary);
}
}
@@ -89,10 +89,10 @@
}
.header-enable {
border-bottom: 1px solid #c2e7b0;
border-bottom: 1px solid var(--el-color-success-light-5);
}
.header-disable {
border-bottom: 1px solid #fbc4c4;
border-bottom: 1px solid var(--el-color-danger-light-5);
}
</style>
@@ -95,7 +95,7 @@
width: 100%;
height: 55px;
display: flex;
border-bottom: 1px solid #dcdfe6;
border-bottom: 1px solid var(--el-border-color);
}
.skeleton-card-icon {
@@ -152,7 +152,7 @@
display: flex;
justify-content: flex-end;
align-items: center;
border-top: 1px solid #dcdfe6;
border-top: 1px solid var(--el-border-color);
}
.skeleton-card-operation {
+20 -20
View File
@@ -118,19 +118,19 @@
// --stat-card-accent values declared in the scoped styles below so a
// palette change only needs updating in one place conceptually.
const TONE_ACCENT: Record<string, string> = {
blue: '#409eff',
green: '#67c23a',
orange: '#e6a23c',
blue: 'var(--el-color-primary)',
green: 'var(--el-color-success)',
orange: 'var(--el-color-warning)',
purple: '#9059f6',
red: '#f56c6c',
red: 'var(--el-color-danger)',
};
const accentColor = computed(() => TONE_ACCENT[props.tone] || TONE_ACCENT.blue);
</script>
<style lang="scss" scoped>
.stat-card {
--stat-card-accent: #409eff;
--stat-card-bg: rgba(64, 158, 255, 0.08);
--stat-card-accent: var(--el-color-primary);
--stat-card-bg: var(--el-color-primary-light-9);
cursor: pointer;
transition: transform 0.15s ease;
height: 100%;
@@ -149,24 +149,24 @@
}
&--blue {
--stat-card-accent: #409eff;
--stat-card-bg: rgba(64, 158, 255, 0.1);
--stat-card-accent: var(--el-color-primary);
--stat-card-bg: var(--el-color-primary-light-9);
}
&--green {
--stat-card-accent: #67c23a;
--stat-card-bg: rgba(103, 194, 58, 0.1);
--stat-card-accent: var(--el-color-success);
--stat-card-bg: var(--el-color-success-light-9);
}
&--orange {
--stat-card-accent: #e6a23c;
--stat-card-bg: rgba(230, 162, 60, 0.1);
--stat-card-accent: var(--el-color-warning);
--stat-card-bg: var(--el-color-warning-light-9);
}
&--purple {
--stat-card-accent: #9059f6;
--stat-card-bg: rgba(144, 89, 246, 0.1);
}
&--red {
--stat-card-accent: #f56c6c;
--stat-card-bg: rgba(245, 108, 108, 0.1);
--stat-card-accent: var(--el-color-danger);
--stat-card-bg: var(--el-color-danger-light-9);
}
:deep(.el-card__body) {
@@ -211,7 +211,7 @@
.stat-card__title {
font-size: 13px;
color: #909399;
color: var(--el-text-color-secondary);
margin-bottom: 2px;
}
@@ -225,7 +225,7 @@
.stat-card__value-text {
font-size: 24px;
font-weight: 600;
color: #303133;
color: var(--el-text-color-primary);
}
.stat-card__trend {
@@ -236,18 +236,18 @@
}
.stat-card__trend--up {
color: #67c23a;
color: var(--el-color-success);
}
.stat-card__trend--down {
color: #f56c6c;
color: var(--el-color-danger);
}
.stat-card__trend--flat {
color: #909399;
color: var(--el-text-color-secondary);
}
.stat-card__subtitle {
font-size: 12px;
color: #909399;
color: var(--el-text-color-secondary);
margin-top: 4px;
}
+11 -3
View File
@@ -44,7 +44,7 @@
/** Accent colour for the line stroke and the gradient's inner stop. */
color: {
type: String,
default: '#409eff',
default: 'var(--el-color-primary)',
},
/** Chart height in px. Container is stretched to the parent width. */
height: {
@@ -71,6 +71,13 @@
const containerRef = ref<HTMLElement>();
let chart: Chart | undefined;
const resolveCssColor = (color: string) => {
const match = color.match(/^var\((--[^),]+)(?:,[^)]+)?\)$/);
const token = match?.[1];
if (!token) return color;
return getComputedStyle(document.documentElement).getPropertyValue(token).trim() || color;
};
const draw = (attempt = 0) => {
const el = containerRef.value;
if (!el || !props.data || props.data.length === 0) return;
@@ -98,7 +105,8 @@
paddingRight: 2,
});
const fillGradient = `linear-gradient(90deg, rgba(255,255,255,0) 0%, ${props.color} 100%)`;
const color = resolveCssColor(props.color);
const fillGradient = `linear-gradient(90deg, rgba(255,255,255,0) 0%, ${color} 100%)`;
const area = chart
.area()
@@ -132,7 +140,7 @@
.encode('x', 'x')
.encode('y', 'y')
.encode('shape', 'smooth')
.style('stroke', props.color)
.style('stroke', color)
.style('lineWidth', 2)
.axis(false)
.legend(false);
+2 -6
View File
@@ -23,6 +23,7 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { isEnabledFlag } from '@/utils/thingModelFormatUtil';
const props = withDefaults(
defineProps<{
@@ -36,10 +37,5 @@
);
const { t } = useI18n();
const enabled = computed(() => {
if (props.value === true) return true;
if (props.value === false) return false;
const value = String(props.value ?? '').toUpperCase();
return value === 'ENABLE' || value === 'ENABLED' || value === 'TRUE' || value === '0';
});
const enabled = computed(() => isEnabledFlag(props.value));
</script>
+6
View File
@@ -136,6 +136,12 @@ export const CALL_TYPE_OPTIONS: EnumOption[] = [
{ label: 'ASYNC', value: 'ASYNC' },
];
// Backend: ParamDirectionFlagEnum
export const PARAM_DIRECTION_OPTIONS: EnumOption[] = [
{ label: 'INPUT', value: 'INPUT' },
{ label: 'OUTPUT', value: 'OUTPUT' },
];
// Backend: EventTypeFlagEnum
export const EVENT_TYPE_OPTIONS: EnumOption[] = [
{ label: 'INFO', value: 'INFO' },
+101 -3
View File
@@ -474,11 +474,62 @@ export default {
commandName: 'Command Name',
commandNamePlaceholder: 'Enter command name',
},
form: {
addTitle: 'Add Command',
editTitle: 'Edit Command',
code: 'Code',
commandType: 'Command Type',
callType: 'Call Type',
timeout: 'Timeout (s)',
params: 'Command Params',
direction: 'Direction',
type: 'Type',
required: 'Required',
defaultValue: 'Default',
enabled: 'Enabled',
nameRequired: 'Command name is required',
codeRequired: 'Command code is required',
paramRequired: 'Command param name, code, direction, and type are required.',
paramCodeUnique: 'Command param code must be unique.',
},
detail: {
title: 'Command Detail',
code: 'Code',
commandType: 'Command Type',
callType: 'Call Type',
timeout: 'Timeout (s)',
profileId: 'Profile ID',
tenantId: 'Tenant ID',
},
history: {
detailTitle: 'Command Record Detail',
recordId: 'Record ID',
deviceId: 'Device ID',
commandId: 'Command ID',
commandCode: 'Command Code',
status: 'Status',
error: 'Error',
errorCode: 'Error Code',
errorMessage: 'Error Message',
source: 'Source',
sourceUserId: 'Source User ID',
paramValues: 'Param Values',
resultValues: 'Result Values',
configSnapshot: 'Config Snapshot',
occurTime: 'Occur Time',
sendTime: 'Send Time',
finishTime: 'Finish Time',
expireTime: 'Expire Time',
},
errors: {
idNotReturned: 'Command ID was not returned.',
idMissing: 'Command ID is missing.',
},
card: {
code: 'Command Code',
type: 'Command Type',
callType: 'Call Type',
timeout: 'Timeout',
timeout: 'Timeout (s)',
remarkTitle: 'Command description',
confirmDisable: 'Are you sure to disable this command? This action cannot be undone!',
confirmEnable: 'Are you sure to enable this command?',
@@ -491,6 +542,32 @@ export default {
eventName: 'Event Name',
eventNamePlaceholder: 'Enter event name',
},
form: {
addTitle: 'Add Event',
editTitle: 'Edit Event',
code: 'Code',
eventType: 'Event Type',
eventLevel: 'Event Level',
params: 'Event Params',
type: 'Type',
enabled: 'Enabled',
nameRequired: 'Event name is required',
codeRequired: 'Event code is required',
paramRequired: 'Event param name, code, and type are required.',
paramCodeUnique: 'Event param code must be unique.',
},
detail: {
title: 'Event Detail',
code: 'Code',
eventType: 'Event Type',
eventLevel: 'Event Level',
profileId: 'Profile ID',
tenantId: 'Tenant ID',
},
errors: {
idNotReturned: 'Event ID was not returned.',
idMissing: 'Event ID is missing.',
},
card: {
code: 'Event Code',
type: 'Event Type',
@@ -502,6 +579,24 @@ export default {
},
empty: 'No event data!',
},
eventHistory: {
detailTitle: 'Event History Detail',
recordId: 'Record ID',
deviceId: 'Device ID',
eventId: 'Event ID',
eventCode: 'Event Code',
type: 'Type',
level: 'Level',
ack: 'Ack',
message: 'Message',
paramValues: 'Param Values',
configSnapshot: 'Config Snapshot',
occurTime: 'Occur Time',
receiveTime: 'Receive Time',
acknowledgeFlag: 'Acknowledge Flag',
acknowledgeUserId: 'Acknowledge User ID',
acknowledgeTime: 'Acknowledge Time',
},
point: {
tool: {
pointName: 'Point Name',
@@ -588,6 +683,7 @@ export default {
card: {
pointValueId: 'Point Value ID',
rwType: 'R/W Type',
write: 'Write',
processedValue: 'Processed Value',
rawValue: 'Raw Value',
device: 'Device',
@@ -596,10 +692,9 @@ export default {
saveTime: 'Save Time',
noLatestValue: 'No Value',
noHistory: 'No History',
confirmDelete: 'Are you sure to delete this point value? This action cannot be undone!',
},
edit: {
title: 'Edit Point Value',
title: 'Write Point Value',
pointValue: 'Point Value',
description: 'Description',
pointValuePlaceholder: 'Enter point value',
@@ -1103,6 +1198,9 @@ export default {
dialogDeleteConfirm: 'Delete this conversation?',
attachAnalyze: 'Please analyze the attached files.',
canceled: 'Canceled.',
uploadFailed: 'Upload failed.',
actionConfirmed: 'Action confirmed.',
actionRejected: 'Action rejected.',
requestFailed: 'Request failed.',
failedStream: 'Agentic chat failed.',
failedContext: 'Missing conversation context.',
+101 -3
View File
@@ -473,11 +473,62 @@ export default {
commandName: '指令名称',
commandNamePlaceholder: '请输入指令名称',
},
form: {
addTitle: '新增指令',
editTitle: '编辑指令',
code: '标识',
commandType: '指令类型',
callType: '调用类型',
timeout: '超时时间(s)',
params: '指令参数',
direction: '方向',
type: '类型',
required: '必填',
defaultValue: '默认值',
enabled: '启用',
nameRequired: '指令名称不能为空',
codeRequired: '指令标识不能为空',
paramRequired: '指令参数名称、标识、方向和类型不能为空。',
paramCodeUnique: '指令参数标识不能重复。',
},
detail: {
title: '指令详情',
code: '标识',
commandType: '指令类型',
callType: '调用类型',
timeout: '超时时间(s)',
profileId: '模板 ID',
tenantId: '租户 ID',
},
history: {
detailTitle: '指令调用记录详情',
recordId: '记录 ID',
deviceId: '设备 ID',
commandId: '指令 ID',
commandCode: '指令标识',
status: '状态',
error: '错误',
errorCode: '错误码',
errorMessage: '错误信息',
source: '来源',
sourceUserId: '来源用户 ID',
paramValues: '参数值',
resultValues: '结果值',
configSnapshot: '配置快照',
occurTime: '发生时间',
sendTime: '发送时间',
finishTime: '完成时间',
expireTime: '过期时间',
},
errors: {
idNotReturned: '后端未返回指令 ID。',
idMissing: '指令 ID 缺失。',
},
card: {
code: '指令标识',
type: '指令类型',
callType: '调用类型',
timeout: '超时时间',
timeout: '超时时间(s)',
remarkTitle: '指令描述信息',
confirmDisable: '是否确定停用该指令? 该操作不可恢复!',
confirmEnable: '是否确定启用该指令?',
@@ -490,6 +541,32 @@ export default {
eventName: '事件名称',
eventNamePlaceholder: '请输入事件名称',
},
form: {
addTitle: '新增事件',
editTitle: '编辑事件',
code: '标识',
eventType: '事件类型',
eventLevel: '事件等级',
params: '事件参数',
type: '类型',
enabled: '启用',
nameRequired: '事件名称不能为空',
codeRequired: '事件标识不能为空',
paramRequired: '事件参数名称、标识和类型不能为空。',
paramCodeUnique: '事件参数标识不能重复。',
},
detail: {
title: '事件详情',
code: '标识',
eventType: '事件类型',
eventLevel: '事件等级',
profileId: '模板 ID',
tenantId: '租户 ID',
},
errors: {
idNotReturned: '后端未返回事件 ID。',
idMissing: '事件 ID 缺失。',
},
card: {
code: '事件标识',
type: '事件类型',
@@ -501,6 +578,24 @@ export default {
},
empty: '暂无事件数据!',
},
eventHistory: {
detailTitle: '事件上报记录详情',
recordId: '记录 ID',
deviceId: '设备 ID',
eventId: '事件 ID',
eventCode: '事件标识',
type: '类型',
level: '等级',
ack: '确认',
message: '消息',
paramValues: '参数值',
configSnapshot: '配置快照',
occurTime: '发生时间',
receiveTime: '接收时间',
acknowledgeFlag: '确认标识',
acknowledgeUserId: '确认用户 ID',
acknowledgeTime: '确认时间',
},
point: {
tool: {
pointName: '位号名称',
@@ -587,6 +682,7 @@ export default {
card: {
pointValueId: '位号值ID',
rwType: '读写标识',
write: '写入',
processedValue: '处理值',
rawValue: '原始值',
device: '所属设备',
@@ -595,10 +691,9 @@ export default {
saveTime: '保存日期',
noLatestValue: '暂无值',
noHistory: '暂无历史',
confirmDelete: '是否确定删除该位号值? 该操作不可恢复!',
},
edit: {
title: '编辑位号值',
title: '写入位号值',
pointValue: '位号值',
description: '操作描述',
pointValuePlaceholder: '请输入位号值',
@@ -1103,6 +1198,9 @@ export default {
dialogDeleteConfirm: '确定删除该对话?',
attachAnalyze: '请分析附件内容。',
canceled: '已取消。',
uploadFailed: '上传失败。',
actionConfirmed: '操作已确认。',
actionRejected: '操作已拒绝。',
requestFailed: '请求失败。',
failedStream: 'AI 对话失败。',
failedContext: '缺少对话上下文。',
@@ -14,41 +14,10 @@
* limitations under the License.
*/
/**
* Element Plus form component size variables
* Used to define consistent widths for form inputs and selects
*/
// Form size widths
// Element Plus form component size tokens. Keep this file token-only because
// Vite injects it into every SCSS block via additionalData.
$form-width-small: 120px;
$form-width-medium: 150px;
$form-width-default: 250px;
$form-width-special: 300px;
$form-width-large: 500px;
// Small form elements
.el-select.edit-form-small,
.el-input.edit-form-small {
width: $form-width-small;
}
// Medium form elements
.el-select.edit-form-medium {
width: $form-width-medium;
}
// Default form elements
.el-select.edit-form-default {
width: $form-width-default;
}
// Special form elements
.el-select.edit-form-special,
.el-input.edit-form-special {
width: $form-width-special;
}
// Large form elements
.el-select.edit-form-large {
width: $form-width-large;
}
-1
View File
@@ -17,7 +17,6 @@
import type { App } from 'vue';
import * as ElementIcons from '@element-plus/icons-vue';
import 'element-plus/dist/index.css';
import './element-variables.scss';
/**
* Registers every icon exported by `@element-plus/icons-vue` globally so
+16
View File
@@ -53,6 +53,22 @@ export interface CommandForm {
[key: string]: unknown;
}
export interface CommandParamRecord {
id?: string;
paramName?: string;
paramCode?: string;
paramDirectionFlag?: string | number;
paramTypeFlag?: string | number;
requiredFlag?: boolean;
defaultValue?: string;
paramExt?: Record<string, unknown>;
commandId?: string;
enableFlag?: string | number;
signature?: string;
version?: number;
[key: string]: unknown;
}
/**
* Command call history (dc3_command_history).
*/
+13
View File
@@ -51,6 +51,19 @@ export interface EventForm {
[key: string]: unknown;
}
export interface EventParamRecord {
id?: string;
paramName?: string;
paramCode?: string;
paramTypeFlag?: string | number;
paramExt?: Record<string, unknown>;
eventId?: string;
enableFlag?: string | number;
signature?: string;
version?: number;
[key: string]: unknown;
}
/**
* Event report history (dc3_event_history).
*/
+2 -2
View File
@@ -56,8 +56,8 @@ export type {
AlarmEntityRecord,
} from './alarm';
export type { CommandRecord, CommandForm, CommandHistory } from './command';
export type { EventRecord, EventForm, EventHistory } from './event';
export type { CommandRecord, CommandForm, CommandParamRecord, CommandHistory } from './command';
export type { EventRecord, EventForm, EventParamRecord, EventHistory } from './event';
export type {
UserForm,
+47
View File
@@ -14,6 +14,9 @@
* limitations under the License.
*/
@use '@/styles/things-dialog.scss';
@use '@/styles/things-card.scss';
// 真正的全局样式,在 main.ts 里一次性 import。
// 不能放到 element-variables.scss 里,因为后者是 Vite additionalData,
// 会被预置到每个 scoped <style> 块,所有选择器都会被附加 scope hash,
@@ -25,6 +28,50 @@ body {
min-height: 768px;
}
// Form width helper classes are real CSS, so they live in the one-time global
// stylesheet. The Sass variables behind them are still injected everywhere for
// token reuse.
.el-select.edit-form-small,
.el-input.edit-form-small {
width: $form-width-small;
}
.el-select.edit-form-medium {
width: $form-width-medium;
}
.el-select.edit-form-default {
width: $form-width-default;
}
.el-select.edit-form-special,
.el-input.edit-form-special {
width: $form-width-special;
}
.el-select.edit-form-large {
width: $form-width-large;
}
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
.json-preview {
max-height: 220px;
margin: 0;
padding: 8px;
overflow: auto;
border-radius: 4px;
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
// el-popconfirm 的 popper 也通过 teleport 挂到 body,作用域外样式必须放在全局。
// Element Plus 默认 popconfirm 只按文字宽度撑开,文字 + 两个按钮常常挤在一行,
// 视觉上又窄又紧。这里统一:
+18 -12
View File
@@ -15,9 +15,7 @@
*/
// Vertically centre things-dialog + cap its height so tall forms scroll
// inside the dialog rather than bleed past the viewport bottom. Applied
// via the wrapper .el-overlay-dialog because el-dialog teleports to body
// and its direct container controls vertical alignment.
// inside the dialog rather than bleed past the viewport bottom.
.el-overlay-dialog:has(.things-dialog) {
display: flex;
align-items: center;
@@ -35,8 +33,13 @@
}
.el-dialog__body {
overflow-y: auto;
flex: 1 1 auto;
padding: 30px 30px 15px 30px;
overflow-y: auto;
pre {
margin: auto;
}
}
// Opt-in helper: put `class="things-dialog things-dialog--wide"` on a
@@ -101,6 +104,17 @@
margin-bottom: 10px;
}
.param-editor {
margin-top: 12px;
}
.param-editor__toolbar {
align-items: center;
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.things-dialog-footer {
display: flex;
justify-content: flex-end;
@@ -111,14 +125,6 @@
font-weight: bold;
}
.el-dialog__body {
padding: 30px 30px 15px 30px;
pre {
margin: auto;
}
}
.el-form-item__content {
width: 100%;
+5
View File
@@ -36,6 +36,11 @@ export const timestampColumn = (_row: unknown, _col: unknown, cellValue: unknown
return timestamp(String(cellValue));
};
export const timestampLabel = (value: unknown, fallback = '-'): string => {
if (value == null || value === '') return fallback;
return timestamp(String(value)) || fallback;
};
/**
* Format date to string
*
@@ -14,23 +14,14 @@
* limitations under the License.
*/
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'PointValueDetail',
props: {
detailData: {
type: Object,
default: () => {
return {};
},
},
},
setup() {
const detailVisible = ref(false);
return {
detailVisible,
};
},
});
export const prettyJson = (value: unknown, fallback = '-'): string => {
if (value == null || value === '') return fallback;
if (typeof value === 'string') {
try {
return JSON.stringify(JSON.parse(value), null, 2);
} catch {
return value;
}
}
return JSON.stringify(value, null, 2);
};
+85 -2
View File
@@ -14,7 +14,11 @@
* limitations under the License.
*/
type FlagValue = string | number | null | undefined;
type FlagValue = string | number | boolean | null | undefined;
type TimeoutValue = string | number | null | undefined;
const LEGACY_TIMEOUT_MILLISECONDS_THRESHOLD = 1000;
const MILLISECONDS_PER_SECOND = 1000;
const COMMAND_TYPE_BY_INDEX: Record<string, string> = {
'0': 'CUSTOM',
@@ -27,6 +31,22 @@ const CALL_TYPE_BY_INDEX: Record<string, string> = {
'1': 'ASYNC',
};
const PARAM_DIRECTION_BY_INDEX: Record<string, string> = {
'0': 'INPUT',
'1': 'OUTPUT',
};
const POINT_TYPE_BY_INDEX: Record<string, string> = {
'0': 'STRING',
'1': 'BYTE',
'2': 'SHORT',
'3': 'INT',
'4': 'LONG',
'5': 'FLOAT',
'6': 'DOUBLE',
'7': 'BOOLEAN',
};
const EVENT_TYPE_BY_INDEX: Record<string, string> = {
'0': 'INFO',
'1': 'ALERT',
@@ -41,6 +61,11 @@ const EVENT_LEVEL_BY_INDEX: Record<string, string> = {
'3': 'CRITICAL',
};
const ENABLE_FLAG_BY_INDEX: Record<string, string> = {
'0': 'ENABLE',
'1': 'DISABLE',
};
function normalizeFlag(value: FlagValue, indexMap: Record<string, string>): string {
if (value === null || value === undefined || value === '') {
return '-';
@@ -50,26 +75,63 @@ function normalizeFlag(value: FlagValue, indexMap: Record<string, string>): stri
return indexMap[raw] ?? raw.toUpperCase();
}
function normalizeFormFlag(value: FlagValue, indexMap: Record<string, string>, fallback: string): string {
const normalized = normalizeFlag(value, indexMap);
return normalized === '-' ? fallback : normalized;
}
export function isEnabledFlag(value: FlagValue): boolean {
return String(value).toUpperCase() === 'ENABLE' || Number(value) === 0;
if (value === true) return true;
if (value === false || value === null || value === undefined || value === '') return false;
const normalized = String(value).trim().toUpperCase();
return normalized === 'ENABLE' || normalized === 'ENABLED' || normalized === 'TRUE' || normalized === '0';
}
export function enableFlagValue(value: FlagValue, fallback = 'ENABLE'): string {
return normalizeFormFlag(value, ENABLE_FLAG_BY_INDEX, fallback);
}
export function commandTypeLabel(value: FlagValue): string {
return normalizeFlag(value, COMMAND_TYPE_BY_INDEX);
}
export function commandTypeValue(value: FlagValue, fallback = 'CUSTOM'): string {
return normalizeFormFlag(value, COMMAND_TYPE_BY_INDEX, fallback);
}
export function callTypeLabel(value: FlagValue): string {
return normalizeFlag(value, CALL_TYPE_BY_INDEX);
}
export function callTypeValue(value: FlagValue, fallback = 'SYNC'): string {
return normalizeFormFlag(value, CALL_TYPE_BY_INDEX, fallback);
}
export function paramDirectionValue(value: FlagValue, fallback = 'INPUT'): string {
return normalizeFormFlag(value, PARAM_DIRECTION_BY_INDEX, fallback);
}
export function pointTypeValue(value: FlagValue, fallback = 'STRING'): string {
return normalizeFormFlag(value, POINT_TYPE_BY_INDEX, fallback);
}
export function eventTypeLabel(value: FlagValue): string {
return normalizeFlag(value, EVENT_TYPE_BY_INDEX);
}
export function eventTypeValue(value: FlagValue, fallback = 'INFO'): string {
return normalizeFormFlag(value, EVENT_TYPE_BY_INDEX, fallback);
}
export function eventLevelLabel(value: FlagValue): string {
return normalizeFlag(value, EVENT_LEVEL_BY_INDEX);
}
export function eventLevelValue(value: FlagValue, fallback = 'LOW'): string {
return normalizeFormFlag(value, EVENT_LEVEL_BY_INDEX, fallback);
}
export function eventLevelTag(value: FlagValue): 'success' | 'warning' | 'danger' | 'info' {
const level = eventLevelLabel(value);
if (level === 'CRITICAL') return 'danger';
@@ -77,3 +139,24 @@ export function eventLevelTag(value: FlagValue): 'success' | 'warning' | 'danger
if (level === 'MEDIUM') return 'success';
return 'info';
}
export function normalizeCommandTimeoutSeconds(value: TimeoutValue): number | undefined {
if (value === null || value === undefined || value === '') {
return undefined;
}
const timeout = Number(value);
if (!Number.isFinite(timeout) || timeout <= 0) {
return undefined;
}
if (timeout >= LEGACY_TIMEOUT_MILLISECONDS_THRESHOLD && timeout % MILLISECONDS_PER_SECOND === 0) {
return timeout / MILLISECONDS_PER_SECOND;
}
return timeout;
}
export function commandTimeoutLabel(value: TimeoutValue): string {
return normalizeCommandTimeoutSeconds(value)?.toString() ?? '-';
}
-2
View File
@@ -347,5 +347,3 @@
list();
</script>
<style lang="scss" scoped></style>
+1 -5
View File
@@ -87,7 +87,7 @@
<div class="things-dialog-footer">
<slot name="footer">
<el-button @click="cancel">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ $t('common.reset') }}</el-button>
<el-button type="primary" @click="addThing">{{ $t('common.confirm') }}</el-button>
</slot>
</div>
@@ -238,7 +238,3 @@
addThing,
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+5 -7
View File
@@ -19,7 +19,7 @@
<el-card shadow="hover">
<div class="things-card-content">
<things-card-header
:enabled="data.enableFlag === 'ENABLE'"
:enabled="enabled"
:icon="icon"
:name="data.deviceName"
:status-title="$t('common.enableFlag')"
@@ -59,7 +59,7 @@
:delete-title="$t('device.card.confirmDelete')"
:disable-title="$t('device.card.confirmDisable')"
:enable-title="$t('device.card.confirmEnable')"
:enabled="data.enableFlag === 'ENABLE'"
:enabled="enabled"
@delete="emitDelete"
@detail="detail"
@disable="emitToggle('disable-thing')"
@@ -72,12 +72,13 @@
</template>
<script lang="ts" setup>
import type { PropType } from 'vue';
import { computed, type PropType } from 'vue';
import { Edit, Promotion, Sunset } from '@element-plus/icons-vue';
import router from '@/config/router';
import { copy } from '@/utils/commonUtil';
import { timestamp } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import { isEnabledFlag } from '@/utils/thingModelFormatUtil';
import ThingsCardHeader from '@/components/card/header/ThingsCardHeader.vue';
import ThingsCardActions from '@/components/card/actions/ThingsCardActions.vue';
@@ -90,6 +91,7 @@
});
const emit = defineEmits(['disable-thing', 'enable-thing', 'delete-thing']);
const enabled = computed(() => isEnabledFlag(props.data.enableFlag));
const emitToggle = (name: 'disable-thing' | 'enable-thing') => {
emit(name, props.data.id, props.data.driverId, () => successMessage());
@@ -111,7 +113,3 @@
});
};
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
-4
View File
@@ -202,7 +202,3 @@
device();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
+10 -10
View File
@@ -96,9 +96,9 @@
/>
</el-form-item>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="done">{{ $t('common.return') }}</el-button>
<el-button :icon="Back" plain @click="done">{{ $t('common.return') }}</el-button>
<el-button :icon="RefreshLeft" @click="deviceReset">{{ $t('common.reset') }}</el-button>
<el-button :icon="Right" plain type="warning" @click="next">{{ $t('common.next') }}</el-button>
<el-button :icon="Right" plain type="primary" @click="next">{{ $t('common.next') }}</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -155,11 +155,11 @@
</el-form>
<el-empty v-else :description="$t('device.edit.driverConfigEmpty')" />
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Back" plain @click="pre">{{ $t('common.previous') }}</el-button>
<el-button v-if="hasDriverAttributes" :icon="RefreshLeft" @click="driverInfoReset">
{{ $t('common.reset') }}
</el-button>
<el-button :icon="Right" plain type="warning" @click="next">{{ $t('common.next') }}</el-button>
<el-button :icon="Right" plain type="primary" @click="next">{{ $t('common.next') }}</el-button>
</el-form-item>
</el-card>
@@ -297,8 +297,8 @@
</el-table>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" :loading="reactiveData.pointSaving" plain type="warning" @click="next">
<el-button :icon="Back" plain @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" :loading="reactiveData.pointSaving" plain type="primary" @click="next">
{{ $t('common.next') }}
</el-button>
</el-form-item>
@@ -436,8 +436,8 @@
</el-table-column>
</el-table>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" :loading="reactiveData.commandSaving" plain type="warning" @click="next">
<el-button :icon="Back" plain @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" :loading="reactiveData.commandSaving" plain type="primary" @click="next">
{{ $t('common.next') }}
</el-button>
</el-form-item>
@@ -575,8 +575,8 @@
</el-table-column>
</el-table>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" :loading="reactiveData.eventSaving" plain type="warning" @click="next">
<el-button :icon="Back" plain @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" :loading="reactiveData.eventSaving" plain type="primary" @click="next">
{{ $t('common.next') }}
</el-button>
</el-form-item>
+1 -5
View File
@@ -95,7 +95,7 @@
<div class="things-dialog-footer">
<slot name="footer">
<el-button @click="cancel">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain type="warning" @click="importTemplate">{{ $t('device.import.template') }}</el-button>
<el-button type="primary" @click="importThing">{{ $t('common.confirm') }}</el-button>
</slot>
@@ -293,7 +293,3 @@
importThing,
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
-2
View File
@@ -156,5 +156,3 @@
list();
</script>
<style lang="scss" scoped></style>
+3 -2
View File
@@ -19,7 +19,7 @@
<el-card shadow="hover">
<div class="things-card-content">
<things-card-header
:enabled="data.enableFlag === 'ENABLE'"
:enabled="enabled"
:icon="icon"
:name="data.driverName"
:status-title="$t('common.name')"
@@ -71,6 +71,7 @@
import router from '@/config/router';
import { copy } from '@/utils/commonUtil';
import { timestamp } from '@/utils/dateUtil';
import { isEnabledFlag } from '@/utils/thingModelFormatUtil';
import ThingsCardHeader from '@/components/card/header/ThingsCardHeader.vue';
const props = defineProps({
@@ -81,6 +82,7 @@
});
defineEmits(['select-change']);
const enabled = computed(() => isEnabledFlag(props.data.enableFlag));
const status = computed(() => {
const id = props.data.id;
@@ -112,7 +114,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
@use '@/views/driver/card/style.scss';
// DriverCard 的 footer 只有单个 detail 按钮,不使用 ThingsCardActions,在此补齐样式。
+1 -5
View File
@@ -98,7 +98,7 @@
<div class="things-dialog-footer">
<slot name="footer">
<el-button @click="cancel">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ $t('common.reset') }}</el-button>
<el-button type="primary" @click="addThing">{{ $t('common.confirm') }}</el-button>
</slot>
</div>
@@ -230,7 +230,3 @@
addThing,
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+5 -5
View File
@@ -19,7 +19,7 @@
<el-card shadow="hover">
<div class="things-card-content">
<things-card-header
:enabled="data.enableFlag === 'ENABLE'"
:enabled="enabled"
:icon="icon"
:name="data.pointName"
:status-title="$t('common.name')"
@@ -103,7 +103,7 @@
:delete-title="$t('point.card.confirmDelete')"
:disable-title="$t('point.card.confirmDisable')"
:enable-title="$t('point.card.confirmEnable')"
:enabled="data.enableFlag === 'ENABLE'"
:enabled="enabled"
detail-disabled
@delete="emitDelete"
@detail="detail"
@@ -117,13 +117,14 @@
</template>
<script lang="ts" setup>
import type { PropType } from 'vue';
import { computed, type PropType } from 'vue';
import { Edit, List, Location, Sunset } from '@element-plus/icons-vue';
import router from '@/config/router';
import { copy } from '@/utils/commonUtil';
import { timestamp } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import { pointTypeKey, rwFlagKey } from '@/utils/pointFormatUtil';
import { isEnabledFlag } from '@/utils/thingModelFormatUtil';
import ThingsCardHeader from '@/components/card/header/ThingsCardHeader.vue';
import ThingsCardActions from '@/components/card/actions/ThingsCardActions.vue';
@@ -135,6 +136,7 @@
});
const emit = defineEmits(['disable-thing', 'enable-thing', 'delete-thing']);
const enabled = computed(() => isEnabledFlag(props.data.enableFlag));
const emitToggle = (name: 'disable-thing' | 'enable-thing') => {
emit(name, props.data.id, props.data.profileId, () => successMessage());
@@ -163,8 +165,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
// PointCard 用双栏列表展示字段,200px 固定宽度是为了和卡片尺寸匹配。
.things-body-content-item-column-2 {
width: 200px;
-4
View File
@@ -63,7 +63,3 @@
</template>
<script lang="ts" src="./index.ts" />
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
+2 -2
View File
@@ -105,9 +105,9 @@
/>
</el-form-item>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="done">{{ $t('common.return') }}</el-button>
<el-button :icon="Back" plain @click="done">{{ $t('common.return') }}</el-button>
<el-button :icon="RefreshLeft" @click="pointReset">{{ $t('common.reset') }}</el-button>
<el-button :icon="Right" plain type="warning" @click="next">{{ $t('common.next') }}</el-button>
<el-button :icon="Right" plain type="primary" @click="next">{{ $t('common.next') }}</el-button>
</el-form-item>
</el-form>
</el-card>
+3 -5
View File
@@ -79,8 +79,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
// PointInfoCard 内联了一个简化的 header(无状态标签),不使用 ThingsCardHeader,在此补齐样式。
.cursor-pointer {
@@ -110,15 +108,15 @@
line-height: 48px;
font-size: 14px;
font-weight: bold;
color: rgba(0, 0, 0, 0.85);
color: var(--el-text-color-primary);
}
}
.header-enable {
border-bottom: 1px solid #c2e7b0;
border-bottom: 1px solid var(--el-color-success-light-5);
}
.header-disable {
border-bottom: 1px solid #fbc4c4;
border-bottom: 1px solid var(--el-color-danger-light-5);
}
</style>
+2 -2
View File
@@ -72,12 +72,12 @@
</el-form-item>
</template>
<template v-if="pre || next" #buttons="{ search, reset }">
<el-button v-if="pre" :icon="Back" plain type="success" @click="$emit('pre-handle')">
<el-button v-if="pre" :icon="Back" plain @click="$emit('pre-handle')">
{{ $t('common.previous') }}
</el-button>
<el-button :icon="Search" type="primary" @click="search">{{ $t('common.search') }}</el-button>
<el-button :icon="RefreshLeft" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button v-if="next" :icon="Check" plain type="warning" @click="$emit('next-handle')">
<el-button v-if="next" :icon="Check" plain type="primary" @click="$emit('next-handle')">
{{ $t('common.next') }}
</el-button>
</template>
+47 -4
View File
@@ -43,22 +43,29 @@
:embedded="embedded"
:point="reactiveData.pointTable[data.pointId]"
:unit="reactiveData.unitTable[data.pointId]"
@detail-thing="openDetail"
@write-thing="openWrite"
></point-value-card>
</el-col>
</el-row>
</blank-card>
<point-value-edit-form ref="editRef" @update-thing="writeValue" />
<point-value-detail ref="detailRef" :detail-data="reactiveData.detailData" />
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive } from 'vue';
import { getPointByIds, getPointUnit, getPointValueLatest, listPointValue } from '@/api/point';
import { computed, onMounted, reactive, ref } from 'vue';
import { getPointByIds, getPointUnit, getPointValueLatest, listPointValue, writePointValue } from '@/api/point';
import { listDeviceByIds } from '@/api/device';
import blankCard from '@/components/card/blank/BlankCard.vue';
import skeletonCard from '@/components/card/skeleton/SkeletonCard.vue';
import pointValueTool from './tool/PointValueTool.vue';
import pointValueCard from './card/PointValueCard.vue';
import pointValueEditForm from './edit/PointValueEditForm.vue';
import pointValueDetail from './detail/PointValueDetail.vue';
import { isNull } from '@/utils/validationUtil';
@@ -83,6 +90,7 @@
pointTable: {} as Record<string, any>,
unitTable: {} as Record<string, any>,
listData: [] as any[],
detailData: {} as Record<string, unknown>,
query: {},
page: {
total: 0,
@@ -91,6 +99,9 @@
},
});
const editRef = ref<InstanceType<typeof pointValueEditForm>>();
const detailRef = ref<InstanceType<typeof pointValueDetail>>();
const hasData = computed(() => {
return !reactiveData.loading && reactiveData.listData?.length < 1;
});
@@ -199,6 +210,40 @@
list();
};
const openWrite = (row: Record<string, unknown>) => {
editRef.value?.show({
...row,
value: String(row.calValue ?? ''),
});
};
const writeValue = (formData: Record<string, unknown>, done: () => void) => {
writePointValue({
deviceId: formData.deviceId,
pointId: formData.pointId,
value: String(formData.value ?? ''),
})
.then(() => {
refresh();
done();
})
.catch(() => {
// handled globally
});
};
const openDetail = (row: Record<string, unknown>) => {
const deviceId = String(row.deviceId || '');
const pointId = String(row.pointId || '');
reactiveData.detailData = {
...row,
device: reactiveData.deviceTable[deviceId],
point: reactiveData.pointTable[pointId],
unit: reactiveData.unitTable[pointId],
};
detailRef.value?.show();
};
const sizeChange = (size: number) => {
reactiveData.page.size = size;
list();
@@ -218,5 +263,3 @@
list,
});
</script>
<style lang="scss" scoped></style>
+22 -27
View File
@@ -95,25 +95,19 @@
:height="80"
:tooltip-unit="unit"
animate
color="#409eff"
color="var(--el-color-primary)"
/>
<div v-else class="point-value-empty-chart">{{ $t('pointValue.card.noHistory') }}</div>
</div>
</div>
<div v-if="embedded == ''" class="things-card__footer">
<div class="things-card-footer-operation">
<el-popconfirm
:icon="CircleClose"
:title="$t('pointValue.card.confirmDelete')"
icon-color="#f56c6c"
placement="top"
>
<template #reference>
<el-button link type="primary">{{ $t('common.delete') }}</el-button>
</template>
</el-popconfirm>
<el-button link type="primary">{{ $t('common.edit') }}</el-button>
<el-button link type="primary">{{ $t('common.detail') }}</el-button>
<el-button :disabled="writeDisabled" link type="primary" @click="$emit('write-thing', data)">
{{ $t('pointValue.card.write') }}
</el-button>
<el-button link type="primary" @click="$emit('detail-thing', data)">
{{ $t('common.detail') }}
</el-button>
</div>
</div>
</div>
@@ -123,7 +117,7 @@
<script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue';
import { CircleClose, Edit, Management, Sunrise, Sunset, Timer } from '@element-plus/icons-vue';
import { Edit, Management, Sunrise, Sunset, Timer } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import MiniAreaChart from '@/components/chart/MiniAreaChart.vue';
@@ -168,6 +162,8 @@
},
});
defineEmits(['write-thing', 'detail-thing']);
const copyValue = (data: any) => {
const content = {
deviceId: data.deviceId,
@@ -189,6 +185,7 @@
const displayDelay = computed(() => {
return typeof props.data?.interval === 'number' ? `${props.data.interval} ms` : '--';
});
const writeDisabled = computed(() => !['W', 'RW'].includes(String(props.data?.rwFlag || '').toUpperCase()));
const displayTime = (value: string | null | undefined) => {
if (!hasLatestValue.value || !value) {
@@ -241,8 +238,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
// PointValueCard 内联了 header / footer / 实时数值展示区,不使用 ThingsCardHeader / ThingsCardActions,
// 因此在此补齐对应样式。`header-enable` / `header-disable` 语义不同:基于 data.interval 表示延时是否正常。
@@ -269,11 +264,11 @@
line-height: 48px;
font-size: 14px;
font-weight: bold;
color: rgba(0, 0, 0, 0.85);
color: var(--el-text-color-primary);
cursor: pointer;
&:hover {
color: #1890ff;
color: var(--el-color-primary);
}
}
@@ -294,15 +289,15 @@
}
.header-enable {
border-bottom: 1px solid #c2e7b0;
border-bottom: 1px solid var(--el-color-success-light-5);
}
.header-disable {
border-bottom: 1px solid #fbc4c4;
border-bottom: 1px solid var(--el-color-danger-light-5);
}
.header-missing {
border-bottom: 1px solid #dcdfe6;
border-bottom: 1px solid var(--el-border-color);
}
.things-card__body {
@@ -326,7 +321,7 @@
}
.value-missing {
color: #909399;
color: var(--el-text-color-secondary);
animation: none;
}
@@ -342,7 +337,7 @@
.point-value-empty-chart {
height: 80px;
line-height: 80px;
color: #909399;
color: var(--el-text-color-secondary);
font-size: 12px;
}
}
@@ -353,7 +348,7 @@
margin-top: 2px;
display: flex;
justify-content: flex-end;
border-top: 1px solid #dcdfe6;
border-top: 1px solid var(--el-border-color);
.things-card-footer-operation {
height: 35px;
@@ -363,13 +358,13 @@
@keyframes hue {
0% {
color: #409eff;
color: var(--el-color-primary);
}
50% {
color: #f3f4fe;
color: var(--el-color-primary-light-9);
}
100% {
color: #409eff;
color: var(--el-color-primary);
}
}
</style>
@@ -23,10 +23,27 @@
direction="rtl"
size="40%"
>
<pre v-highlightjs>
<code class="json">{{ detailData }}</code>
</pre>
<pre class="json-preview">{{ formatJson(detailData) }}</pre>
</el-drawer>
</template>
<script lang="ts" src="./index.ts" />
<script lang="ts" setup>
import type { PropType } from 'vue';
import { ref } from 'vue';
import { prettyJson } from '@/utils/jsonUtil';
defineProps({
detailData: {
type: Object as PropType<Record<string, unknown>>,
default: () => ({}),
},
});
const detailVisible = ref(false);
const formatJson = (value: unknown) => prettyJson(value);
const show = () => {
detailVisible.value = true;
};
defineExpose({ show });
</script>
@@ -47,7 +47,7 @@
<div class="things-dialog-footer">
<slot name="footer">
<el-button @click="cancel">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ $t('common.reset') }}</el-button>
<el-button type="primary" @click="updateThing">{{ $t('common.confirm') }}</el-button>
</slot>
</div>
@@ -100,12 +100,12 @@
],
});
const syncFormData = () => {
reactiveData.formData = { ...props.formData };
const syncFormData = (value = props.formData) => {
reactiveData.formData = { ...value };
};
const show = () => {
syncFormData();
const show = (value?: PointValueFormData) => {
syncFormData(value);
reactiveData.formVisible = true;
};
@@ -143,7 +143,3 @@
updateThing,
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
-2
View File
@@ -224,5 +224,3 @@
list();
</script>
<style lang="scss" scoped></style>
+1 -5
View File
@@ -47,7 +47,7 @@
<div class="things-dialog-footer">
<slot name="footer">
<el-button @click="cancel">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ $t('common.reset') }}</el-button>
<el-button type="primary" @click="addThing">{{ $t('common.confirm') }}</el-button>
</slot>
</div>
@@ -122,7 +122,3 @@
addThing,
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+5 -7
View File
@@ -19,7 +19,7 @@
<el-card shadow="hover">
<div class="things-card-content">
<things-card-header
:enabled="data.enableFlag === 'ENABLE'"
:enabled="enabled"
:icon="icon"
:name="data.profileName"
:status-title="$t('common.name')"
@@ -49,7 +49,7 @@
:delete-title="$t('profile.card.confirmDelete')"
:disable-title="$t('profile.card.confirmDisable')"
:enable-title="$t('profile.card.confirmEnable')"
:enabled="data.enableFlag === 'ENABLE'"
:enabled="enabled"
@delete="emitAction('delete-thing')"
@detail="detail"
@disable="emitAction('disable-thing')"
@@ -62,12 +62,13 @@
</template>
<script lang="ts" setup>
import type { PropType } from 'vue';
import { computed, type PropType } from 'vue';
import { Edit, Sunset } from '@element-plus/icons-vue';
import router from '@/config/router';
import { copy } from '@/utils/commonUtil';
import { timestamp } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import { isEnabledFlag } from '@/utils/thingModelFormatUtil';
import ThingsCardHeader from '@/components/card/header/ThingsCardHeader.vue';
import ThingsCardActions from '@/components/card/actions/ThingsCardActions.vue';
@@ -78,6 +79,7 @@
});
const emit = defineEmits(['disable-thing', 'enable-thing', 'delete-thing']);
const enabled = computed(() => isEnabledFlag(props.data.enableFlag));
const emitAction = (name: 'disable-thing' | 'enable-thing' | 'delete-thing') => {
emit(name, props.data.id, () => successMessage());
@@ -95,7 +97,3 @@
});
};
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
@@ -63,7 +63,3 @@
</template>
<script lang="ts" src="./index.ts" />
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
+8 -8
View File
@@ -53,9 +53,9 @@
/>
</el-form-item>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="done">{{ $t('common.return') }}</el-button>
<el-button :icon="Back" plain @click="done">{{ $t('common.return') }}</el-button>
<el-button :icon="RefreshLeft" @click="profileReset">{{ $t('common.reset') }}</el-button>
<el-button :icon="Right" plain type="warning" @click="next">{{ $t('common.next') }}</el-button>
<el-button :icon="Right" plain type="primary" @click="next">{{ $t('common.next') }}</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -63,24 +63,24 @@
<el-divider content-position="left">{{ $t('profile.edit.pointConfig') }}</el-divider>
<point :embedded="'edit'" :profile-id="reactiveData.id"></point>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" plain type="warning" @click="next">{{ $t('common.next') }}</el-button>
<el-button :icon="Back" plain @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" plain type="primary" @click="next">{{ $t('common.next') }}</el-button>
</el-form-item>
</el-card>
<el-card v-if="reactiveData.active === 2" shadow="hover">
<el-divider content-position="left">{{ $t('profile.edit.commandConfig') }}</el-divider>
<command-list :embedded="'edit'" :profile-id="reactiveData.id"></command-list>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" plain type="warning" @click="next">{{ $t('common.next') }}</el-button>
<el-button :icon="Back" plain @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" plain type="primary" @click="next">{{ $t('common.next') }}</el-button>
</el-form-item>
</el-card>
<el-card v-if="reactiveData.active === 3" shadow="hover">
<el-divider content-position="left">{{ $t('profile.edit.eventConfig') }}</el-divider>
<event-list :embedded="'edit'" :profile-id="reactiveData.id"></event-list>
<el-form-item class="edit-form-button">
<el-button :icon="Back" plain type="success" @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" plain type="warning" @click="next">{{ $t('common.next') }}</el-button>
<el-button :icon="Back" plain @click="pre">{{ $t('common.previous') }}</el-button>
<el-button :icon="Right" plain type="primary" @click="next">{{ $t('common.next') }}</el-button>
</el-form-item>
</el-card>
<el-card v-if="reactiveData.active === 4" shadow="hover">
@@ -189,11 +189,6 @@
</script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
.agentic-tags {
display: flex;
flex-wrap: wrap;
@@ -174,10 +174,3 @@
load();
</script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
</style>
@@ -65,10 +65,10 @@
{{ reactiveData.data.remark || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -88,7 +88,7 @@
import DefaultTag from '@/components/tag/DefaultTag.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import type { AgenticModelConfig } from '@/config/types';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
const route = useRoute();
@@ -116,8 +116,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
.agentic-tags {
display: flex;
flex-wrap: wrap;
@@ -82,7 +82,3 @@
load();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
@@ -74,7 +74,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="onReset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="onReset">{{ $t('common.reset') }}</el-button>
<el-button :loading="submitting" type="primary" @click="onSubmit">
{{ $t('common.confirm') }}
</el-button>
@@ -165,8 +165,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
.agentic-form-flags {
display: flex;
flex-wrap: wrap;
@@ -69,7 +69,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="visible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="onReset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="onReset">{{ $t('common.reset') }}</el-button>
<el-button :loading="submitting" type="primary" @click="onSubmit">
{{ $t('common.confirm') }}
</el-button>
@@ -152,7 +152,3 @@
defineExpose({ show, showEdit });
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+1 -3
View File
@@ -146,7 +146,7 @@
<template #footer>
<div class="things-dialog-footer">
<el-button @click="formVisible = false">{{ t('common.cancel') }}</el-button>
<el-button plain type="success" @click="resetForm">{{ t('common.reset') }}</el-button>
<el-button plain @click="resetForm">{{ t('common.reset') }}</el-button>
<el-button :loading="state.saving" type="primary" @click="submit">{{ t('common.confirm') }}</el-button>
</div>
</template>
@@ -194,8 +194,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
.alarm-notify {
min-width: 0;
@@ -36,7 +36,7 @@
<el-tab-pane v-for="prop in activeConfig.extProps" :key="prop" :label="extLabel(prop)" :name="prop">
<detail-card>
<pre class="alarm-detail__json">{{ prettyJson(reactiveData.data[prop]) }}</pre>
<pre class="alarm-detail__json">{{ prettyJson(reactiveData.data[prop], '{}') }}</pre>
</detail-card>
</el-tab-pane>
</el-tabs>
@@ -61,7 +61,8 @@
import BlankCard from '@/components/card/blank/BlankCard.vue';
import DetailCard from '@/components/card/detail/DetailCard.vue';
import type { AlarmEntityRecord } from '@/config/types';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
import { prettyJson } from '@/utils/jsonUtil';
type AlarmEntity = 'rule' | 'notify' | 'message' | 'channel' | 'bind' | 'state' | 'history';
type FieldKind = 'text' | 'tag' | 'time' | 'code';
@@ -259,7 +260,7 @@
const formatDetail = (field: DetailField) => {
const value = reactiveData.data[field.prop];
if (field.kind === 'time' || field.prop.endsWith('Time')) return value ? timestamp(String(value)) : '-';
if (field.kind === 'time' || field.prop.endsWith('Time')) return timestampLabel(value);
if (field.kind === 'tag') return enumLabel(value);
if (value == null || value === '') return '-';
return String(value);
@@ -279,18 +280,6 @@
return map[prop] || prop;
};
const prettyJson = (value: unknown) => {
if (value == null || value === '') return '{}';
if (typeof value === 'string') {
try {
return JSON.stringify(JSON.parse(value), null, 2);
} catch {
return value;
}
}
return JSON.stringify(value, null, 2);
};
const load = () => {
if (!reactiveData.id) return;
activeConfig.value
@@ -309,8 +298,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
.alarm-detail__inline-code {
padding: 2px 5px;
border-radius: 4px;
+4 -20
View File
@@ -20,7 +20,8 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import type { AlarmEntityRecord, Order, PageQuery } from '@/config/types';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
import { prettyJson } from '@/utils/jsonUtil';
import { failMessage, successMessage } from '@/utils/notificationUtil';
import { cleanSearchParams, resetSearchForm } from '@/utils/searchParamUtil';
@@ -148,25 +149,13 @@ export const useAlarmEntityPage = (props: AlarmEntityPageProps) => {
load();
};
const prettyJson = (value: unknown) => {
if (value == null || value === '') return '{}';
if (typeof value === 'string') {
try {
return JSON.stringify(JSON.parse(value), null, 2);
} catch {
return value;
}
}
return JSON.stringify(value, null, 2);
};
const assignForm = (value: Record<string, unknown>) => {
Object.keys(formModel).forEach((key) => delete formModel[key]);
Object.assign(formModel, value);
activeConfig.value.fields
.filter((field) => field.kind === 'json')
.forEach((field) => {
formModel[field.prop] = prettyJson(formModel[field.prop]);
formModel[field.prop] = prettyJson(formModel[field.prop], '{}');
});
};
@@ -261,11 +250,6 @@ export const useAlarmEntityPage = (props: AlarmEntityPageProps) => {
});
};
const formatTime = (value: unknown) => {
if (!value) return '-';
return timestamp(String(value)) || '-';
};
const enumLabel = (value: unknown) => {
const text = String(value || '');
const map: Record<string, string> = {
@@ -306,7 +290,7 @@ export const useAlarmEntityPage = (props: AlarmEntityPageProps) => {
const formatCell = (row: AlarmEntityRecord, column: AlarmColumnConfig) => {
const value = row[column.prop];
if (column.kind === 'time') return formatTime(value);
if (column.kind === 'time') return timestampLabel(value);
if (column.kind === 'tag') return enumLabel(value);
if (value == null || value === '') return '-';
return String(value);
+1 -14
View File
@@ -35,13 +35,7 @@
<el-table-column :label="t('settings.api.apiType')" min-width="100" prop="apiTypeFlag" />
<el-table-column :label="t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{
String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0
? t('common.enable')
: t('common.disable')
}}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :label="t('common.remark')" min-width="140" prop="remark" show-overflow-tooltip />
@@ -66,10 +60,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
</style>
+5 -20
View File
@@ -37,28 +37,16 @@
{{ reactiveData.data.apiTypeFlag }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.enable')">
<el-tag
:type="
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? 'success'
: 'info'
"
>
{{
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? $t('common.enable')
: $t('common.disable')
}}
</el-tag>
<enable-tag :value="reactiveData.data.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')" :span="2">
{{ reactiveData.data.remark || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -73,10 +61,11 @@
import { useRoute } from 'vue-router';
import { getApiById } from '@/api/api';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
import blankCard from '@/components/card/blank/BlankCard.vue';
import detailCard from '@/components/card/detail/DetailCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
const route = useRoute();
@@ -101,7 +90,3 @@
load();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
+8 -6
View File
@@ -21,9 +21,10 @@ import { useRouter } from 'vue-router';
import { listApi } from '@/api/api';
import { timestampColumn } from '@/utils/dateUtil';
import type { Order } from '@/config/types';
import type { ApiRecord, Order } from '@/config/types';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import apiTool from './tool/ApiTool.vue';
// APIs are auto-registered by each service on startup — editing them from
@@ -33,6 +34,7 @@ export default defineComponent({
name: 'SettingsApi',
components: {
BlankCard,
EnableTag,
apiTool,
},
setup() {
@@ -41,8 +43,8 @@ export default defineComponent({
const reactiveData = reactive({
loading: false,
listData: [] as any[],
query: {} as Record<string, any>,
listData: [] as ApiRecord[],
query: {} as Record<string, unknown>,
order: false,
page: {
total: 0,
@@ -55,7 +57,7 @@ export default defineComponent({
const load = () => {
reactiveData.loading = true;
listApi({ page: reactiveData.page, ...reactiveData.query })
.then((res: any) => {
.then((res) => {
const data = res.data || {};
reactiveData.listData = data.records || [];
reactiveData.page.total = data.total || 0;
@@ -68,7 +70,7 @@ export default defineComponent({
});
};
const search = (params: any) => {
const search = (params: Record<string, unknown>) => {
reactiveData.query = params || {};
reactiveData.page.current = 1;
load();
@@ -88,7 +90,7 @@ export default defineComponent({
load();
};
const openDetail = (row: any) => {
const openDetail = (row: ApiRecord) => {
router.push({ name: 'settingsApiDetail', query: { id: String(row.id) } }).catch(() => {
// handled globally
});
+99 -80
View File
@@ -26,26 +26,61 @@
@current-change="currentChange"
>
<template #filters>
<el-form-item label="Device ID" prop="deviceId">
<el-input v-model="formData.deviceId" class="edit-form-default" clearable placeholder="Device ID" />
<el-form-item :label="$t('command.history.deviceId')" prop="deviceId">
<el-input
v-model="formData.deviceId"
:placeholder="$t('command.history.deviceId')"
class="edit-form-default"
clearable
/>
</el-form-item>
<el-form-item label="Command Code" prop="commandCode">
<el-input v-model="formData.commandCode" class="edit-form-default" clearable placeholder="Command Code" />
<el-form-item :label="$t('command.history.commandCode')" prop="commandCode">
<el-input
v-model="formData.commandCode"
:placeholder="$t('command.history.commandCode')"
class="edit-form-default"
clearable
/>
</el-form-item>
<el-form-item label="Status" prop="status">
<el-input v-model="formData.status" class="edit-form-default" clearable placeholder="Status" />
<el-form-item :label="$t('command.history.status')" prop="status">
<el-input
v-model="formData.status"
:placeholder="$t('command.history.status')"
class="edit-form-default"
clearable
/>
</el-form-item>
</template>
</tool-card>
<blank-card>
<el-table v-loading="reactiveData.loading" :data="reactiveData.listData" class="settings-table" stripe>
<el-table-column label="Record ID" min-width="180" prop="recordId" show-overflow-tooltip />
<el-table-column label="Device ID" min-width="160" prop="deviceId" show-overflow-tooltip />
<el-table-column label="Command Code" min-width="140" prop="commandCode" />
<el-table-column label="Status" prop="status" width="100" />
<el-table-column label="Error" min-width="180" prop="errorMessage" show-overflow-tooltip />
<el-table-column :formatter="timestampColumn" label="Occur Time" prop="occurTime" width="165" />
<el-table-column
:label="$t('command.history.recordId')"
min-width="180"
prop="recordId"
show-overflow-tooltip
/>
<el-table-column
:label="$t('command.history.deviceId')"
min-width="160"
prop="deviceId"
show-overflow-tooltip
/>
<el-table-column :label="$t('command.history.commandCode')" min-width="140" prop="commandCode" />
<el-table-column :label="$t('command.history.status')" prop="status" width="100" />
<el-table-column
:label="$t('command.history.error')"
min-width="180"
prop="errorMessage"
show-overflow-tooltip
/>
<el-table-column
:formatter="timestampColumn"
:label="$t('command.history.occurTime')"
prop="occurTime"
width="165"
/>
<el-table-column :formatter="timestampColumn" :label="$t('common.createTime')" prop="createTime" width="165" />
<el-table-column :label="$t('common.operation')" fixed="right" width="100">
<template #default="{ row }">
@@ -58,44 +93,60 @@
</el-table>
</blank-card>
<el-dialog v-model="detailVisible" :append-to-body="true" draggable title="Record Detail" width="700px">
<el-dialog
v-model="detailVisible"
:append-to-body="true"
:title="$t('command.history.detailTitle')"
draggable
width="700px"
>
<el-descriptions v-if="detailRow" :column="2" border>
<el-descriptions-item :span="2" label="Record ID">{{ detailRow.recordId }}</el-descriptions-item>
<el-descriptions-item label="Device ID">{{ detailRow.deviceId }}</el-descriptions-item>
<el-descriptions-item label="Command ID">{{ detailRow.commandId }}</el-descriptions-item>
<el-descriptions-item label="Command Code">{{ detailRow.commandCode }}</el-descriptions-item>
<el-descriptions-item label="Status">{{ detailRow.status }}</el-descriptions-item>
<el-descriptions-item label="Error Code">{{ detailRow.errorCode || '-' }}</el-descriptions-item>
<el-descriptions-item label="Error Message">{{ detailRow.errorMessage || '-' }}</el-descriptions-item>
<el-descriptions-item label="Source">{{ detailRow.source || '-' }}</el-descriptions-item>
<el-descriptions-item label="Source User ID">{{ detailRow.sourceUserId || '-' }}</el-descriptions-item>
<el-descriptions-item :span="2" label="Param Values">
<el-descriptions-item :label="$t('command.history.recordId')" :span="2">
{{ detailRow.recordId }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.deviceId')">{{ detailRow.deviceId }}</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.commandId')">{{ detailRow.commandId }}</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.commandCode')">
{{ detailRow.commandCode }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.status')">{{ detailRow.status }}</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.errorCode')">
{{ detailRow.errorCode || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.errorMessage')">
{{ detailRow.errorMessage || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.source')">{{ detailRow.source || '-' }}</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.sourceUserId')">
{{ detailRow.sourceUserId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.paramValues')" :span="2">
<pre class="json-preview">{{ formatJson(detailRow.paramValues) }}</pre>
</el-descriptions-item>
<el-descriptions-item :span="2" label="Result Values">
<el-descriptions-item :label="$t('command.history.resultValues')" :span="2">
<pre class="json-preview">{{ formatJson(detailRow.resultValues) }}</pre>
</el-descriptions-item>
<el-descriptions-item :span="2" label="Config Snapshot">
<el-descriptions-item :label="$t('command.history.configSnapshot')" :span="2">
<pre class="json-preview">{{ formatJson(detailRow.configSnapshot) }}</pre>
</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Occur Time">{{
detailRow.occurTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Send Time">{{
detailRow.sendTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Finish Time">{{
detailRow.finishTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Expire Time">{{
detailRow.expireTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Create Time">{{
detailRow.createTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Operate Time">{{
detailRow.operateTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.occurTime')">
{{ timestampLabel(detailRow.occurTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.sendTime')">
{{ timestampLabel(detailRow.sendTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.finishTime')">
{{ timestampLabel(detailRow.finishTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.history.expireTime')">
{{ timestampLabel(detailRow.expireTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ timestampLabel(detailRow.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ timestampLabel(detailRow.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
@@ -104,7 +155,8 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { getCommandHistoryById, listCommandHistory } from '@/api/command';
import { timestampColumn } from '@/utils/dateUtil';
import { timestampColumn, timestampLabel } from '@/utils/dateUtil';
import { prettyJson } from '@/utils/jsonUtil';
import type { CommandHistory, Order } from '@/config/types';
import ToolCard from '@/components/card/tool/ToolCard.vue';
import BlankCard from '@/components/card/blank/BlankCard.vue';
@@ -117,23 +169,11 @@
page: { total: 0, size: 12, current: 1, orders: [] as Order[] },
});
const formData = reactive<Record<string, any>>({});
const formData = reactive<Record<string, string>>({});
const detailVisible = ref(false);
const detailRow = ref<CommandHistory | null>(null);
const formatJson = (value: unknown) => {
if (!value) {
return '-';
}
if (typeof value === 'string') {
try {
return JSON.stringify(JSON.parse(value), null, 2);
} catch {
return value;
}
}
return JSON.stringify(value, null, 2);
};
const formatJson = (value: unknown) => prettyJson(value);
const load = () => {
reactiveData.loading = true;
@@ -148,7 +188,7 @@
});
};
const onSearch = (data: Record<string, any>) => {
const onSearch = (data: Record<string, string>) => {
reactiveData.query = cleanSearchParams(data);
reactiveData.page.current = 1;
load();
@@ -186,24 +226,3 @@
load();
</script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
.json-preview {
max-height: 220px;
margin: 0;
padding: 8px;
overflow: auto;
border-radius: 4px;
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
</style>
+96 -43
View File
@@ -57,46 +57,40 @@
<command-edit-form ref="editRef" @add-thing="onAdd" @update-thing="onUpdate" />
<el-drawer v-model="reactiveData.detailVisible" title="Command Detail" size="520px">
<el-drawer v-model="reactiveData.detailVisible" :title="$t('command.detail.title')" size="520px">
<el-descriptions v-if="reactiveData.detailRecord" :column="1" border>
<el-descriptions-item :label="$t('common.name')">{{
reactiveData.detailRecord.commandName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="Code">{{ reactiveData.detailRecord.commandCode || '-' }}</el-descriptions-item>
<el-descriptions-item label="Command Type">{{
<el-descriptions-item :label="$t('command.detail.code')">
{{ reactiveData.detailRecord.commandCode || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.detail.commandType')">{{
reactiveData.detailRecord.commandTypeFlag || '-'
}}</el-descriptions-item>
<el-descriptions-item label="Call Type">{{
<el-descriptions-item :label="$t('command.detail.callType')">{{
reactiveData.detailRecord.callTypeFlag || '-'
}}</el-descriptions-item>
<el-descriptions-item label="Timeout (ms)">{{ reactiveData.detailRecord.timeout ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="$t('command.detail.timeout')">{{
commandTimeoutLabel(reactiveData.detailRecord.timeout)
}}</el-descriptions-item>
<el-descriptions-item :label="$t('common.enableFlag')">
<el-tag
:type="
String(reactiveData.detailRecord.enableFlag) === 'ENABLE' ||
Number(reactiveData.detailRecord.enableFlag) === 0
? 'success'
: 'info'
"
>
{{
String(reactiveData.detailRecord.enableFlag) === 'ENABLE' ||
Number(reactiveData.detailRecord.enableFlag) === 0
? $t('common.enable')
: $t('common.disable')
}}
</el-tag>
<enable-tag :value="reactiveData.detailRecord.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')">{{
reactiveData.detailRecord.remark || '-'
}}</el-descriptions-item>
<el-descriptions-item label="Profile ID">{{ reactiveData.detailRecord.profileId || '-' }}</el-descriptions-item>
<el-descriptions-item label="Tenant ID">{{ reactiveData.detailRecord.tenantId || '-' }}</el-descriptions-item>
<el-descriptions-item label="Create Time">{{
formatTime(reactiveData.detailRecord.createTime)
<el-descriptions-item :label="$t('command.detail.profileId')">
{{ reactiveData.detailRecord.profileId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('command.detail.tenantId')">
{{ reactiveData.detailRecord.tenantId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">{{
timestampLabel(reactiveData.detailRecord.createTime)
}}</el-descriptions-item>
<el-descriptions-item label="Operate Time">{{
formatTime(reactiveData.detailRecord.operateTime)
<el-descriptions-item :label="$t('common.operationTime')">{{
timestampLabel(reactiveData.detailRecord.operateTime)
}}</el-descriptions-item>
</el-descriptions>
<el-empty v-else :description="$t('common.description')" />
@@ -106,13 +100,24 @@
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue';
import { addCommand, deleteCommand, listCommand, updateCommand } from '@/api/command';
import { timestamp } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import { useI18n } from 'vue-i18n';
import {
addCommand,
addCommandParam,
deleteCommand,
deleteCommandParam,
listCommand,
updateCommand,
updateCommandParam,
} from '@/api/command';
import { timestampLabel } from '@/utils/dateUtil';
import { failMessage, successMessage } from '@/utils/notificationUtil';
import { commandTimeoutLabel } from '@/utils/thingModelFormatUtil';
import { isNull } from '@/utils/validationUtil';
import type { CommandForm, CommandRecord, Order } from '@/config/types';
import type { CommandForm, CommandParamRecord, CommandRecord, Order } from '@/config/types';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import SkeletonCard from '@/components/card/skeleton/SkeletonCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import CommandCard from './card/CommandCard.vue';
import CommandTool from './tool/CommandTool.vue';
import CommandEditForm from './edit/CommandEditForm.vue';
@@ -133,6 +138,7 @@
}>();
const editRef = ref<InstanceType<typeof CommandEditForm>>();
const { t } = useI18n();
const canManage = computed(() => props.embedded === '' || props.embedded === 'edit');
const hasData = computed(() => !reactiveData.loading && reactiveData.listData.length < 1);
@@ -162,8 +168,6 @@
return isNull(profileId) ? { ...form } : { ...form, profileId };
};
const formatTime = (value: unknown) => (isNull(value) ? '-' : timestamp(String(value)));
const load = () => {
reactiveData.loading = true;
const query = withFixedQuery(reactiveData.query);
@@ -205,20 +209,69 @@
};
const openEdit = (row: CommandRecord) => editRef.value?.showEdit(row);
const onAdd = (form: CommandForm, done: () => void) => {
addCommand(withFixedProfile(form)).then(() => {
successMessage();
load();
done();
type DoneCallback = (close?: boolean) => void;
const isValidCreatedId = (id: string) => /^\d+$/.test(id);
const syncCommandParams = (
commandId: string,
params: CommandParamRecord[],
originalParams: CommandParamRecord[] = []
) => {
const currentIds = new Set(params.map((item) => String(item.id || '')).filter(Boolean));
const deleteTasks = originalParams
.filter((item) => item.id && !currentIds.has(String(item.id)))
.map((item) => deleteCommandParam(String(item.id)));
const saveTasks = params.map((item) => {
const payload = { ...item, commandId };
return item.id ? updateCommandParam(payload) : addCommandParam({ ...payload, id: undefined });
});
return Promise.all(deleteTasks).then(() => Promise.all(saveTasks));
};
const onUpdate = (form: CommandForm, done: () => void) => {
updateCommand(withFixedProfile(form)).then(() => {
successMessage();
load();
done();
});
const onAdd = (form: CommandForm, params: CommandParamRecord[], done: DoneCallback) => {
addCommand(withFixedProfile(form))
.then((res) => {
const commandId = String(res.data || '');
if (!isValidCreatedId(commandId)) {
failMessage(t('command.errors.idNotReturned'));
return Promise.reject(new Error(t('command.errors.idNotReturned')));
}
return syncCommandParams(commandId, params).then(() => {
successMessage();
load();
done();
});
})
.catch(() => {
done(false);
});
};
const onUpdate = (
form: CommandForm,
params: CommandParamRecord[],
originalParams: CommandParamRecord[],
done: DoneCallback
) => {
updateCommand(withFixedProfile(form))
.then(() => {
const commandId = String(form.id || '');
if (!isValidCreatedId(commandId)) {
failMessage(t('command.errors.idMissing'));
return Promise.reject(new Error(t('command.errors.idMissing')));
}
return syncCommandParams(commandId, params, originalParams).then(() => {
successMessage();
load();
done();
});
})
.catch(() => {
done(false);
});
};
const disableThing = (id: string, profileId: string, done: () => void) => {
@@ -56,7 +56,7 @@
<span>
<el-icon><Location /></el-icon> {{ $t('command.card.timeout') }}:
</span>
{{ data.timeout ?? '-' }}
{{ commandTimeoutLabel(data.timeout) }}
</li>
<li class="nowrap-item">
<span>
@@ -102,7 +102,7 @@
import { copy } from '@/utils/commonUtil';
import { timestamp } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import { callTypeLabel, commandTypeLabel, isEnabledFlag } from '@/utils/thingModelFormatUtil';
import { callTypeLabel, commandTimeoutLabel, commandTypeLabel, isEnabledFlag } from '@/utils/thingModelFormatUtil';
import ThingsCardHeader from '@/components/card/header/ThingsCardHeader.vue';
import ThingsCardActions from '@/components/card/actions/ThingsCardActions.vue';
import type { CommandRecord } from '@/config/types';
@@ -127,8 +127,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
.things-body-content-item-column-2 {
max-width: 200px;
}
@@ -21,7 +21,7 @@
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
:title="reactiveData.mode === 'add' ? $t('common.add') + ' Command' : $t('common.edit') + ' Command'"
:title="reactiveData.mode === 'add' ? $t('command.form.addTitle') : $t('command.form.editTitle')"
class="things-dialog"
draggable
@closed="reset"
@@ -30,21 +30,21 @@
<el-form-item :label="$t('common.name')" prop="commandName">
<el-input v-model="reactiveData.form.commandName" :placeholder="$t('common.name')" clearable />
</el-form-item>
<el-form-item label="Code" prop="commandCode">
<el-input v-model="reactiveData.form.commandCode" clearable placeholder="Code" />
<el-form-item :label="$t('command.form.code')" prop="commandCode">
<el-input v-model="reactiveData.form.commandCode" :placeholder="$t('command.form.code')" clearable />
</el-form-item>
<el-form-item label="Command Type" prop="commandTypeFlag">
<el-form-item :label="$t('command.form.commandType')" prop="commandTypeFlag">
<el-select v-model="reactiveData.form.commandTypeFlag" clearable>
<el-option v-for="opt in COMMAND_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="Call Type" prop="callTypeFlag">
<el-form-item :label="$t('command.form.callType')" prop="callTypeFlag">
<el-select v-model="reactiveData.form.callTypeFlag" clearable>
<el-option v-for="opt in CALL_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="Timeout (ms)" prop="timeout">
<el-input-number v-model="reactiveData.form.timeout" :min="0" :step="1000" />
<el-form-item :label="$t('command.form.timeout')" prop="timeout">
<el-input-number v-model="reactiveData.form.timeout" :min="1" :precision="0" :step="1" />
</el-form-item>
<el-form-item :label="$t('common.enableFlag')" prop="enableFlag">
<enable-flag-segmented v-model="reactiveData.form.enableFlag" />
@@ -53,9 +53,70 @@
<el-input v-model="reactiveData.form.remark" clearable maxlength="300" show-word-limit type="textarea" />
</el-form-item>
</el-form>
<div v-loading="reactiveData.paramLoading" class="param-editor">
<div class="param-editor__toolbar">
<span>{{ $t('command.form.params') }}</span>
<el-button :icon="Plus" size="small" type="success" @click="addParamRow">
{{ $t('common.add') }}
</el-button>
</div>
<el-table :data="reactiveData.params" border max-height="260" size="small">
<el-table-column :label="$t('common.name')" min-width="150">
<template #default="{ row }">
<el-input v-model="row.paramName" clearable />
</template>
</el-table-column>
<el-table-column :label="$t('command.form.code')" min-width="150">
<template #default="{ row }">
<el-input v-model="row.paramCode" clearable />
</template>
</el-table-column>
<el-table-column :label="$t('command.form.direction')" min-width="130">
<template #default="{ row }">
<el-select v-model="row.paramDirectionFlag">
<el-option
v-for="opt in PARAM_DIRECTION_OPTIONS"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('command.form.type')" min-width="130">
<template #default="{ row }">
<el-select v-model="row.paramTypeFlag">
<el-option v-for="opt in POINT_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('command.form.required')" width="96">
<template #default="{ row }">
<el-checkbox v-model="row.requiredFlag" />
</template>
</el-table-column>
<el-table-column :label="$t('command.form.defaultValue')" min-width="150">
<template #default="{ row }">
<el-input v-model="row.defaultValue" clearable />
</template>
</el-table-column>
<el-table-column :label="$t('command.form.enabled')" width="104">
<template #default="{ row }">
<el-switch v-model="row.enableFlag" active-value="ENABLE" inactive-value="DISABLE" />
</template>
</el-table-column>
<el-table-column align="center" width="64">
<template #default="{ $index }">
<el-tooltip :content="$t('common.delete')" placement="top">
<el-button :icon="Delete" link type="danger" @click="removeParamRow($index)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ $t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ $t('common.confirm') }}
</el-button>
@@ -66,16 +127,44 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
import { Delete, Plus } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { listCommandParamByCommandId } from '@/api/command';
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
import { CALL_TYPE_OPTIONS, COMMAND_TYPE_OPTIONS } from '@/config/constant/enums';
import type { CommandRecord } from '@/config/types';
import {
CALL_TYPE_OPTIONS,
COMMAND_TYPE_OPTIONS,
PARAM_DIRECTION_OPTIONS,
POINT_TYPE_OPTIONS,
} from '@/config/constant/enums';
import type { CommandForm, CommandParamRecord, CommandRecord } from '@/config/types';
import { failMessage } from '@/utils/notificationUtil';
import {
callTypeValue,
commandTypeValue,
enableFlagValue,
normalizeCommandTimeoutSeconds,
paramDirectionValue,
pointTypeValue,
} from '@/utils/thingModelFormatUtil';
type FormMode = 'add' | 'edit';
type DoneCallback = (close?: boolean) => void;
const { t } = useI18n();
const emit = defineEmits(['add-thing', 'update-thing']);
type CommandParamDraft = CommandParamRecord & { _key: string };
const emit = defineEmits<{
(e: 'add-thing', form: CommandForm, params: CommandParamRecord[], done: DoneCallback): void;
(
e: 'update-thing',
form: CommandForm,
params: CommandParamRecord[],
originalParams: CommandParamRecord[],
done: DoneCallback
): void;
}>();
const formRef = ref<FormInstance>();
@@ -86,7 +175,7 @@
commandCode: '',
commandTypeFlag: 'CUSTOM' as string,
callTypeFlag: 'SYNC' as string,
timeout: 30000,
timeout: 30,
enableFlag: 'ENABLE' as string,
remark: '',
});
@@ -97,24 +186,95 @@
submitting: false,
form: createEmptyForm(),
originalForm: createEmptyForm(),
params: [] as CommandParamDraft[],
originalParams: [] as CommandParamRecord[],
paramLoading: false,
});
const rules: FormRules = {
commandName: [{ required: true, message: t('common.name'), trigger: 'blur' }],
commandCode: [{ required: true, message: 'Code is required', trigger: 'blur' }],
commandName: [{ required: true, message: t('command.form.nameRequired'), trigger: 'blur' }],
commandCode: [{ required: true, message: t('command.form.codeRequired'), trigger: 'blur' }],
};
const reset = () => {
reactiveData.form = { ...reactiveData.originalForm };
reactiveData.params = cloneParams(reactiveData.originalParams);
reactiveData.submitting = false;
formRef.value?.clearValidate();
};
const rowKey = () => `${Date.now()}-${Math.random().toString(16).slice(2)}`;
const createEmptyParam = (): CommandParamDraft => ({
_key: rowKey(),
paramName: '',
paramCode: '',
paramDirectionFlag: 'INPUT',
paramTypeFlag: 'STRING',
requiredFlag: false,
defaultValue: '',
enableFlag: 'ENABLE',
});
const cloneParams = (params: CommandParamRecord[] = []): CommandParamDraft[] =>
params.map((item) => ({
...item,
_key: rowKey(),
paramDirectionFlag: paramDirectionValue(item.paramDirectionFlag),
paramTypeFlag: pointTypeValue(item.paramTypeFlag),
requiredFlag: Boolean(item.requiredFlag),
enableFlag: enableFlagValue(item.enableFlag),
}));
const normalizeParams = (): CommandParamRecord[] =>
reactiveData.params
.filter((item) => String(item.paramName || item.paramCode || '').trim() !== '')
.map((item) => {
const param = { ...item } as CommandParamRecord;
delete (param as { _key?: string })._key;
return {
...param,
paramName: String(item.paramName || '').trim(),
paramCode: String(item.paramCode || '').trim(),
paramDirectionFlag: item.paramDirectionFlag || 'INPUT',
paramTypeFlag: item.paramTypeFlag || 'STRING',
requiredFlag: Boolean(item.requiredFlag),
enableFlag: item.enableFlag || 'ENABLE',
};
});
const validateParams = (params: CommandParamRecord[]) => {
const codes = new Set<string>();
for (const item of params) {
const code = String(item.paramCode || '').trim();
if (!item.paramName || !code || !item.paramDirectionFlag || !item.paramTypeFlag) {
failMessage(t('command.form.paramRequired'));
return false;
}
if (codes.has(code)) {
failMessage(t('command.form.paramCodeUnique'));
return false;
}
codes.add(code);
}
return true;
};
const addParamRow = () => {
reactiveData.params.push(createEmptyParam());
};
const removeParamRow = (index: number) => {
reactiveData.params.splice(index, 1);
};
const show = (profileId = '') => {
reactiveData.mode = 'add';
const emptyForm = createEmptyForm(profileId);
reactiveData.originalForm = { ...emptyForm };
reactiveData.form = { ...emptyForm };
reactiveData.originalParams = [];
reactiveData.params = [];
reactiveData.visible = true;
};
@@ -125,35 +285,55 @@
...emptyForm,
...row,
profileId: String(row.profileId ?? emptyForm.profileId),
commandTypeFlag: String(row.commandTypeFlag ?? emptyForm.commandTypeFlag),
callTypeFlag: String(row.callTypeFlag ?? emptyForm.callTypeFlag),
enableFlag: String(row.enableFlag ?? emptyForm.enableFlag),
commandTypeFlag: commandTypeValue(row.commandTypeFlag, emptyForm.commandTypeFlag),
callTypeFlag: callTypeValue(row.callTypeFlag, emptyForm.callTypeFlag),
timeout: normalizeCommandTimeoutSeconds(row.timeout) ?? emptyForm.timeout,
enableFlag: enableFlagValue(row.enableFlag, emptyForm.enableFlag),
};
reactiveData.originalForm = { ...initial };
reactiveData.form = { ...initial };
reactiveData.originalParams = [];
reactiveData.params = [];
reactiveData.visible = true;
if (row.id) {
reactiveData.paramLoading = true;
listCommandParamByCommandId(String(row.id))
.then((res) => {
reactiveData.originalParams = res.data || [];
reactiveData.params = cloneParams(reactiveData.originalParams);
})
.finally(() => {
reactiveData.paramLoading = false;
});
}
};
const done = () => {
const done: DoneCallback = (close = true) => {
reactiveData.submitting = false;
reactiveData.visible = false;
if (close) {
reactiveData.visible = false;
}
};
const submit = async () => {
const valid = await formRef.value?.validate().catch(() => false);
if (!valid) return;
reactiveData.submitting = true;
const payload = { ...reactiveData.form };
const payload = {
...reactiveData.form,
timeout: normalizeCommandTimeoutSeconds(reactiveData.form.timeout) ?? 30,
};
const params = normalizeParams();
if (!validateParams(params)) {
reactiveData.submitting = false;
return;
}
if (reactiveData.mode === 'add') {
emit('add-thing', payload, done);
emit('add-thing', payload, params, done);
} else {
emit('update-thing', payload, done);
emit('update-thing', payload, params, reactiveData.originalParams, done);
}
};
defineExpose({ show, showEdit });
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
@@ -54,12 +54,12 @@
</el-form-item>
</template>
<template v-if="pre || next" #buttons="{ search, reset }">
<el-button v-if="pre" :icon="Back" plain type="success" @click="$emit('pre-handle')">
<el-button v-if="pre" :icon="Back" plain @click="$emit('pre-handle')">
{{ $t('common.previous') }}
</el-button>
<el-button :icon="Search" type="primary" @click="search">{{ $t('common.search') }}</el-button>
<el-button :icon="RefreshLeft" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button v-if="next" :icon="Check" plain type="warning" @click="$emit('next-handle')">
<el-button v-if="next" :icon="Check" plain type="primary" @click="$emit('next-handle')">
{{ $t('common.next') }}
</el-button>
</template>
+70 -75
View File
@@ -26,25 +26,40 @@
@current-change="currentChange"
>
<template #filters>
<el-form-item label="Device ID" prop="deviceId">
<el-input v-model="formData.deviceId" class="edit-form-default" clearable placeholder="Device ID" />
<el-form-item :label="$t('eventHistory.deviceId')" prop="deviceId">
<el-input
v-model="formData.deviceId"
:placeholder="$t('eventHistory.deviceId')"
class="edit-form-default"
clearable
/>
</el-form-item>
<el-form-item label="Event Code" prop="eventCode">
<el-input v-model="formData.eventCode" class="edit-form-default" clearable placeholder="Event Code" />
<el-form-item :label="$t('eventHistory.eventCode')" prop="eventCode">
<el-input
v-model="formData.eventCode"
:placeholder="$t('eventHistory.eventCode')"
class="edit-form-default"
clearable
/>
</el-form-item>
</template>
</tool-card>
<blank-card>
<el-table v-loading="reactiveData.loading" :data="reactiveData.listData" class="settings-table" stripe>
<el-table-column label="Record ID" min-width="180" prop="recordId" show-overflow-tooltip />
<el-table-column label="Device ID" min-width="160" prop="deviceId" show-overflow-tooltip />
<el-table-column label="Event Code" min-width="140" prop="eventCode" />
<el-table-column label="Type" prop="eventTypeFlag" width="110" />
<el-table-column label="Level" prop="eventLevelFlag" width="100" />
<el-table-column label="Ack" prop="acknowledgeFlag" width="90" />
<el-table-column label="Message" min-width="200" prop="message" show-overflow-tooltip />
<el-table-column :formatter="timestampColumn" label="Occur Time" prop="occurTime" width="165" />
<el-table-column :label="$t('eventHistory.recordId')" min-width="180" prop="recordId" show-overflow-tooltip />
<el-table-column :label="$t('eventHistory.deviceId')" min-width="160" prop="deviceId" show-overflow-tooltip />
<el-table-column :label="$t('eventHistory.eventCode')" min-width="140" prop="eventCode" />
<el-table-column :label="$t('eventHistory.type')" prop="eventTypeFlag" width="110" />
<el-table-column :label="$t('eventHistory.level')" prop="eventLevelFlag" width="100" />
<el-table-column :label="$t('eventHistory.ack')" prop="acknowledgeFlag" width="90" />
<el-table-column :label="$t('eventHistory.message')" min-width="200" prop="message" show-overflow-tooltip />
<el-table-column
:formatter="timestampColumn"
:label="$t('eventHistory.occurTime')"
prop="occurTime"
width="165"
/>
<el-table-column :formatter="timestampColumn" :label="$t('common.createTime')" prop="createTime" width="165" />
<el-table-column :label="$t('common.operation')" fixed="right" width="100">
<template #default="{ row }">
@@ -57,37 +72,49 @@
</el-table>
</blank-card>
<el-dialog v-model="detailVisible" :append-to-body="true" draggable title="Event History Detail" width="700px">
<el-dialog
v-model="detailVisible"
:append-to-body="true"
:title="$t('eventHistory.detailTitle')"
draggable
width="700px"
>
<el-descriptions v-if="detailRow" :column="2" border>
<el-descriptions-item :span="2" label="Record ID">{{ detailRow.recordId }}</el-descriptions-item>
<el-descriptions-item label="Device ID">{{ detailRow.deviceId }}</el-descriptions-item>
<el-descriptions-item label="Event ID">{{ detailRow.eventId }}</el-descriptions-item>
<el-descriptions-item label="Event Code">{{ detailRow.eventCode }}</el-descriptions-item>
<el-descriptions-item label="Type">{{ detailRow.eventTypeFlag }}</el-descriptions-item>
<el-descriptions-item label="Level">{{ detailRow.eventLevelFlag }}</el-descriptions-item>
<el-descriptions-item :span="2" label="Message">{{ detailRow.message || '-' }}</el-descriptions-item>
<el-descriptions-item :span="2" label="Param Values">
<el-descriptions-item :label="$t('eventHistory.recordId')" :span="2">
{{ detailRow.recordId }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.deviceId')">{{ detailRow.deviceId }}</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.eventId')">{{ detailRow.eventId }}</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.eventCode')">{{ detailRow.eventCode }}</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.type')">{{ detailRow.eventTypeFlag }}</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.level')">{{ detailRow.eventLevelFlag }}</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.message')" :span="2">
{{ detailRow.message || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.paramValues')" :span="2">
<pre class="json-preview">{{ formatJson(detailRow.paramValues) }}</pre>
</el-descriptions-item>
<el-descriptions-item :span="2" label="Config Snapshot">
<el-descriptions-item :label="$t('eventHistory.configSnapshot')" :span="2">
<pre class="json-preview">{{ formatJson(detailRow.configSnapshot) }}</pre>
</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Occur Time">{{
detailRow.occurTime
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Receive Time">{{
detailRow.receiveTime
}}</el-descriptions-item>
<el-descriptions-item label="Acknowledge Flag">{{ detailRow.acknowledgeFlag }}</el-descriptions-item>
<el-descriptions-item label="Acknowledge User ID">{{
detailRow.acknowledgeUserId || '-'
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Acknowledge Time">{{
detailRow.acknowledgeTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :formatter="timestampColumn" label="Operate Time">{{
detailRow.operateTime || '-'
}}</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.occurTime')">
{{ timestampLabel(detailRow.occurTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.receiveTime')">
{{ timestampLabel(detailRow.receiveTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.acknowledgeFlag')">
{{ detailRow.acknowledgeFlag }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.acknowledgeUserId')">
{{ detailRow.acknowledgeUserId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventHistory.acknowledgeTime')">
{{ timestampLabel(detailRow.acknowledgeTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ timestampLabel(detailRow.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
@@ -96,7 +123,8 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { getEventHistoryById, listEventHistory } from '@/api/event';
import { timestampColumn } from '@/utils/dateUtil';
import { timestampColumn, timestampLabel } from '@/utils/dateUtil';
import { prettyJson } from '@/utils/jsonUtil';
import type { EventHistory, Order } from '@/config/types';
import ToolCard from '@/components/card/tool/ToolCard.vue';
import BlankCard from '@/components/card/blank/BlankCard.vue';
@@ -109,23 +137,11 @@
page: { total: 0, size: 12, current: 1, orders: [] as Order[] },
});
const formData = reactive<Record<string, any>>({});
const formData = reactive<Record<string, string>>({});
const detailVisible = ref(false);
const detailRow = ref<EventHistory | null>(null);
const formatJson = (value: unknown) => {
if (!value) {
return '-';
}
if (typeof value === 'string') {
try {
return JSON.stringify(JSON.parse(value), null, 2);
} catch {
return value;
}
}
return JSON.stringify(value, null, 2);
};
const formatJson = (value: unknown) => prettyJson(value);
const load = () => {
reactiveData.loading = true;
@@ -140,7 +156,7 @@
});
};
const onSearch = (data: Record<string, any>) => {
const onSearch = (data: Record<string, string>) => {
reactiveData.query = cleanSearchParams(data);
reactiveData.page.current = 1;
load();
@@ -178,24 +194,3 @@
load();
</script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
.json-preview {
max-height: 220px;
margin: 0;
padding: 8px;
overflow: auto;
border-radius: 4px;
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
</style>
@@ -447,11 +447,6 @@
</script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
.settings-table__sub {
margin-left: 6px;
color: #909399;
@@ -57,45 +57,37 @@
<event-edit-form ref="editRef" @add-thing="onAdd" @update-thing="onUpdate" />
<el-drawer v-model="reactiveData.detailVisible" title="Event Detail" size="520px">
<el-drawer v-model="reactiveData.detailVisible" :title="$t('eventDefinition.detail.title')" size="520px">
<el-descriptions v-if="reactiveData.detailRecord" :column="1" border>
<el-descriptions-item :label="$t('common.name')">{{
reactiveData.detailRecord.eventName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="Code">{{ reactiveData.detailRecord.eventCode || '-' }}</el-descriptions-item>
<el-descriptions-item label="Event Type">{{
<el-descriptions-item :label="$t('eventDefinition.detail.code')">
{{ reactiveData.detailRecord.eventCode || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventDefinition.detail.eventType')">{{
reactiveData.detailRecord.eventTypeFlag || '-'
}}</el-descriptions-item>
<el-descriptions-item label="Event Level">{{
<el-descriptions-item :label="$t('eventDefinition.detail.eventLevel')">{{
reactiveData.detailRecord.eventLevelFlag || '-'
}}</el-descriptions-item>
<el-descriptions-item :label="$t('common.enableFlag')">
<el-tag
:type="
String(reactiveData.detailRecord.enableFlag) === 'ENABLE' ||
Number(reactiveData.detailRecord.enableFlag) === 0
? 'success'
: 'info'
"
>
{{
String(reactiveData.detailRecord.enableFlag) === 'ENABLE' ||
Number(reactiveData.detailRecord.enableFlag) === 0
? $t('common.enable')
: $t('common.disable')
}}
</el-tag>
<enable-tag :value="reactiveData.detailRecord.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')">{{
reactiveData.detailRecord.remark || '-'
}}</el-descriptions-item>
<el-descriptions-item label="Profile ID">{{ reactiveData.detailRecord.profileId || '-' }}</el-descriptions-item>
<el-descriptions-item label="Tenant ID">{{ reactiveData.detailRecord.tenantId || '-' }}</el-descriptions-item>
<el-descriptions-item label="Create Time">{{
formatTime(reactiveData.detailRecord.createTime)
<el-descriptions-item :label="$t('eventDefinition.detail.profileId')">
{{ reactiveData.detailRecord.profileId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('eventDefinition.detail.tenantId')">
{{ reactiveData.detailRecord.tenantId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">{{
timestampLabel(reactiveData.detailRecord.createTime)
}}</el-descriptions-item>
<el-descriptions-item label="Operate Time">{{
formatTime(reactiveData.detailRecord.operateTime)
<el-descriptions-item :label="$t('common.operationTime')">{{
timestampLabel(reactiveData.detailRecord.operateTime)
}}</el-descriptions-item>
</el-descriptions>
<el-empty v-else :description="$t('common.description')" />
@@ -105,13 +97,23 @@
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue';
import { addEvent, deleteEvent, listEvent, updateEvent } from '@/api/event';
import { timestamp } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import { useI18n } from 'vue-i18n';
import {
addEvent,
addEventParam,
deleteEvent,
deleteEventParam,
listEvent,
updateEvent,
updateEventParam,
} from '@/api/event';
import { timestampLabel } from '@/utils/dateUtil';
import { failMessage, successMessage } from '@/utils/notificationUtil';
import { isNull } from '@/utils/validationUtil';
import type { EventForm, EventRecord, Order } from '@/config/types';
import type { EventForm, EventParamRecord, EventRecord, Order } from '@/config/types';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import SkeletonCard from '@/components/card/skeleton/SkeletonCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import EventCard from './card/EventCard.vue';
import EventTool from './tool/EventTool.vue';
import EventEditForm from './edit/EventEditForm.vue';
@@ -132,6 +134,7 @@
}>();
const editRef = ref<InstanceType<typeof EventEditForm>>();
const { t } = useI18n();
const canManage = computed(() => props.embedded === '' || props.embedded === 'edit');
const hasData = computed(() => !reactiveData.loading && reactiveData.listData.length < 1);
@@ -161,8 +164,6 @@
return isNull(profileId) ? { ...form } : { ...form, profileId };
};
const formatTime = (value: unknown) => (isNull(value) ? '-' : timestamp(String(value)));
const load = () => {
reactiveData.loading = true;
const query = withFixedQuery(reactiveData.query);
@@ -204,20 +205,65 @@
};
const openEdit = (row: EventRecord) => editRef.value?.showEdit(row);
const onAdd = (form: EventForm, done: () => void) => {
addEvent(withFixedProfile(form)).then(() => {
successMessage();
load();
done();
type DoneCallback = (close?: boolean) => void;
const isValidCreatedId = (id: string) => /^\d+$/.test(id);
const syncEventParams = (eventId: string, params: EventParamRecord[], originalParams: EventParamRecord[] = []) => {
const currentIds = new Set(params.map((item) => String(item.id || '')).filter(Boolean));
const deleteTasks = originalParams
.filter((item) => item.id && !currentIds.has(String(item.id)))
.map((item) => deleteEventParam(String(item.id)));
const saveTasks = params.map((item) => {
const payload = { ...item, eventId };
return item.id ? updateEventParam(payload) : addEventParam({ ...payload, id: undefined });
});
return Promise.all(deleteTasks).then(() => Promise.all(saveTasks));
};
const onUpdate = (form: EventForm, done: () => void) => {
updateEvent(withFixedProfile(form)).then(() => {
successMessage();
load();
done();
});
const onAdd = (form: EventForm, params: EventParamRecord[], done: DoneCallback) => {
addEvent(withFixedProfile(form))
.then((res) => {
const eventId = String(res.data || '');
if (!isValidCreatedId(eventId)) {
failMessage(t('eventDefinition.errors.idNotReturned'));
return Promise.reject(new Error(t('eventDefinition.errors.idNotReturned')));
}
return syncEventParams(eventId, params).then(() => {
successMessage();
load();
done();
});
})
.catch(() => {
done(false);
});
};
const onUpdate = (
form: EventForm,
params: EventParamRecord[],
originalParams: EventParamRecord[],
done: DoneCallback
) => {
updateEvent(withFixedProfile(form))
.then(() => {
const eventId = String(form.id || '');
if (!isValidCreatedId(eventId)) {
failMessage(t('eventDefinition.errors.idMissing'));
return Promise.reject(new Error(t('eventDefinition.errors.idMissing')));
}
return syncEventParams(eventId, params, originalParams).then(() => {
successMessage();
load();
done();
});
})
.catch(() => {
done(false);
});
};
const disableThing = (id: string, profileId: string, done: () => void) => {
@@ -127,8 +127,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
.things-body-content-item-column-2 {
max-width: 200px;
}
@@ -21,7 +21,7 @@
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
:title="reactiveData.mode === 'add' ? $t('common.add') + ' Event' : $t('common.edit') + ' Event'"
:title="reactiveData.mode === 'add' ? $t('eventDefinition.form.addTitle') : $t('eventDefinition.form.editTitle')"
class="things-dialog"
draggable
@closed="reset"
@@ -30,15 +30,15 @@
<el-form-item :label="$t('common.name')" prop="eventName">
<el-input v-model="reactiveData.form.eventName" :placeholder="$t('common.name')" clearable />
</el-form-item>
<el-form-item label="Code" prop="eventCode">
<el-input v-model="reactiveData.form.eventCode" clearable placeholder="Code" />
<el-form-item :label="$t('eventDefinition.form.code')" prop="eventCode">
<el-input v-model="reactiveData.form.eventCode" :placeholder="$t('eventDefinition.form.code')" clearable />
</el-form-item>
<el-form-item label="Event Type" prop="eventTypeFlag">
<el-form-item :label="$t('eventDefinition.form.eventType')" prop="eventTypeFlag">
<el-select v-model="reactiveData.form.eventTypeFlag" clearable>
<el-option v-for="opt in EVENT_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</el-form-item>
<el-form-item label="Event Level" prop="eventLevelFlag">
<el-form-item :label="$t('eventDefinition.form.eventLevel')" prop="eventLevelFlag">
<el-select v-model="reactiveData.form.eventLevelFlag" clearable>
<el-option v-for="opt in EVENT_LEVEL_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
@@ -50,9 +50,48 @@
<el-input v-model="reactiveData.form.remark" clearable maxlength="300" show-word-limit type="textarea" />
</el-form-item>
</el-form>
<div v-loading="reactiveData.paramLoading" class="param-editor">
<div class="param-editor__toolbar">
<span>{{ $t('eventDefinition.form.params') }}</span>
<el-button :icon="Plus" size="small" type="success" @click="addParamRow">
{{ $t('common.add') }}
</el-button>
</div>
<el-table :data="reactiveData.params" border max-height="260" size="small">
<el-table-column :label="$t('common.name')" min-width="170">
<template #default="{ row }">
<el-input v-model="row.paramName" clearable />
</template>
</el-table-column>
<el-table-column :label="$t('eventDefinition.form.code')" min-width="170">
<template #default="{ row }">
<el-input v-model="row.paramCode" clearable />
</template>
</el-table-column>
<el-table-column :label="$t('eventDefinition.form.type')" min-width="140">
<template #default="{ row }">
<el-select v-model="row.paramTypeFlag">
<el-option v-for="opt in POINT_TYPE_OPTIONS" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
</template>
</el-table-column>
<el-table-column :label="$t('eventDefinition.form.enabled')" width="104">
<template #default="{ row }">
<el-switch v-model="row.enableFlag" active-value="ENABLE" inactive-value="DISABLE" />
</template>
</el-table-column>
<el-table-column align="center" width="64">
<template #default="{ $index }">
<el-tooltip :content="$t('common.delete')" placement="top">
<el-button :icon="Delete" link type="danger" @click="removeParamRow($index)" />
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ $t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ $t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ $t('common.confirm') }}
</el-button>
@@ -63,16 +102,32 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
import { Delete, Plus } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { listEventParamByEventId } from '@/api/event';
import EnableFlagSegmented from '@/components/segmented/EnableFlagSegmented.vue';
import { EVENT_LEVEL_OPTIONS, EVENT_TYPE_OPTIONS } from '@/config/constant/enums';
import type { EventRecord } from '@/config/types';
import { EVENT_LEVEL_OPTIONS, EVENT_TYPE_OPTIONS, POINT_TYPE_OPTIONS } from '@/config/constant/enums';
import type { EventForm, EventParamRecord, EventRecord } from '@/config/types';
import { failMessage } from '@/utils/notificationUtil';
import { enableFlagValue, eventLevelValue, eventTypeValue, pointTypeValue } from '@/utils/thingModelFormatUtil';
type FormMode = 'add' | 'edit';
type DoneCallback = (close?: boolean) => void;
const { t } = useI18n();
const emit = defineEmits(['add-thing', 'update-thing']);
type EventParamDraft = EventParamRecord & { _key: string };
const emit = defineEmits<{
(e: 'add-thing', form: EventForm, params: EventParamRecord[], done: DoneCallback): void;
(
e: 'update-thing',
form: EventForm,
params: EventParamRecord[],
originalParams: EventParamRecord[],
done: DoneCallback
): void;
}>();
const formRef = ref<FormInstance>();
@@ -93,24 +148,88 @@
submitting: false,
form: createEmptyForm(),
originalForm: createEmptyForm(),
params: [] as EventParamDraft[],
originalParams: [] as EventParamRecord[],
paramLoading: false,
});
const rules: FormRules = {
eventName: [{ required: true, message: t('common.name'), trigger: 'blur' }],
eventCode: [{ required: true, message: 'Code is required', trigger: 'blur' }],
eventName: [{ required: true, message: t('eventDefinition.form.nameRequired'), trigger: 'blur' }],
eventCode: [{ required: true, message: t('eventDefinition.form.codeRequired'), trigger: 'blur' }],
};
const reset = () => {
reactiveData.form = { ...reactiveData.originalForm };
reactiveData.params = cloneParams(reactiveData.originalParams);
reactiveData.submitting = false;
formRef.value?.clearValidate();
};
const rowKey = () => `${Date.now()}-${Math.random().toString(16).slice(2)}`;
const createEmptyParam = (): EventParamDraft => ({
_key: rowKey(),
paramName: '',
paramCode: '',
paramTypeFlag: 'STRING',
enableFlag: 'ENABLE',
});
const cloneParams = (params: EventParamRecord[] = []): EventParamDraft[] =>
params.map((item) => ({
...item,
_key: rowKey(),
paramTypeFlag: pointTypeValue(item.paramTypeFlag),
enableFlag: enableFlagValue(item.enableFlag),
}));
const normalizeParams = (): EventParamRecord[] =>
reactiveData.params
.filter((item) => String(item.paramName || item.paramCode || '').trim() !== '')
.map((item) => {
const param = { ...item } as EventParamRecord;
delete (param as { _key?: string })._key;
return {
...param,
paramName: String(item.paramName || '').trim(),
paramCode: String(item.paramCode || '').trim(),
paramTypeFlag: item.paramTypeFlag || 'STRING',
enableFlag: item.enableFlag || 'ENABLE',
};
});
const validateParams = (params: EventParamRecord[]) => {
const codes = new Set<string>();
for (const item of params) {
const code = String(item.paramCode || '').trim();
if (!item.paramName || !code || !item.paramTypeFlag) {
failMessage(t('eventDefinition.form.paramRequired'));
return false;
}
if (codes.has(code)) {
failMessage(t('eventDefinition.form.paramCodeUnique'));
return false;
}
codes.add(code);
}
return true;
};
const addParamRow = () => {
reactiveData.params.push(createEmptyParam());
};
const removeParamRow = (index: number) => {
reactiveData.params.splice(index, 1);
};
const show = (profileId = '') => {
reactiveData.mode = 'add';
const emptyForm = createEmptyForm(profileId);
reactiveData.originalForm = { ...emptyForm };
reactiveData.form = { ...emptyForm };
reactiveData.originalParams = [];
reactiveData.params = [];
reactiveData.visible = true;
};
@@ -121,18 +240,33 @@
...emptyForm,
...row,
profileId: String(row.profileId ?? emptyForm.profileId),
eventTypeFlag: String(row.eventTypeFlag ?? emptyForm.eventTypeFlag),
eventLevelFlag: String(row.eventLevelFlag ?? emptyForm.eventLevelFlag),
enableFlag: String(row.enableFlag ?? emptyForm.enableFlag),
eventTypeFlag: eventTypeValue(row.eventTypeFlag, emptyForm.eventTypeFlag),
eventLevelFlag: eventLevelValue(row.eventLevelFlag, emptyForm.eventLevelFlag),
enableFlag: enableFlagValue(row.enableFlag, emptyForm.enableFlag),
};
reactiveData.originalForm = { ...initial };
reactiveData.form = { ...initial };
reactiveData.originalParams = [];
reactiveData.params = [];
reactiveData.visible = true;
if (row.id) {
reactiveData.paramLoading = true;
listEventParamByEventId(String(row.id))
.then((res) => {
reactiveData.originalParams = res.data || [];
reactiveData.params = cloneParams(reactiveData.originalParams);
})
.finally(() => {
reactiveData.paramLoading = false;
});
}
};
const done = () => {
const done: DoneCallback = (close = true) => {
reactiveData.submitting = false;
reactiveData.visible = false;
if (close) {
reactiveData.visible = false;
}
};
const submit = async () => {
@@ -140,16 +274,17 @@
if (!valid) return;
reactiveData.submitting = true;
const payload = { ...reactiveData.form };
const params = normalizeParams();
if (!validateParams(params)) {
reactiveData.submitting = false;
return;
}
if (reactiveData.mode === 'add') {
emit('add-thing', payload, done);
emit('add-thing', payload, params, done);
} else {
emit('update-thing', payload, done);
emit('update-thing', payload, params, reactiveData.originalParams, done);
}
};
defineExpose({ show, showEdit });
</script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
@@ -54,12 +54,12 @@
</el-form-item>
</template>
<template v-if="pre || next" #buttons="{ search, reset }">
<el-button v-if="pre" :icon="Back" plain type="success" @click="$emit('pre-handle')">
<el-button v-if="pre" :icon="Back" plain @click="$emit('pre-handle')">
{{ $t('common.previous') }}
</el-button>
<el-button :icon="Search" type="primary" @click="search">{{ $t('common.search') }}</el-button>
<el-button :icon="RefreshLeft" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button v-if="next" :icon="Check" plain type="warning" @click="$emit('next-handle')">
<el-button v-if="next" :icon="Check" plain type="primary" @click="$emit('next-handle')">
{{ $t('common.next') }}
</el-button>
</template>
+1 -14
View File
@@ -44,13 +44,7 @@
</el-table-column>
<el-table-column :label="t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{
String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0
? t('common.enable')
: t('common.disable')
}}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :label="t('common.remark')" min-width="180" prop="remark" show-overflow-tooltip />
@@ -82,10 +76,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
</style>
@@ -40,9 +40,7 @@
{{ reactiveData.data.groupIndex ?? '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.enable')">
<el-tag :type="enableTagType(reactiveData.data.enableFlag)">
{{ enableLabel(reactiveData.data.enableFlag) }}
</el-tag>
<enable-tag :value="reactiveData.data.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')" :span="2">
{{ reactiveData.data.remark || '-' }}
@@ -51,13 +49,13 @@
{{ reactiveData.data.creatorName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operatorName')">
{{ reactiveData.data.operatorName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -75,7 +73,8 @@
import { getGroupById, listGroup } from '@/api/group';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import DetailCard from '@/components/card/detail/DetailCard.vue';
import { timestamp } from '@/utils/dateUtil';
import EnableTag from '@/components/tag/EnableTag.vue';
import { timestampLabel } from '@/utils/dateUtil';
const route = useRoute();
const { t } = useI18n();
@@ -93,14 +92,6 @@
return reactiveData.parentMap[String(id)] || String(id);
});
const enableTagType = (value: unknown) => {
return String(value) === 'ENABLE' || Number(value) === 0 ? 'success' : 'info';
};
const enableLabel = (value: unknown) => {
return String(value) === 'ENABLE' || Number(value) === 0 ? t('common.enable') : t('common.disable');
};
const loadParents = () => {
listGroup({ page: { current: 1, size: 5000 } })
.then((res: any) => {
@@ -130,7 +121,3 @@
load();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
@@ -69,7 +69,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ t('common.confirm') }}
</el-button>
@@ -78,7 +78,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+2
View File
@@ -26,6 +26,7 @@ import type { Order } from '@/config/types';
import type { GroupForm, GroupRecord } from '@/config/types/manager';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import groupTool from './tool/GroupTool.vue';
import groupEditForm from './edit/GroupEditForm.vue';
@@ -33,6 +34,7 @@ export default defineComponent({
name: 'SettingsGroup',
components: {
BlankCard,
EnableTag,
groupTool,
groupEditForm,
},
+1 -12
View File
@@ -47,13 +47,7 @@
</el-table-column>
<el-table-column :label="t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{
String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0
? t('common.enable')
: t('common.disable')
}}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :label="t('common.remark')" min-width="180" prop="remark" show-overflow-tooltip />
@@ -87,11 +81,6 @@
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
.label-color {
display: inline-flex;
align-items: center;
@@ -40,9 +40,7 @@
</span>
</el-descriptions-item>
<el-descriptions-item :label="$t('common.enable')">
<el-tag :type="enableTagType(reactiveData.data.enableFlag)">
{{ enableLabel(reactiveData.data.enableFlag) }}
</el-tag>
<enable-tag :value="reactiveData.data.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')" :span="2">
{{ reactiveData.data.remark || '-' }}
@@ -51,13 +49,13 @@
{{ reactiveData.data.creatorName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operatorName')">
{{ reactiveData.data.operatorName || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -69,16 +67,15 @@
<script lang="ts" setup>
import { onMounted, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { getLabelById } from '@/api/label';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import DetailCard from '@/components/card/detail/DetailCard.vue';
import { timestamp } from '@/utils/dateUtil';
import EnableTag from '@/components/tag/EnableTag.vue';
import { timestampLabel } from '@/utils/dateUtil';
const route = useRoute();
const { t } = useI18n();
const reactiveData = reactive({
id: route.query.id as string,
@@ -86,14 +83,6 @@
data: {} as Record<string, any>,
});
const enableTagType = (value: unknown) => {
return String(value) === 'ENABLE' || Number(value) === 0 ? 'success' : 'info';
};
const enableLabel = (value: unknown) => {
return String(value) === 'ENABLE' || Number(value) === 0 ? t('common.enable') : t('common.disable');
};
const load = () => {
if (!reactiveData.id) return;
getLabelById(reactiveData.id)
@@ -111,8 +100,6 @@
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
.label-color {
display: inline-flex;
align-items: center;
@@ -58,7 +58,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ t('common.confirm') }}
</el-button>
@@ -67,7 +67,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+2
View File
@@ -26,6 +26,7 @@ import type { Order } from '@/config/types';
import type { LabelForm, LabelRecord } from '@/config/types/manager';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import labelTool from './tool/LabelTool.vue';
import labelEditForm from './edit/LabelEditForm.vue';
@@ -33,6 +34,7 @@ export default defineComponent({
name: 'SettingsLabel',
components: {
BlankCard,
EnableTag,
labelTool,
labelEditForm,
},
+1 -12
View File
@@ -51,13 +51,7 @@
</el-table-column>
<el-table-column :label="t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{
String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0
? t('common.enable')
: t('common.disable')
}}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :formatter="timestampColumn" :label="t('common.createTime')" prop="createTime" width="165" />
@@ -90,11 +84,6 @@
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
.menu-icon-cell {
display: inline-flex;
align-items: center;
+5 -20
View File
@@ -46,28 +46,16 @@
{{ reactiveData.data.menuExt?.content?.url || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.enable')">
<el-tag
:type="
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? 'success'
: 'info'
"
>
{{
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? $t('common.enable')
: $t('common.disable')
}}
</el-tag>
<enable-tag :value="reactiveData.data.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')" :span="2">
{{ reactiveData.data.remark || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -83,10 +71,11 @@
import { getMenuById } from '@/api/menu';
import { useMenuStore } from '@/store';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
import blankCard from '@/components/card/blank/BlankCard.vue';
import detailCard from '@/components/card/detail/DetailCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
const route = useRoute();
const menuStore = useMenuStore();
@@ -125,7 +114,3 @@
load();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
@@ -103,7 +103,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ t('common.confirm') }}
</el-button>
@@ -114,8 +114,6 @@
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
.icon-option {
display: inline-flex;
align-items: center;
+2
View File
@@ -25,6 +25,7 @@ import { timestampColumn } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import menuTool from './tool/MenuTool.vue';
import menuEditForm from './edit/MenuEditForm.vue';
@@ -32,6 +33,7 @@ export default defineComponent({
name: 'SettingsMenu',
components: {
BlankCard,
EnableTag,
menuTool,
menuEditForm,
},
+1 -14
View File
@@ -57,13 +57,7 @@
<el-table-column :label="t('common.remark')" min-width="140" prop="remark" show-overflow-tooltip />
<el-table-column :label="t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{
String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0
? t('common.enable')
: t('common.disable')
}}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :formatter="timestampColumn" :label="t('common.createTime')" prop="createTime" width="165" />
@@ -97,10 +91,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
</style>
@@ -40,28 +40,16 @@
{{ reactiveData.data.entityId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.enable')">
<el-tag
:type="
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? 'success'
: 'info'
"
>
{{
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? $t('common.enable')
: $t('common.disable')
}}
</el-tag>
<enable-tag :value="reactiveData.data.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')" :span="2">
{{ reactiveData.data.remark || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -73,9 +61,7 @@
<el-table-column :label="$t('settings.role.roleCode')" min-width="180" prop="roleCode" />
<el-table-column :label="$t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{ Number(row.enableFlag) === 0 ? $t('common.enable') : $t('common.disable') }}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :label="$t('common.remark')" min-width="220" prop="remark" show-overflow-tooltip />
@@ -121,10 +107,11 @@
import { getResourceById, listResourceTree } from '@/api/resource';
import { getRoleListByResourceId } from '@/api/roleResourceBind';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
import blankCard from '@/components/card/blank/BlankCard.vue';
import detailCard from '@/components/card/detail/DetailCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
const route = useRoute();
const router = useRouter();
@@ -213,7 +200,3 @@
if (reactiveData.active === 'children') loadChildren();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
@@ -80,7 +80,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ t('common.confirm') }}
</el-button>
@@ -89,7 +89,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+47 -24
View File
@@ -28,14 +28,29 @@ import { useMenuStore } from '@/store';
import { timestampColumn } from '@/utils/dateUtil';
import { successMessage } from '@/utils/notificationUtil';
import type {
ApiRecord,
DeviceRecord,
DriverRecord,
Order,
PointRecord,
ProfileRecord,
ResourceForm,
ResourceRecord,
} from '@/config/types';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import resourceTool from './tool/ResourceTool.vue';
import resourceEditForm from './edit/ResourceEditForm.vue';
type LinkableResourceType = 'DRIVER' | 'DEVICE' | 'POINT' | 'PROFILE' | 'API' | 'MENU';
type EntityRecord = DriverRecord | DeviceRecord | PointRecord | ProfileRecord | ApiRecord;
export default defineComponent({
name: 'SettingsResource',
components: {
BlankCard,
EnableTag,
resourceTool,
resourceEditForm,
},
@@ -48,32 +63,32 @@ export default defineComponent({
const reactiveData = reactive({
loading: false,
listData: [] as any[],
query: {} as Record<string, any>,
listData: [] as ResourceRecord[],
query: {} as Record<string, unknown>,
page: {
total: 0,
size: 12,
current: 1,
orders: [] as any[],
orders: [] as Order[],
},
});
const entityNameMap = reactive<Record<string, string>>({});
const isGroupingNode = (row: any): boolean => {
const isGroupingNode = (row: ResourceRecord): boolean => {
// Virtual grouping nodes registered by ResourceRegistrySync carry entity_id=0.
return !row.entityId || String(row.entityId) === '0';
};
const formatEntityId = (row: any) => {
const formatEntityId = (row: ResourceRecord) => {
if (isGroupingNode(row)) return '—';
const id = String(row.entityId);
return entityNameMap[id] || id;
};
const LINKABLE_TYPES = ['DRIVER', 'DEVICE', 'POINT', 'PROFILE', 'API', 'MENU'];
const LINKABLE_TYPES: LinkableResourceType[] = ['DRIVER', 'DEVICE', 'POINT', 'PROFILE', 'API', 'MENU'];
const ENTITY_ROUTE_MAP: Record<string, string> = {
const ENTITY_ROUTE_MAP: Record<LinkableResourceType, string> = {
DRIVER: 'driverDetail',
DEVICE: 'deviceDetail',
POINT: 'pointDetail',
@@ -82,27 +97,34 @@ export default defineComponent({
MENU: 'settingsMenuDetail',
};
const isEntityLinkable = (row: any) => {
if (isGroupingNode(row)) return false;
return LINKABLE_TYPES.includes(row.resourceTypeFlag);
const resourceType = (row: ResourceRecord): LinkableResourceType | undefined => {
const type = String(row.resourceTypeFlag || '') as LinkableResourceType;
return LINKABLE_TYPES.includes(type) ? type : undefined;
};
const goEntityDetail = (row: any) => {
const routeName = ENTITY_ROUTE_MAP[row.resourceTypeFlag];
const isEntityLinkable = (row: ResourceRecord) => {
if (isGroupingNode(row)) return false;
return Boolean(resourceType(row));
};
const goEntityDetail = (row: ResourceRecord) => {
const type = resourceType(row);
if (!type) return;
const routeName = ENTITY_ROUTE_MAP[type];
if (!routeName) return;
router.push({ name: routeName, query: { id: String(row.entityId) } }).catch(() => {
// handled globally
});
};
const openDetail = (row: any) => {
const openDetail = (row: ResourceRecord) => {
router.push({ name: 'settingsResourceDetail', query: { id: String(row.id) } }).catch(() => {
// handled globally
});
};
// Flatten tree rows into a list for post-load entity-name resolution.
const flatten = (nodes: any[], acc: any[] = []): any[] => {
const flatten = (nodes: ResourceRecord[], acc: ResourceRecord[] = []): ResourceRecord[] => {
for (const n of nodes || []) {
acc.push(n);
if (n.children && n.children.length > 0) {
@@ -112,7 +134,7 @@ export default defineComponent({
return acc;
};
const resolveEntityNames = (records: any[]) => {
const resolveEntityNames = (records: ResourceRecord[]) => {
const driverIds: string[] = [];
const deviceIds: string[] = [];
const pointIds: string[] = [];
@@ -144,11 +166,12 @@ export default defineComponent({
}
}
const fill = (ids: string[], res: any, nameKey: string) => {
const fill = (ids: string[], res: R<Record<string, EntityRecord>>, nameKey: keyof EntityRecord) => {
const data = res.data || {};
ids.forEach((id) => {
const item = data[id];
if (item) entityNameMap[id] = item[nameKey] || id;
const name = item?.[nameKey];
if (name) entityNameMap[id] = String(name);
});
};
@@ -183,8 +206,8 @@ export default defineComponent({
if (apiIds.length)
promises.push(
listApi({ page: { size: 1000, current: 1 } })
.then((r: any) => {
const records = (r.data?.records as any[]) || [];
.then((r) => {
const records = r.data?.records || [];
const byId = new Map(records.map((a) => [String(a.id), a.apiName]));
apiIds.forEach((id) => {
const name = byId.get(id);
@@ -211,7 +234,7 @@ export default defineComponent({
// names — fetchTree is idempotent and skips the network if already
// loaded (Layout mounts it on startup, so this is usually a no-op).
await menuStore.fetchTree();
const res: any = await listResourceTree(reactiveData.query);
const res = await listResourceTree(reactiveData.query);
const tree = res.data || [];
reactiveData.listData = tree;
reactiveData.page.total = tree.length;
@@ -223,7 +246,7 @@ export default defineComponent({
}
};
const search = (params: any) => {
const search = (params: Record<string, unknown>) => {
reactiveData.query = params || {};
load();
};
@@ -241,9 +264,9 @@ export default defineComponent({
};
const openAdd = () => editRef.value?.show();
const openEdit = (row: any) => editRef.value?.showEdit(row);
const openEdit = (row: ResourceRecord) => editRef.value?.showEdit(row);
const onAdd = (form: any, done: () => void) => {
const onAdd = (form: ResourceForm, done: () => void) => {
addResource(form)
.then(() => {
successMessage();
@@ -255,7 +278,7 @@ export default defineComponent({
});
};
const onUpdate = (form: any, done: () => void) => {
const onUpdate = (form: ResourceForm, done: () => void) => {
updateResource(form)
.then(() => {
successMessage();
+1 -14
View File
@@ -33,13 +33,7 @@
<el-table-column :label="t('settings.role.roleCode')" min-width="160" prop="roleCode" />
<el-table-column :label="t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{
String(row.enableFlag) === 'ENABLE' || Number(row.enableFlag) === 0
? t('common.enable')
: t('common.disable')
}}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :label="t('common.remark')" min-width="200" prop="remark" show-overflow-tooltip />
@@ -75,10 +69,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
.settings-table {
margin-top: 1px;
border-radius: 4px;
}
</style>
+6 -23
View File
@@ -31,28 +31,16 @@
{{ reactiveData.data.parentRoleId || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.enable')">
<el-tag
:type="
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? 'success'
: 'info'
"
>
{{
String(reactiveData.data.enableFlag) === 'ENABLE' || Number(reactiveData.data.enableFlag) === 0
? $t('common.enable')
: $t('common.disable')
}}
</el-tag>
<enable-tag :value="reactiveData.data.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')" :span="2">
{{ reactiveData.data.remark || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -66,9 +54,7 @@
<el-table-column :label="$t('settings.user.email')" min-width="180" prop="email" show-overflow-tooltip />
<el-table-column :label="$t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{ Number(row.enableFlag) === 0 ? $t('common.enable') : $t('common.disable') }}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<template #empty>
@@ -102,10 +88,11 @@
import { getRoleById } from '@/api/role';
import { getResourceListByRoleId } from '@/api/roleResourceBind';
import { getUserListByRoleId } from '@/api/roleUserBind';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
import blankCard from '@/components/card/blank/BlankCard.vue';
import detailCard from '@/components/card/detail/DetailCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
const route = useRoute();
const router = useRouter();
@@ -178,7 +165,3 @@
if (reactiveData.active === 'resource') loadResources();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
@@ -61,7 +61,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ t('common.confirm') }}
</el-button>
@@ -70,7 +70,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+2
View File
@@ -27,6 +27,7 @@ import type { Order } from '@/config/types';
import type { RoleForm, RoleRecord } from '@/config/types/auth';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
import roleTool from './tool/RoleTool.vue';
import roleEditForm from './edit/RoleEditForm.vue';
import roleAssignResources from './assign/RoleAssignResources.vue';
@@ -35,6 +36,7 @@ export default defineComponent({
name: 'SettingsRole',
components: {
BlankCard,
EnableTag,
roleTool,
roleEditForm,
roleAssignResources,
+1 -9
View File
@@ -35,9 +35,7 @@
<el-table-column :label="t('settings.user.email')" min-width="180" prop="email" show-overflow-tooltip />
<el-table-column :label="t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{ Number(row.enableFlag) === 0 ? t('common.enable') : t('common.disable') }}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :formatter="timestampColumn" :label="t('common.createTime')" prop="createTime" width="165" />
@@ -72,9 +70,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
.settings-table {
border-radius: 4px;
}
</style>
+6 -13
View File
@@ -34,18 +34,16 @@
{{ reactiveData.data.email || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.enable')">
<el-tag :type="Number(reactiveData.data.enableFlag) === 0 ? 'success' : 'info'">
{{ Number(reactiveData.data.enableFlag) === 0 ? $t('common.enable') : $t('common.disable') }}
</el-tag>
<enable-tag :value="reactiveData.data.enableFlag" />
</el-descriptions-item>
<el-descriptions-item :label="$t('common.remark')">
{{ reactiveData.data.remark || '-' }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.createTime')">
{{ reactiveData.data.createTime ? timestamp(reactiveData.data.createTime) : '-' }}
{{ timestampLabel(reactiveData.data.createTime) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('common.operationTime')">
{{ reactiveData.data.operateTime ? timestamp(reactiveData.data.operateTime) : '-' }}
{{ timestampLabel(reactiveData.data.operateTime) }}
</el-descriptions-item>
</el-descriptions>
</detail-card>
@@ -57,9 +55,7 @@
<el-table-column :label="$t('settings.role.roleCode')" min-width="180" prop="roleCode" />
<el-table-column :label="$t('common.enable')" width="90">
<template #default="{ row }">
<el-tag :type="Number(row.enableFlag) === 0 ? 'success' : 'info'">
{{ Number(row.enableFlag) === 0 ? $t('common.enable') : $t('common.disable') }}
</el-tag>
<enable-tag :value="row.enableFlag" />
</template>
</el-table-column>
<el-table-column :label="$t('common.remark')" min-width="220" prop="remark" show-overflow-tooltip />
@@ -94,10 +90,11 @@
import { getResourceListByUserId } from '@/api/roleResourceBind';
import { getRoleListByUserId } from '@/api/roleUserBind';
import { getUserById } from '@/api/user';
import { timestamp } from '@/utils/dateUtil';
import { timestampLabel } from '@/utils/dateUtil';
import blankCard from '@/components/card/blank/BlankCard.vue';
import detailCard from '@/components/card/detail/DetailCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
const route = useRoute();
const router = useRouter();
@@ -170,7 +167,3 @@
if (reactiveData.active === 'resource') loadResources();
});
</script>
<style lang="scss" scoped>
@use '@/styles/things-card.scss';
</style>
@@ -54,7 +54,7 @@
</el-form>
<div class="things-dialog-footer">
<el-button @click="reactiveData.visible = false">{{ t('common.cancel') }}</el-button>
<el-button plain type="success" @click="reset">{{ t('common.reset') }}</el-button>
<el-button plain @click="reset">{{ t('common.reset') }}</el-button>
<el-button :loading="reactiveData.submitting" type="primary" @click="submit">
{{ t('common.confirm') }}
</el-button>
@@ -63,7 +63,3 @@
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="scss" scoped>
@use '@/styles/things-dialog.scss';
</style>
+2
View File
@@ -30,11 +30,13 @@ import userTool from './tool/UserTool.vue';
import userEditForm from './edit/UserEditForm.vue';
import userAssignRoles from './assign/UserAssignRoles.vue';
import BlankCard from '@/components/card/blank/BlankCard.vue';
import EnableTag from '@/components/tag/EnableTag.vue';
export default defineComponent({
name: 'SettingsUser',
components: {
BlankCard,
EnableTag,
userTool,
userEditForm,
userAssignRoles,