Merge branch 'dev' of https://gitee.com/yudaocode/yudao-ui-admin-vben into dev
This commit is contained in:
@@ -4,17 +4,7 @@
|
|||||||
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
|
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
|
||||||
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
|
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
|
||||||
// import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右侧框样式
|
// import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右侧框样式
|
||||||
import {
|
import { computed, h, onBeforeUnmount, onMounted, provide, ref } from 'vue';
|
||||||
computed,
|
|
||||||
defineEmits,
|
|
||||||
defineOptions,
|
|
||||||
defineProps,
|
|
||||||
h,
|
|
||||||
onBeforeUnmount,
|
|
||||||
onMounted,
|
|
||||||
provide,
|
|
||||||
ref,
|
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AlignLeftOutlined,
|
AlignLeftOutlined,
|
||||||
@@ -655,7 +645,7 @@ onBeforeUnmount(() => {
|
|||||||
type="file"
|
type="file"
|
||||||
id="files"
|
id="files"
|
||||||
ref="refFile"
|
ref="refFile"
|
||||||
style="display: none"
|
class="hidden"
|
||||||
accept=".xml, .bpmn"
|
accept=".xml, .bpmn"
|
||||||
@change="importLocalFile"
|
@change="importLocalFile"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineProps, h, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
import { h, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { BpmProcessInstanceStatus, DICT_TYPE } from '@vben/constants';
|
import { BpmProcessInstanceStatus, DICT_TYPE } from '@vben/constants';
|
||||||
import { UndoOutlined, ZoomInOutlined, ZoomOutOutlined } from '@vben/icons';
|
import { UndoOutlined, ZoomInOutlined, ZoomOutOutlined } from '@vben/icons';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
import { defineOptions, defineProps, ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
import { CustomConfigMap } from './data';
|
import { CustomConfigMap } from './data';
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { inject, nextTick, ref, toRaw, watch } from 'vue';
|
||||||
defineOptions,
|
|
||||||
defineProps,
|
|
||||||
inject,
|
|
||||||
nextTick,
|
|
||||||
ref,
|
|
||||||
toRaw,
|
|
||||||
watch,
|
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Divider,
|
Divider,
|
||||||
|
|||||||
@@ -153,11 +153,7 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="panel-tab__content">
|
<div class="panel-tab__content">
|
||||||
<Form
|
<Form :model="flowConditionForm">
|
||||||
:model="flowConditionForm"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:wrapper-col="{ span: 18 }"
|
|
||||||
>
|
|
||||||
<Form.Item label="流转类型">
|
<Form.Item label="流转类型">
|
||||||
<Select v-model:value="flowConditionForm.type" @change="updateFlowType">
|
<Select v-model:value="flowConditionForm.type" @change="updateFlowType">
|
||||||
<Select.Option value="normal">普通流转路径</Select.Option>
|
<Select.Option value="normal">普通流转路径</Select.Option>
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="panel-tab__content">
|
<div class="panel-tab__content">
|
||||||
<Form :label-col="{ style: { width: '80px' } }">
|
<Form>
|
||||||
<FormItem label="流程表单">
|
<FormItem label="流程表单">
|
||||||
<!-- <Input v-model:value="formKey" @change="updateElementFormKey" />-->
|
<!-- <Input v-model:value="formKey" @change="updateElementFormKey" />-->
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, nextTick, ref, watch } from 'vue';
|
import { inject, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { IconifyIcon, PlusOutlined } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { cloneDeep } from '@vben/utils';
|
import { cloneDeep } from '@vben/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -290,7 +290,7 @@ watch(
|
|||||||
<div class="element-drawer__button">
|
<div class="element-drawer__button">
|
||||||
<Button type="primary" size="small" @click="openListenerForm(null, -1)">
|
<Button type="primary" size="small" @click="openListenerForm(null, -1)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<PlusOutlined />
|
<IconifyIcon icon="ep:plus" />
|
||||||
</template>
|
</template>
|
||||||
添加监听器
|
添加监听器
|
||||||
</Button>
|
</Button>
|
||||||
@@ -309,12 +309,7 @@ watch(
|
|||||||
:width="width as any"
|
:width="width as any"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<Form
|
<Form :model="listenerForm" ref="listenerFormRef">
|
||||||
:model="listenerForm"
|
|
||||||
ref="listenerFormRef"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:wrapper-col="{ span: 18 }"
|
|
||||||
>
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label="事件类型"
|
label="事件类型"
|
||||||
name="event"
|
name="event"
|
||||||
@@ -462,20 +457,23 @@ watch(
|
|||||||
</template>
|
</template>
|
||||||
</Form>
|
</Form>
|
||||||
<Divider />
|
<Divider />
|
||||||
<p class="listener-filed__title">
|
<div class="mb-2 flex justify-between">
|
||||||
<span><IconifyIcon icon="ep:menu" />注入字段:</span>
|
<span class="flex items-center">
|
||||||
<Button type="primary" @click="openListenerFieldForm(null, -1)">
|
<IconifyIcon icon="ep:menu" class="mr-2 text-gray-600" />
|
||||||
|
注入字段
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
title="添加字段"
|
||||||
|
@click="openListenerFieldForm(null, -1)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<IconifyIcon icon="ep:plus" />
|
||||||
|
</template>
|
||||||
添加字段
|
添加字段
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</div>
|
||||||
<Table
|
<Table :data-source="fieldsListOfListener" size="small" bordered>
|
||||||
:data-source="fieldsListOfListener"
|
|
||||||
size="small"
|
|
||||||
:scroll="{ y: 240 }"
|
|
||||||
:pagination="false"
|
|
||||||
bordered
|
|
||||||
style="flex: none"
|
|
||||||
>
|
|
||||||
<TableColumn title="序号" width="50px">
|
<TableColumn title="序号" width="50px">
|
||||||
<template #default="{ index }">
|
<template #default="{ index }">
|
||||||
{{ index + 1 }}
|
{{ index + 1 }}
|
||||||
@@ -492,12 +490,12 @@ watch(
|
|||||||
/>
|
/>
|
||||||
<TableColumn
|
<TableColumn
|
||||||
title="字段值/表达式"
|
title="字段值/表达式"
|
||||||
width="100px"
|
width="120px"
|
||||||
:custom-render="
|
:custom-render="
|
||||||
({ record }: any) => record.string || record.expression
|
({ record }: any) => record.string || record.expression
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<TableColumn title="操作" width="130px">
|
<TableColumn title="操作" width="80px" fixed="right">
|
||||||
<template #default="{ record, index }">
|
<template #default="{ record, index }">
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
@@ -532,13 +530,7 @@ watch(
|
|||||||
width="600px"
|
width="600px"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<Form
|
<Form :model="listenerFieldForm" ref="listenerFieldFormRef">
|
||||||
:model="listenerFieldForm"
|
|
||||||
ref="listenerFieldFormRef"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:wrapper-col="{ span: 18 }"
|
|
||||||
style="height: 136px"
|
|
||||||
>
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label="字段名称:"
|
label="字段名称:"
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ const select = async (row: BpmProcessListenerApi.ProcessListener) => {
|
|||||||
</template>
|
</template>
|
||||||
</Table.Column>
|
</Table.Column>
|
||||||
<Table.Column title="值" align="center" data-index="value" />
|
<Table.Column title="值" align="center" data-index="value" />
|
||||||
<Table.Column title="操作" align="center">
|
<Table.Column title="操作" align="center" fixed="right">
|
||||||
<template #default="{ record }">
|
<template #default="{ record }">
|
||||||
<Button type="primary" @click="select(record)"> 选择 </Button>
|
<Button type="primary" @click="select(record)"> 选择 </Button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject, nextTick, ref, watch } from 'vue';
|
import { inject, nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { MenuOutlined, PlusOutlined, SelectOutlined } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { cloneDeep } from '@vben/utils';
|
import { cloneDeep } from '@vben/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -300,11 +300,11 @@ watch(
|
|||||||
</Table>
|
</Table>
|
||||||
<div class="element-drawer__button">
|
<div class="element-drawer__button">
|
||||||
<Button size="small" type="primary" @click="openListenerForm(null)">
|
<Button size="small" type="primary" @click="openListenerForm(null)">
|
||||||
<template #icon><PlusOutlined /></template>
|
<template #icon> <IconifyIcon icon="ep:plus" /></template>
|
||||||
添加监听器
|
添加监听器
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="small" @click="openProcessListenerDialog">
|
<Button size="small" @click="openProcessListenerDialog">
|
||||||
<template #icon><SelectOutlined /></template>
|
<template #icon> <IconifyIcon icon="ep:select" /></template>
|
||||||
选择监听器
|
选择监听器
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -316,12 +316,7 @@ watch(
|
|||||||
:width="width"
|
:width="width"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<Form
|
<Form :model="listenerForm" ref="listenerFormRef">
|
||||||
:model="listenerForm"
|
|
||||||
:label-col="{ span: 8 }"
|
|
||||||
:wrapper-col="{ span: 16 }"
|
|
||||||
ref="listenerFormRef"
|
|
||||||
>
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label="事件类型"
|
label="事件类型"
|
||||||
name="event"
|
name="event"
|
||||||
@@ -458,16 +453,22 @@ watch(
|
|||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<p class="listener-filed__title">
|
<div class="mb-2 flex justify-between">
|
||||||
<span><MenuOutlined />注入字段:</span>
|
<span class="flex items-center">
|
||||||
|
<IconifyIcon icon="ep:menu" class="mr-2 text-gray-600" />
|
||||||
|
注入字段
|
||||||
|
</span>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
|
||||||
type="primary"
|
type="primary"
|
||||||
|
title="添加字段"
|
||||||
@click="openListenerFieldForm(null)"
|
@click="openListenerFieldForm(null)"
|
||||||
>
|
>
|
||||||
|
<template #icon>
|
||||||
|
<IconifyIcon icon="ep:plus" />
|
||||||
|
</template>
|
||||||
添加字段
|
添加字段
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
:data="fieldsListOfListener"
|
:data="fieldsListOfListener"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -533,13 +534,7 @@ watch(
|
|||||||
:width="600"
|
:width="600"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<Form
|
<Form :model="listenerFieldForm" ref="listenerFieldFormRef">
|
||||||
:model="listenerFieldForm"
|
|
||||||
:label-col="{ span: 8 }"
|
|
||||||
:wrapper-col="{ span: 16 }"
|
|
||||||
ref="listenerFieldFormRef"
|
|
||||||
style="height: 136px"
|
|
||||||
>
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label="字段名称:"
|
label="字段名称:"
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -421,7 +421,7 @@ watch(
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<div v-else>除了UserTask以外节点的多实例待实现</div>
|
<div v-else>除了UserTask以外节点的多实例待实现</div>
|
||||||
<!-- 与Simple设计器配置合并,保留以前的代码 -->
|
<!-- 与Simple设计器配置合并,保留以前的代码 -->
|
||||||
<Form :label-col="{ span: 6 }" style="display: none">
|
<Form class="hidden">
|
||||||
<FormItem label="快捷配置">
|
<FormItem label="快捷配置">
|
||||||
<Button size="small" @click="() => changeConfig('依次审批')">
|
<Button size="small" @click="() => changeConfig('依次审批')">
|
||||||
依次审批
|
依次审批
|
||||||
@@ -467,7 +467,7 @@ watch(
|
|||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none -->
|
<!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none -->
|
||||||
<FormItem label="元素变量" key="elementVariable" style="display: none">
|
<FormItem label="元素变量" key="elementVariable" class="hidden">
|
||||||
<Input
|
<Input
|
||||||
v-model:value="loopInstanceForm.elementVariable"
|
v-model:value="loopInstanceForm.elementVariable"
|
||||||
allow-clear
|
allow-clear
|
||||||
@@ -485,7 +485,7 @@ watch(
|
|||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none -->
|
<!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none -->
|
||||||
<FormItem label="异步状态" key="async" style="display: none">
|
<FormItem label="异步状态" key="async" class="hidden">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-model:checked="loopInstanceForm.asyncBefore"
|
v-model:checked="loopInstanceForm.asyncBefore"
|
||||||
@change="() => updateLoopAsync('asyncBefore')"
|
@change="() => updateLoopAsync('asyncBefore')"
|
||||||
|
|||||||
@@ -161,25 +161,15 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="panel-tab__content">
|
<div class="panel-tab__content">
|
||||||
<Table :data="elementPropertyList" :scroll="{ y: 240 }" bordered>
|
<Table :data="elementPropertyList" size="small" bordered>
|
||||||
<TableColumn title="序号" width="50">
|
<TableColumn title="序号" width="50">
|
||||||
<template #default="{ index }">
|
<template #default="{ index }">
|
||||||
{{ index + 1 }}
|
{{ index + 1 }}
|
||||||
</template>
|
</template>
|
||||||
</TableColumn>
|
</TableColumn>
|
||||||
<TableColumn
|
<TableColumn title="属性名" data-index="name" />
|
||||||
title="属性名"
|
<TableColumn title="属性值" data-index="value" />
|
||||||
data-index="name"
|
<TableColumn title="操作">
|
||||||
:min-width="100"
|
|
||||||
:ellipsis="{ showTitle: true }"
|
|
||||||
/>
|
|
||||||
<TableColumn
|
|
||||||
title="属性值"
|
|
||||||
data-index="value"
|
|
||||||
:min-width="100"
|
|
||||||
:ellipsis="{ showTitle: true }"
|
|
||||||
/>
|
|
||||||
<TableColumn title="操作" width="110">
|
|
||||||
<template #default="{ record, index }">
|
<template #default="{ record, index }">
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
@@ -215,11 +205,7 @@ watch(
|
|||||||
:width="600"
|
:width="600"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<Form
|
<Form :model="propertyForm" ref="attributeFormRef">
|
||||||
:model="propertyForm"
|
|
||||||
ref="attributeFormRef"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
>
|
|
||||||
<FormItem label="属性名:" name="name">
|
<FormItem label="属性名:" name="name">
|
||||||
<Input v-model:value="propertyForm.name" allow-clear />
|
<Input v-model:value="propertyForm.name" allow-clear />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="panel-tab__content">
|
<div class="panel-tab__content">
|
||||||
<div class="panel-tab__content--title">
|
<div class="panel-tab__content--title">
|
||||||
<span>
|
<span class="flex items-center">
|
||||||
<IconifyIcon icon="ep:menu" style="margin-right: 8px; color: #555" />
|
<IconifyIcon icon="ep:menu" class="mr-2 text-gray-600" />
|
||||||
消息列表
|
消息列表
|
||||||
</span>
|
</span>
|
||||||
<Button type="primary" title="创建新消息" @click="openModel('message')">
|
<Button type="primary" title="创建新消息" @click="openModel('message')">
|
||||||
@@ -95,33 +95,19 @@ onMounted(() => {
|
|||||||
创建新消息
|
创建新消息
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Table :data-source="messageList" :bordered="true" :pagination="false">
|
<Table :data-source="messageList" size="small" bordered>
|
||||||
<TableColumn title="序号" width="60px">
|
<TableColumn title="序号" width="60px">
|
||||||
<template #default="{ index }">
|
<template #default="{ index }">
|
||||||
{{ index + 1 }}
|
{{ index + 1 }}
|
||||||
</template>
|
</template>
|
||||||
</TableColumn>
|
</TableColumn>
|
||||||
<TableColumn
|
<TableColumn title="消息ID" data-index="id" />
|
||||||
title="消息ID"
|
<TableColumn title="消息名称" data-index="name" />
|
||||||
data-index="id"
|
|
||||||
:width="300"
|
|
||||||
:ellipsis="{ showTitle: true }"
|
|
||||||
/>
|
|
||||||
<TableColumn
|
|
||||||
title="消息名称"
|
|
||||||
data-index="name"
|
|
||||||
:width="300"
|
|
||||||
:ellipsis="{ showTitle: true }"
|
|
||||||
/>
|
|
||||||
</Table>
|
</Table>
|
||||||
<div
|
<div class="panel-tab__content--title mt-2 border-t border-gray-200 pt-2">
|
||||||
class="panel-tab__content--title"
|
<span class="flex items-center">
|
||||||
style="padding-top: 8px; margin-top: 8px; border-top: 1px solid #eee"
|
<IconifyIcon icon="ep:menu" class="mr-2 text-gray-600" />
|
||||||
>
|
信号列表
|
||||||
<span>
|
|
||||||
<IconifyIcon icon="ep:menu" style="margin-right: 8px; color: #555">
|
|
||||||
信号列表
|
|
||||||
</IconifyIcon>
|
|
||||||
</span>
|
</span>
|
||||||
<Button type="primary" title="创建新信号" @click="openModel('signal')">
|
<Button type="primary" title="创建新信号" @click="openModel('signal')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -130,24 +116,14 @@ onMounted(() => {
|
|||||||
创建新信号
|
创建新信号
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Table :data-source="signalList" :bordered="true" :pagination="false">
|
<Table :data-source="signalList" size="small" bordered>
|
||||||
<TableColumn title="序号" width="60px">
|
<TableColumn title="序号" width="60px">
|
||||||
<template #default="{ index }">
|
<template #default="{ index }">
|
||||||
{{ index + 1 }}
|
{{ index + 1 }}
|
||||||
</template>
|
</template>
|
||||||
</TableColumn>
|
</TableColumn>
|
||||||
<TableColumn
|
<TableColumn title="信号ID" data-index="id" />
|
||||||
title="信号ID"
|
<TableColumn title="信号名称" data-index="name" />
|
||||||
data-index="id"
|
|
||||||
:width="300"
|
|
||||||
:ellipsis="{ showTitle: true }"
|
|
||||||
/>
|
|
||||||
<TableColumn
|
|
||||||
title="信号名称"
|
|
||||||
data-index="name"
|
|
||||||
:width="300"
|
|
||||||
:ellipsis="{ showTitle: true }"
|
|
||||||
/>
|
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -157,11 +133,7 @@ onMounted(() => {
|
|||||||
width="400px"
|
width="400px"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<Form
|
<Form :model="modelObjectForm">
|
||||||
:model="modelObjectForm"
|
|
||||||
:label-col="{ span: 9 }"
|
|
||||||
:wrapper-col="{ span: 15 }"
|
|
||||||
>
|
|
||||||
<FormItem :label="modelConfig.idLabel">
|
<FormItem :label="modelConfig.idLabel">
|
||||||
<Input v-model:value="modelObjectForm.id" allow-clear />
|
<Input v-model:value="modelObjectForm.id" allow-clear />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@@ -63,9 +63,9 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="panel-tab__content">
|
<div class="panel-tab__content">
|
||||||
<Form :label-col="{ span: 9 }" :wrapper-col="{ span: 15 }">
|
<Form>
|
||||||
<!-- add by 芋艿:由于「异步延续」暂时用不到,所以这里 display 为 none -->
|
<!-- add by 芋艿:由于「异步延续」暂时用不到,所以这里 display 为 none -->
|
||||||
<FormItem label="异步延续" style="display: none">
|
<FormItem label="异步延续" class="hidden">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-model:checked="taskConfigForm.asyncBefore"
|
v-model:checked="taskConfigForm.asyncBefore"
|
||||||
@change="changeTaskAsync"
|
@change="changeTaskAsync"
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
<Form>
|
||||||
<FormItem label="实例名称">
|
<FormItem label="实例名称">
|
||||||
<Input
|
<Input
|
||||||
v-model:value="formData.processInstanceName"
|
v-model:value="formData.processInstanceName"
|
||||||
@@ -341,12 +341,7 @@ watch(
|
|||||||
@ok="saveVariable"
|
@ok="saveVariable"
|
||||||
@cancel="variableDialogVisible = false"
|
@cancel="variableDialogVisible = false"
|
||||||
>
|
>
|
||||||
<Form
|
<Form :model="varialbeFormData" ref="varialbeFormRef">
|
||||||
:model="varialbeFormData"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:wrapper-col="{ span: 18 }"
|
|
||||||
ref="varialbeFormRef"
|
|
||||||
>
|
|
||||||
<FormItem label="源:" name="source">
|
<FormItem label="源:" name="source">
|
||||||
<Input v-model:value="varialbeFormData.source" allow-clear />
|
<Input v-model:value="varialbeFormData.source" allow-clear />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ watch(
|
|||||||
width="400px"
|
width="400px"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
>
|
>
|
||||||
<Form :model="newMessageForm" size="small" :label-col="{ span: 6 }">
|
<Form :model="newMessageForm" size="small">
|
||||||
<Form.Item label="消息ID">
|
<Form.Item label="消息ID">
|
||||||
<Input v-model:value="newMessageForm.id" allow-clear />
|
<Input v-model:value="newMessageForm.id" allow-clear />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue';
|
||||||
defineOptions,
|
|
||||||
defineProps,
|
|
||||||
nextTick,
|
|
||||||
onBeforeUnmount,
|
|
||||||
ref,
|
|
||||||
toRaw,
|
|
||||||
watch,
|
|
||||||
} from 'vue';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FormItem,
|
FormItem,
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ onBeforeUnmount(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
<Form>
|
||||||
<FormItem label="规则类型" name="candidateStrategy">
|
<FormItem label="规则类型" name="candidateStrategy">
|
||||||
<Select
|
<Select
|
||||||
v-model:value="userTaskForm.candidateStrategy"
|
v-model:value="userTaskForm.candidateStrategy"
|
||||||
|
|||||||
@@ -3,12 +3,7 @@ import type { Ref } from 'vue';
|
|||||||
|
|
||||||
import { computed, nextTick, onMounted, ref, toRaw, watch } from 'vue';
|
import { computed, nextTick, onMounted, ref, toRaw, watch } from 'vue';
|
||||||
|
|
||||||
import {
|
import { IconifyIcon } from '@vben/icons';
|
||||||
CheckCircleFilled,
|
|
||||||
ExclamationCircleFilled,
|
|
||||||
IconifyIcon,
|
|
||||||
QuestionCircleFilled,
|
|
||||||
} from '@vben/icons';
|
|
||||||
|
|
||||||
import { Button, DatePicker, Input, Modal, Tooltip } from 'ant-design-vue';
|
import { Button, DatePicker, Input, Modal, Tooltip } from 'ant-design-vue';
|
||||||
|
|
||||||
@@ -240,7 +235,11 @@ watch(
|
|||||||
循环
|
循环
|
||||||
</Button>
|
</Button>
|
||||||
</Button.Group>
|
</Button.Group>
|
||||||
<CheckCircleFilled v-if="valid" style="color: green; margin-left: 8px" />
|
<IconifyIcon
|
||||||
|
icon="ant-design:check-circle-filled"
|
||||||
|
v-if="valid"
|
||||||
|
style="color: green; margin-left: 8px"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; align-items: center; margin-top: 10px">
|
<div style="display: flex; align-items: center; margin-top: 10px">
|
||||||
<span>条件:</span>
|
<span>条件:</span>
|
||||||
@@ -254,11 +253,15 @@ watch(
|
|||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<Tooltip v-if="!valid" title="格式错误" placement="top">
|
<Tooltip v-if="!valid" title="格式错误" placement="top">
|
||||||
<ExclamationCircleFilled style="color: orange" />
|
<IconifyIcon
|
||||||
|
icon="ant-design:exclamation-circle-filled"
|
||||||
|
class="text-orange-400"
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip :title="helpText" placement="top">
|
<Tooltip :title="helpText" placement="top">
|
||||||
<QuestionCircleFilled
|
<IconifyIcon
|
||||||
style="color: #409eff; cursor: pointer"
|
icon="ant-design:question-circle-filled"
|
||||||
|
class="cursor-pointer text-[#409eff]"
|
||||||
@click="showHelp = true"
|
@click="showHelp = true"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -351,7 +354,3 @@ watch(
|
|||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* 相关样式 */
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { FileUploadProps } from './typing';
|
|||||||
|
|
||||||
import type { AxiosProgressEvent } from '#/api/infra/file';
|
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||||
|
|
||||||
import { ref, toRefs, watch } from 'vue';
|
import { computed, ref, toRefs, watch } from 'vue';
|
||||||
|
|
||||||
import { CloudUpload } from '@vben/icons';
|
import { CloudUpload } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@@ -22,8 +22,10 @@ defineOptions({ name: 'FileUpload', inheritAttrs: false });
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<FileUploadProps>(), {
|
const props = withDefaults(defineProps<FileUploadProps>(), {
|
||||||
value: () => [],
|
value: () => [],
|
||||||
|
modelValue: undefined,
|
||||||
directory: undefined,
|
directory: undefined,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
drag: false,
|
||||||
helpText: '',
|
helpText: '',
|
||||||
maxSize: 2,
|
maxSize: 2,
|
||||||
maxNumber: 1,
|
maxNumber: 1,
|
||||||
@@ -33,7 +35,14 @@ const props = withDefaults(defineProps<FileUploadProps>(), {
|
|||||||
resultField: '',
|
resultField: '',
|
||||||
showDescription: false,
|
showDescription: false,
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['change', 'update:value', 'delete', 'returnText']);
|
const emit = defineEmits([
|
||||||
|
'change',
|
||||||
|
'update:value',
|
||||||
|
'update:modelValue',
|
||||||
|
'delete',
|
||||||
|
'returnText',
|
||||||
|
'preview',
|
||||||
|
]);
|
||||||
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
||||||
const isInnerOperate = ref<boolean>(false);
|
const isInnerOperate = ref<boolean>(false);
|
||||||
const { getStringAccept } = useUploadType({
|
const { getStringAccept } = useUploadType({
|
||||||
@@ -43,13 +52,25 @@ const { getStringAccept } = useUploadType({
|
|||||||
maxSizeRef: maxSize,
|
maxSizeRef: maxSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 计算当前绑定的值,优先使用 modelValue
|
||||||
|
const currentValue = computed(() => {
|
||||||
|
return props.modelValue === undefined ? props.value : props.modelValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断是否使用 modelValue
|
||||||
|
const isUsingModelValue = computed(() => {
|
||||||
|
return props.modelValue !== undefined;
|
||||||
|
});
|
||||||
|
|
||||||
const fileList = ref<UploadProps['fileList']>([]);
|
const fileList = ref<UploadProps['fileList']>([]);
|
||||||
const isLtMsg = ref<boolean>(true); // 文件大小错误提示
|
const isLtMsg = ref<boolean>(true); // 文件大小错误提示
|
||||||
const isActMsg = ref<boolean>(true); // 文件类型错误提示
|
const isActMsg = ref<boolean>(true); // 文件类型错误提示
|
||||||
const isFirstRender = ref<boolean>(true); // 是否第一次渲染
|
const isFirstRender = ref<boolean>(true); // 是否第一次渲染
|
||||||
|
const uploadNumber = ref<number>(0); // 上传文件计数器
|
||||||
|
const uploadList = ref<any[]>([]); // 临时上传列表
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.value,
|
currentValue,
|
||||||
(v) => {
|
(v) => {
|
||||||
if (isInnerOperate.value) {
|
if (isInnerOperate.value) {
|
||||||
isInnerOperate.value = false;
|
isInnerOperate.value = false;
|
||||||
@@ -94,15 +115,40 @@ async function handleRemove(file: UploadFile) {
|
|||||||
const value = getValue();
|
const value = getValue();
|
||||||
isInnerOperate.value = true;
|
isInnerOperate.value = true;
|
||||||
emit('update:value', value);
|
emit('update:value', value);
|
||||||
|
emit('update:modelValue', value);
|
||||||
emit('change', value);
|
emit('change', value);
|
||||||
emit('delete', file);
|
emit('delete', file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理文件预览
|
||||||
|
function handlePreview(file: UploadFile) {
|
||||||
|
emit('preview', file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件数量超限
|
||||||
|
function handleExceed() {
|
||||||
|
message.error($t('ui.upload.maxNumber', [maxNumber.value]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理上传错误
|
||||||
|
function handleUploadError(error: any) {
|
||||||
|
console.error('上传错误:', error);
|
||||||
|
message.error($t('ui.upload.uploadError'));
|
||||||
|
// 上传失败时减少计数器
|
||||||
|
uploadNumber.value = Math.max(0, uploadNumber.value - 1);
|
||||||
|
}
|
||||||
|
|
||||||
async function beforeUpload(file: File) {
|
async function beforeUpload(file: File) {
|
||||||
const fileContent = await file.text();
|
const fileContent = await file.text();
|
||||||
emit('returnText', fileContent);
|
emit('returnText', fileContent);
|
||||||
|
|
||||||
|
// 检查文件数量限制
|
||||||
|
if (fileList.value!.length >= props.maxNumber) {
|
||||||
|
message.error($t('ui.upload.maxNumber', [props.maxNumber]));
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
const { maxSize, accept } = props;
|
const { maxSize, accept } = props;
|
||||||
const isAct = checkFileType(file, accept);
|
const isAct = checkFileType(file, accept);
|
||||||
if (!isAct) {
|
if (!isAct) {
|
||||||
@@ -110,6 +156,7 @@ async function beforeUpload(file: File) {
|
|||||||
isActMsg.value = false;
|
isActMsg.value = false;
|
||||||
// 防止弹出多个错误提示
|
// 防止弹出多个错误提示
|
||||||
setTimeout(() => (isActMsg.value = true), 1000);
|
setTimeout(() => (isActMsg.value = true), 1000);
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
}
|
}
|
||||||
const isLt = file.size / 1024 / 1024 > maxSize;
|
const isLt = file.size / 1024 / 1024 > maxSize;
|
||||||
if (isLt) {
|
if (isLt) {
|
||||||
@@ -117,8 +164,12 @@ async function beforeUpload(file: File) {
|
|||||||
isLtMsg.value = false;
|
isLtMsg.value = false;
|
||||||
// 防止弹出多个错误提示
|
// 防止弹出多个错误提示
|
||||||
setTimeout(() => (isLtMsg.value = true), 1000);
|
setTimeout(() => (isLtMsg.value = true), 1000);
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
}
|
}
|
||||||
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
|
||||||
|
// 只有在验证通过后才增加计数器
|
||||||
|
uploadNumber.value++;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function customRequest(info: UploadRequestOption<any>) {
|
async function customRequest(info: UploadRequestOption<any>) {
|
||||||
@@ -133,17 +184,48 @@ async function customRequest(info: UploadRequestOption<any>) {
|
|||||||
info.onProgress!({ percent });
|
info.onProgress!({ percent });
|
||||||
};
|
};
|
||||||
const res = await api?.(info.file as File, progressEvent);
|
const res = await api?.(info.file as File, progressEvent);
|
||||||
|
|
||||||
|
// 处理上传成功后的逻辑
|
||||||
|
handleUploadSuccess(res, info.file as File);
|
||||||
|
|
||||||
info.onSuccess!(res);
|
info.onSuccess!(res);
|
||||||
message.success($t('ui.upload.uploadSuccess'));
|
message.success($t('ui.upload.uploadSuccess'));
|
||||||
|
|
||||||
// 更新文件
|
|
||||||
const value = getValue();
|
|
||||||
isInnerOperate.value = true;
|
|
||||||
emit('update:value', value);
|
|
||||||
emit('change', value);
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
info.onError!(error);
|
info.onError!(error);
|
||||||
|
handleUploadError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理上传成功
|
||||||
|
function handleUploadSuccess(res: any, file: File) {
|
||||||
|
// 删除临时文件
|
||||||
|
const index = fileList.value?.findIndex((item) => item.name === file.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
fileList.value?.splice(index!, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到临时上传列表
|
||||||
|
const fileUrl = res?.url || res?.data || res;
|
||||||
|
uploadList.value.push({
|
||||||
|
name: file.name,
|
||||||
|
url: fileUrl,
|
||||||
|
status: UploadResultStatus.DONE,
|
||||||
|
uid: file.name + Date.now(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查是否所有文件都上传完成
|
||||||
|
if (uploadList.value.length >= uploadNumber.value) {
|
||||||
|
fileList.value?.push(...uploadList.value);
|
||||||
|
uploadList.value = [];
|
||||||
|
uploadNumber.value = 0;
|
||||||
|
|
||||||
|
// 更新值
|
||||||
|
const value = getValue();
|
||||||
|
isInnerOperate.value = true;
|
||||||
|
emit('update:value', value);
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
emit('change', value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,11 +238,26 @@ function getValue() {
|
|||||||
}
|
}
|
||||||
return item?.url || item?.response?.url || item?.response;
|
return item?.url || item?.response?.url || item?.response;
|
||||||
});
|
});
|
||||||
// add by 芋艿:【特殊】单个文件的情况,获取首个元素,保证返回的是 String 类型
|
|
||||||
|
// 单个文件的情况,根据输入参数类型决定返回格式
|
||||||
if (props.maxNumber === 1) {
|
if (props.maxNumber === 1) {
|
||||||
return list.length > 0 ? list[0] : '';
|
const singleValue = list.length > 0 ? list[0] : '';
|
||||||
|
// 如果原始值是字符串或 modelValue 是字符串,返回字符串
|
||||||
|
if (
|
||||||
|
isString(props.value) ||
|
||||||
|
(isUsingModelValue.value && isString(props.modelValue))
|
||||||
|
) {
|
||||||
|
return singleValue;
|
||||||
|
}
|
||||||
|
return singleValue;
|
||||||
}
|
}
|
||||||
return list;
|
|
||||||
|
// 多文件情况,根据输入参数类型决定返回格式
|
||||||
|
if (isUsingModelValue.value) {
|
||||||
|
return Array.isArray(props.modelValue) ? list : list.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.isArray(props.value) ? list : list.join(',');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -177,15 +274,34 @@ function getValue() {
|
|||||||
:multiple="multiple"
|
:multiple="multiple"
|
||||||
list-type="text"
|
list-type="text"
|
||||||
:progress="{ showInfo: true }"
|
:progress="{ showInfo: true }"
|
||||||
|
:show-upload-list="{
|
||||||
|
showPreviewIcon: true,
|
||||||
|
showRemoveIcon: true,
|
||||||
|
showDownloadIcon: true,
|
||||||
|
}"
|
||||||
@remove="handleRemove"
|
@remove="handleRemove"
|
||||||
|
@preview="handlePreview"
|
||||||
|
@reject="handleExceed"
|
||||||
>
|
>
|
||||||
<div v-if="fileList && fileList.length < maxNumber">
|
<div v-if="drag" class="upload-drag-area">
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<CloudUpload />
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">点击或拖拽文件到此区域上传</p>
|
||||||
|
<p class="ant-upload-hint">
|
||||||
|
支持{{ accept.join('/') }}格式文件,不超过{{ maxSize }}MB
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="fileList && fileList.length < maxNumber">
|
||||||
<Button>
|
<Button>
|
||||||
<CloudUpload />
|
<CloudUpload />
|
||||||
{{ $t('ui.upload.upload') }}
|
{{ $t('ui.upload.upload') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showDescription" class="mt-2 flex flex-wrap items-center">
|
<div
|
||||||
|
v-if="showDescription && !drag"
|
||||||
|
class="mt-2 flex flex-wrap items-center"
|
||||||
|
>
|
||||||
请上传不超过
|
请上传不超过
|
||||||
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
||||||
的
|
的
|
||||||
@@ -195,3 +311,35 @@ function getValue() {
|
|||||||
</Upload>
|
</Upload>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.upload-drag-area {
|
||||||
|
border: 2px dashed #d9d9d9;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #fafafa;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-drag-area:hover {
|
||||||
|
border-color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-upload-drag-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
color: #d9d9d9;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-upload-text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-upload-hint {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { FileUploadProps } from './typing';
|
|||||||
|
|
||||||
import type { AxiosProgressEvent } from '#/api/infra/file';
|
import type { AxiosProgressEvent } from '#/api/infra/file';
|
||||||
|
|
||||||
import { ref, toRefs, watch } from 'vue';
|
import { computed, ref, toRefs, watch } from 'vue';
|
||||||
|
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@@ -22,6 +22,7 @@ defineOptions({ name: 'ImageUpload', inheritAttrs: false });
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<FileUploadProps>(), {
|
const props = withDefaults(defineProps<FileUploadProps>(), {
|
||||||
value: () => [],
|
value: () => [],
|
||||||
|
modelValue: undefined,
|
||||||
directory: undefined,
|
directory: undefined,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
listType: 'picture-card',
|
listType: 'picture-card',
|
||||||
@@ -34,7 +35,12 @@ const props = withDefaults(defineProps<FileUploadProps>(), {
|
|||||||
resultField: '',
|
resultField: '',
|
||||||
showDescription: true,
|
showDescription: true,
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
const emit = defineEmits([
|
||||||
|
'change',
|
||||||
|
'update:value',
|
||||||
|
'update:modelValue',
|
||||||
|
'delete',
|
||||||
|
]);
|
||||||
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
||||||
const isInnerOperate = ref<boolean>(false);
|
const isInnerOperate = ref<boolean>(false);
|
||||||
const { getStringAccept } = useUploadType({
|
const { getStringAccept } = useUploadType({
|
||||||
@@ -43,6 +49,16 @@ const { getStringAccept } = useUploadType({
|
|||||||
maxNumberRef: maxNumber,
|
maxNumberRef: maxNumber,
|
||||||
maxSizeRef: maxSize,
|
maxSizeRef: maxSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 计算当前绑定的值,优先使用 modelValue
|
||||||
|
const currentValue = computed(() => {
|
||||||
|
return props.modelValue === undefined ? props.value : props.modelValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断是否使用 modelValue
|
||||||
|
const isUsingModelValue = computed(() => {
|
||||||
|
return props.modelValue !== undefined;
|
||||||
|
});
|
||||||
const previewOpen = ref<boolean>(false); // 是否展示预览
|
const previewOpen = ref<boolean>(false); // 是否展示预览
|
||||||
const previewImage = ref<string>(''); // 预览图片
|
const previewImage = ref<string>(''); // 预览图片
|
||||||
const previewTitle = ref<string>(''); // 预览标题
|
const previewTitle = ref<string>(''); // 预览标题
|
||||||
@@ -51,9 +67,11 @@ const fileList = ref<UploadProps['fileList']>([]);
|
|||||||
const isLtMsg = ref<boolean>(true); // 文件大小错误提示
|
const isLtMsg = ref<boolean>(true); // 文件大小错误提示
|
||||||
const isActMsg = ref<boolean>(true); // 文件类型错误提示
|
const isActMsg = ref<boolean>(true); // 文件类型错误提示
|
||||||
const isFirstRender = ref<boolean>(true); // 是否第一次渲染
|
const isFirstRender = ref<boolean>(true); // 是否第一次渲染
|
||||||
|
const uploadNumber = ref<number>(0); // 上传文件计数器
|
||||||
|
const uploadList = ref<any[]>([]); // 临时上传列表
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.value,
|
currentValue,
|
||||||
async (v) => {
|
async (v) => {
|
||||||
if (isInnerOperate.value) {
|
if (isInnerOperate.value) {
|
||||||
isInnerOperate.value = false;
|
isInnerOperate.value = false;
|
||||||
@@ -122,6 +140,7 @@ async function handleRemove(file: UploadFile) {
|
|||||||
const value = getValue();
|
const value = getValue();
|
||||||
isInnerOperate.value = true;
|
isInnerOperate.value = true;
|
||||||
emit('update:value', value);
|
emit('update:value', value);
|
||||||
|
emit('update:modelValue', value);
|
||||||
emit('change', value);
|
emit('change', value);
|
||||||
emit('delete', file);
|
emit('delete', file);
|
||||||
}
|
}
|
||||||
@@ -133,6 +152,12 @@ function handleCancel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function beforeUpload(file: File) {
|
async function beforeUpload(file: File) {
|
||||||
|
// 检查文件数量限制
|
||||||
|
if (fileList.value!.length >= props.maxNumber) {
|
||||||
|
message.error($t('ui.upload.maxNumber', [props.maxNumber]));
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
const { maxSize, accept } = props;
|
const { maxSize, accept } = props;
|
||||||
const isAct = checkImgType(file, accept);
|
const isAct = checkImgType(file, accept);
|
||||||
if (!isAct) {
|
if (!isAct) {
|
||||||
@@ -140,6 +165,7 @@ async function beforeUpload(file: File) {
|
|||||||
isActMsg.value = false;
|
isActMsg.value = false;
|
||||||
// 防止弹出多个错误提示
|
// 防止弹出多个错误提示
|
||||||
setTimeout(() => (isActMsg.value = true), 1000);
|
setTimeout(() => (isActMsg.value = true), 1000);
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
}
|
}
|
||||||
const isLt = file.size / 1024 / 1024 > maxSize;
|
const isLt = file.size / 1024 / 1024 > maxSize;
|
||||||
if (isLt) {
|
if (isLt) {
|
||||||
@@ -147,8 +173,12 @@ async function beforeUpload(file: File) {
|
|||||||
isLtMsg.value = false;
|
isLtMsg.value = false;
|
||||||
// 防止弹出多个错误提示
|
// 防止弹出多个错误提示
|
||||||
setTimeout(() => (isLtMsg.value = true), 1000);
|
setTimeout(() => (isLtMsg.value = true), 1000);
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
}
|
}
|
||||||
return (isAct && !isLt) || Upload.LIST_IGNORE;
|
|
||||||
|
// 只有在验证通过后才增加计数器
|
||||||
|
uploadNumber.value++;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function customRequest(info: UploadRequestOption<any>) {
|
async function customRequest(info: UploadRequestOption<any>) {
|
||||||
@@ -163,20 +193,59 @@ async function customRequest(info: UploadRequestOption<any>) {
|
|||||||
info.onProgress!({ percent });
|
info.onProgress!({ percent });
|
||||||
};
|
};
|
||||||
const res = await api?.(info.file as File, progressEvent);
|
const res = await api?.(info.file as File, progressEvent);
|
||||||
|
|
||||||
|
// 处理上传成功后的逻辑
|
||||||
|
handleUploadSuccess(res, info.file as File);
|
||||||
|
|
||||||
info.onSuccess!(res);
|
info.onSuccess!(res);
|
||||||
message.success($t('ui.upload.uploadSuccess'));
|
message.success($t('ui.upload.uploadSuccess'));
|
||||||
|
|
||||||
// 更新文件
|
|
||||||
const value = getValue();
|
|
||||||
isInnerOperate.value = true;
|
|
||||||
emit('update:value', value);
|
|
||||||
emit('change', value);
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
info.onError!(error);
|
info.onError!(error);
|
||||||
|
handleUploadError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理上传成功
|
||||||
|
function handleUploadSuccess(res: any, file: File) {
|
||||||
|
// 删除临时文件
|
||||||
|
const index = fileList.value?.findIndex((item) => item.name === file.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
fileList.value?.splice(index!, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到临时上传列表
|
||||||
|
const fileUrl = res?.url || res?.data || res;
|
||||||
|
uploadList.value.push({
|
||||||
|
name: file.name,
|
||||||
|
url: fileUrl,
|
||||||
|
status: UploadResultStatus.DONE,
|
||||||
|
uid: file.name + Date.now(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查是否所有文件都上传完成
|
||||||
|
if (uploadList.value.length >= uploadNumber.value) {
|
||||||
|
fileList.value?.push(...uploadList.value);
|
||||||
|
uploadList.value = [];
|
||||||
|
uploadNumber.value = 0;
|
||||||
|
|
||||||
|
// 更新值
|
||||||
|
const value = getValue();
|
||||||
|
isInnerOperate.value = true;
|
||||||
|
emit('update:value', value);
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
emit('change', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理上传错误
|
||||||
|
function handleUploadError(error: any) {
|
||||||
|
console.error('上传错误:', error);
|
||||||
|
message.error($t('ui.upload.uploadError'));
|
||||||
|
// 上传失败时减少计数器
|
||||||
|
uploadNumber.value = Math.max(0, uploadNumber.value - 1);
|
||||||
|
}
|
||||||
|
|
||||||
function getValue() {
|
function getValue() {
|
||||||
const list = (fileList.value || [])
|
const list = (fileList.value || [])
|
||||||
.filter((item) => item?.status === UploadResultStatus.DONE)
|
.filter((item) => item?.status === UploadResultStatus.DONE)
|
||||||
@@ -186,11 +255,26 @@ function getValue() {
|
|||||||
}
|
}
|
||||||
return item?.url || item?.response?.url || item?.response;
|
return item?.url || item?.response?.url || item?.response;
|
||||||
});
|
});
|
||||||
// add by 芋艿:【特殊】单个文件的情况,获取首个元素,保证返回的是 String 类型
|
|
||||||
|
// 单个文件的情况,根据输入参数类型决定返回格式
|
||||||
if (props.maxNumber === 1) {
|
if (props.maxNumber === 1) {
|
||||||
return list.length > 0 ? list[0] : '';
|
const singleValue = list.length > 0 ? list[0] : '';
|
||||||
|
// 如果原始值是字符串或 modelValue 是字符串,返回字符串
|
||||||
|
if (
|
||||||
|
isString(props.value) ||
|
||||||
|
(isUsingModelValue.value && isString(props.modelValue))
|
||||||
|
) {
|
||||||
|
return singleValue;
|
||||||
|
}
|
||||||
|
return singleValue;
|
||||||
}
|
}
|
||||||
return list;
|
|
||||||
|
// 多文件情况,根据输入参数类型决定返回格式
|
||||||
|
if (isUsingModelValue.value) {
|
||||||
|
return Array.isArray(props.modelValue) ? list : list.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.isArray(props.value) ? list : list.join(',');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ export interface FileUploadProps {
|
|||||||
// 上传的目录
|
// 上传的目录
|
||||||
directory?: string;
|
directory?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
drag?: boolean; // 是否支持拖拽上传
|
||||||
helpText?: string;
|
helpText?: string;
|
||||||
listType?: UploadListType;
|
listType?: UploadListType;
|
||||||
// 最大数量的文件,Infinity不限制
|
// 最大数量的文件,Infinity不限制
|
||||||
maxNumber?: number;
|
maxNumber?: number;
|
||||||
|
modelValue?: string | string[]; // v-model 支持
|
||||||
// 文件最大多少MB
|
// 文件最大多少MB
|
||||||
maxSize?: number;
|
maxSize?: number;
|
||||||
// 是否支持多选
|
// 是否支持多选
|
||||||
|
|||||||
@@ -61,15 +61,3 @@ export const MenuOutlined = createIconifyIcon('ant-design:menu-outlined');
|
|||||||
export const PlusOutlined = createIconifyIcon('ant-design:plus-outlined');
|
export const PlusOutlined = createIconifyIcon('ant-design:plus-outlined');
|
||||||
|
|
||||||
export const SelectOutlined = createIconifyIcon('ant-design:select-outlined');
|
export const SelectOutlined = createIconifyIcon('ant-design:select-outlined');
|
||||||
|
|
||||||
export const CheckCircleFilled = createIconifyIcon(
|
|
||||||
'ant-design:check-circle-filled',
|
|
||||||
);
|
|
||||||
|
|
||||||
export const ExclamationCircleFilled = createIconifyIcon(
|
|
||||||
'ant-design:exclamation-circle-filled',
|
|
||||||
);
|
|
||||||
|
|
||||||
export const QuestionCircleFilled = createIconifyIcon(
|
|
||||||
'ant-design:question-circle-filled',
|
|
||||||
);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user