From 8021c78cf22bc024c7f28f8e0c901f005c88b240 Mon Sep 17 00:00:00 2001 From: ygx Date: Sat, 28 Mar 2026 23:24:34 +0800 Subject: [PATCH] feat --- src/api/ops/suppression.ts | 136 +++++++ .../suppress/components/SuppressionModal.vue | 354 ++++++++++++++++++ .../pages/alert/suppress/config/columns.ts | 92 +++++ .../pages/alert/suppress/config/formItems.ts | 54 +++ .../ops/pages/alert/suppress/constants.ts | 59 +++ .../ops/pages/alert/suppress/hooks/index.ts | 2 + .../pages/alert/suppress/hooks/usePolicy.ts | 42 +++ .../pages/alert/suppress/hooks/useTable.ts | 193 ++++++++++ src/views/ops/pages/alert/suppress/index.vue | 208 ++++++++++ src/views/ops/pages/alert/suppress/types.ts | 44 +++ .../ops/pages/alert/suppress/utils/index.ts | 136 +++++++ .../ops/pages/assets/device/form/index.vue | 8 +- .../ops/pages/assets/supplier/form/index.vue | 17 +- 13 files changed, 1343 insertions(+), 2 deletions(-) create mode 100644 src/api/ops/suppression.ts create mode 100644 src/views/ops/pages/alert/suppress/components/SuppressionModal.vue create mode 100644 src/views/ops/pages/alert/suppress/config/columns.ts create mode 100644 src/views/ops/pages/alert/suppress/config/formItems.ts create mode 100644 src/views/ops/pages/alert/suppress/constants.ts create mode 100644 src/views/ops/pages/alert/suppress/hooks/index.ts create mode 100644 src/views/ops/pages/alert/suppress/hooks/usePolicy.ts create mode 100644 src/views/ops/pages/alert/suppress/hooks/useTable.ts create mode 100644 src/views/ops/pages/alert/suppress/index.vue create mode 100644 src/views/ops/pages/alert/suppress/types.ts create mode 100644 src/views/ops/pages/alert/suppress/utils/index.ts diff --git a/src/api/ops/suppression.ts b/src/api/ops/suppression.ts new file mode 100644 index 0000000..a7c489e --- /dev/null +++ b/src/api/ops/suppression.ts @@ -0,0 +1,136 @@ +import { request } from '@/api/request' + +// 抑制规则类型 +export type SuppressionType = 'dedup' | 'aggregate' | 'dependency' | 'throttle' | 'schedule' + +// 抑制规则接口定义 +export interface SuppressionRule { + id?: number + name: string + description?: string + type: SuppressionType + enabled?: boolean + priority?: number + policy_id?: number + // dedup 类型字段 + dedup_window?: number + dedup_keys?: string + // aggregate 类型字段 + aggregate_window?: number + group_by?: string + aggregate_count?: number + // dependency 类型字段 + source_matchers?: string + target_matchers?: string + // throttle 类型字段 + throttle_count?: number + throttle_window?: number + // schedule 类型字段 + schedule?: string + // 通用匹配条件 + matchers?: string + // 运行态统计 + hit_count?: number + suppressed_count?: number + last_hit_at?: string + created_at?: string + updated_at?: string +} + +// 抑制规则列表查询参数 +export interface SuppressionListParams { + page?: number + page_size?: number + keyword?: string + sort?: string + order?: string + type?: SuppressionType + policy_id?: number + enabled?: boolean +} + +// 创建抑制规则参数 +export interface SuppressionCreateParams { + name: string + type: SuppressionType + description?: string + enabled?: boolean + priority?: number + policy_id?: number + dedup_window?: number + dedup_keys?: string + aggregate_window?: number + group_by?: string + aggregate_count?: number + source_matchers?: string + target_matchers?: string + throttle_count?: number + throttle_window?: number + schedule?: string + matchers?: string +} + +// 更新抑制规则参数 +export interface SuppressionUpdateParams { + id: number + name?: string + type?: SuppressionType + description?: string + enabled?: boolean + priority?: number + policy_id?: number + dedup_window?: number + dedup_keys?: string + aggregate_window?: number + group_by?: string + aggregate_count?: number + source_matchers?: string + target_matchers?: string + throttle_count?: number + throttle_window?: number + schedule?: string + matchers?: string +} + +// 抑制类型选项 +export const SUPPRESSION_TYPE_OPTIONS = [ + { value: 'dedup', label: '去重' }, + { value: 'aggregate', label: '聚合' }, + { value: 'dependency', label: '依赖抑制' }, + { value: 'throttle', label: '限流' }, + { value: 'schedule', label: '定时屏蔽' }, +] + +// 抑制类型标签颜色 +export const SUPPRESSION_TYPE_COLORS: Record = { + dedup: 'arcoblue', + aggregate: 'green', + dependency: 'orangered', + throttle: 'orange', + schedule: 'purple', +} + +/** 获取抑制规则列表 */ +export const fetchSuppressionList = (params?: SuppressionListParams) => { + return request.get('/Alert/v1/suppression/list', { params }) +} + +/** 获取抑制规则详情 */ +export const fetchSuppressionDetail = (id: number) => { + return request.get(`/Alert/v1/suppression/get/${id}`) +} + +/** 创建抑制规则 */ +export const createSuppression = (data: SuppressionCreateParams) => { + return request.post('/Alert/v1/suppression/create', data) +} + +/** 更新抑制规则 */ +export const updateSuppression = (data: SuppressionUpdateParams) => { + return request.post('/Alert/v1/suppression/update', data) +} + +/** 删除抑制规则 */ +export const deleteSuppression = (id: number) => { + return request.delete(`/Alert/v1/suppression/delete/${id}`) +} \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/components/SuppressionModal.vue b/src/views/ops/pages/alert/suppress/components/SuppressionModal.vue new file mode 100644 index 0000000..81bb428 --- /dev/null +++ b/src/views/ops/pages/alert/suppress/components/SuppressionModal.vue @@ -0,0 +1,354 @@ + + + + + \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/config/columns.ts b/src/views/ops/pages/alert/suppress/config/columns.ts new file mode 100644 index 0000000..62bb2f3 --- /dev/null +++ b/src/views/ops/pages/alert/suppress/config/columns.ts @@ -0,0 +1,92 @@ +import type { TableColumnData } from '@arco-design/web-vue/es/table/interface' + +/** + * 表格列配置 + */ +export const getTableColumns = (): TableColumnData[] => [ + { + title: 'ID', + dataIndex: 'id', + width: 80, + }, + { + title: '规则名称', + dataIndex: 'name', + width: 180, + ellipsis: true, + tooltip: true, + }, + { + title: '描述', + dataIndex: 'description', + width: 200, + ellipsis: true, + tooltip: true, + }, + { + title: '抑制类型', + dataIndex: 'type', + width: 100, + slotName: 'type', + }, + { + title: '启用状态', + dataIndex: 'enabled', + width: 100, + slotName: 'enabled', + }, + { + title: '优先级', + dataIndex: 'priority', + width: 80, + }, + { + title: '作用范围', + dataIndex: 'policy_id', + width: 120, + slotName: 'policy', + }, + { + title: '关键配置', + dataIndex: 'config', + width: 200, + slotName: 'config', + ellipsis: true, + tooltip: true, + }, + { + title: '匹配条件', + dataIndex: 'matchers', + width: 100, + slotName: 'matchers', + }, + { + title: '命中次数', + dataIndex: 'hit_count', + width: 100, + }, + { + title: '已抑制', + dataIndex: 'suppressed_count', + width: 100, + }, + { + title: '最后命中', + dataIndex: 'last_hit_at', + width: 160, + slotName: 'last_hit_at', + }, + { + title: '创建时间', + dataIndex: 'created_at', + width: 160, + slotName: 'created_at', + }, + { + title: '操作', + dataIndex: 'operations', + width: 220, + slotName: 'operations', + fixed: 'right', + }, +] \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/config/formItems.ts b/src/views/ops/pages/alert/suppress/config/formItems.ts new file mode 100644 index 0000000..741af63 --- /dev/null +++ b/src/views/ops/pages/alert/suppress/config/formItems.ts @@ -0,0 +1,54 @@ +import { computed } from 'vue' +import type { FormItem } from '@/components/search-form/types' +import { SUPPRESSION_TYPE_OPTIONS } from '@/api/ops/suppression' +import type { PolicyOption } from '../types' + +/** + * 获取筛选表单项配置 + */ +export const useFormItems = (policyOptions: PolicyOption[]) => { + return computed(() => [ + { + field: 'keyword', + label: '关键字', + type: 'input', + span: 6, + placeholder: '搜索名称/描述', + }, + { + field: 'type', + label: '抑制类型', + type: 'select', + span: 6, + placeholder: '请选择', + options: [ + { value: '', label: '全部' }, + ...SUPPRESSION_TYPE_OPTIONS, + ], + }, + { + field: 'policy_id', + label: '关联策略', + type: 'select', + span: 6, + placeholder: '请选择', + options: [ + { value: '', label: '全部' }, + { value: '0', label: '仅全局规则' }, + ...policyOptions.map((p) => ({ value: String(p.id), label: p.name })), + ], + }, + { + field: 'enabled', + label: '启用状态', + type: 'select', + span: 6, + placeholder: '请选择', + options: [ + { value: '', label: '全部' }, + { value: 'true', label: '已启用' }, + { value: 'false', label: '已禁用' }, + ], + }, + ]) +} \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/constants.ts b/src/views/ops/pages/alert/suppress/constants.ts new file mode 100644 index 0000000..22ff21c --- /dev/null +++ b/src/views/ops/pages/alert/suppress/constants.ts @@ -0,0 +1,59 @@ +import type { SuppressionFormData } from './types' + +/** + * 页面标题 + */ +export const PAGE_TITLE = '告警抑制规则(降噪)管理' + +/** + * 默认表单数据 + */ +export const DEFAULT_FORM_DATA: SuppressionFormData = { + name: '', + type: 'dedup', + enabled: true, + priority: 100, + policy_id: 0, + description: '', + matchers: '{}', + dedup_window: 300, + dedup_keys: '', + aggregate_window: 300, + group_by: '', + aggregate_count: 5, + source_matchers: '{}', + target_matchers: '{}', + throttle_count: 1, + throttle_window: 300, + schedule: '{}', +} + +/** + * 表单校验规则 + */ +export const FORM_RULES = { + name: [{ required: true, message: '请输入规则名称' }], + type: [{ required: true, message: '请选择抑制类型' }], +} + +/** + * 默认分页配置 + */ +export const DEFAULT_PAGINATION = { + current: 1, + pageSize: 20, + total: 0, + showTotal: true, + showJumper: true, + showPageSize: true, +} + +/** + * 默认筛选表单 + */ +export const DEFAULT_SEARCH_FORM = { + keyword: '', + type: '', + policy_id: '', + enabled: '', +} \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/hooks/index.ts b/src/views/ops/pages/alert/suppress/hooks/index.ts new file mode 100644 index 0000000..9715857 --- /dev/null +++ b/src/views/ops/pages/alert/suppress/hooks/index.ts @@ -0,0 +1,2 @@ +export { usePolicy } from './usePolicy' +export { useTable } from './useTable' \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/hooks/usePolicy.ts b/src/views/ops/pages/alert/suppress/hooks/usePolicy.ts new file mode 100644 index 0000000..9693f9d --- /dev/null +++ b/src/views/ops/pages/alert/suppress/hooks/usePolicy.ts @@ -0,0 +1,42 @@ +import { ref } from 'vue' +import { Message } from '@arco-design/web-vue' +import { fetchPolicyList } from '@/api/ops/alertPolicy' +import type { PolicyOption } from '../types' + +/** + * 策略相关逻辑 + */ +export const usePolicy = () => { + const policyOptions = ref([]) + + /** + * 获取策略列表 + */ + const fetchPolicyOptions = async () => { + try { + const res = await fetchPolicyList({ page_size: 1000 }) + if (res.code === 0 && res.details) { + policyOptions.value = (res.details.data || []).map((p: any) => ({ + id: p.id, + name: p.name, + })) + } + } catch (error) { + console.error('获取策略列表失败:', error) + } + } + + /** + * 获取策略名称 + */ + const getPolicyName = (policyId: number) => { + const policy = policyOptions.value.find((p) => p.id === policyId) + return policy?.name || '' + } + + return { + policyOptions, + fetchPolicyOptions, + getPolicyName, + } +} \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/hooks/useTable.ts b/src/views/ops/pages/alert/suppress/hooks/useTable.ts new file mode 100644 index 0000000..31aa37c --- /dev/null +++ b/src/views/ops/pages/alert/suppress/hooks/useTable.ts @@ -0,0 +1,193 @@ +import { ref, reactive } from 'vue' +import { Message, Modal } from '@arco-design/web-vue' +import { + fetchSuppressionList, + updateSuppression, + deleteSuppression, + type SuppressionRule, +} from '@/api/ops/suppression' +import type { SearchFormModel, PaginationConfig, SwitchLoadingMap } from '../types' +import { DEFAULT_PAGINATION, DEFAULT_SEARCH_FORM } from '../constants' + +/** + * 表格相关逻辑 + */ +export const useTable = () => { + const loading = ref(false) + const tableData = ref([]) + const switchLoadingMap = ref({}) + const formModel = ref({ ...DEFAULT_SEARCH_FORM }) + const pagination = reactive({ ...DEFAULT_PAGINATION }) + + /** + * 获取抑制规则列表 + */ + const fetchList = async () => { + loading.value = true + try { + const params: any = { + page: pagination.current, + page_size: pagination.pageSize, + } + + if (formModel.value.keyword) { + params.keyword = formModel.value.keyword + } + + if (formModel.value.type) { + params.type = formModel.value.type + } + + if (formModel.value.policy_id) { + params.policy_id = Number(formModel.value.policy_id) + } + + if (formModel.value.enabled) { + params.enabled = formModel.value.enabled === 'true' + } + + const res = await fetchSuppressionList(params) + if (res.code === 0 && res.details) { + tableData.value = (res.details.data || []).map((item: any) => ({ + ...item, + switchLoading: false, + })) + pagination.total = res.details.total || 0 + } else { + Message.error(res.message || '获取抑制规则列表失败') + } + } catch (error: any) { + console.error('获取抑制规则列表失败:', error) + Message.error(error.message || '获取抑制规则列表失败') + } finally { + loading.value = false + } + } + + /** + * 处理表单模型更新 + */ + const handleFormModelUpdate = (value: Record) => { + formModel.value = value as SearchFormModel + } + + /** + * 搜索 + */ + const handleSearch = () => { + pagination.current = 1 + fetchList() + } + + /** + * 重置 + */ + const handleReset = () => { + formModel.value = { ...DEFAULT_SEARCH_FORM } + pagination.current = 1 + fetchList() + } + + /** + * 刷新 + */ + const handleRefresh = () => { + fetchList() + } + + /** + * 分页变化 + */ + const handlePageChange = (current: number) => { + pagination.current = current + fetchList() + } + + /** + * 每页条数变化 + */ + const handlePageSizeChange = (pageSize: number) => { + pagination.pageSize = pageSize + pagination.current = 1 + fetchList() + } + + /** + * 切换启用状态 + */ + const handleToggleEnabled = async (record: SuppressionRule) => { + if (!record.id) return + switchLoadingMap.value[record.id] = true + try { + const res = await updateSuppression({ + id: record.id, + enabled: record.enabled, + }) + if (res.code === 0) { + Message.success(record.enabled ? '已启用' : '已禁用') + } else { + // 恢复原状态 + record.enabled = !record.enabled + Message.error(res.message || '操作失败') + } + } catch (error: any) { + record.enabled = !record.enabled + console.error('切换状态失败:', error) + Message.error(error.message || '操作失败') + } finally { + switchLoadingMap.value[record.id] = false + } + } + + /** + * 删除抑制规则 + */ + const handleDelete = (record: SuppressionRule) => { + Modal.confirm({ + title: '确认删除', + content: `确定要删除抑制规则「${record.name}」吗?删除后将影响告警降噪行为,请确认是否被模板引用。`, + okText: '删除', + cancelText: '取消', + okButtonProps: { status: 'danger' }, + onOk: async () => { + try { + const res = await deleteSuppression(record.id!) + if (res.code === 0 || res.data === '删除成功') { + Message.success('删除成功') + fetchList() + } else { + Message.error(res.message || '删除失败') + } + } catch (error: any) { + console.error('删除失败:', error) + Message.error(error.message || '删除失败') + } + }, + }) + } + + /** + * 跳转到策略详情 + */ + const handleGoToPolicy = (policyId: number) => { + Message.info(`跳转到策略 #${policyId} 详情页`) + } + + return { + loading, + tableData, + switchLoadingMap, + formModel, + pagination, + fetchList, + handleFormModelUpdate, + handleSearch, + handleReset, + handleRefresh, + handlePageChange, + handlePageSizeChange, + handleToggleEnabled, + handleDelete, + handleGoToPolicy, + } +} \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/index.vue b/src/views/ops/pages/alert/suppress/index.vue new file mode 100644 index 0000000..e38c8da --- /dev/null +++ b/src/views/ops/pages/alert/suppress/index.vue @@ -0,0 +1,208 @@ + + + + + + + diff --git a/src/views/ops/pages/alert/suppress/types.ts b/src/views/ops/pages/alert/suppress/types.ts new file mode 100644 index 0000000..ad9eb22 --- /dev/null +++ b/src/views/ops/pages/alert/suppress/types.ts @@ -0,0 +1,44 @@ +import type { SuppressionRule, SuppressionType } from '@/api/ops/suppression' + +/** + * 筛选表单模型 + */ +export interface SearchFormModel { + keyword: string + type: string + policy_id: string + enabled: string +} + +/** + * 策略选项 + */ +export interface PolicyOption { + id: number + name: string +} + +/** + * 分页配置 + */ +export interface PaginationConfig { + current: number + pageSize: number + total: number + showTotal: boolean + showJumper: boolean + showPageSize: boolean +} + +/** + * 表单数据(创建/编辑) + */ +export type SuppressionFormData = SuppressionRule + +/** + * 开关加载状态映射 + */ +export type SwitchLoadingMap = Record + +// 重新导出 API 类型 +export type { SuppressionRule, SuppressionType } \ No newline at end of file diff --git a/src/views/ops/pages/alert/suppress/utils/index.ts b/src/views/ops/pages/alert/suppress/utils/index.ts new file mode 100644 index 0000000..c77f0cd --- /dev/null +++ b/src/views/ops/pages/alert/suppress/utils/index.ts @@ -0,0 +1,136 @@ +import { + SUPPRESSION_TYPE_OPTIONS, + SUPPRESSION_TYPE_COLORS, + type SuppressionType, + type SuppressionRule, + type SuppressionUpdateParams, + type SuppressionCreateParams, +} from '@/api/ops/suppression' + +/** + * 获取抑制类型标签 + */ +export const getTypeLabel = (type: SuppressionType) => { + const option = SUPPRESSION_TYPE_OPTIONS.find((o) => o.value === type) + return option?.label || type +} + +/** + * 获取抑制类型颜色 + */ +export const getTypeColor = (type: SuppressionType) => { + return SUPPRESSION_TYPE_COLORS[type] || 'gray' +} + +/** + * 获取关键配置摘要 + */ +export const getConfigSummary = (record: SuppressionRule) => { + switch (record.type) { + case 'dedup': + return `窗口 ${record.dedup_window || 0}s · 键 ${record.dedup_keys || '-'}` + case 'aggregate': + return `窗口 ${record.aggregate_window || 0}s · 分组 ${record.group_by || '-'} · 阈值 ${record.aggregate_count || 0}` + case 'throttle': + return `${record.throttle_count || 0} 次 / ${record.throttle_window || 0}s` + case 'schedule': + return '已配置时间窗' + case 'dependency': + return '源/目标匹配器已配置' + default: + return '-' + } +} + +/** + * 格式化日期时间 + */ +export const formatDateTime = (dateStr?: string) => { + if (!dateStr) return '-' + try { + const date = new Date(dateStr) + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }) + } catch { + return dateStr + } +} + +/** + * 校验 JSON 字段 + * @returns 错误信息,如果校验通过则返回 null + */ +export const validateJsonFields = (formData: SuppressionRule, fields: string[]): string | null => { + for (const field of fields) { + const value = formData[field as keyof SuppressionRule] + if (value && typeof value === 'string' && value !== '{}' && value.trim() !== '') { + try { + JSON.parse(value) + } catch { + return `${field} 字段 JSON 格式不正确` + } + } + } + return null +} + +/** + * 构建更新数据(只传非空字段) + */ +export const buildUpdateData = (formData: SuppressionRule): SuppressionUpdateParams => { + const updateData: SuppressionUpdateParams = { id: formData.id! } + + if (formData.name) updateData.name = formData.name + if (formData.type) updateData.type = formData.type + if (formData.description) updateData.description = formData.description + updateData.enabled = formData.enabled + if (formData.priority && formData.priority > 0) updateData.priority = formData.priority + if (formData.policy_id && formData.policy_id > 0) updateData.policy_id = formData.policy_id + if (formData.dedup_window && formData.dedup_window > 0) updateData.dedup_window = formData.dedup_window + if (formData.dedup_keys) updateData.dedup_keys = formData.dedup_keys + if (formData.aggregate_window && formData.aggregate_window > 0) updateData.aggregate_window = formData.aggregate_window + if (formData.group_by) updateData.group_by = formData.group_by + if (formData.aggregate_count && formData.aggregate_count > 0) updateData.aggregate_count = formData.aggregate_count + if (formData.source_matchers) updateData.source_matchers = formData.source_matchers + if (formData.target_matchers) updateData.target_matchers = formData.target_matchers + if (formData.throttle_count && formData.throttle_count > 0) updateData.throttle_count = formData.throttle_count + if (formData.throttle_window && formData.throttle_window > 0) updateData.throttle_window = formData.throttle_window + if (formData.schedule) updateData.schedule = formData.schedule + if (formData.matchers) updateData.matchers = formData.matchers + + return updateData +} + +/** + * 构建创建数据 + */ +export const buildCreateData = (formData: SuppressionRule): SuppressionCreateParams => { + const createData: SuppressionCreateParams = { + name: formData.name, + type: formData.type, + enabled: formData.enabled, + } + + if (formData.description) createData.description = formData.description + if (formData.priority) createData.priority = formData.priority + if (formData.policy_id !== undefined) createData.policy_id = formData.policy_id + if (formData.dedup_window) createData.dedup_window = formData.dedup_window + if (formData.dedup_keys) createData.dedup_keys = formData.dedup_keys + if (formData.aggregate_window) createData.aggregate_window = formData.aggregate_window + if (formData.group_by) createData.group_by = formData.group_by + if (formData.aggregate_count) createData.aggregate_count = formData.aggregate_count + if (formData.source_matchers) createData.source_matchers = formData.source_matchers + if (formData.target_matchers) createData.target_matchers = formData.target_matchers + if (formData.throttle_count) createData.throttle_count = formData.throttle_count + if (formData.throttle_window) createData.throttle_window = formData.throttle_window + if (formData.schedule) createData.schedule = formData.schedule + if (formData.matchers) createData.matchers = formData.matchers + + return createData +} \ No newline at end of file diff --git a/src/views/ops/pages/assets/device/form/index.vue b/src/views/ops/pages/assets/device/form/index.vue index dae5ca4..614abbf 100644 --- a/src/views/ops/pages/assets/device/form/index.vue +++ b/src/views/ops/pages/assets/device/form/index.vue @@ -23,7 +23,7 @@
- + @@ -450,6 +450,12 @@ const form = ref({ remarks: '', }) +// 表单验证规则 +const rules = { + asset_name: [{ required: true, message: '请输入资产名称' }], + asset_code: [{ required: true, message: '请输入资产编号' }], +} + // 提取列表数据的辅助函数 const extractList = (res: any): any[] => { const candidate = diff --git a/src/views/ops/pages/assets/supplier/form/index.vue b/src/views/ops/pages/assets/supplier/form/index.vue index 7c95511..bd201fd 100644 --- a/src/views/ops/pages/assets/supplier/form/index.vue +++ b/src/views/ops/pages/assets/supplier/form/index.vue @@ -23,7 +23,7 @@
- + @@ -351,6 +351,21 @@ const form = ref({ status: 'active', }) +// 表单验证规则 +const rules = { + name: [{ required: true, message: '请输入供应商名称' }], + code: [{ required: true, message: '请输入供应商编码' }], + supplier_type: [{ required: true, message: '请选择供应商类型' }], + status: [{ required: true, message: '请选择状态' }], + contact_person: [{ required: true, message: '请输入联系人' }], + contact_phone: [{ required: true, message: '请输入联系电话' }], + contact_mobile: [{ required: true, message: '请输入备用联系电话' }], + contact_email: [ + { required: true, message: '请输入联系邮箱' }, + { type: 'email', message: '请输入正确的邮箱格式' }, + ], +} + // 初始化页面 const initPage = async () => { const id = route.query.id