!139 fix: type error

Merge pull request !139 from xingyu/dev
pull/143/MERGE
xingyu 2025-06-12 09:55:43 +00:00 committed by Gitee
commit cac4efc227
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
65 changed files with 655 additions and 228 deletions

View File

@ -10,7 +10,12 @@ import {
setupVbenVxeTable, setupVbenVxeTable,
useVbenVxeGrid, useVbenVxeGrid,
} from '@vben/plugins/vxe-table'; } 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'; import { Button, Image, Popconfirm, Switch } from 'ant-design-vue';
@ -313,33 +318,13 @@ setupVbenVxeTable({
// add by 星语:数量格式化,例如说:金额 // add by 星语:数量格式化,例如说:金额
vxeUI.formats.add('formatNumber', { vxeUI.formats.add('formatNumber', {
tableCellFormatMethod({ cellValue }, digits = 2) { tableCellFormatMethod({ cellValue }, digits = 2) {
if (cellValue === null || cellValue === undefined) { return formatToFractionDigit(cellValue, digits);
return '';
}
if (isString(cellValue)) {
cellValue = Number.parseFloat(cellValue);
}
// 如果非 number则直接返回空串
if (Number.isNaN(cellValue)) {
return '';
}
return cellValue.toFixed(digits);
}, },
}); });
vxeUI.formats.add('formatAmount2', { vxeUI.formats.add('formatAmount2', {
tableCellFormatMethod({ cellValue }) { tableCellFormatMethod({ cellValue }) {
if (cellValue === null || cellValue === undefined) { return `${floatToFixed2(cellValue)}`;
return '0.00';
}
if (isString(cellValue)) {
cellValue = Number.parseFloat(cellValue);
}
// 如果非 number则直接返回空串
if (Number.isNaN(cellValue)) {
return '0.00';
}
return `${(cellValue / 100).toFixed(2)}`;
}, },
}); });
}, },

View File

@ -23,6 +23,11 @@ interface DictTagProps {
const props = defineProps<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(() => { const dictTag = computed(() => {
// //
@ -66,7 +71,16 @@ const dictTag = computed(() => {
</script> </script>
<template> <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 }} {{ dictTag.label }}
</Tag> </Tag>
</template> </template>

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; import type { SimpleFlowNode } from '../../consts';
import { ref, watch } from 'vue'; import { nextTick, ref, watch } from 'vue';
import { useVbenDrawer } from '@vben/common-ui'; import { useVbenDrawer } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
@ -53,8 +53,6 @@ const condition = ref<any>({
}, },
}); });
//
const showInput = ref(false);
const conditionRef = ref(); const conditionRef = ref();
const fieldOptions = useFormFieldsAndStartUser(); // const fieldOptions = useFormFieldsAndStartUser(); //
@ -130,13 +128,24 @@ watch(
currentNode.value = newValue; 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() { function clickIcon() {
showInput.value = true; showInput.value = true;
} }
// //
function blurEvent() { function changeNodeName() {
showInput.value = false; showInput.value = false;
currentNode.value.name = currentNode.value.name =
currentNode.value.name || currentNode.value.name ||
@ -153,10 +162,12 @@ defineExpose({ open }); // 提供 open 方法,用于打开弹窗
<template #title> <template #title>
<div class="flex items-center"> <div class="flex items-center">
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="mr-2 w-48" class="mr-2 w-48"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="currentNode.name" v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />

View File

@ -75,9 +75,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.COPY_TASK_NODE, useNodeName(BpmNodeTypeEnum.COPY_TASK_NODE);
);
// Tab // Tab
const activeTabName = ref('user'); const activeTabName = ref('user');
@ -213,9 +212,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
<div class="config-header"> <div class="config-header">
<Input <Input
v-if="showInput" v-if="showInput"
ref="inputRef"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -45,9 +45,8 @@ const props = defineProps({
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.DELAY_TIMER_NODE, useNodeName(BpmNodeTypeEnum.DELAY_TIMER_NODE);
);
// //
const formRef = ref(); // Ref const formRef = ref(); // Ref
@ -158,9 +157,11 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
<div class="flex items-center"> <div class="flex items-center">
<Input <Input
v-if="showInput" v-if="showInput"
ref="inputRef"
type="text" type="text"
class="mr-2 w-48" class="mr-2 w-48"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -25,7 +25,7 @@ import {
Tooltip, Tooltip,
} from 'ant-design-vue'; } from 'ant-design-vue';
import { BpmModelFormType } from '#/utils/constants'; import { BpmModelFormType } from '#/utils';
import { import {
COMPARISON_OPERATORS, COMPARISON_OPERATORS,

View File

@ -41,9 +41,8 @@ const processNodeTree = inject<Ref<SimpleFlowNode>>('processNodeTree');
/** 当前节点 */ /** 当前节点 */
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
/** 节点名称 */ /** 节点名称 */
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.ROUTER_BRANCH_NODE, useNodeName(BpmNodeTypeEnum.ROUTER_BRANCH_NODE);
);
const routerGroups = ref<RouterSetting[]>([]); const routerGroups = ref<RouterSetting[]>([]);
const nodeOptions = ref<any[]>([]); const nodeOptions = ref<any[]>([]);
const conditionRef = ref<any[]>([]); const conditionRef = ref<any[]>([]);
@ -205,10 +204,12 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
<template #title> <template #title>
<div class="flex items-center"> <div class="flex items-center">
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="mr-2 w-48" class="mr-2 w-48"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -52,9 +52,8 @@ const deptOptions = inject<Ref<SystemDeptApi.Dept[]>>('deptList');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.START_USER_NODE, useNodeName(BpmNodeTypeEnum.START_USER_NODE);
);
// Tab // Tab
const activeTabName = ref('user'); const activeTabName = ref('user');
@ -145,12 +144,13 @@ defineExpose({ showStartUserNodeConfig });
<Drawer> <Drawer>
<template #title> <template #title>
<div class="config-header"> <div class="config-header">
<!-- TODO v-mountedFocus 自动聚集 需要迁移一下 -->
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -72,9 +72,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.TRIGGER_NODE, useNodeName(BpmNodeTypeEnum.TRIGGER_NODE);
);
// //
const formRef = ref(); // Ref const formRef = ref(); // Ref
@ -388,10 +387,12 @@ onMounted(() => {
<template #title> <template #title>
<div class="config-header"> <div class="config-header">
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -114,9 +114,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
}); });
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.USER_TASK_NODE, useNodeName(BpmNodeTypeEnum.USER_TASK_NODE);
);
// Tab // Tab
const activeTabName = ref('user'); const activeTabName = ref('user');
@ -586,9 +585,11 @@ onMounted(() => {
<div class="config-header"> <div class="config-header">
<Input <Input
v-if="showInput" v-if="showInput"
ref="inputRef"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -32,7 +32,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.COPY_TASK_NODE, BpmNodeTypeEnum.COPY_TASK_NODE,
); );
@ -67,11 +67,13 @@ function deleteNode() {
<span class="iconfont icon-copy"></span> <span class="iconfont icon-copy"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -30,7 +30,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.DELAY_TIMER_NODE, BpmNodeTypeEnum.DELAY_TIMER_NODE,
); );
@ -64,11 +64,13 @@ function deleteNode() {
<span class="iconfont icon-delay"></span> <span class="iconfont icon-delay"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; 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 { IconifyIcon } from '@vben/icons';
import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils'; import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils';
@ -51,11 +51,30 @@ watch(
currentNode.value = newValue; currentNode.value = newValue;
}, },
); );
//
const inputRefs = ref<HTMLInputElement[]>([]);
//
const showInputs = ref<boolean[]>([]); 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; showInputs.value[index] = false;
const conditionNode = currentNode.value.conditionNodes?.at( const conditionNode = currentNode.value.conditionNodes?.at(
index, index,
@ -188,10 +207,16 @@ function recursiveFindParentNode(
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="!readonly && showInputs[index]"> <div v-if="!readonly && showInputs[index]">
<Input <Input
:ref="
(el) => {
inputRefs[index] = el as HTMLInputElement;
}
"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent(index)" @blur="changeNodeName(index)"
v-model="item.name" @press-enter="changeNodeName(index)"
v-model:value="item.name"
/> />
</div> </div>
<div v-else class="branch-title" @click="clickEvent(index)"> <div v-else class="branch-title" @click="clickEvent(index)">

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; 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 { IconifyIcon } from '@vben/icons';
import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils'; import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils';
@ -57,10 +57,28 @@ watch(
currentNode.value = newValue; currentNode.value = newValue;
}, },
); );
//
const inputRefs = ref<HTMLInputElement[]>([]);
//
const showInputs = ref<boolean[]>([]); 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; showInputs.value[index] = false;
const conditionNode = currentNode.value.conditionNodes?.at( const conditionNode = currentNode.value.conditionNodes?.at(
index, index,
@ -192,10 +210,16 @@ function recursiveFindParentNode(
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="!readonly && showInputs[index]"> <div v-if="!readonly && showInputs[index]">
<Input <Input
:ref="
(el) => {
inputRefs[index] = el as HTMLInputElement;
}
"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent(index)" @blur="changeNodeName(index)"
v-model="item.name" @press-enter="changeNodeName(index)"
v-model:value="item.name"
/> />
</div> </div>
<div v-else class="branch-title" @click="clickEvent(index)"> <div v-else class="branch-title" @click="clickEvent(index)">

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; import type { SimpleFlowNode } from '../../consts';
import { inject, ref, watch } from 'vue'; import { inject, nextTick, ref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { buildShortUUID as generateUUID } from '@vben/utils'; import { buildShortUUID as generateUUID } from '@vben/utils';
@ -46,10 +46,28 @@ watch(
}, },
); );
//
const inputRefs = ref<HTMLInputElement[]>([]);
//
const showInputs = ref<boolean[]>([]); const showInputs = ref<boolean[]>([]);
//
// watch(
function blurEvent(index: number) { 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; showInputs.value[index] = false;
const conditionNode = currentNode.value.conditionNodes?.at( const conditionNode = currentNode.value.conditionNodes?.at(
index, index,
@ -150,10 +168,16 @@ function recursiveFindParentNode(
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="showInputs[index]"> <div v-if="showInputs[index]">
<Input <Input
:ref="
(el) => {
inputRefs[index] = el as HTMLInputElement;
}
"
type="text" type="text"
class="input-max-width editable-title-input" class="input-max-width editable-title-input"
@blur="blurEvent(index)" @blur="changeNodeName(index)"
v-model="item.name" @press-enter="changeNodeName(index)"
v-model:value="item.name"
/> />
</div> </div>
<div v-else class="branch-title" @click="clickEvent(index)"> <div v-else class="branch-title" @click="clickEvent(index)">

View File

@ -33,7 +33,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.ROUTER_BRANCH_NODE, BpmNodeTypeEnum.ROUTER_BRANCH_NODE,
); );
@ -67,11 +67,13 @@ function deleteNode() {
<span class="iconfont icon-router"></span> <span class="iconfont icon-router"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -37,7 +37,7 @@ const tasks = inject<Ref<any[]>>('tasks', ref([]));
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.START_USER_NODE, BpmNodeTypeEnum.START_USER_NODE,
); );
@ -81,10 +81,12 @@ function nodeClick() {
<span class="iconfont icon-start-user"></span> <span class="iconfont icon-start-user"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="currentNode.name" v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />

View File

@ -35,7 +35,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.TRIGGER_NODE, BpmNodeTypeEnum.TRIGGER_NODE,
); );
@ -69,11 +69,13 @@ function deleteNode() {
<span class="iconfont icon-trigger"></span> <span class="iconfont icon-trigger"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -36,7 +36,7 @@ const tasks = inject<Ref<any[]>>('tasks', ref([]));
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.USER_TASK_NODE, BpmNodeTypeEnum.USER_TASK_NODE,
); );
@ -87,11 +87,13 @@ function findReturnTaskNodes(
</span> </span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -12,7 +12,7 @@ import type { SystemPostApi } from '#/api/system/post';
import type { SystemRoleApi } from '#/api/system/role'; import type { SystemRoleApi } from '#/api/system/role';
import type { SystemUserApi } from '#/api/system/user'; 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 { import {
BpmNodeTypeEnum, BpmNodeTypeEnum,
@ -622,21 +622,33 @@ export function useNodeName(nodeType: BpmNodeTypeEnum) {
const nodeName = ref<string>(); const nodeName = ref<string>();
// 节点名称输入框 // 节点名称输入框
const showInput = ref(false); const showInput = ref(false);
// 输入框的引用
const inputRef = ref<HTMLInputElement | null>(null);
// 点击节点名称编辑图标 // 点击节点名称编辑图标
function clickIcon() { function clickIcon() {
showInput.value = true; showInput.value = true;
} }
// 节点名称输入框失去焦点 // 修改节点名称
function blurEvent() { function changeNodeName() {
showInput.value = false; showInput.value = false;
nodeName.value = nodeName.value =
nodeName.value || (NODE_DEFAULT_NAME.get(nodeType) as string); nodeName.value || (NODE_DEFAULT_NAME.get(nodeType) as string);
} }
// 监听 showInput 的变化,当变为 true 时自动聚焦
watch(showInput, (value) => {
if (value) {
nextTick(() => {
inputRef.value?.focus();
});
}
});
return { return {
nodeName, nodeName,
showInput, showInput,
inputRef,
clickIcon, clickIcon,
blurEvent, changeNodeName,
}; };
} }
@ -646,11 +658,24 @@ export function useNodeName2(
) { ) {
// 显示节点名称输入框 // 显示节点名称输入框
const showInput = ref(false); 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; showInput.value = false;
node.value.name = node.value.name =
node.value.name || (NODE_DEFAULT_NAME.get(nodeType) as string); node.value.name || (NODE_DEFAULT_NAME.get(nodeType) as string);
console.warn('node.value.name===>', node.value.name);
} }
// 点击节点标题进行输入 // 点击节点标题进行输入
function clickTitle() { function clickTitle() {
@ -658,8 +683,9 @@ export function useNodeName2(
} }
return { return {
showInput, showInput,
inputRef,
clickTitle, clickTitle,
blurEvent, changeNodeName,
}; };
} }

View File

@ -43,28 +43,27 @@ const { hasAccessByCodes } = useAccess();
function isIfShow(action: ActionItem): boolean { function isIfShow(action: ActionItem): boolean {
const ifShow = action.ifShow; const ifShow = action.ifShow;
let isIfShow = true; let isIfShow = true;
if (isBoolean(ifShow)) { if (isBoolean(ifShow)) {
isIfShow = ifShow; isIfShow = ifShow;
} }
if (isFunction(ifShow)) { if (isFunction(ifShow)) {
isIfShow = ifShow(action); isIfShow = ifShow(action);
} }
if (isIfShow) {
isIfShow =
hasAccessByCodes(action.auth || []) || (action.auth || []).length === 0;
}
return isIfShow; return isIfShow;
} }
const getActions = computed(() => { const getActions = computed(() => {
return (toRaw(props.actions) || []) const actions = toRaw(props.actions) || [];
.filter((action) => { return actions
return ( .filter((action: ActionItem) => {
(hasAccessByCodes(action.auth || []) || return isIfShow(action);
(action.auth || []).length === 0) &&
isIfShow(action)
);
}) })
.map((action) => { .map((action: ActionItem) => {
const { popConfirm } = action; const { popConfirm } = action;
return { return {
type: action.type || 'link', type: action.type || 'link',
@ -78,24 +77,21 @@ const getActions = computed(() => {
}); });
const getDropdownList = computed((): any[] => { const getDropdownList = computed((): any[] => {
return (toRaw(props.dropDownActions) || []) const dropDownActions = toRaw(props.dropDownActions) || [];
.filter((action) => { return dropDownActions
return ( .filter((action: ActionItem) => {
(hasAccessByCodes(action.auth || []) || return isIfShow(action);
(action.auth || []).length === 0) &&
isIfShow(action)
);
}) })
.map((action, index) => { .map((action: ActionItem, index: number) => {
const { label, popConfirm } = action; const { label, popConfirm } = action;
delete action.icon;
return { return {
...action, ...action,
...popConfirm, ...popConfirm,
onConfirm: popConfirm?.confirm, onConfirm: popConfirm?.confirm,
onCancel: popConfirm?.cancel, onCancel: popConfirm?.cancel,
text: label, text: label,
divider: divider: index < dropDownActions.length - 1 ? props.divider : false,
index < props.dropDownActions.length - 1 ? props.divider : false,
}; };
}); });
}); });

View File

@ -86,6 +86,18 @@ const routes: RouteRecordRaw[] = [
keepAlive: true, 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,
},
},
], ],
}, },
]; ];

View File

@ -1,7 +1,6 @@
export * from './constants'; export * from './constants';
export * from './dict'; export * from './dict';
export * from './download'; export * from './download';
export * from './formatNumber';
export * from './formCreate'; export * from './formCreate';
export * from './rangePickerProps'; export * from './rangePickerProps';
export * from './routerHelper'; export * from './routerHelper';

View File

@ -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' },
},
];
}

View File

@ -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>

View File

@ -26,8 +26,11 @@ import {
HttpRequestSetting, HttpRequestSetting,
parseFormFields, parseFormFields,
} from '#/components/simple-process-design'; } from '#/components/simple-process-design';
import { ProcessVariableEnum } from '#/utils'; import {
import { BpmAutoApproveType, BpmModelFormType } from '#/utils/constants'; BpmAutoApproveType,
BpmModelFormType,
ProcessVariableEnum,
} from '#/utils';
const modelData = defineModel<any>(); const modelData = defineModel<any>();

View File

@ -178,13 +178,13 @@ async function handleCategorySortSubmit() {
<template #overlay> <template #overlay>
<Menu @click="(e) => handleCommand(e.key as string)"> <Menu @click="(e) => handleCommand(e.key as string)">
<Menu.Item key="handleCategoryAdd"> <Menu.Item key="handleCategoryAdd">
<div class="flex items-center"> <div class="flex items-center gap-1">
<IconifyIcon icon="lucide:plus" /> <IconifyIcon icon="lucide:plus" />
新建分类 新建分类
</div> </div>
</Menu.Item> </Menu.Item>
<Menu.Item key="handleCategorySort"> <Menu.Item key="handleCategorySort">
<div class="flex items-center"> <div class="flex items-center gap-1">
<IconifyIcon icon="lucide:align-start-vertical" /> <IconifyIcon icon="lucide:align-start-vertical" />
分类排序 分类排序
</div> </div>

View File

@ -4,7 +4,7 @@ import type { BpmModelApi, ModelCategoryInfo } from '#/api/bpm/model';
import { computed, ref, watchEffect } from 'vue'; import { computed, ref, watchEffect } from 'vue';
import { useRouter } from 'vue-router'; 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 { IconifyIcon } from '@vben/icons';
import { useUserStore } from '@vben/stores'; import { useUserStore } from '@vben/stores';
import { cloneDeep, formatDateTime, isEqual } from '@vben/utils'; import { cloneDeep, formatDateTime, isEqual } from '@vben/utils';
@ -33,10 +33,12 @@ import {
} from '#/api/bpm/model'; } from '#/api/bpm/model';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { DICT_TYPE } from '#/utils'; import { BpmModelFormType, DICT_TYPE } from '#/utils';
// //
import CategoryRenameForm from '../../category/modules/rename-form.vue'; import CategoryRenameForm from '../../category/modules/rename-form.vue';
// FormCreate
import FormCreateDetail from '../../form/modules/detail.vue';
const props = defineProps<{ const props = defineProps<{
categoryInfo: ModelCategoryInfo; categoryInfo: ModelCategoryInfo;
@ -45,12 +47,18 @@ const props = defineProps<{
const emit = defineEmits(['success']); const emit = defineEmits(['success']);
// /** 重命名分类对话框 */
const [CategoryRenameModal, categoryRenameModalApi] = useVbenModal({ const [CategoryRenameModal, categoryRenameModalApi] = useVbenModal({
connectedComponent: CategoryRenameForm, connectedComponent: CategoryRenameForm,
destroyOnClose: true, destroyOnClose: true,
}); });
/** 流程表单详情对话框 */
const [FormCreateDetailModal, formCreateDetailModalApi] = useVbenModal({
connectedComponent: FormCreateDetail,
destroyOnClose: true,
});
const router = useRouter(); const router = useRouter();
// Id // Id
const userStore = useUserStore(); const userStore = useUserStore();
@ -73,8 +81,7 @@ const columns = [
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
align: 'left' as const, align: 'left' as const,
ellipsis: true, width: 250,
width: 230,
}, },
{ {
title: '可见范围', title: '可见范围',
@ -193,8 +200,15 @@ async function handleDeleteCategory() {
/** 处理表单详情点击 */ /** 处理表单详情点击 */
function handleFormDetail(row: any) { function handleFormDetail(row: any) {
if (row.formType === BpmModelFormType.NORMAL) {
const data = {
id: row.formId,
};
formCreateDetailModalApi.setData(data).open();
} else {
// TODO // TODO
console.warn('待实现', row); console.warn('业务表单待实现', row);
}
} }
/** 判断是否是流程管理员 */ /** 判断是否是流程管理员 */
@ -243,7 +257,7 @@ function handleModelCommand(command: string, row: any) {
break; break;
} }
case 'handleDefinitionList': { case 'handleDefinitionList': {
console.warn('历史待实现', row); handleDefinitionList(row);
break; break;
} }
case 'handleDelete': { case 'handleDelete': {
@ -317,6 +331,16 @@ function handleDelete(row: any) {
}); });
} }
/** 跳转到指定流程定义列表 */
function handleDefinitionList(row: any) {
router.push({
name: 'BpmProcessDefinition',
query: {
key: row.key,
},
});
}
/** 更新 modelList 模型列表 */ /** 更新 modelList 模型列表 */
const updateModelList = useDebounceFn(() => { const updateModelList = useDebounceFn(() => {
const newModelList = props.categoryInfo.modelList; const newModelList = props.categoryInfo.modelList;
@ -486,7 +510,9 @@ const handleRenameSuccess = () => {
class="mr-2.5 h-9 w-9 rounded" class="mr-2.5 h-9 w-9 rounded"
alt="图标" alt="图标"
/> />
<EllipsisText :max-width="160" :tooltip-when-ellipsis="true">
{{ record.name }} {{ record.name }}
</EllipsisText>
</div> </div>
</template> </template>
@ -543,7 +569,7 @@ const handleRenameSuccess = () => {
<template v-else-if="column.key === 'formType'"> <template v-else-if="column.key === 'formType'">
<!-- TODO BpmModelFormType.NORMAL --> <!-- TODO BpmModelFormType.NORMAL -->
<Button <Button
v-if="record.formType === 10" v-if="record.formType === BpmModelFormType.NORMAL"
type="link" type="link"
@click="handleFormDetail(record)" @click="handleFormDetail(record)"
> >
@ -551,7 +577,7 @@ const handleRenameSuccess = () => {
</Button> </Button>
<!-- TODO BpmModelFormType.CUSTOM --> <!-- TODO BpmModelFormType.CUSTOM -->
<Button <Button
v-else-if="record.formType === 20" v-else-if="record.formType === BpmModelFormType.CUSTOM"
type="link" type="link"
@click="handleFormDetail(record)" @click="handleFormDetail(record)"
> >
@ -604,7 +630,6 @@ const handleRenameSuccess = () => {
</Button> </Button>
<Dropdown placement="bottomRight" arrow> <Dropdown placement="bottomRight" arrow>
<Button type="link" size="small" class="px-1">更多</Button> <Button type="link" size="small" class="px-1">更多</Button>
<!-- TODO 待实现 -->
<template #overlay> <template #overlay>
<Menu <Menu
@click=" @click="
@ -613,12 +638,14 @@ const handleRenameSuccess = () => {
> >
<Menu.Item key="handleCopy"> 复制 </Menu.Item> <Menu.Item key="handleCopy"> 复制 </Menu.Item>
<Menu.Item key="handleDefinitionList"> 历史 </Menu.Item> <Menu.Item key="handleDefinitionList"> 历史 </Menu.Item>
<!-- TODO 待实现报表
<Menu.Item <Menu.Item
key="handleReport" key="handleReport"
:disabled="!isManagerUser(record)" :disabled="!isManagerUser(record)"
> >
报表 报表
</Menu.Item> </Menu.Item> -->
<Menu.Item <Menu.Item
key="handleChangeState" key="handleChangeState"
v-if="record.processDefinition" v-if="record.processDefinition"
@ -657,6 +684,8 @@ const handleRenameSuccess = () => {
<!-- 重命名分类弹窗 --> <!-- 重命名分类弹窗 -->
<CategoryRenameModal @success="handleRenameSuccess" /> <CategoryRenameModal @success="handleRenameSuccess" />
<!-- 流程表单详情对话框 -->
<FormCreateDetailModal />
</div> </div>
</template> </template>

View File

@ -1,10 +1,11 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { erpPriceMultiply } from '@vben/utils';
import { getBusinessStatusTypeSimpleList } from '#/api/crm/business/status'; import { getBusinessStatusTypeSimpleList } from '#/api/crm/business/status';
import { getCustomerSimpleList } from '#/api/crm/customer'; import { getCustomerSimpleList } from '#/api/crm/customer';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { erpPriceMultiply } from '#/utils';
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
@ -167,7 +168,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
field: 'totalPrice', field: 'totalPrice',
title: '商机金额(元)', title: '商机金额(元)',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'dealTime', field: 'dealTime',

View File

@ -1,9 +1,7 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DescriptionItemSchema } from '#/components/description'; import type { DescriptionItemSchema } from '#/components/description';
import { formatDateTime } from '@vben/utils'; import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
import { erpPriceInputFormatter } from '#/utils';
/** 详情页的字段 */ /** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] { export function useDetailSchema(): DescriptionItemSchema[] {
@ -97,7 +95,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
{ {
field: 'totalPrice', field: 'totalPrice',
title: '商机金额(元)', title: '商机金额(元)',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'dealTime', field: 'dealTime',

View File

@ -4,6 +4,7 @@ import type { CrmBusinessApi } from '#/api/crm/business';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { erpPriceMultiply } from '@vben/utils';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@ -15,7 +16,6 @@ import {
} from '#/api/crm/business'; } from '#/api/crm/business';
import { BizTypeEnum } from '#/api/crm/permission'; import { BizTypeEnum } from '#/api/crm/permission';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { erpPriceMultiply } from '#/utils';
import { ProductEditTable } from '#/views/crm/product'; import { ProductEditTable } from '#/views/crm/product';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';

View File

@ -1,13 +1,14 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { erpPriceMultiply, floatToFixed2 } from '@vben/utils';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
import { getSimpleBusinessList } from '#/api/crm/business'; import { getSimpleBusinessList } from '#/api/crm/business';
import { getSimpleContactList } from '#/api/crm/contact'; import { getSimpleContactList } from '#/api/crm/contact';
import { getCustomerSimpleList } from '#/api/crm/customer'; import { getCustomerSimpleList } from '#/api/crm/customer';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { erpPriceMultiply, floatToFixed2 } from '#/utils'; import { DICT_TYPE } from '#/utils';
import { DICT_TYPE } from '#/utils/dict';
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
@ -242,7 +243,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
title: '合同金额(元)', title: '合同金额(元)',
field: 'totalPrice', field: 'totalPrice',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '下单时间', title: '下单时间',
@ -277,7 +278,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
title: '已回款金额(元)', title: '已回款金额(元)',
field: 'totalReceivablePrice', field: 'totalReceivablePrice',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '未回款金额(元)', title: '未回款金额(元)',

View File

@ -3,11 +3,14 @@ import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue'; import { h } from 'vue';
import { formatDateTime } from '@vben/utils'; import {
erpPriceInputFormatter,
floatToFixed2,
formatDateTime,
} from '@vben/utils';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, erpPriceInputFormatter, floatToFixed2 } from '#/utils'; import { DICT_TYPE } from '#/utils';
/** 详情头部的配置 */ /** 详情头部的配置 */
export function useDetailSchema(): DescriptionItemSchema[] { export function useDetailSchema(): DescriptionItemSchema[] {
return [ return [
@ -120,7 +123,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
title: '合同金额(元)', title: '合同金额(元)',
field: 'totalPrice', field: 'totalPrice',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '合同开始时间', title: '合同开始时间',
@ -138,7 +141,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
title: '已回款金额(元)', title: '已回款金额(元)',
field: 'totalReceivablePrice', field: 'totalReceivablePrice',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '未回款金额(元)', title: '未回款金额(元)',

View File

@ -4,6 +4,7 @@ import type { CrmContractApi } from '#/api/crm/contract';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useVbenForm, useVbenModal } from '@vben/common-ui'; import { useVbenForm, useVbenModal } from '@vben/common-ui';
import { erpPriceMultiply } from '@vben/utils';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@ -14,7 +15,6 @@ import {
} from '#/api/crm/contract'; } from '#/api/crm/contract';
import { BizTypeEnum } from '#/api/crm/permission'; import { BizTypeEnum } from '#/api/crm/permission';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { erpPriceMultiply } from '#/utils';
import { ProductEditTable } from '#/views/crm/product'; import { ProductEditTable } from '#/views/crm/product';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';

View File

@ -1,7 +1,7 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE, getDictOptions } from '#/utils/dict'; import { DICT_TYPE, getDictOptions } from '#/utils';
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {

View File

@ -140,7 +140,7 @@ async function handleQuit() {
message.warning('你不是团队成员!'); message.warning('你不是团队成员!');
return; return;
} }
await deleteSelfPermission(userPermission.id); await deleteSelfPermission(userPermission.id as number);
message.success('退出团队成员成功!'); message.success('退出团队成员成功!');
emits('quitTeam'); emits('quitTeam');
} }

View File

@ -134,7 +134,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
field: 'price', field: 'price',
title: '价格(元)', title: '价格(元)',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'description', field: 'description',
@ -203,7 +203,7 @@ export function useProductEditTableColumns(): VxeTableGridOptions['columns'] {
field: 'productPrice', field: 'productPrice',
title: '价格(元)', title: '价格(元)',
minWidth: 100, minWidth: 100,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'sellingPrice', field: 'sellingPrice',
@ -221,7 +221,7 @@ export function useProductEditTableColumns(): VxeTableGridOptions['columns'] {
field: 'totalPrice', field: 'totalPrice',
title: '合计', title: '合计',
minWidth: 100, minWidth: 100,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '操作', title: '操作',

View File

@ -3,8 +3,10 @@ import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue'; import { h } from 'vue';
import { erpPriceInputFormatter } from '@vben/utils';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, erpPriceInputFormatter } from '#/utils'; import { DICT_TYPE } from '#/utils';
/** 详情页的字段 */ /** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] { export function useDetailSchema(): DescriptionItemSchema[] {
@ -94,12 +96,12 @@ export function useDetailListColumns(
{ {
field: 'productPrice', field: 'productPrice',
title: '产品价格(元)', title: '产品价格(元)',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'businessPrice', field: 'businessPrice',
title: '商机价格(元)', title: '商机价格(元)',
formatter: 'formatNumber', formatter: 'formatAmount2',
visible: showBussinePrice, visible: showBussinePrice,
}, },
{ {
@ -110,7 +112,7 @@ export function useDetailListColumns(
{ {
field: 'totalPrice', field: 'totalPrice',
title: '合计金额(元)', title: '合计金额(元)',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
]; ];
} }

View File

@ -4,11 +4,12 @@ import type { CrmProductApi } from '#/api/crm/product';
import { ref } from 'vue'; import { ref } from 'vue';
import { erpPriceInputFormatter } from '@vben/utils';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getBusiness } from '#/api/crm/business'; import { getBusiness } from '#/api/crm/business';
import { getContract } from '#/api/crm/contract'; import { getContract } from '#/api/crm/contract';
import { BizTypeEnum } from '#/api/crm/permission'; import { BizTypeEnum } from '#/api/crm/permission';
import { erpPriceInputFormatter } from '#/utils';
import { useDetailListColumns } from './detail-data'; import { useDetailListColumns } from './detail-data';

View File

@ -5,12 +5,13 @@ import type { CrmProductApi } from '#/api/crm/product';
import { nextTick, onMounted, ref, watch } from 'vue'; import { nextTick, onMounted, ref, watch } from 'vue';
import { erpPriceMultiply } from '@vben/utils';
import { InputNumber, Select } from 'ant-design-vue'; import { InputNumber, Select } from 'ant-design-vue';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { BizTypeEnum } from '#/api/crm/permission'; import { BizTypeEnum } from '#/api/crm/permission';
import { getProductSimpleList } from '#/api/crm/product'; import { getProductSimpleList } from '#/api/crm/product';
import { erpPriceMultiply } from '#/utils';
import { useProductEditTableColumns } from '../data'; import { useProductEditTableColumns } from '../data';

View File

@ -5,7 +5,7 @@ import { getContractSimpleList } from '#/api/crm/contract';
import { getCustomerSimpleList } from '#/api/crm/customer'; import { getCustomerSimpleList } from '#/api/crm/customer';
import { getReceivablePlanSimpleList } from '#/api/crm/receivable/plan'; import { getReceivablePlanSimpleList } from '#/api/crm/receivable/plan';
import { getSimpleUserList } from '#/api/system/user'; import { getSimpleUserList } from '#/api/system/user';
import { DICT_TYPE, getDictOptions } from '#/utils/dict'; import { DICT_TYPE, getDictOptions } from '#/utils';
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
@ -199,7 +199,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
title: '回款金额(元)', title: '回款金额(元)',
field: 'price', field: 'price',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '回款方式', title: '回款方式',
@ -219,7 +219,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
title: '合同金额(元)', title: '合同金额(元)',
field: 'contract.totalPrice', field: 'contract.totalPrice',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '负责人', title: '负责人',

View File

@ -3,10 +3,10 @@ import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue'; import { h } from 'vue';
import { formatDateTime } from '@vben/utils'; import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, erpPriceInputFormatter } from '#/utils'; import { DICT_TYPE } from '#/utils';
/** 详情页的字段 */ /** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] { export function useDetailSchema(): DescriptionItemSchema[] {
@ -108,7 +108,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
title: '回款金额(元)', title: '回款金额(元)',
field: 'price', field: 'price',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '回款方式', title: '回款方式',

View File

@ -1,8 +1,10 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { floatToFixed2 } from '@vben/utils';
import { getCustomerSimpleList } from '#/api/crm/customer'; import { getCustomerSimpleList } from '#/api/crm/customer';
import { DICT_TYPE, getDictOptions } from '#/utils/dict'; import { DICT_TYPE, getDictOptions } from '#/utils';
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(): VbenFormSchema[] { export function useFormSchema(): VbenFormSchema[] {
@ -141,7 +143,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
title: '计划回款金额(元)', title: '计划回款金额(元)',
field: 'price', field: 'price',
minWidth: 160, minWidth: 160,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '计划回款日期', title: '计划回款日期',
@ -183,7 +185,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
title: '实际回款金额(元)', title: '实际回款金额(元)',
field: 'receivable.price', field: 'receivable.price',
minWidth: 160, minWidth: 160,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '实际回款日期', title: '实际回款日期',
@ -197,9 +199,9 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
minWidth: 160, minWidth: 160,
formatter: ({ row }) => { formatter: ({ row }) => {
if (row.receivable) { if (row.receivable) {
return row.price - row.receivable.price; return floatToFixed2(row.price - row.receivable.price);
} }
return row.price; return floatToFixed2(row.price);
}, },
}, },
{ {

View File

@ -3,10 +3,10 @@ import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue'; import { h } from 'vue';
import { formatDateTime } from '@vben/utils'; import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE, erpPriceInputFormatter } from '#/utils'; import { DICT_TYPE } from '#/utils';
/** 详情页的字段 */ /** 详情页的字段 */
export function useDetailSchema(): DescriptionItemSchema[] { export function useDetailSchema(): DescriptionItemSchema[] {
@ -101,7 +101,7 @@ export function useDetailListColumns(): VxeTableGridOptions['columns'] {
title: '计划回款(元)', title: '计划回款(元)',
field: 'price', field: 'price',
minWidth: 150, minWidth: 150,
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '计划回款日期', title: '计划回款日期',

View File

@ -8,6 +8,7 @@ import { useRouter } from 'vue-router';
import { confirm, DocAlert, Page } from '@vben/common-ui'; import { confirm, DocAlert, Page } from '@vben/common-ui';
import { import {
downloadFileFromBlobPart, downloadFileFromBlobPart,
fenToYuan,
handleTree, handleTree,
treeToString, treeToString,
} from '@vben/utils'; } from '@vben/utils';
@ -24,7 +25,7 @@ import {
updateStatus, updateStatus,
} from '#/api/mall/product/spu'; } from '#/api/mall/product/spu';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { fenToYuan, ProductSpuStatusEnum } from '#/utils'; import { ProductSpuStatusEnum } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';

View File

@ -5,6 +5,7 @@ import type { MallOrderApi } from '#/api/mall/trade/order';
import { h, onMounted, ref } from 'vue'; import { h, onMounted, ref } from 'vue';
import { Page, prompt } from '@vben/common-ui'; import { Page, prompt } from '@vben/common-ui';
import { fenToYuan } from '@vben/utils';
import { Card, Input, message } from 'ant-design-vue'; import { Card, Input, message } from 'ant-design-vue';
@ -15,7 +16,7 @@ import {
getOrderSummary, getOrderSummary,
} from '#/api/mall/trade/order'; } from '#/api/mall/trade/order';
import { SummaryCard } from '#/components/summary-card'; import { SummaryCard } from '#/components/summary-card';
import { DeliveryTypeEnum, fenToYuan, TradeOrderStatusEnum } from '#/utils'; import { DeliveryTypeEnum, TradeOrderStatusEnum } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';

View File

@ -6,6 +6,7 @@ import { h } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { DocAlert, Page, prompt, useVbenModal } from '@vben/common-ui'; import { DocAlert, Page, prompt, useVbenModal } from '@vben/common-ui';
import { fenToYuan } from '@vben/utils';
import { Image, List, Tag, Textarea } from 'ant-design-vue'; 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 { getOrderPage, updateOrderRemark } from '#/api/mall/trade/order';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { import { DeliveryTypeEnum, DICT_TYPE, TradeOrderStatusEnum } from '#/utils';
DeliveryTypeEnum,
DICT_TYPE,
fenToYuan,
TradeOrderStatusEnum,
} from '#/utils';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
import DeleveryForm from './modules/delevery-form.vue'; import DeleveryForm from './modules/delevery-form.vue';

View File

@ -2,10 +2,11 @@
import type { MemberUserApi } from '#/api/member/user'; import type { MemberUserApi } from '#/api/member/user';
import type { PayWalletApi } from '#/api/pay/wallet/balance'; import type { PayWalletApi } from '#/api/pay/wallet/balance';
import { fenToYuan } from '@vben/utils';
import { Card } from 'ant-design-vue'; import { Card } from 'ant-design-vue';
import { useDescription } from '#/components/description'; import { useDescription } from '#/components/description';
import { fenToYuan } from '#/utils';
withDefaults( withDefaults(
defineProps<{ defineProps<{

View File

@ -3,6 +3,8 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { h } from 'vue'; import { h } from 'vue';
import { convertToInteger, formatToFraction } from '@vben/utils';
import { Tag } from 'ant-design-vue'; import { Tag } from 'ant-design-vue';
import { z } from '#/adapter/form'; import { z } from '#/adapter/form';
@ -12,9 +14,7 @@ import { getSimpleTagList } from '#/api/member/tag';
import { getAreaTree } from '#/api/system/area'; import { getAreaTree } from '#/api/system/area';
import { import {
CommonStatusEnum, CommonStatusEnum,
convertToInteger,
DICT_TYPE, DICT_TYPE,
formatToFraction,
getDictOptions, getDictOptions,
getRangePickerDefaultProps, getRangePickerDefaultProps,
} from '#/utils'; } from '#/utils';

View File

@ -4,6 +4,7 @@ import type { MemberUserApi } from '#/api/member/user';
import { ref } from 'vue'; import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { formatToFraction } from '@vben/utils';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@ -11,7 +12,6 @@ import { useVbenForm } from '#/adapter/form';
import { getUser, updateUser } from '#/api/member/user'; import { getUser, updateUser } from '#/api/member/user';
import { getWallet } from '#/api/pay/wallet/balance'; import { getWallet } from '#/api/pay/wallet/balance';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { formatToFraction } from '#/utils';
import { useBalanceFormSchema } from '../data'; import { useBalanceFormSchema } from '../data';

View File

@ -2,8 +2,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { PayAppApi } from '#/api/pay/app'; import type { PayAppApi } from '#/api/pay/app';
import { CommonStatusEnum } from '#/utils/constants'; import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [

View File

@ -10,7 +10,7 @@ import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { changeAppStatus, deleteApp, getAppPage } from '#/api/pay/app'; import { changeAppStatus, deleteApp, getAppPage } from '#/api/pay/app';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { CommonStatusEnum, PayChannelEnum } from '#/utils/constants'; import { CommonStatusEnum, PayChannelEnum } from '#/utils';
import { useGridColumns, useGridFormSchema } from './data'; import { useGridColumns, useGridFormSchema } from './data';
import appFrom from './modules/app-form.vue'; import appFrom from './modules/app-form.vue';

View File

@ -3,7 +3,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import { h } from 'vue'; import { h } from 'vue';
import { InputUpload } from '#/components/upload'; import { InputUpload } from '#/components/upload';
import { DICT_TYPE, getDictOptions } from '#/utils/dict'; import { DICT_TYPE, getDictOptions } from '#/utils';
export function channelSchema(formType: string): VbenFormSchema[] { export function channelSchema(formType: string): VbenFormSchema[] {
if (formType.includes('alipay_')) { if (formType.includes('alipay_')) {

View File

@ -6,7 +6,7 @@ import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';
import { useTabs } from '@vben/hooks'; import { useTabs } from '@vben/hooks';
import { formatDate } from '@vben/utils'; import { fenToYuan, formatDate } from '@vben/utils';
import { import {
Button, Button,
@ -19,7 +19,6 @@ import {
import { getOrder, submitOrder } from '#/api/pay/order'; import { getOrder, submitOrder } from '#/api/pay/order';
import { import {
fenToYuan,
PayChannelEnum, PayChannelEnum,
PayDisplayModeEnum, PayDisplayModeEnum,
PayOrderStatusEnum, PayOrderStatusEnum,

View File

@ -50,12 +50,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
field: 'price', field: 'price',
title: '支付价格', title: '支付价格',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'refundPrice', field: 'refundPrice',
title: '退款金额', title: '退款金额',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'createTime', field: 'createTime',

View File

@ -78,7 +78,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
field: 'price', field: 'price',
title: '提现金额', title: '提现金额',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
field: 'userName', field: 'userName',

View File

@ -3,6 +3,7 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { DemoWithdrawApi } from '#/api/pay/demo/withdraw'; import type { DemoWithdrawApi } from '#/api/pay/demo/withdraw';
import { DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { floatToFixed2 } from '@vben/utils';
import { message, Tag } from 'ant-design-vue'; import { message, Tag } from 'ant-design-vue';
@ -109,7 +110,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
<Tag v-else-if="row.type === 3">钱包余额</Tag> <Tag v-else-if="row.type === 3">钱包余额</Tag>
</template> </template>
<template #price="{ row }"> <template #price="{ row }">
<span>{{ (row.price / 100.0).toFixed(2) }}</span> <span>{{ floatToFixed2(row.price) }}</span>
</template> </template>
<template #status="{ row }"> <template #status="{ row }">
<Tag v-if="row.status === 0 && !row.payTransferId" type="warning"> <Tag v-if="row.status === 0 && !row.payTransferId" type="warning">

View File

@ -78,17 +78,17 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
title: '支付金额', title: '支付金额',
field: 'price', field: 'price',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '退款金额', title: '退款金额',
field: 'refundPrice', field: 'refundPrice',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '手续金额', title: '手续金额',
field: 'channelFeePrice', field: 'channelFeePrice',
formatter: 'formatNumber', formatter: 'formatAmount2',
}, },
{ {
title: '订单号', title: '订单号',

View File

@ -4,13 +4,13 @@ import type { PayOrderApi } from '#/api/pay/order';
import { ref } from 'vue'; import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; 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 { Descriptions, Divider, Tag } from 'ant-design-vue';
import { getOrder } from '#/api/pay/order'; import { getOrder } from '#/api/pay/order';
import { DictTag } from '#/components/dict-tag'; import { DictTag } from '#/components/dict-tag';
import { DICT_TYPE } from '#/utils/dict'; import { DICT_TYPE } from '#/utils';
const detailData = ref<PayOrderApi.Order>(); const detailData = ref<PayOrderApi.Order>();
@ -63,16 +63,16 @@ const [Modal, modalApi] = useVbenModal({
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="支付金额"> <Descriptions.Item label="支付金额">
<Tag color="green" size="small"> <Tag color="green" size="small">
{{ (detailData?.price || 0 / 100.0).toFixed(2) }} {{ floatToFixed2(detailData?.price) }}
</Tag> </Tag>
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="手续费"> <Descriptions.Item label="手续费">
<Tag color="orange" size="small"> <Tag color="orange" size="small">
{{ (detailData?.channelFeePrice || 0 / 100.0).toFixed(2) }} {{ floatToFixed2(detailData?.channelFeePrice) }}
</Tag> </Tag>
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="手续费比例"> <Descriptions.Item label="手续费比例">
{{ (detailData?.channelFeeRate || 0 / 100.0).toFixed(2) }}% {{ floatToFixed2(detailData?.channelFeeRate) }}%
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="支付时间"> <Descriptions.Item label="支付时间">
{{ formatDateTime(detailData?.successTime) }} {{ formatDateTime(detailData?.successTime) }}
@ -115,7 +115,7 @@ const [Modal, modalApi] = useVbenModal({
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="退款金额" :span="2"> <Descriptions.Item label="退款金额" :span="2">
<Tag size="small" color="red"> <Tag size="small" color="red">
{{ (detailData?.refundPrice || 0 / 100.0).toFixed(2) }} {{ floatToFixed2(detailData?.refundPrice) }}
</Tag> </Tag>
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="通知 URL"> <Descriptions.Item label="通知 URL">

View File

@ -4,7 +4,7 @@ import type { DescriptionItemSchema } from '#/components/description';
import { h } from 'vue'; import { h } from 'vue';
import { formatDateTime } from '@vben/utils'; import { floatToFixed2, formatDateTime } from '@vben/utils';
import { Tag } from 'ant-design-vue'; import { Tag } from 'ant-design-vue';
@ -120,7 +120,7 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
{ {
field: 'price', field: 'price',
title: '转账金额', title: '转账金额',
formatter: ({ cellValue }) => `${(cellValue / 100).toFixed(2)}`, formatter: 'formatAmount2',
}, },
{ {
field: 'status', field: 'status',
@ -217,7 +217,7 @@ export function useDetailSchema(): DescriptionItemSchema[] {
content: (data) => { content: (data) => {
return h(Tag, { return h(Tag, {
color: 'blue', color: 'blue',
content: `${(data?.price / 100).toFixed(2)}`, content: `${floatToFixed2(data?.price)}`,
}); });
}, },
}, },

View File

@ -4,6 +4,7 @@ import type { WalletRechargePackageApi } from '#/api/pay/wallet/rechargePackage'
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { fenToYuan, yuanToFen } from '@vben/utils';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@ -14,7 +15,6 @@ import {
updatePackage, updatePackage,
} from '#/api/pay/wallet/rechargePackage'; } from '#/api/pay/wallet/rechargePackage';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { fenToYuan, yuanToFen } from '#/utils';
import { useFormSchema } from '../data'; import { useFormSchema } from '../data';

View File

@ -76,6 +76,15 @@ export function useTypeGridFormSchema(): VbenFormSchema[] {
clearable: true, clearable: true,
}, },
}, },
{
fieldName: 'type',
label: '字典类型',
component: 'Input',
componentProps: {
placeholder: '请输入字典类型',
clearable: true,
},
},
{ {
fieldName: 'status', fieldName: 'status',
label: '状态', label: '状态',

View File

@ -1,4 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Recordable } from '@vben/types';
import type { SystemDeptApi } from '#/api/system/dept'; import type { SystemDeptApi } from '#/api/system/dept';
import type { SystemRoleApi } from '#/api/system/role'; import type { SystemRoleApi } from '#/api/system/role';
@ -13,6 +15,7 @@ import { useVbenForm } from '#/adapter/form';
import { getMenuList } from '#/api/system/menu'; import { getMenuList } from '#/api/system/menu';
import { assignRoleMenu, getRoleMenuList } from '#/api/system/permission'; import { assignRoleMenu, getRoleMenuList } from '#/api/system/permission';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { SystemMenuTypeEnum } from '#/utils';
import { useAssignMenuFormSchema } from '../data'; import { useAssignMenuFormSchema } from '../data';
@ -121,6 +124,18 @@ function getAllNodeIds(nodes: any[], ids: number[] = []): number[] {
}); });
return ids; 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> </script>
<template> <template>
@ -134,9 +149,11 @@ function getAllNodeIds(nodes: any[], ids: number[] = []): number[] {
multiple multiple
bordered bordered
:expanded="expandedKeys" :expanded="expandedKeys"
:get-node-class="getNodeClass"
v-bind="slotProps" v-bind="slotProps"
value-field="id" value-field="id"
label-field="name" label-field="name"
icon-field="meta.icon"
/> />
</template> </template>
</Form> </Form>

View File

@ -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 * @param num
*/ */
export function formatToFraction(num: number | string | undefined): string { export function formatToFraction(num: number | string | undefined): string {
if (num === undefined) return '0.00'; return formatToFractionDigit(num, 2);
const parsedNumber = typeof num === 'string' ? Number.parseFloat(num) : num;
return (parsedNumber / 100).toFixed(2);
} }
/** /**
@ -17,9 +30,7 @@ export function formatToFraction(num: number | string | undefined): string {
*/ */
export function floatToFixed2(num: number | string | undefined): string { export function floatToFixed2(num: number | string | undefined): string {
let str = '0.00'; let str = '0.00';
if (num === undefined) { if (isUndefined(num)) return str;
return str;
}
const f = formatToFraction(num); const f = formatToFraction(num);
const decimalPart = f.toString().split('.')[1]; const decimalPart = f.toString().split('.')[1];
const len = decimalPart ? decimalPart.length : 0; const len = decimalPart ? decimalPart.length : 0;
@ -45,8 +56,8 @@ export function floatToFixed2(num: number | string | undefined): string {
* @param num * @param num
*/ */
export function convertToInteger(num: number | string | undefined): number { export function convertToInteger(num: number | string | undefined): number {
if (num === undefined) return 0; if (isUndefined(num)) return 0;
const parsedNumber = typeof num === 'string' ? Number.parseFloat(num) : num; const parsedNumber = isString(num) ? Number.parseFloat(num) : num;
return Math.round(parsedNumber * 100); return Math.round(parsedNumber * 100);
} }
@ -125,7 +136,6 @@ export function erpCountInputFormatter(num: number | string | undefined) {
return erpNumberFormatter(num, ERP_COUNT_DIGIT); return erpNumberFormatter(num, ERP_COUNT_DIGIT);
} }
// noinspection JSCommentMatchesSignature
/** /**
* ERP * ERP
* *
@ -148,7 +158,6 @@ export function erpPriceInputFormatter(num: number | string | undefined) {
return erpNumberFormatter(num, ERP_PRICE_DIGIT); return erpNumberFormatter(num, ERP_PRICE_DIGIT);
} }
// noinspection JSCommentMatchesSignature
/** /**
* ERP * ERP
* *
@ -167,9 +176,7 @@ export function erpPriceTableColumnFormatter(cellValue: any) {
* @return undefined * @return undefined
*/ */
export function erpPriceMultiply(price: number, count: number) { export function erpPriceMultiply(price: number, count: number) {
if (price === null || count === null) { if (isEmpty(price) || isEmpty(count)) return undefined;
return undefined;
}
return Number.parseFloat((price * count).toFixed(ERP_PRICE_DIGIT)); return Number.parseFloat((price * count).toFixed(ERP_PRICE_DIGIT));
} }

View File

@ -3,6 +3,7 @@ export * from './date';
export * from './diff'; export * from './diff';
export * from './dom'; export * from './dom';
export * from './download'; export * from './download';
export * from './formatNumber';
export * from './inference'; export * from './inference';
export * from './letter'; export * from './letter';
export * from './merge'; export * from './merge';