refactor(platform): remove deprecated reporting and audit modules
This commit is contained in:
@@ -7,19 +7,16 @@ const resourcesSource = readFileSync('src/api/resources.ts', 'utf8');
|
||||
const legacySafetyPrefix = ['sa', 'f_'].join('');
|
||||
const legacyAuditPrefix = ['au', 'd_'].join('');
|
||||
|
||||
test('资源定义使用 safe 和 audit 前缀', () => {
|
||||
test('资源定义使用 safe 前缀且不保留已删除审计资源', () => {
|
||||
assert.match(resourcesSource, /define\(\s*'safe_rule',\s*'\/safety\/safe_rule'/);
|
||||
assert.match(resourcesSource, /define\(\s*'safe_event',\s*'\/safety\/safe_event'/);
|
||||
assert.match(resourcesSource, /define\(\s*'safe_inspection',\s*'\/safety\/safe_inspection'/);
|
||||
assert.match(resourcesSource, /define\(\s*'safe_event_disposal',\s*'\/safety\/safe_event\/:identity\/disposals'/);
|
||||
assert.match(resourcesSource, /define\(\s*'audit_operation_log',\s*'\/audit\/audit_operation_log'/);
|
||||
assert.match(resourcesSource, /define\(\s*'audit_export_log',\s*'\/audit\/audit_export_log'/);
|
||||
assert.match(resourcesSource, /define\(\s*'audit_approval',\s*'\/audit\/audit_approval'/);
|
||||
|
||||
assert.doesNotMatch(resourcesSource, new RegExp(`define\\('${legacySafetyPrefix}(?:rule|event|inspection|event_disposal)',`));
|
||||
assert.doesNotMatch(resourcesSource, new RegExp(`action\\('${legacySafetyPrefix}event_disposal',`));
|
||||
assert.doesNotMatch(resourcesSource, new RegExp(`define\\('${legacyAuditPrefix}(?:operation_log|export_log|approval)',`));
|
||||
assert.doesNotMatch(resourcesSource, new RegExp(`/audit/${legacyAuditPrefix}approval/:identity/approve`));
|
||||
assert.doesNotMatch(resourcesSource, /audit_(?:operation_log|export_log|approval)/);
|
||||
});
|
||||
|
||||
test('只读页面将状态变更视为违规写操作', () => {
|
||||
|
||||
@@ -28,13 +28,13 @@ function loadResourceForm() {
|
||||
return resourceModule.exports;
|
||||
}
|
||||
|
||||
test('资源字段声明保留数字、布尔、时间与 JSON 类型', () => {
|
||||
test('资源字段声明保留数字、布尔、时间与文本类型', () => {
|
||||
const resources = loadResources();
|
||||
const field = (resource, key) => resources.find((item) => item.name === resource).fields.find((item) => item.key === key);
|
||||
assert.equal(field('ec_product', 'price_amount').type, 'number');
|
||||
assert.equal(field('ec_product_image', 'is_cover').type, 'boolean');
|
||||
assert.equal(field('delivery_track', 'started_at').type, 'datetime');
|
||||
assert.equal(field('dev_telemetry', 'payload').type, 'json');
|
||||
assert.equal(field('dev_telemetry', 'payload').type, 'textarea');
|
||||
});
|
||||
|
||||
test('表单载荷构造器省略空的可选关系并转换字段类型', async () => {
|
||||
@@ -56,7 +56,7 @@ test('表单载荷构造器省略空的可选关系并转换字段类型', async
|
||||
);
|
||||
});
|
||||
|
||||
test('安全规则与订单明细将 JSON 字段作为有效 JSON 字符串提交', () => {
|
||||
test('安全规则与订单明细的文本字段允许任意文本提交', () => {
|
||||
const resources = loadResources();
|
||||
const { buildResourcePayload } = loadResourceForm();
|
||||
const fields = (name) => resources.find((item) => item.name === name).fields;
|
||||
@@ -64,29 +64,29 @@ test('安全规则与订单明细将 JSON 字段作为有效 JSON 字符串提
|
||||
assert.deepEqual(
|
||||
JSON.parse(JSON.stringify(buildResourcePayload(fields('safe_rule'), {
|
||||
rule_code: 'pressure-limit',
|
||||
threshold: '{"max":10}',
|
||||
threshold: '{invalid}',
|
||||
action: 'close-valve',
|
||||
gray_scope: '["north"]',
|
||||
gray_scope: 'north region only',
|
||||
}))),
|
||||
{
|
||||
rule_code: 'pressure-limit',
|
||||
threshold: '{"max":10}',
|
||||
threshold: '{invalid}',
|
||||
action: 'close-valve',
|
||||
gray_scope: '["north"]',
|
||||
gray_scope: 'north region only',
|
||||
},
|
||||
);
|
||||
assert.deepEqual(
|
||||
JSON.parse(JSON.stringify(buildResourcePayload(fields('ec_order_item'), {
|
||||
ec_order_identity: 'order-a',
|
||||
ec_product_identity: 'product-a',
|
||||
product_snapshot: '{"name":"液化气"}',
|
||||
product_snapshot: 'arbitrary product snapshot text',
|
||||
quantity: 2,
|
||||
sale_amount: 500,
|
||||
}))),
|
||||
{
|
||||
ec_order_identity: 'order-a',
|
||||
ec_product_identity: 'product-a',
|
||||
product_snapshot: '{"name":"液化气"}',
|
||||
product_snapshot: 'arbitrary product snapshot text',
|
||||
quantity: 2,
|
||||
sale_amount: 500,
|
||||
},
|
||||
@@ -131,11 +131,11 @@ test('日期按 RFC3339 提交,密码只在创建时必填并提交', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('审批只读页提供同意和驳回操作', () => {
|
||||
test('已删除的审批模块不再保留前端资源或页面', () => {
|
||||
const source = fs.readFileSync(fromProjectRoot('src/views/shared/ReadOnlyListPage.vue'), 'utf8');
|
||||
const resources = fs.readFileSync(fromProjectRoot('src/api/resources.ts'), 'utf8');
|
||||
assert.match(resources, /audit_approval[\s\S]*\/audit\/audit_approval\/:identity\/approve/);
|
||||
assert.match(source, /submitDetailAction/);
|
||||
assert.doesNotMatch(resources, /audit_approval|audit_operation_log|audit_export_log/);
|
||||
assert.equal(fs.existsSync(fromProjectRoot('src/views/audit')), false);
|
||||
});
|
||||
|
||||
test('树页面通过资源归档接口归档节点', () => {
|
||||
|
||||
@@ -43,12 +43,6 @@ export function buildResourcePayload(
|
||||
case 'boolean':
|
||||
payload[field.key] = value === true || value === 'true';
|
||||
break;
|
||||
case 'json':
|
||||
if (typeof value !== 'string')
|
||||
throw new Error(`${field.label}必须是有效 JSON`);
|
||||
JSON.parse(value);
|
||||
payload[field.key] = value;
|
||||
break;
|
||||
case 'date':
|
||||
case 'datetime': {
|
||||
const date = new Date(String(value));
|
||||
|
||||
@@ -10,7 +10,6 @@ export type ResourceFieldType =
|
||||
| 'boolean'
|
||||
| 'date'
|
||||
| 'datetime'
|
||||
| 'json'
|
||||
| 'textarea';
|
||||
export type ResourceField = {
|
||||
key: string;
|
||||
@@ -175,14 +174,17 @@ const datetimeFields = new Set([
|
||||
'handled_at',
|
||||
'created_at',
|
||||
]);
|
||||
const jsonFields = new Set([
|
||||
// 大文本字段按普通文本处理,不再要求或解析 JSON。
|
||||
const textareaFields = new Set([
|
||||
'body',
|
||||
'content',
|
||||
'reason',
|
||||
'opinion',
|
||||
'payload',
|
||||
'threshold',
|
||||
'gray_scope',
|
||||
'product_snapshot',
|
||||
'field_scope',
|
||||
]);
|
||||
const textareaFields = new Set(['body', 'content', 'reason', 'opinion']);
|
||||
const fieldType = (key: string): ResourceFieldType => {
|
||||
if (key === 'platform_role_code') return 'role-code';
|
||||
if (key === 'menu_identities') return 'menu-identities';
|
||||
@@ -192,7 +194,6 @@ const fieldType = (key: string): ResourceFieldType => {
|
||||
if (booleanFields.has(key)) return 'boolean';
|
||||
if (dateFields.has(key)) return 'date';
|
||||
if (datetimeFields.has(key)) return 'datetime';
|
||||
if (jsonFields.has(key)) return 'json';
|
||||
if (textareaFields.has(key)) return 'textarea';
|
||||
return 'text';
|
||||
};
|
||||
@@ -237,8 +238,7 @@ const titles: Record<string, string> = {
|
||||
fin_payment: '支付记录',
|
||||
fin_settlement: '财务结算',
|
||||
fin_reconciliation: '财务对账',
|
||||
cnt_content: '内容管理',
|
||||
ntf_template: '通知模板',
|
||||
cms_content: '内容管理',
|
||||
cs_ticket: '客服工单',
|
||||
platfrom_account: '平台账户',
|
||||
platform_role: '平台角色',
|
||||
@@ -247,12 +247,6 @@ const titles: Record<string, string> = {
|
||||
wallet_ledger: '钱包流水',
|
||||
wallet_recharge: '钱包充值',
|
||||
wallet_withdrawal: '钱包提现',
|
||||
report: '报表',
|
||||
report_item: '报表项目',
|
||||
report_metric_snapshot: '指标快照',
|
||||
audit_operation_log: '操作审计',
|
||||
audit_export_log: '导出审计',
|
||||
audit_approval: '审批审计',
|
||||
};
|
||||
const define = (
|
||||
name: string,
|
||||
@@ -523,18 +517,13 @@ export const resources: ResourceUiDefinition[] = [
|
||||
'list',
|
||||
['channel!', 'bill_date!', 'difference_amount!'],
|
||||
),
|
||||
define('cnt_content', '/content/cnt_content', 'writable', 'list', [
|
||||
define('cms_content', '/content/cms_content', 'writable', 'list', [
|
||||
'content_type!',
|
||||
'title!',
|
||||
'body!',
|
||||
'version_no',
|
||||
'publish_status',
|
||||
]),
|
||||
define('ntf_template', '/notification/ntf_template', 'writable', 'list', [
|
||||
'template_code!',
|
||||
'channel!',
|
||||
'content!',
|
||||
]),
|
||||
define('cs_ticket', '/customer_service/cs_ticket', 'writable', 'list', [
|
||||
'user_account_identity!',
|
||||
'ticket_no!',
|
||||
@@ -592,62 +581,6 @@ export const resources: ResourceUiDefinition[] = [
|
||||
'amount',
|
||||
'status',
|
||||
]),
|
||||
define('report', '/report/report', 'readonly', 'list', [
|
||||
'report_code',
|
||||
'report_type',
|
||||
'stat_period',
|
||||
'generated_at',
|
||||
'status',
|
||||
]),
|
||||
define('report_item', '/report/report_item', 'readonly', 'list', [
|
||||
'report_identity',
|
||||
'dimension',
|
||||
'metric_code',
|
||||
'metric_value',
|
||||
]),
|
||||
define(
|
||||
'report_metric_snapshot',
|
||||
'/report/report_metric_snapshot',
|
||||
'readonly',
|
||||
'list',
|
||||
['metric_code', 'scope_type', 'stat_at', 'metric_value'],
|
||||
),
|
||||
define('audit_operation_log', '/audit/audit_operation_log', 'readonly', 'list', [
|
||||
'operator_identity',
|
||||
'action',
|
||||
'object_identity',
|
||||
'created_at',
|
||||
]),
|
||||
define('audit_export_log', '/audit/audit_export_log', 'readonly', 'list', [
|
||||
'applicant_identity',
|
||||
'purpose',
|
||||
'field_scope',
|
||||
'approved_at',
|
||||
'file_uri',
|
||||
]),
|
||||
define(
|
||||
'audit_approval',
|
||||
'/audit/audit_approval',
|
||||
'readonly',
|
||||
'list',
|
||||
[
|
||||
'business_type',
|
||||
'business_identity',
|
||||
'applicant_identity',
|
||||
'opinion',
|
||||
'handler_identity',
|
||||
'status',
|
||||
'handled_at',
|
||||
],
|
||||
[
|
||||
action('同意', '/audit/audit_approval/:identity/approve', ['opinion'], {
|
||||
status: 'approved',
|
||||
}),
|
||||
action('驳回', '/audit/audit_approval/:identity/approve', ['opinion!'], {
|
||||
status: 'rejected',
|
||||
}),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
export const resourceByName = Object.fromEntries(
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
const routes: AppRouteRecordRaw[] = [{
|
||||
path: '/audit', name: 'audit', component: DEFAULT_LAYOUT,
|
||||
meta: { locale: 'menu.platform.audit', requiresAuth: true, icon: 'icon-apps', order: 22 },
|
||||
children: [
|
||||
{ path: 'audit-operation-log', name: 'audit-audit-operation-log', component: () => import('@/views/audit/audit_operation_log/ListPage.vue'), meta: { locale: 'menu.platform.audit.audit_operation_log', requiresAuth: true, menuCode: 'audit' } },
|
||||
{ path: 'audit-export-log', name: 'audit-audit-export-log', component: () => import('@/views/audit/audit_export_log/ListPage.vue'), meta: { locale: 'menu.platform.audit.audit_export_log', requiresAuth: true, menuCode: 'audit' } },
|
||||
{ path: 'audit-approval', name: 'audit-audit-approval', component: () => import('@/views/audit/audit_approval/ListPage.vue'), meta: { locale: 'menu.platform.audit.audit_approval', requiresAuth: true, menuCode: 'audit' } }
|
||||
],
|
||||
}];
|
||||
export default routes;
|
||||
@@ -4,7 +4,7 @@ const routes: AppRouteRecordRaw[] = [{
|
||||
path: '/content', name: 'content', component: DEFAULT_LAYOUT,
|
||||
meta: { locale: 'menu.platform.content', requiresAuth: true, icon: 'icon-apps', order: 17 },
|
||||
children: [
|
||||
{ path: 'cnt-content', name: 'content-cnt-content', component: () => import('@/views/content/cnt_content/ListPage.vue'), meta: { locale: 'menu.platform.content.cnt_content', requiresAuth: true, menuCode: 'content' } }
|
||||
{ path: 'cms-content', name: 'content-cms-content', component: () => import('@/views/content/cms_content/ListPage.vue'), meta: { locale: 'menu.platform.content.cms_content', requiresAuth: true, menuCode: 'content' } }
|
||||
],
|
||||
}];
|
||||
export default routes;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
const routes: AppRouteRecordRaw[] = [{
|
||||
path: '/notification', name: 'notification', component: DEFAULT_LAYOUT,
|
||||
meta: { locale: 'menu.platform.notification', requiresAuth: true, icon: 'icon-apps', order: 18 },
|
||||
children: [
|
||||
{ path: 'ntf-template', name: 'notification-ntf-template', component: () => import('@/views/notification/ntf_template/ListPage.vue'), meta: { locale: 'menu.platform.notification.ntf_template', requiresAuth: true, menuCode: 'notification' } }
|
||||
],
|
||||
}];
|
||||
export default routes;
|
||||
@@ -9,11 +9,8 @@ import safetyRoutes from './safety';
|
||||
import ecRoutes from './ec';
|
||||
import financeRoutes from './finance';
|
||||
import contentRoutes from './content';
|
||||
import notificationRoutes from './notification';
|
||||
import customerServiceRoutes from './customer_service';
|
||||
import walletRoutes from './wallet';
|
||||
import reportRoutes from './report';
|
||||
import auditRoutes from './audit';
|
||||
const platformRoutes: AppRouteRecordRaw[] = [
|
||||
{ path: '/dashboard', name: 'dashboard', component: DEFAULT_LAYOUT, meta: { locale: 'menu.dashboard', requiresAuth: true, icon: 'icon-dashboard', order: 0 }, children: [{ path: 'overview', name: 'DashboardOverview', component: () => import('@/views/dashboard/DashboardPage.vue'), meta: { locale: 'menu.platform.dashboard', requiresAuth: true, menuCode: 'dashboard' } }] },
|
||||
{ path: '/platform', name: 'platform', component: DEFAULT_LAYOUT, meta: { locale: 'menu.platform.config', requiresAuth: true, icon: 'icon-settings', order: 30 }, children: [
|
||||
@@ -22,4 +19,4 @@ const platformRoutes: AppRouteRecordRaw[] = [
|
||||
{ path: 'platform-menu', name: 'platform-platform-menu', component: () => import('@/views/platform/platform_menu/TreePage.vue'), meta: { locale: 'menu.platform.platform.platform_menu', requiresAuth: true, menuCode: 'platform' } },
|
||||
] },
|
||||
];
|
||||
export default [...platformRoutes, ...gasRoutes, ...deliveryRoutes, ...staffRoutes, ...userRoutes, ...deviceRoutes, ...safetyRoutes, ...ecRoutes, ...financeRoutes, ...contentRoutes, ...notificationRoutes, ...customerServiceRoutes, ...walletRoutes, ...reportRoutes, ...auditRoutes];
|
||||
export default [...platformRoutes, ...gasRoutes, ...deliveryRoutes, ...staffRoutes, ...userRoutes, ...deviceRoutes, ...safetyRoutes, ...ecRoutes, ...financeRoutes, ...contentRoutes, ...customerServiceRoutes, ...walletRoutes];
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { DEFAULT_LAYOUT } from '../base';
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
const routes: AppRouteRecordRaw[] = [{
|
||||
path: '/report', name: 'report', component: DEFAULT_LAYOUT,
|
||||
meta: { locale: 'menu.platform.report', requiresAuth: true, icon: 'icon-apps', order: 21 },
|
||||
children: [
|
||||
{ path: 'report', name: 'report-report', component: () => import('@/views/report/report/ListPage.vue'), meta: { locale: 'menu.platform.report.report', requiresAuth: true, menuCode: 'report' } },
|
||||
{ path: 'report-item', name: 'report-report-item', component: () => import('@/views/report/report_item/ListPage.vue'), meta: { locale: 'menu.platform.report.report_item', requiresAuth: true, menuCode: 'report' } },
|
||||
{ path: 'report-metric-snapshot', name: 'report-report-metric-snapshot', component: () => import('@/views/report/report_metric_snapshot/ListPage.vue'), meta: { locale: 'menu.platform.report.report_metric_snapshot', requiresAuth: true, menuCode: 'report' } }
|
||||
],
|
||||
}];
|
||||
export default routes;
|
||||
@@ -1,6 +0,0 @@
|
||||
<template><ReadOnlyListPage :definition="definition" /></template>
|
||||
<script setup lang="ts">
|
||||
import ReadOnlyListPage from '@/views/shared/ReadOnlyListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/audit/audit_approval');
|
||||
</script>
|
||||
@@ -1,6 +0,0 @@
|
||||
<template><ReadOnlyListPage :definition="definition" /></template>
|
||||
<script setup lang="ts">
|
||||
import ReadOnlyListPage from '@/views/shared/ReadOnlyListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/audit/audit_export_log');
|
||||
</script>
|
||||
@@ -1,6 +0,0 @@
|
||||
<template><ReadOnlyListPage :definition="definition" /></template>
|
||||
<script setup lang="ts">
|
||||
import ReadOnlyListPage from '@/views/shared/ReadOnlyListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/audit/audit_operation_log');
|
||||
</script>
|
||||
@@ -2,5 +2,5 @@
|
||||
<script setup lang="ts">
|
||||
import CrudListPage from '@/views/shared/CrudListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/content/cnt_content');
|
||||
const definition = getResource('/content/cms_content');
|
||||
</script>
|
||||
@@ -1,6 +0,0 @@
|
||||
<template><CrudListPage :definition="definition" /></template>
|
||||
<script setup lang="ts">
|
||||
import CrudListPage from '@/views/shared/CrudListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/notification/ntf_template');
|
||||
</script>
|
||||
@@ -1,6 +0,0 @@
|
||||
<template><ReadOnlyListPage :definition="definition" /></template>
|
||||
<script setup lang="ts">
|
||||
import ReadOnlyListPage from '@/views/shared/ReadOnlyListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/report/report');
|
||||
</script>
|
||||
@@ -1,6 +0,0 @@
|
||||
<template><ReadOnlyListPage :definition="definition" /></template>
|
||||
<script setup lang="ts">
|
||||
import ReadOnlyListPage from '@/views/shared/ReadOnlyListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/report/report_item');
|
||||
</script>
|
||||
@@ -1,6 +0,0 @@
|
||||
<template><ReadOnlyListPage :definition="definition" /></template>
|
||||
<script setup lang="ts">
|
||||
import ReadOnlyListPage from '@/views/shared/ReadOnlyListPage.vue';
|
||||
import { getResource } from '@/api/resources';
|
||||
const definition = getResource('/report/report_metric_snapshot');
|
||||
</script>
|
||||
@@ -37,7 +37,7 @@
|
||||
<a-input-number v-else-if="field.type === 'number'" v-model="form[field.key]" />
|
||||
<a-date-picker v-else-if="field.type === 'date'" v-model="form[field.key]" value-format="YYYY-MM-DD" />
|
||||
<a-date-picker v-else-if="field.type === 'datetime'" v-model="form[field.key]" show-time value-format="YYYY-MM-DDTHH:mm:ssZ" />
|
||||
<a-textarea v-else-if="field.type === 'json' || field.type === 'textarea'" v-model="form[field.key]" :auto-size="{ minRows: 3, maxRows: 8 }" />
|
||||
<a-textarea v-else-if="field.type === 'textarea'" v-model="form[field.key]" :auto-size="{ minRows: 3, maxRows: 8 }" />
|
||||
<a-input-password v-else-if="field.type === 'password'" v-model="form[field.key]" />
|
||||
<a-select v-else-if="field.type === 'role-code'" v-model="form[field.key]">
|
||||
<a-option v-for="role in roleOptions" :key="role.role_code" :value="role.role_code">{{ role.name }}</a-option>
|
||||
@@ -61,7 +61,7 @@
|
||||
<a-form-item v-for="field in activeAction?.fields" :key="field.key" :label="field.label" :required="field.required">
|
||||
<a-input-number v-if="field.type === 'number'" v-model="actionForm[field.key]" />
|
||||
<a-switch v-else-if="field.type === 'boolean'" v-model="actionForm[field.key]" />
|
||||
<a-textarea v-else-if="field.type === 'json' || field.type === 'textarea'" v-model="actionForm[field.key]" />
|
||||
<a-textarea v-else-if="field.type === 'textarea'" v-model="actionForm[field.key]" />
|
||||
<a-select v-else-if="field.type === 'menu-identities'" v-model="actionForm[field.key]" multiple allow-search>
|
||||
<a-option v-for="menu in menuOptions" :key="menu.identity" :value="menu.identity">{{ menu.name }}</a-option>
|
||||
</a-select>
|
||||
@@ -124,12 +124,7 @@ const detailEntries = computed(() =>
|
||||
function resetForm(data?: Row) {
|
||||
for (const field of props.definition.fields) {
|
||||
const value = data?.[field.key];
|
||||
form[field.key] =
|
||||
field.type === 'json' && value != null && typeof value !== 'string'
|
||||
? JSON.stringify(value, null, 2)
|
||||
: value == null
|
||||
? undefined
|
||||
: value;
|
||||
form[field.key] = value == null ? undefined : value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<a-modal :visible="actionVisible" :title="activeAction?.name" @cancel="actionVisible = false" @ok="submitDetailAction">
|
||||
<a-form :model="actionForm" layout="vertical">
|
||||
<a-form-item v-for="field in activeAction?.fields" :key="field.key" :label="field.label" :required="field.required">
|
||||
<a-textarea v-if="field.type === 'textarea' || field.type === 'json'" v-model="actionForm[field.key]" />
|
||||
<a-textarea v-if="field.type === 'textarea'" v-model="actionForm[field.key]" />
|
||||
<a-input v-else v-model="actionForm[field.key]" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
Reference in New Issue
Block a user