feat: 新增流程定义页面

This commit is contained in:
xlsea
2025-05-22 22:56:59 +08:00
parent 32c241564d
commit 11aba9e2c8
19 changed files with 899 additions and 15 deletions

View File

@ -0,0 +1,46 @@
<script setup lang="tsx">
import { useAttrs } from 'vue';
import type { TreeSelectProps } from 'naive-ui';
import { useLoading } from '@sa/hooks';
import { fetchGetCategoryTree } from '@/service/api/workflow';
defineOptions({ name: 'WorkflowCategorySelect' });
interface Props {
[key: string]: any;
}
defineProps<Props>();
const value = defineModel<CommonType.IdType | null>('value', { required: false });
const options = defineModel<Api.Common.CommonTreeRecord>('options', { required: false, default: [] });
const attrs: TreeSelectProps = useAttrs();
const { loading, startLoading, endLoading } = useLoading();
async function getCategoryList() {
startLoading();
const { error, data } = await fetchGetCategoryTree();
if (error) return;
options.value = data;
endLoading();
}
getCategoryList();
</script>
<template>
<NTreeSelect
v-model:value="value"
filterable
class="h-full"
:loading="loading"
key-field="id"
label-field="label"
:options="options as []"
:default-expanded-keys="[0]"
v-bind="attrs"
/>
</template>
<style scoped></style>

10
src/constants/workflow.ts Normal file
View File

@ -0,0 +1,10 @@
import { transformRecordToOption } from '@/utils/common';
/** workflow publish status */
export const workflowPublishStatusRecord: Record<Api.Workflow.WorkflowPublishStatus, string> = {
'0': '未发布',
'1': '已发布',
'9': '失效'
};
export const workflowPublishStatusOptions = transformRecordToOption(workflowPublishStatusRecord);

View File

@ -217,7 +217,8 @@ const local: App.I18n.Schema = {
exception: 'Exception',
exception_403: '403',
exception_404: '404',
exception_500: '500'
exception_500: '500',
'workflow_process-definition': 'Process Definition'
},
page: {
login: {

View File

@ -217,7 +217,8 @@ const local: App.I18n.Schema = {
exception: '异常页',
exception_403: '403',
exception_404: '404',
exception_500: '500'
exception_500: '500',
'workflow_process-definition': '流程定义'
},
page: {
login: {

View File

@ -44,4 +44,5 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
system_user: () => import("@/views/system/user/index.vue"),
tool_gen: () => import("@/views/tool/gen/index.vue"),
workflow_category: () => import("@/views/workflow/category/index.vue"),
"workflow_process-definition": () => import("@/views/workflow/process-definition/index.vue"),
};

View File

@ -349,6 +349,15 @@ export const generatedRoutes: GeneratedRoute[] = [
title: 'workflow_category',
i18nKey: 'route.workflow_category'
}
},
{
name: 'workflow_process-definition',
path: '/workflow/process-definition',
component: 'view.workflow_process-definition',
meta: {
title: 'workflow_process-definition',
i18nKey: 'route.workflow_process-definition'
}
}
]
}

View File

@ -200,7 +200,8 @@ const routeMap: RouteMap = {
"tool_gen": "/tool/gen",
"user-center": "/user-center",
"workflow": "/workflow",
"workflow_category": "/workflow/category"
"workflow_category": "/workflow/category",
"workflow_process-definition": "/workflow/process-definition"
};
/**

View File

@ -43,3 +43,11 @@ export function fetchExportCategory(params?: Api.Workflow.WorkflowCategorySearch
params
});
}
/** 获取分类树 */
export function fetchGetCategoryTree() {
return request<Api.Common.CommonTreeRecord>({
url: '/workflow/category/categoryTree',
method: 'get'
});
}

View File

@ -0,0 +1,56 @@
import { request } from '@/service/request';
/** 获取流程定义列表 */
export function fetchGetDefinitionList(params?: Api.Workflow.DefinitionSearchParams) {
return request<Api.Workflow.DefinitionList>({
url: '/workflow/definition/list',
method: 'get',
params
});
}
/** 获取未发布流程定义列表 */
export function fetchGetUnPublishDefinitionList(params?: Api.Workflow.DefinitionSearchParams) {
return request<Api.Workflow.DefinitionList>({
url: '/workflow/definition/unPublishList',
method: 'get',
params
});
}
/** 新增流程定义 */
export function fetchCreateDefinition(data: Api.Workflow.DefinitionOperateParams) {
return request<boolean>({
url: '/workflow/definition',
method: 'post',
data
});
}
/** 修改流程定义 */
export function fetchUpdateDefinition(data: Api.Workflow.DefinitionOperateParams) {
return request<boolean>({
url: '/workflow/definition',
method: 'put',
data
});
}
/** 批量删除流程定义 */
export function fetchBatchDeleteDefinition(ids: CommonType.IdType[]) {
return request<boolean>({
url: `/workflow/definition/${ids.join(',')}`,
method: 'delete'
});
}
/** 激活/挂起流程定义 */
export function fetchActiveDefinition(id: CommonType.IdType, active: boolean) {
return request<boolean>({
url: `/workflow/definition/active/${id}`,
method: 'put',
params: {
active
}
});
}

View File

@ -1 +1,2 @@
export * from './category';
export * from './definition';

View File

@ -40,5 +40,51 @@ declare namespace Api {
/** 工作流分类列表 */
type WorkflowCategoryList = WorkflowCategory[];
/** 工作流发布状态 */
type WorkflowPublishStatus = 0 | 1 | 9;
/** definition */
type Definition = Common.CommonTenantRecord<{
/** 主键id */
id: CommonType.IdType;
/** 流程编码 */
flowCode: string;
/** 流程名称 */
flowName: string;
/** 流程类别 */
category: string;
/** 流程版本 */
version: string;
/** 是否发布0未发布 1已发布 9失效 */
isPublish: WorkflowPublishStatus;
/** 审批表单是否自定义Y是 N否 */
formCustom: string;
/** 审批表单路径 */
formPath: string;
/** 流程激活状态0挂起 1激活 */
activityStatus: number;
/** 监听器类型 */
listenerType: string;
/** 监听器路径 */
listenerPath: string;
/** 业务详情 存业务表对象json字符串 */
ext: string;
/** 删除标志 */
delFlag: string;
}>;
/** definition search params */
type DefinitionSearchParams = CommonType.RecordNullable<
Pick<Api.Workflow.Definition, 'flowCode' | 'flowName' | 'category'> & Api.Common.CommonSearchParams
>;
/** definition operate params */
type DefinitionOperateParams = CommonType.RecordNullable<
Pick<Api.Workflow.Definition, 'id' | 'flowCode' | 'flowName' | 'category' | 'formPath'>
>;
/** definition list */
type DefinitionList = Api.Common.PaginatingQueryRecord<Definition>;
}
}

View File

@ -12,6 +12,7 @@ declare module 'vue' {
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
copy: typeof import('./../components/custom/dept-tree-select copy.vue')['default']
CountTo: typeof import('./../components/custom/count-to.vue')['default']
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
DeptTree: typeof import('./../components/custom/dept-tree.vue')['default']
@ -144,5 +145,6 @@ declare module 'vue' {
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
UserSelect: typeof import('./../components/custom/user-select.vue')['default']
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
WorkflowCategorySelect: typeof import('./../components/custom/workflow-category-select.vue')['default']
}
}

View File

@ -55,6 +55,7 @@ declare module "@elegant-router/types" {
"user-center": "/user-center";
"workflow": "/workflow";
"workflow_category": "/workflow/category";
"workflow_process-definition": "/workflow/process-definition";
};
/**
@ -149,6 +150,7 @@ declare module "@elegant-router/types" {
| "system_user"
| "tool_gen"
| "workflow_category"
| "workflow_process-definition"
>;
/**

View File

@ -197,7 +197,6 @@ function handleExport() {
v-model:visible="drawerVisible"
:operate-type="operateType"
:row-data="editingData"
:category-tree-list="data"
@submitted="getData"
/>
</NCard>

View File

@ -12,8 +12,6 @@ interface Props {
operateType: NaiveUI.TableOperateType;
/** the edit row data */
rowData?: Api.Workflow.WorkflowCategory | null;
/** the category data */
categoryTreeList?: Api.Workflow.WorkflowCategory[] | null;
}
const props = defineProps<Props>();
@ -109,15 +107,7 @@ watch(visible, () => {
<NDrawerContent :title="title" :native-scrollbar="false" closable>
<NForm ref="formRef" :model="model" :rules="rules">
<NFormItem label="上级分类" path="parentId">
<NTreeSelect
v-model:value="model.parentId"
filterable
class="h-full"
key-field="categoryId"
label-field="categoryName"
:options="categoryTreeList!"
:default-expanded-keys="[0]"
/>
<WorkflowCategorySelect v-model:value="model.parentId" />
</NFormItem>
<NFormItem label="分类名称" path="categoryName">
<NInput v-model:value="model.categoryName" placeholder="请输入分类名称" />

View File

@ -0,0 +1,352 @@
<script setup lang="tsx">
import { computed, ref } from 'vue';
import { NDivider, NSwitch, NTag } from 'naive-ui';
import { useBoolean, useLoading } from '@sa/hooks';
import { workflowPublishStatusRecord } from '@/constants/workflow';
import {
fetchActiveDefinition,
fetchBatchDeleteDefinition,
fetchGetCategoryTree,
fetchGetUnPublishDefinitionList
} from '@/service/api/workflow';
import { useAppStore } from '@/store/modules/app';
import { useAuth } from '@/hooks/business/auth';
import { useDownload } from '@/hooks/business/download';
import { useTable, useTableOperate } from '@/hooks/common/table';
import { $t } from '@/locales';
import ButtonIcon from '@/components/custom/button-icon.vue';
import DefinitionOperateDrawer from './modules/definition-operate-drawer.vue';
import DefinitionSearch from './modules/definition-search.vue';
import DefinitionImportModal from './modules/definition-import-modal.vue';
defineOptions({
name: 'DefinitionList'
});
const appStore = useAppStore();
const { download } = useDownload();
const { hasAuth } = useAuth();
const { bool: importVisible, setTrue: showImportModal } = useBoolean();
const {
columns,
columnChecks,
data,
getData,
getDataByPage,
loading,
mobilePagination,
searchParams,
resetSearchParams
} = useTable({
apiFn: fetchGetUnPublishDefinitionList,
apiParams: {
pageNum: 1,
pageSize: 10,
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
// the value can not be undefined, otherwise the property in Form will not be reactive
flowCode: null,
flowName: null,
category: null,
params: {}
},
columns: () => [
{
type: 'selection',
align: 'center',
width: 48
},
{
key: 'flowName',
title: '流程定义名称',
align: 'center',
minWidth: 120
},
{
key: 'flowCode',
title: '标识 Key',
align: 'center',
minWidth: 120
},
{
key: 'category',
title: '流程分类',
align: 'center',
minWidth: 120
},
{
key: 'version',
title: '版本号',
align: 'center',
minWidth: 120
},
{
key: 'activityStatus',
title: '激活状态',
align: 'center',
minWidth: 120,
render(row) {
const {
loading: activityLoading,
startLoading: startActivityLoading,
endLoading: endActivityLoading
} = useLoading();
/** 处理状态切换 */
async function handleStatusChange(value: boolean) {
window.$dialog?.warning({
title: '系统提示',
content: `确定要${value ? '激活' : '挂起'} ${row.flowCode} 吗?`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
startActivityLoading();
const { error } = await fetchActiveDefinition(row.id, value);
if (error) return;
if (!error) row.activityStatus = value ? 1 : 0;
window.$message?.success($t('page.system.user.statusChangeSuccess'));
getData();
endActivityLoading();
},
onNegativeClick: () => {}
});
}
return (
<NSwitch
v-model:value={row.activityStatus}
loading={activityLoading.value}
rubber-band={false}
checked-value={1}
unchecked-value={0}
on-update:value={handleStatusChange}
/>
);
}
},
{
key: 'isPublish',
title: '发布状态',
align: 'center',
minWidth: 120,
render: row => {
if (row.isPublish === null) {
return null;
}
const tagMap: Record<Api.Workflow.WorkflowPublishStatus, NaiveUI.ThemeColor> = {
0: 'warning',
1: 'success',
9: 'error'
};
return <NTag type={tagMap[row.isPublish]}>{workflowPublishStatusRecord[row.isPublish]}</NTag>;
}
},
{
key: 'operate',
title: $t('common.operate'),
align: 'center',
width: 130,
render: row => {
const divider = () => {
if (!hasAuth('workflow:definition:edit') || !hasAuth('workflow:definition:remove')) {
return null;
}
return <NDivider vertical />;
};
const editBtn = () => {
if (!hasAuth('workflow:definition:edit')) {
return null;
}
return (
<ButtonIcon
text
type="primary"
icon="material-symbols:drive-file-rename-outline-outline"
tooltipContent={$t('common.edit')}
onClick={() => edit(row.id!)}
/>
);
};
const deleteBtn = () => {
if (!hasAuth('workflow:definition:remove')) {
return null;
}
return (
<ButtonIcon
text
type="error"
icon="material-symbols:delete-outline"
tooltipContent={$t('common.delete')}
popconfirmContent={$t('common.confirmDelete')}
onPositiveClick={() => handleDelete(row.id!)}
/>
);
};
return (
<div class="flex-center gap-8px">
{editBtn()}
{divider()}
{deleteBtn()}
</div>
);
}
}
]
});
const { drawerVisible, operateType, editingData, handleAdd, handleEdit, checkedRowKeys, onBatchDeleted, onDeleted } =
useTableOperate(data, getData);
async function handleBatchDelete() {
// request
const { error } = await fetchBatchDeleteDefinition(checkedRowKeys.value);
if (error) return;
onBatchDeleted();
}
async function handleDelete(id: CommonType.IdType) {
// request
const { error } = await fetchBatchDeleteDefinition([id]);
if (error) return;
onDeleted();
}
function edit(id: CommonType.IdType) {
handleEdit('id', id);
}
function handleExport() {
download('/workflow/definition/export', searchParams, `流程定义_${new Date().getTime()}.xlsx`);
}
function handleDeploy() {
showImportModal();
}
const { loading: categoryLoading, startLoading: startCategoryLoading, endLoading: endCategoryLoading } = useLoading();
const categoryPattern = ref<string>();
const categoryData = ref<Api.Common.CommonTreeRecord>([]);
const selectedKeys = ref<string[]>([]);
async function getTreeData() {
startCategoryLoading();
const { data: tree, error } = await fetchGetCategoryTree();
if (!error) {
categoryData.value = tree;
}
endCategoryLoading();
}
getTreeData();
function handleClickTree(keys: string[]) {
searchParams.category = keys.length ? keys[0] : null;
checkedRowKeys.value = [];
getDataByPage();
}
function handleResetTreeData() {
categoryPattern.value = undefined;
getTreeData();
}
const expandedKeys = ref<CommonType.IdType[]>([100]);
const selectable = computed(() => {
return !loading.value;
});
</script>
<template>
<TableSiderLayout :sider-title="$t('page.system.dept.title')">
<template #header-extra>
<NButton size="small" text class="h-18px" @click.stop="() => handleResetTreeData()">
<template #icon>
<SvgIcon icon="ic:round-refresh" />
</template>
</NButton>
</template>
<template #sider>
<NInput v-model:value="categoryPattern" clearable :placeholder="$t('common.keywordSearch')" />
<NSpin class="dept-tree" :show="categoryLoading">
<NTree
v-model:expanded-keys="expandedKeys"
v-model:selected-keys="selectedKeys"
block-node
show-line
:data="categoryData as []"
:show-irrelevant-nodes="false"
:pattern="categoryPattern"
class="infinite-scroll h-full min-h-200px py-3"
key-field="id"
label-field="label"
virtual-scroll
:selectable="selectable"
@update:selected-keys="handleClickTree"
>
<template #empty>
<NEmpty :description="$t('page.system.dept.empty')" class="h-full min-h-200px justify-center" />
</template>
</NTree>
</NSpin>
</template>
<div class="h-full flex-col-stretch gap-12px overflow-hidden lt-sm:overflow-auto">
<DefinitionSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getDataByPage" />
<NCard title="流程定义列表" :bordered="false" size="small" class="sm:flex-1-hidden card-wrapper">
<template #header-extra>
<TableHeaderOperation
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
:show-add="hasAuth('workflow:definition:add')"
:show-delete="hasAuth('workflow:definition:remove')"
:show-export="hasAuth('workflow:definition:export')"
@add="handleAdd"
@delete="handleBatchDelete"
@export="handleExport"
@refresh="getData"
>
<template #prefix>
<NButton size="small" ghost @click="handleDeploy">
<template #icon>
<icon-material-symbols:upload-rounded class="text-icon" />
</template>
部署流程文件
</NButton>
</template>
</TableHeaderOperation>
</template>
<NDataTable
v-model:checked-row-keys="checkedRowKeys"
:columns="columns"
:data="data"
size="small"
:flex-height="!appStore.isMobile"
:scroll-x="962"
:loading="loading"
remote
:row-key="row => row.id"
:pagination="mobilePagination"
class="sm:h-full"
/>
<DefinitionOperateDrawer
v-model:visible="drawerVisible"
:operate-type="operateType"
:row-data="editingData"
@submitted="getDataByPage"
/>
<DefinitionImportModal v-model:visible="importVisible" @submitted="getDataByPage" />
</NCard>
</div>
</TableSiderLayout>
</template>
<style scoped></style>

View File

@ -0,0 +1,159 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import type { UploadFileInfo } from 'naive-ui';
import { getToken } from '@/store/modules/auth/shared';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { getServiceBaseURL } from '@/utils/service';
import type FileUpload from '@/components/custom/file-upload.vue';
import { $t } from '@/locales';
defineOptions({
name: 'DefinitionImportModal'
});
interface Emits {
(e: 'submitted'): void;
}
const { baseURL } = getServiceBaseURL(import.meta.env);
const headers: Record<string, string> = {
Authorization: `Bearer ${getToken()}`,
clientid: import.meta.env.VITE_APP_CLIENT_ID!
};
const emit = defineEmits<Emits>();
const uploadRef = ref<typeof FileUpload>();
const message = ref<string>('');
const success = ref<boolean>(false);
const visible = defineModel<boolean>('visible', {
default: false
});
const data = ref<Record<string, any>>({
category: undefined
});
const { formRef, validate, restoreValidation } = useNaiveForm();
const { createRequiredRule } = useFormRules();
const rules: Record<string, App.Global.FormRule> = {
category: createRequiredRule('流程分类不能为空')
};
const fileList = ref<UploadFileInfo[]>([]);
function closeDrawer() {
visible.value = false;
if (success.value) {
emit('submitted');
}
}
async function handleSubmit() {
await validate();
if (fileList.value.length === 0) {
window.$message?.error('请选择流程文件');
return;
}
fileList.value.forEach(item => {
item.status = 'pending';
});
uploadRef.value?.submit();
visible.value = false;
}
function isErrorState(xhr: XMLHttpRequest) {
const responseText = xhr?.responseText;
const response = JSON.parse(responseText);
return response.code !== 200;
}
function handleFinish(options: { file: UploadFileInfo; event?: ProgressEvent }) {
const { file, event } = options;
// @ts-expect-error Ignore type errors
const responseText = event?.target?.responseText;
const response = JSON.parse(responseText);
message.value = response.msg;
window.$message?.success($t('common.importSuccess'));
success.value = true;
return file;
}
function handleError(options: { file: UploadFileInfo; event?: ProgressEvent }) {
const { event } = options;
// @ts-expect-error Ignore type errors
const responseText = event?.target?.responseText;
const msg = JSON.parse(responseText).msg;
message.value = msg;
window.$message?.error(msg || $t('common.importFail'));
success.value = false;
}
watch(visible, () => {
if (visible.value) {
fileList.value = [];
success.value = false;
message.value = '';
restoreValidation();
}
});
</script>
<template>
<NModal
v-model:show="visible"
title="部署流程文件"
preset="card"
:bordered="false"
display-directive="show"
class="max-w-90% w-600px"
@close="closeDrawer"
>
<NForm ref="formRef" label-placement="left" :model="data" :rules="rules">
<NFormItem label="流程分类" path="category">
<WorkflowCategorySelect v-model:value="data.category" />
</NFormItem>
</NForm>
<NUpload
ref="uploadRef"
v-model:file-list="fileList"
class="mt-12px"
:action="`${baseURL}/workflow/definition/importDef`"
:headers="headers"
:data="data"
:max="1"
:file-size="50"
accept=".json"
:multiple="false"
directory-dnd
:default-upload="false"
list-type="text"
:is-error-state="isErrorState"
@finish="handleFinish"
@error="handleError"
>
<NUploadDragger>
<div class="mb-12px flex-center">
<SvgIcon icon="material-symbols:unarchive-outline" class="text-58px color-#d8d8db dark:color-#a1a1a2" />
</div>
<NText class="text-16px">请选择 JSON 流程文件上传</NText>
<NP depth="3" class="mt-8px text-center">
仅支持 JSON 格式文件
<br />
PS: 如若部署请部署从本项目模型管理导出的数据
</NP>
</NUploadDragger>
</NUpload>
<template #footer>
<NSpace justify="end" :size="16">
<NButton type="primary" @click="handleSubmit">{{ $t('common.import') }}</NButton>
</NSpace>
</template>
</NModal>
</template>
<style scoped></style>

View File

@ -0,0 +1,133 @@
<script setup lang="ts">
import { computed, reactive, watch } from 'vue';
import { fetchCreateDefinition, fetchUpdateDefinition } from '@/service/api/workflow/definition';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
defineOptions({
name: 'DefinitionOperateDrawer'
});
interface Props {
/** the type of operation */
operateType: NaiveUI.TableOperateType;
/** the edit row data */
rowData?: Api.Workflow.Definition | null;
}
const props = defineProps<Props>();
interface Emits {
(e: 'submitted'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
const { formRef, validate, restoreValidation } = useNaiveForm();
const { createRequiredRule } = useFormRules();
const title = computed(() => {
const titles: Record<NaiveUI.TableOperateType, string> = {
add: '新增流程定义',
edit: '编辑流程定义'
};
return titles[props.operateType];
});
type Model = Api.Workflow.DefinitionOperateParams;
const model: Model = reactive(createDefaultModel());
function createDefaultModel(): Model {
return {
flowCode: '',
flowName: '',
category: '',
formPath: ''
};
}
type RuleKey = Extract<keyof Model, 'flowCode' | 'flowName' | 'category'>;
const rules: Record<RuleKey, App.Global.FormRule> = {
flowCode: createRequiredRule('流程编码不能为空'),
flowName: createRequiredRule('流程名称不能为空'),
category: createRequiredRule('流程类别不能为空')
};
function handleUpdateModelWhenEdit() {
if (props.operateType === 'add') {
Object.assign(model, createDefaultModel());
return;
}
if (props.operateType === 'edit' && props.rowData) {
Object.assign(model, props.rowData);
}
}
function closeDrawer() {
visible.value = false;
}
async function handleSubmit() {
await validate();
// request
if (props.operateType === 'add') {
const { flowCode, flowName, category, formPath } = model;
const { error } = await fetchCreateDefinition({ flowCode, flowName, category, formPath });
if (error) return;
}
if (props.operateType === 'edit') {
const { id, flowCode, flowName, category, formPath } = model;
const { error } = await fetchUpdateDefinition({ id, flowCode, flowName, category, formPath });
if (error) return;
}
window.$message?.success($t('common.saveSuccess'));
closeDrawer();
emit('submitted');
}
watch(visible, () => {
if (visible.value) {
handleUpdateModelWhenEdit();
restoreValidation();
}
});
</script>
<template>
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
<NDrawerContent :title="title" :native-scrollbar="false" closable>
<NForm ref="formRef" :model="model" :rules="rules">
<NFormItem label="流程类别" path="category">
<WorkflowCategorySelect v-model:value="model.category" placeholder="请选择流程类别" />
</NFormItem>
<NFormItem label="流程编码" path="flowCode">
<NInput v-model:value="model.flowCode" placeholder="请输入流程编码" />
</NFormItem>
<NFormItem label="流程名称" path="flowName">
<NInput v-model:value="model.flowName" placeholder="请输入流程名称" />
</NFormItem>
<NFormItem label="审批表单路径" path="formPath">
<NInput v-model:value="model.formPath" placeholder="请输入审批表单路径" />
</NFormItem>
</NForm>
<template #footer>
<NSpace :size="16">
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
</NSpace>
</template>
</NDrawerContent>
</NDrawer>
</template>
<style scoped></style>

View File

@ -0,0 +1,67 @@
<script setup lang="ts">
import { useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
defineOptions({
name: 'DefinitionSearch'
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
const { formRef, validate, restoreValidation } = useNaiveForm();
const model = defineModel<Api.Workflow.DefinitionSearchParams>('model', { required: true });
async function reset() {
Object.assign(model.value.params!, {});
await restoreValidation();
emit('reset');
}
async function search() {
await validate();
emit('search');
}
</script>
<template>
<NCard :bordered="false" size="small" class="card-wrapper">
<NCollapse>
<NCollapseItem :title="$t('common.search')" name="user-search">
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
<NGrid responsive="screen" item-responsive>
<NFormItemGi span="24 s:12 m:6" label="流程编码" path="flowCode" class="pr-24px">
<NInput v-model:value="model.flowCode" placeholder="请输入流程编码" />
</NFormItemGi>
<NFormItemGi span="24 s:12 m:6" label="流程名称" path="flowName" class="pr-24px">
<NInput v-model:value="model.flowName" placeholder="请输入流程名称" />
</NFormItemGi>
<NFormItemGi span="24 s:12 m:12" class="pr-24px">
<NSpace class="w-full" justify="end">
<NButton @click="reset">
<template #icon>
<icon-ic-round-refresh class="text-icon" />
</template>
{{ $t('common.reset') }}
</NButton>
<NButton type="primary" ghost @click="search">
<template #icon>
<icon-ic-round-search class="text-icon" />
</template>
{{ $t('common.search') }}
</NButton>
</NSpace>
</NFormItemGi>
</NGrid>
</NForm>
</NCollapseItem>
</NCollapse>
</NCard>
</template>
<style scoped></style>