commit
cac4efc227
|
@ -10,7 +10,12 @@ import {
|
|||
setupVbenVxeTable,
|
||||
useVbenVxeGrid,
|
||||
} from '@vben/plugins/vxe-table';
|
||||
import { isFunction, isString } from '@vben/utils';
|
||||
import {
|
||||
floatToFixed2,
|
||||
formatToFractionDigit,
|
||||
isFunction,
|
||||
isString,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { Button, Image, Popconfirm, Switch } from 'ant-design-vue';
|
||||
|
||||
|
@ -313,33 +318,13 @@ setupVbenVxeTable({
|
|||
// add by 星语:数量格式化,例如说:金额
|
||||
vxeUI.formats.add('formatNumber', {
|
||||
tableCellFormatMethod({ cellValue }, digits = 2) {
|
||||
if (cellValue === null || cellValue === undefined) {
|
||||
return '';
|
||||
}
|
||||
if (isString(cellValue)) {
|
||||
cellValue = Number.parseFloat(cellValue);
|
||||
}
|
||||
// 如果非 number,则直接返回空串
|
||||
if (Number.isNaN(cellValue)) {
|
||||
return '';
|
||||
}
|
||||
return cellValue.toFixed(digits);
|
||||
return formatToFractionDigit(cellValue, digits);
|
||||
},
|
||||
});
|
||||
|
||||
vxeUI.formats.add('formatAmount2', {
|
||||
tableCellFormatMethod({ cellValue }) {
|
||||
if (cellValue === null || cellValue === undefined) {
|
||||
return '0.00';
|
||||
}
|
||||
if (isString(cellValue)) {
|
||||
cellValue = Number.parseFloat(cellValue);
|
||||
}
|
||||
// 如果非 number,则直接返回空串
|
||||
if (Number.isNaN(cellValue)) {
|
||||
return '0.00';
|
||||
}
|
||||
return `${(cellValue / 100).toFixed(2)}元`;
|
||||
return `${floatToFixed2(cellValue)}元`;
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -23,6 +23,11 @@ interface DictTagProps {
|
|||
|
||||
const props = defineProps<DictTagProps>();
|
||||
|
||||
function isHexColor(color: string) {
|
||||
const reg = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
|
||||
return reg.test(color);
|
||||
}
|
||||
|
||||
/** 获取字典标签 */
|
||||
const dictTag = computed(() => {
|
||||
// 校验参数有效性
|
||||
|
@ -66,7 +71,16 @@ const dictTag = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Tag v-if="dictTag" :color="dictTag.colorType">
|
||||
<Tag
|
||||
v-if="dictTag"
|
||||
:color="
|
||||
dictTag.colorType
|
||||
? dictTag.colorType
|
||||
: dictTag.cssClass && isHexColor(dictTag.cssClass)
|
||||
? dictTag.cssClass
|
||||
: ''
|
||||
"
|
||||
>
|
||||
{{ dictTag.label }}
|
||||
</Tag>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import type { SimpleFlowNode } from '../../consts';
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
@ -53,8 +53,6 @@ const condition = ref<any>({
|
|||
},
|
||||
});
|
||||
|
||||
// 显示名称输入框
|
||||
const showInput = ref(false);
|
||||
const conditionRef = ref();
|
||||
const fieldOptions = useFormFieldsAndStartUser(); // 流程表单字段和发起人字段
|
||||
|
||||
|
@ -130,13 +128,24 @@ watch(
|
|||
currentNode.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
// 显示名称输入框
|
||||
const showInput = ref(false);
|
||||
// 输入框的引用
|
||||
const inputRef = ref<HTMLInputElement | null>(null);
|
||||
// 监听 showInput 的变化,当变为 true 时自动聚焦
|
||||
watch(showInput, (value) => {
|
||||
if (value) {
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
function clickIcon() {
|
||||
showInput.value = true;
|
||||
}
|
||||
|
||||
// 输入框失去焦点
|
||||
function blurEvent() {
|
||||
// 修改节点名称
|
||||
function changeNodeName() {
|
||||
showInput.value = false;
|
||||
currentNode.value.name =
|
||||
currentNode.value.name ||
|
||||
|
@ -153,10 +162,12 @@ defineExpose({ open }); // 提供 open 方法,用于打开弹窗
|
|||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="mr-2 w-48"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
|
|
|
@ -75,9 +75,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||
const currentNode = useWatchNode(props);
|
||||
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
|
||||
BpmNodeTypeEnum.COPY_TASK_NODE,
|
||||
);
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.COPY_TASK_NODE);
|
||||
|
||||
// 激活的 Tab 标签页
|
||||
const activeTabName = ref('user');
|
||||
|
@ -213,9 +212,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
|
|||
<div class="config-header">
|
||||
<Input
|
||||
v-if="showInput"
|
||||
ref="inputRef"
|
||||
type="text"
|
||||
class="config-editable-input"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
|
|
|
@ -45,9 +45,8 @@ const props = defineProps({
|
|||
// 当前节点
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
|
||||
BpmNodeTypeEnum.DELAY_TIMER_NODE,
|
||||
);
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.DELAY_TIMER_NODE);
|
||||
// 抄送人表单配置
|
||||
const formRef = ref(); // 表单 Ref
|
||||
|
||||
|
@ -158,9 +157,11 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
|
|||
<div class="flex items-center">
|
||||
<Input
|
||||
v-if="showInput"
|
||||
ref="inputRef"
|
||||
type="text"
|
||||
class="mr-2 w-48"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
Tooltip,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { BpmModelFormType } from '#/utils/constants';
|
||||
import { BpmModelFormType } from '#/utils';
|
||||
|
||||
import {
|
||||
COMPARISON_OPERATORS,
|
||||
|
|
|
@ -41,9 +41,8 @@ const processNodeTree = inject<Ref<SimpleFlowNode>>('processNodeTree');
|
|||
/** 当前节点 */
|
||||
const currentNode = useWatchNode(props);
|
||||
/** 节点名称 */
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
|
||||
BpmNodeTypeEnum.ROUTER_BRANCH_NODE,
|
||||
);
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.ROUTER_BRANCH_NODE);
|
||||
const routerGroups = ref<RouterSetting[]>([]);
|
||||
const nodeOptions = ref<any[]>([]);
|
||||
const conditionRef = ref<any[]>([]);
|
||||
|
@ -205,10 +204,12 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
|
|||
<template #title>
|
||||
<div class="flex items-center">
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="mr-2 w-48"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
|
|
|
@ -52,9 +52,8 @@ const deptOptions = inject<Ref<SystemDeptApi.Dept[]>>('deptList');
|
|||
// 当前节点
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
|
||||
BpmNodeTypeEnum.START_USER_NODE,
|
||||
);
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.START_USER_NODE);
|
||||
// 激活的 Tab 标签页
|
||||
const activeTabName = ref('user');
|
||||
|
||||
|
@ -145,12 +144,13 @@ defineExpose({ showStartUserNodeConfig });
|
|||
<Drawer>
|
||||
<template #title>
|
||||
<div class="config-header">
|
||||
<!-- TODO v-mountedFocus 自动聚集 需要迁移一下 -->
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="config-editable-input"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
|
|
|
@ -72,9 +72,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||
// 当前节点
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
|
||||
BpmNodeTypeEnum.TRIGGER_NODE,
|
||||
);
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.TRIGGER_NODE);
|
||||
// 触发器表单配置
|
||||
const formRef = ref(); // 表单 Ref
|
||||
|
||||
|
@ -388,10 +387,12 @@ onMounted(() => {
|
|||
<template #title>
|
||||
<div class="config-header">
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="config-editable-input"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
|
|
|
@ -114,9 +114,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||
});
|
||||
|
||||
// 节点名称配置
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
|
||||
BpmNodeTypeEnum.USER_TASK_NODE,
|
||||
);
|
||||
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
|
||||
useNodeName(BpmNodeTypeEnum.USER_TASK_NODE);
|
||||
|
||||
// 激活的 Tab 标签页
|
||||
const activeTabName = ref('user');
|
||||
|
@ -586,9 +585,11 @@ onMounted(() => {
|
|||
<div class="config-header">
|
||||
<Input
|
||||
v-if="showInput"
|
||||
ref="inputRef"
|
||||
type="text"
|
||||
class="config-editable-input"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
|
|
|
@ -32,7 +32,7 @@ const readonly = inject<Boolean>('readonly');
|
|||
// 监控节点的变化
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(
|
||||
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||
currentNode,
|
||||
BpmNodeTypeEnum.COPY_TASK_NODE,
|
||||
);
|
||||
|
@ -67,11 +67,13 @@ function deleteNode() {
|
|||
<span class="iconfont icon-copy"></span>
|
||||
</div>
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
v-model="currentNode.name"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
<div v-else class="node-title" @click="clickTitle">
|
||||
|
|
|
@ -30,7 +30,7 @@ const readonly = inject<Boolean>('readonly');
|
|||
// 监控节点的变化
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(
|
||||
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||
currentNode,
|
||||
BpmNodeTypeEnum.DELAY_TIMER_NODE,
|
||||
);
|
||||
|
@ -64,11 +64,13 @@ function deleteNode() {
|
|||
<span class="iconfont icon-delay"></span>
|
||||
</div>
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
v-model="currentNode.name"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
<div v-else class="node-title" @click="clickTitle">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import type { SimpleFlowNode } from '../../consts';
|
||||
|
||||
import { getCurrentInstance, inject, ref, watch } from 'vue';
|
||||
import { getCurrentInstance, inject, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils';
|
||||
|
@ -51,11 +51,30 @@ watch(
|
|||
currentNode.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
// 条件节点名称输入框引用
|
||||
const inputRefs = ref<HTMLInputElement[]>([]);
|
||||
// 节点名称输入框显示状态
|
||||
const showInputs = ref<boolean[]>([]);
|
||||
|
||||
// 失去焦点
|
||||
function blurEvent(index: number) {
|
||||
// 监听显示状态变化
|
||||
watch(
|
||||
showInputs,
|
||||
(newValues) => {
|
||||
// 当状态为 true 时, 自动聚焦
|
||||
newValues.forEach((value, index) => {
|
||||
if (value) {
|
||||
// 当显示状态从 false 变为 true 时, 自动聚焦
|
||||
nextTick(() => {
|
||||
inputRefs.value[index]?.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
// 修改节点名称
|
||||
function changeNodeName(index: number) {
|
||||
showInputs.value[index] = false;
|
||||
const conditionNode = currentNode.value.conditionNodes?.at(
|
||||
index,
|
||||
|
@ -188,10 +207,16 @@ function recursiveFindParentNode(
|
|||
<div class="branch-node-title-container">
|
||||
<div v-if="!readonly && showInputs[index]">
|
||||
<Input
|
||||
:ref="
|
||||
(el) => {
|
||||
inputRefs[index] = el as HTMLInputElement;
|
||||
}
|
||||
"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent(index)"
|
||||
v-model="item.name"
|
||||
@blur="changeNodeName(index)"
|
||||
@press-enter="changeNodeName(index)"
|
||||
v-model:value="item.name"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="branch-title" @click="clickEvent(index)">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import type { SimpleFlowNode } from '../../consts';
|
||||
|
||||
import { getCurrentInstance, inject, ref, watch } from 'vue';
|
||||
import { getCurrentInstance, inject, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils';
|
||||
|
@ -57,10 +57,28 @@ watch(
|
|||
currentNode.value = newValue;
|
||||
},
|
||||
);
|
||||
|
||||
// 条件节点名称输入框引用
|
||||
const inputRefs = ref<HTMLInputElement[]>([]);
|
||||
// 节点名称输入框显示状态
|
||||
const showInputs = ref<boolean[]>([]);
|
||||
// 失去焦点
|
||||
function blurEvent(index: number) {
|
||||
// 监听显示状态变化
|
||||
watch(
|
||||
showInputs,
|
||||
(newValues) => {
|
||||
// 当状态为 true 时, 自动聚焦
|
||||
newValues.forEach((value, index) => {
|
||||
if (value) {
|
||||
// 当显示状态从 false 变为 true 时, 自动聚焦
|
||||
nextTick(() => {
|
||||
inputRefs.value[index]?.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
// 修改节点名称
|
||||
function changeNodeName(index: number) {
|
||||
showInputs.value[index] = false;
|
||||
const conditionNode = currentNode.value.conditionNodes?.at(
|
||||
index,
|
||||
|
@ -192,10 +210,16 @@ function recursiveFindParentNode(
|
|||
<div class="branch-node-title-container">
|
||||
<div v-if="!readonly && showInputs[index]">
|
||||
<Input
|
||||
:ref="
|
||||
(el) => {
|
||||
inputRefs[index] = el as HTMLInputElement;
|
||||
}
|
||||
"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent(index)"
|
||||
v-model="item.name"
|
||||
@blur="changeNodeName(index)"
|
||||
@press-enter="changeNodeName(index)"
|
||||
v-model:value="item.name"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="branch-title" @click="clickEvent(index)">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import type { SimpleFlowNode } from '../../consts';
|
||||
|
||||
import { inject, ref, watch } from 'vue';
|
||||
import { inject, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { buildShortUUID as generateUUID } from '@vben/utils';
|
||||
|
@ -46,10 +46,28 @@ watch(
|
|||
},
|
||||
);
|
||||
|
||||
// 条件节点名称输入框引用
|
||||
const inputRefs = ref<HTMLInputElement[]>([]);
|
||||
// 节点名称输入框显示状态
|
||||
const showInputs = ref<boolean[]>([]);
|
||||
|
||||
// 失去焦点
|
||||
function blurEvent(index: number) {
|
||||
// 监听显示状态变化
|
||||
watch(
|
||||
showInputs,
|
||||
(newValues) => {
|
||||
// 当输入框显示时, 自动聚焦
|
||||
newValues.forEach((value, index) => {
|
||||
if (value) {
|
||||
// 当显示状态从 false 变为 true 时, 自动聚焦
|
||||
nextTick(() => {
|
||||
inputRefs.value[index]?.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
// 修改节点名称
|
||||
function changeNodeName(index: number) {
|
||||
showInputs.value[index] = false;
|
||||
const conditionNode = currentNode.value.conditionNodes?.at(
|
||||
index,
|
||||
|
@ -150,10 +168,16 @@ function recursiveFindParentNode(
|
|||
<div class="branch-node-title-container">
|
||||
<div v-if="showInputs[index]">
|
||||
<Input
|
||||
:ref="
|
||||
(el) => {
|
||||
inputRefs[index] = el as HTMLInputElement;
|
||||
}
|
||||
"
|
||||
type="text"
|
||||
class="input-max-width editable-title-input"
|
||||
@blur="blurEvent(index)"
|
||||
v-model="item.name"
|
||||
@blur="changeNodeName(index)"
|
||||
@press-enter="changeNodeName(index)"
|
||||
v-model:value="item.name"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="branch-title" @click="clickEvent(index)">
|
||||
|
|
|
@ -33,7 +33,7 @@ const readonly = inject<Boolean>('readonly');
|
|||
// 监控节点的变化
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(
|
||||
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||
currentNode,
|
||||
BpmNodeTypeEnum.ROUTER_BRANCH_NODE,
|
||||
);
|
||||
|
@ -67,11 +67,13 @@ function deleteNode() {
|
|||
<span class="iconfont icon-router"></span>
|
||||
</div>
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
v-model="currentNode.name"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
<div v-else class="node-title" @click="clickTitle">
|
||||
|
|
|
@ -37,7 +37,7 @@ const tasks = inject<Ref<any[]>>('tasks', ref([]));
|
|||
// 监控节点变化
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(
|
||||
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||
currentNode,
|
||||
BpmNodeTypeEnum.START_USER_NODE,
|
||||
);
|
||||
|
@ -81,10 +81,12 @@ function nodeClick() {
|
|||
<span class="iconfont icon-start-user"></span>
|
||||
</div>
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
|
|
|
@ -35,7 +35,7 @@ const readonly = inject<Boolean>('readonly');
|
|||
// 监控节点的变化
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(
|
||||
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||
currentNode,
|
||||
BpmNodeTypeEnum.TRIGGER_NODE,
|
||||
);
|
||||
|
@ -69,11 +69,13 @@ function deleteNode() {
|
|||
<span class="iconfont icon-trigger"></span>
|
||||
</div>
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
v-model="currentNode.name"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
<div v-else class="node-title" @click="clickTitle">
|
||||
|
|
|
@ -36,7 +36,7 @@ const tasks = inject<Ref<any[]>>('tasks', ref([]));
|
|||
// 监控节点变化
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(
|
||||
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
|
||||
currentNode,
|
||||
BpmNodeTypeEnum.USER_TASK_NODE,
|
||||
);
|
||||
|
@ -87,11 +87,13 @@ function findReturnTaskNodes(
|
|||
</span>
|
||||
</div>
|
||||
<Input
|
||||
ref="inputRef"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
v-model="currentNode.name"
|
||||
@blur="changeNodeName()"
|
||||
@press-enter="changeNodeName()"
|
||||
v-model:value="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
<div v-else class="node-title" @click="clickTitle">
|
||||
|
|
|
@ -12,7 +12,7 @@ import type { SystemPostApi } from '#/api/system/post';
|
|||
import type { SystemRoleApi } from '#/api/system/role';
|
||||
import type { SystemUserApi } from '#/api/system/user';
|
||||
|
||||
import { inject, ref, toRaw, unref, watch } from 'vue';
|
||||
import { inject, nextTick, ref, toRaw, unref, watch } from 'vue';
|
||||
|
||||
import {
|
||||
BpmNodeTypeEnum,
|
||||
|
@ -622,21 +622,33 @@ export function useNodeName(nodeType: BpmNodeTypeEnum) {
|
|||
const nodeName = ref<string>();
|
||||
// 节点名称输入框
|
||||
const showInput = ref(false);
|
||||
// 输入框的引用
|
||||
const inputRef = ref<HTMLInputElement | null>(null);
|
||||
// 点击节点名称编辑图标
|
||||
function clickIcon() {
|
||||
showInput.value = true;
|
||||
}
|
||||
// 节点名称输入框失去焦点
|
||||
function blurEvent() {
|
||||
// 修改节点名称
|
||||
function changeNodeName() {
|
||||
showInput.value = false;
|
||||
nodeName.value =
|
||||
nodeName.value || (NODE_DEFAULT_NAME.get(nodeType) as string);
|
||||
}
|
||||
// 监听 showInput 的变化,当变为 true 时自动聚焦
|
||||
watch(showInput, (value) => {
|
||||
if (value) {
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
nodeName,
|
||||
showInput,
|
||||
inputRef,
|
||||
clickIcon,
|
||||
blurEvent,
|
||||
changeNodeName,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -646,11 +658,24 @@ export function useNodeName2(
|
|||
) {
|
||||
// 显示节点名称输入框
|
||||
const showInput = ref(false);
|
||||
// 节点名称输入框失去焦点
|
||||
function blurEvent() {
|
||||
// 输入框的引用
|
||||
const inputRef = ref<HTMLInputElement | null>(null);
|
||||
|
||||
// 监听 showInput 的变化,当变为 true 时自动聚焦
|
||||
watch(showInput, (value) => {
|
||||
if (value) {
|
||||
nextTick(() => {
|
||||
inputRef.value?.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 修改节点名称
|
||||
function changeNodeName() {
|
||||
showInput.value = false;
|
||||
node.value.name =
|
||||
node.value.name || (NODE_DEFAULT_NAME.get(nodeType) as string);
|
||||
console.warn('node.value.name===>', node.value.name);
|
||||
}
|
||||
// 点击节点标题进行输入
|
||||
function clickTitle() {
|
||||
|
@ -658,8 +683,9 @@ export function useNodeName2(
|
|||
}
|
||||
return {
|
||||
showInput,
|
||||
inputRef,
|
||||
clickTitle,
|
||||
blurEvent,
|
||||
changeNodeName,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -43,28 +43,27 @@ const { hasAccessByCodes } = useAccess();
|
|||
|
||||
function isIfShow(action: ActionItem): boolean {
|
||||
const ifShow = action.ifShow;
|
||||
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(ifShow)) {
|
||||
isIfShow = ifShow;
|
||||
}
|
||||
if (isFunction(ifShow)) {
|
||||
isIfShow = ifShow(action);
|
||||
}
|
||||
if (isIfShow) {
|
||||
isIfShow =
|
||||
hasAccessByCodes(action.auth || []) || (action.auth || []).length === 0;
|
||||
}
|
||||
return isIfShow;
|
||||
}
|
||||
|
||||
const getActions = computed(() => {
|
||||
return (toRaw(props.actions) || [])
|
||||
.filter((action) => {
|
||||
return (
|
||||
(hasAccessByCodes(action.auth || []) ||
|
||||
(action.auth || []).length === 0) &&
|
||||
isIfShow(action)
|
||||
);
|
||||
const actions = toRaw(props.actions) || [];
|
||||
return actions
|
||||
.filter((action: ActionItem) => {
|
||||
return isIfShow(action);
|
||||
})
|
||||
.map((action) => {
|
||||
.map((action: ActionItem) => {
|
||||
const { popConfirm } = action;
|
||||
return {
|
||||
type: action.type || 'link',
|
||||
|
@ -78,24 +77,21 @@ const getActions = computed(() => {
|
|||
});
|
||||
|
||||
const getDropdownList = computed((): any[] => {
|
||||
return (toRaw(props.dropDownActions) || [])
|
||||
.filter((action) => {
|
||||
return (
|
||||
(hasAccessByCodes(action.auth || []) ||
|
||||
(action.auth || []).length === 0) &&
|
||||
isIfShow(action)
|
||||
);
|
||||
const dropDownActions = toRaw(props.dropDownActions) || [];
|
||||
return dropDownActions
|
||||
.filter((action: ActionItem) => {
|
||||
return isIfShow(action);
|
||||
})
|
||||
.map((action, index) => {
|
||||
.map((action: ActionItem, index: number) => {
|
||||
const { label, popConfirm } = action;
|
||||
delete action.icon;
|
||||
return {
|
||||
...action,
|
||||
...popConfirm,
|
||||
onConfirm: popConfirm?.confirm,
|
||||
onCancel: popConfirm?.cancel,
|
||||
text: label,
|
||||
divider:
|
||||
index < props.dropDownActions.length - 1 ? props.divider : false,
|
||||
divider: index < dropDownActions.length - 1 ? props.divider : false,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -86,6 +86,18 @@ const routes: RouteRecordRaw[] = [
|
|||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'manager/definition',
|
||||
component: () => import('#/views/bpm/model/definition/index.vue'),
|
||||
name: 'BpmProcessDefinition',
|
||||
meta: {
|
||||
title: '流程定义',
|
||||
activePath: '/bpm/manager/model',
|
||||
icon: 'carbon:flow-modeler',
|
||||
hideInMenu: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export * from './constants';
|
||||
export * from './dict';
|
||||
export * from './download';
|
||||
export * from './formatNumber';
|
||||
export * from './formCreate';
|
||||
export * from './rangePickerProps';
|
||||
export * from './routerHelper';
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { BpmProcessDefinitionApi } from '#/api/bpm/definition';
|
||||
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns(): VxeTableGridOptions<BpmProcessDefinitionApi.ProcessDefinitionVO>['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '定义编号',
|
||||
minWidth: 250,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '流程名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
title: '流程图标',
|
||||
minWidth: 100,
|
||||
slots: { default: 'icon' },
|
||||
},
|
||||
{
|
||||
field: 'startUsers',
|
||||
title: '可见范围',
|
||||
minWidth: 100,
|
||||
slots: { default: 'startUsers' },
|
||||
},
|
||||
{
|
||||
field: 'modelType',
|
||||
title: '流程类型',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.BPM_MODEL_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'formType',
|
||||
title: '表单信息',
|
||||
minWidth: 150,
|
||||
slots: { default: 'formInfo' },
|
||||
},
|
||||
{
|
||||
field: 'version',
|
||||
title: '流程版本',
|
||||
minWidth: 80,
|
||||
slots: { default: 'version' },
|
||||
},
|
||||
{
|
||||
field: 'deploymentTime',
|
||||
title: '部署时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
];
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Image, Tag, Tooltip } from 'ant-design-vue';
|
||||
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getProcessDefinitionPage } from '#/api/bpm/definition';
|
||||
import { BpmModelFormType } from '#/utils';
|
||||
|
||||
// 导入 FormCreate 表单详情
|
||||
import FormCreateDetail from '../../form/modules/detail.vue';
|
||||
import { useGridColumns } from './data';
|
||||
|
||||
defineOptions({ name: 'BpmProcessDefinition' });
|
||||
|
||||
const [FormCreateDetailModal, formCreateDetailModalApi] = useVbenModal({
|
||||
connectedComponent: FormCreateDetail,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 查看表单详情 */
|
||||
function handleFormDetail(row: any) {
|
||||
if (row.formType === BpmModelFormType.NORMAL) {
|
||||
const data = {
|
||||
id: row.formId,
|
||||
};
|
||||
formCreateDetailModalApi.setData(data).open();
|
||||
} else {
|
||||
// TODO 待实现
|
||||
console.warn('业务表单待实现', row);
|
||||
}
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
/** 恢复流程模型 */
|
||||
async function openModelForm(id?: number) {
|
||||
await router.push({
|
||||
name: 'BpmModelUpdate',
|
||||
params: { id, type: 'definition' },
|
||||
});
|
||||
}
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: true,
|
||||
},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
const params = {
|
||||
pageNo: page?.currentPage,
|
||||
pageSize: page?.pageSize,
|
||||
key: route.query.key,
|
||||
};
|
||||
return await getProcessDefinitionPage(params);
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
},
|
||||
} as VxeTableGridOptions,
|
||||
});
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
onRefresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
|
||||
</template>
|
||||
<Grid table-title="流程定义列表">
|
||||
<template #icon="{ row }">
|
||||
<Image
|
||||
v-if="row.icon"
|
||||
:src="row.icon"
|
||||
:width="24"
|
||||
:height="24"
|
||||
class="rounded"
|
||||
/>
|
||||
<span v-else> 无图标 </span>
|
||||
</template>
|
||||
<template #startUsers="{ row }">
|
||||
<template v-if="!row.startUsers?.length">全部可见</template>
|
||||
<template v-else-if="row.startUsers.length === 1">
|
||||
{{ row.startUsers[0].nickname }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
:title="row.startUsers.map((user: any) => user.nickname).join(',')"
|
||||
>
|
||||
{{ row.startUsers[0].nickname }}等
|
||||
{{ row.startUsers.length }} 人可见
|
||||
</Tooltip>
|
||||
</template>
|
||||
</template>
|
||||
<template #formInfo="{ row }">
|
||||
<Button
|
||||
v-if="row.formType === BpmModelFormType.NORMAL"
|
||||
type="link"
|
||||
@click="handleFormDetail(row)"
|
||||
>
|
||||
<span>{{ row.formName }}</span>
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="row.formType === BpmModelFormType.CUSTOM"
|
||||
type="link"
|
||||
@click="handleFormDetail(row)"
|
||||
>
|
||||
<span>{{ row.formCustomCreatePath }}</span>
|
||||
</Button>
|
||||
<span v-else>暂无表单</span>
|
||||
</template>
|
||||
<template #version="{ row }">
|
||||
<Tag>v{{ row.version }}</Tag>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '恢复',
|
||||
type: 'link',
|
||||
auth: ['bpm:model:update'],
|
||||
onClick: openModelForm.bind(null, row.id),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<FormCreateDetailModal />
|
||||
</Page>
|
||||
</template>
|
|
@ -26,8 +26,11 @@ import {
|
|||
HttpRequestSetting,
|
||||
parseFormFields,
|
||||
} from '#/components/simple-process-design';
|
||||
import { ProcessVariableEnum } from '#/utils';
|
||||
import { BpmAutoApproveType, BpmModelFormType } from '#/utils/constants';
|
||||
import {
|
||||
BpmAutoApproveType,
|
||||
BpmModelFormType,
|
||||
ProcessVariableEnum,
|
||||
} from '#/utils';
|
||||
|
||||
const modelData = defineModel<any>();
|
||||
|
||||
|
|
|
@ -178,13 +178,13 @@ async function handleCategorySortSubmit() {
|
|||
<template #overlay>
|
||||
<Menu @click="(e) => handleCommand(e.key as string)">
|
||||
<Menu.Item key="handleCategoryAdd">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center gap-1">
|
||||
<IconifyIcon icon="lucide:plus" />
|
||||
新建分类
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="handleCategorySort">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center gap-1">
|
||||
<IconifyIcon icon="lucide:align-start-vertical" />
|
||||
分类排序
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { BpmModelApi, ModelCategoryInfo } from '#/api/bpm/model';
|
|||
import { computed, ref, watchEffect } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { confirm, useVbenModal } from '@vben/common-ui';
|
||||
import { confirm, EllipsisText, useVbenModal } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
import { cloneDeep, formatDateTime, isEqual } from '@vben/utils';
|
||||
|
@ -33,10 +33,12 @@ import {
|
|||
} from '#/api/bpm/model';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { $t } from '#/locales';
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
import { BpmModelFormType, DICT_TYPE } from '#/utils';
|
||||
|
||||
// 导入重命名表单
|
||||
import CategoryRenameForm from '../../category/modules/rename-form.vue';
|
||||
// 导入 FormCreate 表单详情
|
||||
import FormCreateDetail from '../../form/modules/detail.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
categoryInfo: ModelCategoryInfo;
|
||||
|
@ -45,12 +47,18 @@ const props = defineProps<{
|
|||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
// 重命名分类对话框
|
||||
/** 重命名分类对话框 */
|
||||
const [CategoryRenameModal, categoryRenameModalApi] = useVbenModal({
|
||||
connectedComponent: CategoryRenameForm,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 流程表单详情对话框 */
|
||||
const [FormCreateDetailModal, formCreateDetailModalApi] = useVbenModal({
|
||||
connectedComponent: FormCreateDetail,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
// 获取当前登录用户Id
|
||||
const userStore = useUserStore();
|
||||
|
@ -73,8 +81,7 @@ const columns = [
|
|||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
align: 'left' as const,
|
||||
ellipsis: true,
|
||||
width: 230,
|
||||
width: 250,
|
||||
},
|
||||
{
|
||||
title: '可见范围',
|
||||
|
@ -193,8 +200,15 @@ async function handleDeleteCategory() {
|
|||
|
||||
/** 处理表单详情点击 */
|
||||
function handleFormDetail(row: any) {
|
||||
// TODO 待实现
|
||||
console.warn('待实现', row);
|
||||
if (row.formType === BpmModelFormType.NORMAL) {
|
||||
const data = {
|
||||
id: row.formId,
|
||||
};
|
||||
formCreateDetailModalApi.setData(data).open();
|
||||
} else {
|
||||
// TODO 待实现
|
||||
console.warn('业务表单待实现', row);
|
||||
}
|
||||
}
|
||||
|
||||
/** 判断是否是流程管理员 */
|
||||
|
@ -243,7 +257,7 @@ function handleModelCommand(command: string, row: any) {
|
|||
break;
|
||||
}
|
||||
case 'handleDefinitionList': {
|
||||
console.warn('历史待实现', row);
|
||||
handleDefinitionList(row);
|
||||
break;
|
||||
}
|
||||
case 'handleDelete': {
|
||||
|
@ -317,6 +331,16 @@ function handleDelete(row: any) {
|
|||
});
|
||||
}
|
||||
|
||||
/** 跳转到指定流程定义列表 */
|
||||
function handleDefinitionList(row: any) {
|
||||
router.push({
|
||||
name: 'BpmProcessDefinition',
|
||||
query: {
|
||||
key: row.key,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 更新 modelList 模型列表 */
|
||||
const updateModelList = useDebounceFn(() => {
|
||||
const newModelList = props.categoryInfo.modelList;
|
||||
|
@ -486,7 +510,9 @@ const handleRenameSuccess = () => {
|
|||
class="mr-2.5 h-9 w-9 rounded"
|
||||
alt="图标"
|
||||
/>
|
||||
{{ record.name }}
|
||||
<EllipsisText :max-width="160" :tooltip-when-ellipsis="true">
|
||||
{{ record.name }}
|
||||
</EllipsisText>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -543,7 +569,7 @@ const handleRenameSuccess = () => {
|
|||
<template v-else-if="column.key === 'formType'">
|
||||
<!-- TODO BpmModelFormType.NORMAL -->
|
||||
<Button
|
||||
v-if="record.formType === 10"
|
||||
v-if="record.formType === BpmModelFormType.NORMAL"
|
||||
type="link"
|
||||
@click="handleFormDetail(record)"
|
||||
>
|
||||
|
@ -551,7 +577,7 @@ const handleRenameSuccess = () => {
|
|||
</Button>
|
||||
<!-- TODO BpmModelFormType.CUSTOM -->
|
||||
<Button
|
||||
v-else-if="record.formType === 20"
|
||||
v-else-if="record.formType === BpmModelFormType.CUSTOM"
|
||||
type="link"
|
||||
@click="handleFormDetail(record)"
|
||||
>
|
||||
|
@ -604,7 +630,6 @@ const handleRenameSuccess = () => {
|
|||
</Button>
|
||||
<Dropdown placement="bottomRight" arrow>
|
||||
<Button type="link" size="small" class="px-1">更多</Button>
|
||||
<!-- TODO 待实现 -->
|
||||
<template #overlay>
|
||||
<Menu
|
||||
@click="
|
||||
|
@ -613,12 +638,14 @@ const handleRenameSuccess = () => {
|
|||
>
|
||||
<Menu.Item key="handleCopy"> 复制 </Menu.Item>
|
||||
<Menu.Item key="handleDefinitionList"> 历史 </Menu.Item>
|
||||
|
||||
<!-- TODO 待实现报表
|
||||
<Menu.Item
|
||||
key="handleReport"
|
||||
:disabled="!isManagerUser(record)"
|
||||
>
|
||||
报表
|
||||
</Menu.Item>
|
||||
</Menu.Item> -->
|
||||
<Menu.Item
|
||||
key="handleChangeState"
|
||||
v-if="record.processDefinition"
|
||||
|
@ -657,6 +684,8 @@ const handleRenameSuccess = () => {
|
|||
|
||||
<!-- 重命名分类弹窗 -->
|
||||
<CategoryRenameModal @success="handleRenameSuccess" />
|
||||
<!-- 流程表单详情对话框 -->
|
||||
<FormCreateDetailModal />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { erpPriceMultiply } from '@vben/utils';
|
||||
|
||||
import { getBusinessStatusTypeSimpleList } from '#/api/crm/business/status';
|
||||
import { getCustomerSimpleList } from '#/api/crm/customer';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { erpPriceMultiply } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
|
@ -167,7 +168,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'totalPrice',
|
||||
title: '商机金额(元)',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'dealTime',
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { DescriptionItemSchema } from '#/components/description';
|
||||
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { erpPriceInputFormatter } from '#/utils';
|
||||
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
|
||||
|
||||
/** 详情页的字段 */
|
||||
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
|
@ -97,7 +95,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'totalPrice',
|
||||
title: '商机金额(元)',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'dealTime',
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { CrmBusinessApi } from '#/api/crm/business';
|
|||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { erpPriceMultiply } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
|
@ -15,7 +16,6 @@ import {
|
|||
} from '#/api/crm/business';
|
||||
import { BizTypeEnum } from '#/api/crm/permission';
|
||||
import { $t } from '#/locales';
|
||||
import { erpPriceMultiply } from '#/utils';
|
||||
import { ProductEditTable } from '#/views/crm/product';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { erpPriceMultiply, floatToFixed2 } from '@vben/utils';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { getSimpleBusinessList } from '#/api/crm/business';
|
||||
import { getSimpleContactList } from '#/api/crm/contact';
|
||||
import { getCustomerSimpleList } from '#/api/crm/customer';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { erpPriceMultiply, floatToFixed2 } from '#/utils';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
|
@ -242,7 +243,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '合同金额(元)',
|
||||
field: 'totalPrice',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '下单时间',
|
||||
|
@ -277,7 +278,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '已回款金额(元)',
|
||||
field: 'totalReceivablePrice',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '未回款金额(元)',
|
||||
|
|
|
@ -3,11 +3,14 @@ import type { DescriptionItemSchema } from '#/components/description';
|
|||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
import {
|
||||
erpPriceInputFormatter,
|
||||
floatToFixed2,
|
||||
formatDateTime,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE, erpPriceInputFormatter, floatToFixed2 } from '#/utils';
|
||||
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
/** 详情头部的配置 */
|
||||
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
return [
|
||||
|
@ -120,7 +123,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '合同金额(元)',
|
||||
field: 'totalPrice',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '合同开始时间',
|
||||
|
@ -138,7 +141,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '已回款金额(元)',
|
||||
field: 'totalReceivablePrice',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '未回款金额(元)',
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { CrmContractApi } from '#/api/crm/contract';
|
|||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenForm, useVbenModal } from '@vben/common-ui';
|
||||
import { erpPriceMultiply } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
|
@ -14,7 +15,6 @@ import {
|
|||
} from '#/api/crm/contract';
|
||||
import { BizTypeEnum } from '#/api/crm/permission';
|
||||
import { $t } from '#/locales';
|
||||
import { erpPriceMultiply } from '#/utils';
|
||||
import { ProductEditTable } from '#/views/crm/product';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
|
|
|
@ -140,7 +140,7 @@ async function handleQuit() {
|
|||
message.warning('你不是团队成员!');
|
||||
return;
|
||||
}
|
||||
await deleteSelfPermission(userPermission.id);
|
||||
await deleteSelfPermission(userPermission.id as number);
|
||||
message.success('退出团队成员成功!');
|
||||
emits('quitTeam');
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'price',
|
||||
title: '价格(元)',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
|
@ -203,7 +203,7 @@ export function useProductEditTableColumns(): VxeTableGridOptions['columns'] {
|
|||
field: 'productPrice',
|
||||
title: '价格(元)',
|
||||
minWidth: 100,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'sellingPrice',
|
||||
|
@ -221,7 +221,7 @@ export function useProductEditTableColumns(): VxeTableGridOptions['columns'] {
|
|||
field: 'totalPrice',
|
||||
title: '合计',
|
||||
minWidth: 100,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
|
|
|
@ -3,8 +3,10 @@ import type { DescriptionItemSchema } from '#/components/description';
|
|||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { erpPriceInputFormatter } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE, erpPriceInputFormatter } from '#/utils';
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
|
||||
/** 详情页的字段 */
|
||||
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
|
@ -94,12 +96,12 @@ export function useDetailListColumns(
|
|||
{
|
||||
field: 'productPrice',
|
||||
title: '产品价格(元)',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'businessPrice',
|
||||
title: '商机价格(元)',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
visible: showBussinePrice,
|
||||
},
|
||||
{
|
||||
|
@ -110,7 +112,7 @@ export function useDetailListColumns(
|
|||
{
|
||||
field: 'totalPrice',
|
||||
title: '合计金额(元)',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@ import type { CrmProductApi } from '#/api/crm/product';
|
|||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { erpPriceInputFormatter } from '@vben/utils';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getBusiness } from '#/api/crm/business';
|
||||
import { getContract } from '#/api/crm/contract';
|
||||
import { BizTypeEnum } from '#/api/crm/permission';
|
||||
import { erpPriceInputFormatter } from '#/utils';
|
||||
|
||||
import { useDetailListColumns } from './detail-data';
|
||||
|
||||
|
|
|
@ -5,12 +5,13 @@ import type { CrmProductApi } from '#/api/crm/product';
|
|||
|
||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { erpPriceMultiply } from '@vben/utils';
|
||||
|
||||
import { InputNumber, Select } from 'ant-design-vue';
|
||||
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { BizTypeEnum } from '#/api/crm/permission';
|
||||
import { getProductSimpleList } from '#/api/crm/product';
|
||||
import { erpPriceMultiply } from '#/utils';
|
||||
|
||||
import { useProductEditTableColumns } from '../data';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { getContractSimpleList } from '#/api/crm/contract';
|
|||
import { getCustomerSimpleList } from '#/api/crm/customer';
|
||||
import { getReceivablePlanSimpleList } from '#/api/crm/receivable/plan';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
|
@ -199,7 +199,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '回款金额(元)',
|
||||
field: 'price',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '回款方式',
|
||||
|
@ -219,7 +219,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '合同金额(元)',
|
||||
field: 'contract.totalPrice',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '负责人',
|
||||
|
|
|
@ -3,10 +3,10 @@ import type { DescriptionItemSchema } from '#/components/description';
|
|||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE, erpPriceInputFormatter } from '#/utils';
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
|
||||
/** 详情页的字段 */
|
||||
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
|
@ -108,7 +108,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '回款金额(元)',
|
||||
field: 'price',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '回款方式',
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { floatToFixed2 } from '@vben/utils';
|
||||
|
||||
import { getCustomerSimpleList } from '#/api/crm/customer';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
|
@ -141,7 +143,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '计划回款金额(元)',
|
||||
field: 'price',
|
||||
minWidth: 160,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '计划回款日期',
|
||||
|
@ -183,7 +185,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '实际回款金额(元)',
|
||||
field: 'receivable.price',
|
||||
minWidth: 160,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '实际回款日期',
|
||||
|
@ -197,9 +199,9 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
minWidth: 160,
|
||||
formatter: ({ row }) => {
|
||||
if (row.receivable) {
|
||||
return row.price - row.receivable.price;
|
||||
return floatToFixed2(row.price - row.receivable.price);
|
||||
}
|
||||
return row.price;
|
||||
return floatToFixed2(row.price);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -3,10 +3,10 @@ import type { DescriptionItemSchema } from '#/components/description';
|
|||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
|
||||
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE, erpPriceInputFormatter } from '#/utils';
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
|
||||
/** 详情页的字段 */
|
||||
export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
|
@ -101,7 +101,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
|
|||
title: '计划回款(元)',
|
||||
field: 'price',
|
||||
minWidth: 150,
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '计划回款日期',
|
||||
|
|
|
@ -8,6 +8,7 @@ import { useRouter } from 'vue-router';
|
|||
import { confirm, DocAlert, Page } from '@vben/common-ui';
|
||||
import {
|
||||
downloadFileFromBlobPart,
|
||||
fenToYuan,
|
||||
handleTree,
|
||||
treeToString,
|
||||
} from '@vben/utils';
|
||||
|
@ -24,7 +25,7 @@ import {
|
|||
updateStatus,
|
||||
} from '#/api/mall/product/spu';
|
||||
import { $t } from '#/locales';
|
||||
import { fenToYuan, ProductSpuStatusEnum } from '#/utils';
|
||||
import { ProductSpuStatusEnum } from '#/utils';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { MallOrderApi } from '#/api/mall/trade/order';
|
|||
import { h, onMounted, ref } from 'vue';
|
||||
|
||||
import { Page, prompt } from '@vben/common-ui';
|
||||
import { fenToYuan } from '@vben/utils';
|
||||
|
||||
import { Card, Input, message } from 'ant-design-vue';
|
||||
|
||||
|
@ -15,7 +16,7 @@ import {
|
|||
getOrderSummary,
|
||||
} from '#/api/mall/trade/order';
|
||||
import { SummaryCard } from '#/components/summary-card';
|
||||
import { DeliveryTypeEnum, fenToYuan, TradeOrderStatusEnum } from '#/utils';
|
||||
import { DeliveryTypeEnum, TradeOrderStatusEnum } from '#/utils';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import { h } from 'vue';
|
|||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { DocAlert, Page, prompt, useVbenModal } from '@vben/common-ui';
|
||||
import { fenToYuan } from '@vben/utils';
|
||||
|
||||
import { Image, List, Tag, Textarea } from 'ant-design-vue';
|
||||
|
||||
|
@ -13,12 +14,7 @@ import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
|||
import { getOrderPage, updateOrderRemark } from '#/api/mall/trade/order';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { $t } from '#/locales';
|
||||
import {
|
||||
DeliveryTypeEnum,
|
||||
DICT_TYPE,
|
||||
fenToYuan,
|
||||
TradeOrderStatusEnum,
|
||||
} from '#/utils';
|
||||
import { DeliveryTypeEnum, DICT_TYPE, TradeOrderStatusEnum } from '#/utils';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import DeleveryForm from './modules/delevery-form.vue';
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
import type { MemberUserApi } from '#/api/member/user';
|
||||
import type { PayWalletApi } from '#/api/pay/wallet/balance';
|
||||
|
||||
import { fenToYuan } from '@vben/utils';
|
||||
|
||||
import { Card } from 'ant-design-vue';
|
||||
|
||||
import { useDescription } from '#/components/description';
|
||||
import { fenToYuan } from '#/utils';
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
|
|
|
@ -3,6 +3,8 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
|||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { convertToInteger, formatToFraction } from '@vben/utils';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
|
@ -12,9 +14,7 @@ import { getSimpleTagList } from '#/api/member/tag';
|
|||
import { getAreaTree } from '#/api/system/area';
|
||||
import {
|
||||
CommonStatusEnum,
|
||||
convertToInteger,
|
||||
DICT_TYPE,
|
||||
formatToFraction,
|
||||
getDictOptions,
|
||||
getRangePickerDefaultProps,
|
||||
} from '#/utils';
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { MemberUserApi } from '#/api/member/user';
|
|||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatToFraction } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
|
@ -11,7 +12,6 @@ import { useVbenForm } from '#/adapter/form';
|
|||
import { getUser, updateUser } from '#/api/member/user';
|
||||
import { getWallet } from '#/api/pay/wallet/balance';
|
||||
import { $t } from '#/locales';
|
||||
import { formatToFraction } from '#/utils';
|
||||
|
||||
import { useBalanceFormSchema } from '../data';
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
|
|||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { PayAppApi } from '#/api/pay/app';
|
||||
|
||||
import { CommonStatusEnum } from '#/utils/constants';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
|
|
|
@ -10,7 +10,7 @@ import { message } from 'ant-design-vue';
|
|||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { changeAppStatus, deleteApp, getAppPage } from '#/api/pay/app';
|
||||
import { $t } from '#/locales';
|
||||
import { CommonStatusEnum, PayChannelEnum } from '#/utils/constants';
|
||||
import { CommonStatusEnum, PayChannelEnum } from '#/utils';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import appFrom from './modules/app-form.vue';
|
||||
|
|
|
@ -3,7 +3,7 @@ import type { VbenFormSchema } from '#/adapter/form';
|
|||
import { h } from 'vue';
|
||||
|
||||
import { InputUpload } from '#/components/upload';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils';
|
||||
|
||||
export function channelSchema(formType: string): VbenFormSchema[] {
|
||||
if (formType.includes('alipay_')) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useRoute, useRouter } from 'vue-router';
|
|||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { useTabs } from '@vben/hooks';
|
||||
import { formatDate } from '@vben/utils';
|
||||
import { fenToYuan, formatDate } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
|
@ -19,7 +19,6 @@ import {
|
|||
|
||||
import { getOrder, submitOrder } from '#/api/pay/order';
|
||||
import {
|
||||
fenToYuan,
|
||||
PayChannelEnum,
|
||||
PayDisplayModeEnum,
|
||||
PayOrderStatusEnum,
|
||||
|
|
|
@ -50,12 +50,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'price',
|
||||
title: '支付价格',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'refundPrice',
|
||||
title: '退款金额',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
|
|
|
@ -78,7 +78,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'price',
|
||||
title: '提现金额',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'userName',
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
|||
import type { DemoWithdrawApi } from '#/api/pay/demo/withdraw';
|
||||
|
||||
import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
|
||||
import { floatToFixed2 } from '@vben/utils';
|
||||
|
||||
import { message, Tag } from 'ant-design-vue';
|
||||
|
||||
|
@ -109,7 +110,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||
<Tag v-else-if="row.type === 3">钱包余额</Tag>
|
||||
</template>
|
||||
<template #price="{ row }">
|
||||
<span>¥{{ (row.price / 100.0).toFixed(2) }}</span>
|
||||
<span>¥{{ floatToFixed2(row.price) }}</span>
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<Tag v-if="row.status === 0 && !row.payTransferId" type="warning">
|
||||
|
|
|
@ -78,17 +78,17 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
title: '支付金额',
|
||||
field: 'price',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '退款金额',
|
||||
field: 'refundPrice',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '手续金额',
|
||||
field: 'channelFeePrice',
|
||||
formatter: 'formatNumber',
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
title: '订单号',
|
||||
|
|
|
@ -4,13 +4,13 @@ import type { PayOrderApi } from '#/api/pay/order';
|
|||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
import { floatToFixed2, formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions, Divider, Tag } from 'ant-design-vue';
|
||||
|
||||
import { getOrder } from '#/api/pay/order';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
import { DICT_TYPE } from '#/utils';
|
||||
|
||||
const detailData = ref<PayOrderApi.Order>();
|
||||
|
||||
|
@ -63,16 +63,16 @@ const [Modal, modalApi] = useVbenModal({
|
|||
</Descriptions.Item>
|
||||
<Descriptions.Item label="支付金额">
|
||||
<Tag color="green" size="small">
|
||||
¥{{ (detailData?.price || 0 / 100.0).toFixed(2) }}
|
||||
¥{{ floatToFixed2(detailData?.price) }}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="手续费">
|
||||
<Tag color="orange" size="small">
|
||||
¥{{ (detailData?.channelFeePrice || 0 / 100.0).toFixed(2) }}
|
||||
¥{{ floatToFixed2(detailData?.channelFeePrice) }}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="手续费比例">
|
||||
{{ (detailData?.channelFeeRate || 0 / 100.0).toFixed(2) }}%
|
||||
{{ floatToFixed2(detailData?.channelFeeRate) }}%
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="支付时间">
|
||||
{{ formatDateTime(detailData?.successTime) }}
|
||||
|
@ -115,7 +115,7 @@ const [Modal, modalApi] = useVbenModal({
|
|||
</Descriptions.Item>
|
||||
<Descriptions.Item label="退款金额" :span="2">
|
||||
<Tag size="small" color="red">
|
||||
¥{{ (detailData?.refundPrice || 0 / 100.0).toFixed(2) }}
|
||||
¥{{ floatToFixed2(detailData?.refundPrice) }}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="通知 URL">
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { DescriptionItemSchema } from '#/components/description';
|
|||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
import { floatToFixed2, formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
|
@ -120,7 +120,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
|||
{
|
||||
field: 'price',
|
||||
title: '转账金额',
|
||||
formatter: ({ cellValue }) => `¥${(cellValue / 100).toFixed(2)}`,
|
||||
formatter: 'formatAmount2',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
|
@ -217,7 +217,7 @@ export function useDetailSchema(): DescriptionItemSchema[] {
|
|||
content: (data) => {
|
||||
return h(Tag, {
|
||||
color: 'blue',
|
||||
content: `¥${(data?.price / 100).toFixed(2)}`,
|
||||
content: `¥${floatToFixed2(data?.price)}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4,6 +4,7 @@ import type { WalletRechargePackageApi } from '#/api/pay/wallet/rechargePackage'
|
|||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { fenToYuan, yuanToFen } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
|
@ -14,7 +15,6 @@ import {
|
|||
updatePackage,
|
||||
} from '#/api/pay/wallet/rechargePackage';
|
||||
import { $t } from '#/locales';
|
||||
import { fenToYuan, yuanToFen } from '#/utils';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
|
|
|
@ -76,6 +76,15 @@ export function useTypeGridFormSchema(): VbenFormSchema[] {
|
|||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '字典类型',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入字典类型',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { SystemDeptApi } from '#/api/system/dept';
|
||||
import type { SystemRoleApi } from '#/api/system/role';
|
||||
|
||||
|
@ -13,6 +15,7 @@ import { useVbenForm } from '#/adapter/form';
|
|||
import { getMenuList } from '#/api/system/menu';
|
||||
import { assignRoleMenu, getRoleMenuList } from '#/api/system/permission';
|
||||
import { $t } from '#/locales';
|
||||
import { SystemMenuTypeEnum } from '#/utils';
|
||||
|
||||
import { useAssignMenuFormSchema } from '../data';
|
||||
|
||||
|
@ -121,6 +124,18 @@ function getAllNodeIds(nodes: any[], ids: number[] = []): number[] {
|
|||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function getNodeClass(node: Recordable<any>) {
|
||||
const classes: string[] = [];
|
||||
if (node.value?.type === SystemMenuTypeEnum.BUTTON) {
|
||||
classes.push('inline-flex');
|
||||
if (node.index % 3 >= 1) {
|
||||
classes.push('!pl-0');
|
||||
}
|
||||
}
|
||||
|
||||
return classes.join(' ');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -134,9 +149,11 @@ function getAllNodeIds(nodes: any[], ids: number[] = []): number[] {
|
|||
multiple
|
||||
bordered
|
||||
:expanded="expandedKeys"
|
||||
:get-node-class="getNodeClass"
|
||||
v-bind="slotProps"
|
||||
value-field="id"
|
||||
label-field="name"
|
||||
icon-field="meta.icon"
|
||||
/>
|
||||
</template>
|
||||
</Form>
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
// TODO @xingyu:感觉 formatToFraction 可以整合起来;【优先级:低】
|
||||
import { isEmpty, isString, isUndefined } from './inference';
|
||||
|
||||
/**
|
||||
* 将一个整数转换为分数保留传入的小数
|
||||
* @param num
|
||||
* @param digit
|
||||
*/
|
||||
export function formatToFractionDigit(
|
||||
num: number | string | undefined,
|
||||
digit: number = 2,
|
||||
): string {
|
||||
if (isUndefined(num)) return '0.00';
|
||||
const parsedNumber = isString(num) ? Number.parseFloat(num) : num;
|
||||
return (parsedNumber / 100).toFixed(digit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个整数转换为分数保留两位小数
|
||||
* @param num
|
||||
*/
|
||||
export function formatToFraction(num: number | string | undefined): string {
|
||||
if (num === undefined) return '0.00';
|
||||
const parsedNumber = typeof num === 'string' ? Number.parseFloat(num) : num;
|
||||
return (parsedNumber / 100).toFixed(2);
|
||||
return formatToFractionDigit(num, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,9 +30,7 @@ export function formatToFraction(num: number | string | undefined): string {
|
|||
*/
|
||||
export function floatToFixed2(num: number | string | undefined): string {
|
||||
let str = '0.00';
|
||||
if (num === undefined) {
|
||||
return str;
|
||||
}
|
||||
if (isUndefined(num)) return str;
|
||||
const f = formatToFraction(num);
|
||||
const decimalPart = f.toString().split('.')[1];
|
||||
const len = decimalPart ? decimalPart.length : 0;
|
||||
|
@ -45,8 +56,8 @@ export function floatToFixed2(num: number | string | undefined): string {
|
|||
* @param num
|
||||
*/
|
||||
export function convertToInteger(num: number | string | undefined): number {
|
||||
if (num === undefined) return 0;
|
||||
const parsedNumber = typeof num === 'string' ? Number.parseFloat(num) : num;
|
||||
if (isUndefined(num)) return 0;
|
||||
const parsedNumber = isString(num) ? Number.parseFloat(num) : num;
|
||||
return Math.round(parsedNumber * 100);
|
||||
}
|
||||
|
||||
|
@ -125,7 +136,6 @@ export function erpCountInputFormatter(num: number | string | undefined) {
|
|||
return erpNumberFormatter(num, ERP_COUNT_DIGIT);
|
||||
}
|
||||
|
||||
// noinspection JSCommentMatchesSignature
|
||||
/**
|
||||
* 【ERP】格式化数量,保留三位小数
|
||||
*
|
||||
|
@ -148,7 +158,6 @@ export function erpPriceInputFormatter(num: number | string | undefined) {
|
|||
return erpNumberFormatter(num, ERP_PRICE_DIGIT);
|
||||
}
|
||||
|
||||
// noinspection JSCommentMatchesSignature
|
||||
/**
|
||||
* 【ERP】格式化金额,保留二位小数
|
||||
*
|
||||
|
@ -167,9 +176,7 @@ export function erpPriceTableColumnFormatter(cellValue: any) {
|
|||
* @return 总价格。如果有任一为空,则返回 undefined
|
||||
*/
|
||||
export function erpPriceMultiply(price: number, count: number) {
|
||||
if (price === null || count === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (isEmpty(price) || isEmpty(count)) return undefined;
|
||||
return Number.parseFloat((price * count).toFixed(ERP_PRICE_DIGIT));
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ export * from './date';
|
|||
export * from './diff';
|
||||
export * from './dom';
|
||||
export * from './download';
|
||||
export * from './formatNumber';
|
||||
export * from './inference';
|
||||
export * from './letter';
|
||||
export * from './merge';
|
||||
|
|
Loading…
Reference in New Issue