Skip to content

Commit abe02be

Browse files
fit2cloudwxxfit2-zhao
authored andcommitted
feat: add process drawer
1 parent 29162d0 commit abe02be

14 files changed

Lines changed: 283 additions & 37 deletions

File tree

frontend/packages/web/src/components/business/crm-approval-popover/useApprovalPopoverDetail.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ApproverItem } from '@/components/business/crm-approver-avatar-list/ind
66

77
export type ApprovalPopoverFormKeyType =
88
| FormDesignKeyEnum.CONTRACT
9-
| FormDesignKeyEnum.CONTRACT_INVOICE
9+
| FormDesignKeyEnum.INVOICE
1010
| FormDesignKeyEnum.OPPORTUNITY_QUOTATION
1111
| FormDesignKeyEnum.ORDER;
1212

@@ -26,7 +26,7 @@ const detailApiMap: Record<ApprovalPopoverFormKeyType, (sourceId: string) => Pro
2626
approveUserList: [],
2727
resourceId: '',
2828
}),
29-
[FormDesignKeyEnum.CONTRACT_INVOICE]: (sourceId: string) =>
29+
[FormDesignKeyEnum.INVOICE]: (sourceId: string) =>
3030
Promise.resolve({
3131
approveStatus: ProcessStatusEnum.NONE,
3232
approveUserList: [],

frontend/packages/web/src/components/business/crm-approver-avatar-list/index.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,15 @@
104104
display: flex;
105105
padding: 8px 2px;
106106
flex-wrap: wrap;
107-
gap: 8px;
107+
gap: 4px;
108108
}
109109
.crm-approver-avatar-list__item {
110110
display: flex;
111111
align-items: center;
112112
min-width: 0;
113113
max-width: 100%;
114-
gap: 8px;
114+
column-gap: 8px;
115+
row-gap: 4px;
115116
cursor: pointer;
116117
}
117118
.crm-approver-avatar-list__avatar-wrap {
@@ -125,7 +126,7 @@
125126
transition: box-shadow 0.18s ease;
126127
@apply flex flex-shrink-0 items-center justify-between;
127128
&--active {
128-
box-shadow: 0 0 0 0.5px var(--primary-8);
129+
box-shadow: 0 0 0 1px var(--primary-8);
129130
}
130131
}
131132
.crm-approver-avatar-list__item:hover {

frontend/packages/web/src/components/business/crm-editable-text/index.vue

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@
55
v-model:value="inputValue"
66
:maxlength="255"
77
clearable
8+
@update-value="handleInput"
89
@keydown.enter="confirmEdit"
910
@blur="handleBlur"
1011
/>
11-
<div v-else class="flex items-center gap-[8px]">
12+
<div
13+
v-else
14+
class="crm-editable-text-view flex min-w-0 max-w-full items-center gap-[8px]"
15+
:class="{ 'cursor-pointer': props.clickToEdit }"
16+
@click="props.clickToEdit ? enableEditMode() : undefined"
17+
>
1218
<slot>{{ value }} </slot>
1319
<CrmIcon
1420
v-permission="props.permission"
1521
class="table-row-edit cursor-pointer text-[var(--text-n4)]"
1622
type="iconicon_edit"
1723
:size="16"
18-
@click="enableEditMode"
24+
@click.stop="enableEditMode"
1925
/>
2026
</div>
2127
</template>
@@ -28,10 +34,14 @@
2834
const props = defineProps<{
2935
value: string;
3036
permission: string[];
37+
clickToEdit?: boolean;
38+
emptyTextTip?: string;
3139
}>();
3240
3341
const emit = defineEmits<{
3442
(e: 'handleEdit', value: string, done?: () => void): void;
43+
(e: 'input', value: string): void;
44+
(e: 'cancel'): void;
3545
}>();
3646
3747
const { t } = useI18n();
@@ -51,7 +61,7 @@
5161
5262
function confirmEdit() {
5363
if (!inputValue.value.trim().length) {
54-
Message.warning(t('common.value.notNull'));
64+
Message.warning(props.emptyTextTip ?? t('common.value.notNull'));
5565
return;
5666
}
5767
emit('handleEdit', inputValue.value, () => {
@@ -61,10 +71,19 @@
6171
6272
function handleBlur() {
6373
isEditing.value = false;
74+
emit('cancel');
75+
}
76+
77+
function handleInput(value: string) {
78+
emit('input', value);
6479
}
6580
</script>
6681

6782
<style lang="less">
83+
.crm-editable-text-view {
84+
min-width: 0;
85+
max-width: 100%;
86+
}
6887
.n-data-table {
6988
.table-row-edit {
7089
@apply invisible;

frontend/packages/web/src/components/business/crm-process-drawer/index.vue

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,21 @@
1212
>
1313
<template #header>
1414
<div class="crm-process-drawer-header-content">
15-
<div class="crm-process-drawer-header-item flex items-center">
15+
<div class="crm-process-drawer-header-item crm-process-drawer-header-item--title flex items-center">
1616
<n-button text class="mr-[4px] w-[32px]" @click="handleCancel">
1717
<n-icon size="16">
1818
<ChevronBackOutline />
1919
</n-icon>
2020
</n-button>
21-
<div class="one-line-text flex flex-1 items-center gap-[8px]">
22-
<n-tooltip trigger="hover" :delay="300" :disabled="!props.title">
23-
<template #trigger>
24-
<div class="one-line-text !leading-[20px]"> {{ props.title ?? '-' }}</div>
25-
</template>
26-
{{ props.title ?? '-' }}
27-
</n-tooltip>
21+
<div class="crm-process-drawer-title-wrap flex flex-1 items-center gap-[8px]">
22+
<slot name="title">
23+
<n-tooltip trigger="hover" :delay="300" :disabled="!props.title">
24+
<template #trigger>
25+
<div class="one-line-text !leading-[20px]"> {{ props.title ?? '-' }}</div>
26+
</template>
27+
{{ props.title ?? '-' }}
28+
</n-tooltip>
29+
</slot>
2830
</div>
2931
</div>
3032
<div class="flex justify-center">
@@ -111,10 +113,21 @@
111113
<style scoped lang="less">
112114
.crm-process-drawer-header-content {
113115
padding: 8px;
116+
gap: 24px;
114117
box-sizing: border-box;
115118
@apply flex items-center;
116119
.crm-process-drawer-header-item {
117120
@apply flex flex-1;
121+
122+
min-width: 0;
123+
}
124+
.crm-process-drawer-header-item--title {
125+
flex: 1 1 0;
126+
min-width: 0;
127+
}
128+
.crm-process-drawer-title-wrap {
129+
overflow: hidden;
130+
min-width: 0;
118131
}
119132
}
120133
</style>

frontend/packages/web/src/config/process.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { FormDesignKeyEnum } from '@lib/shared/enums/formDesignEnum';
12
import { ProcessStatusEnum } from '@lib/shared/enums/process';
23
import { useI18n } from '@lib/shared/hooks/useI18n';
34
import { ProcessStatusType } from '@lib/shared/models/system/process';
@@ -43,3 +44,17 @@ export const processStatusOptions = Object.entries(processStatusMap).map(([key,
4344
label: value.label,
4445
value: key,
4546
}));
47+
48+
export const defaultBasicForm = {
49+
businessType: FormDesignKeyEnum.OPPORTUNITY_QUOTATION,
50+
name: '',
51+
executionTiming: ['CREATE'],
52+
description: '',
53+
};
54+
55+
export const defaultMoreConfig = {
56+
submitterAuthority: true,
57+
approverAuthority: [],
58+
autoApproval: 'firstNodeApproval',
59+
approvalOpinion: false,
60+
};

frontend/packages/web/src/router/routes/modules/system.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const system: AppRouteRecordRaw = {
1717
'SYSTEM_NOTICE:READ',
1818
'SYSTEM_SETTING:READ',
1919
'OPERATION_LOG:READ',
20+
'APPROVAL_FLOW:READ',
2021
],
2122
icon: 'iconicon_set_up',
2223
collapsedLocale: 'menu.collapsedSettings',
@@ -65,7 +66,7 @@ const system: AppRouteRecordRaw = {
6566
meta: {
6667
hideChildrenInMenu: true,
6768
locale: 'menu.settings.processSetting',
68-
permissions: [], // todo xinxinwu
69+
permissions: ['APPROVAL_FLOW:READ'],
6970
},
7071
children: [
7172
{
@@ -75,7 +76,7 @@ const system: AppRouteRecordRaw = {
7576
meta: {
7677
locale: 'menu.settings.approvalFlow',
7778
isTopMenu: true,
78-
permissions: [], // todo xinxinwu
79+
permissions: ['APPROVAL_FLOW:READ'],
7980
},
8081
},
8182
// todo 这个版本不上工作流

frontend/packages/web/src/views/contract/invoice/components/invoiceTable.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@
459459
approvalStatus: (row: ContractInvoiceItem) =>
460460
h(CrmApprovalPopover, {
461461
status: row.approvalStatus,
462-
formKey: FormDesignKeyEnum.CONTRACT_INVOICE,
462+
formKey: FormDesignKeyEnum.INVOICE,
463463
sourceId: row.id,
464464
disabled: row.approvalStatus !== ProcessStatusEnum.UNAPPROVED,
465465
onMore: () => {

frontend/packages/web/src/views/system/process/locale/en-US.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ export default {
3131
'process.process.approvalAuthority.allowAddSign': 'Allow adding signatory',
3232
'process.process.approvalAuthority.allowAddTempApprover':
3333
'When checked, approver can add temporary approver in the approval flow',
34+
'process.process.processName': 'Process Name',
35+
'process.process.basic.businessType': 'Business Type',
36+
'process.process.basic.executionTiming': 'Execution timing',
37+
'process.process.basic.description': 'Description',
3438
};

frontend/packages/web/src/views/system/process/locale/zh-CN.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,8 @@ export default {
2828
'process.process.approvalAuthority.addTempApprover': '勾选后,审批人可在审批流程中增加临时审批人',
2929
'process.process.approvalAuthority.allowAddSign': '允许加签',
3030
'process.process.approvalAuthority.allowAddTempApprover': '勾选后,审批人可在审批流程中增加临时审批人',
31+
'process.process.processName': '流程名称',
32+
'process.process.basic.businessType': '业务类型',
33+
'process.process.basic.executionTiming': '执行时机',
34+
'process.process.basic.description': '描述信息',
3135
};

frontend/packages/web/src/views/system/process/process/components/addProcessDrawer.vue

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,50 @@
77
@next-step="handleNextStep"
88
@cancel="() => emit('cancel')"
99
>
10+
<template #title>
11+
<div class="process-name-header flex max-w-full flex-1 overflow-hidden">
12+
<CrmEditableText
13+
:status="errorStatus"
14+
size="small"
15+
:value="form.basicConfig.name"
16+
:permission="['APPROVAL_FLOW:UPDATE']"
17+
click-to-edit
18+
:emptyTextTip="t('common.notNull', { value: t('process.process.processName') })"
19+
@handle-edit="handleEditName"
20+
@input="handleInput"
21+
@cancel="handleCancelEditName"
22+
>
23+
<n-tooltip trigger="hover" :delay="300" :disabled="!form.basicConfig.name">
24+
<template #trigger>
25+
<div class="process-name one-line-text">
26+
{{ form.basicConfig.name ?? '-' }}
27+
</div>
28+
</template>
29+
{{ form.basicConfig.name ?? '-' }}
30+
</n-tooltip>
31+
</CrmEditableText>
32+
</div>
33+
</template>
1034
<template v-if="visible">
11-
<ApprovalFlowDesign v-if="activeTab === 'process'" />
12-
<moreSetting v-if="activeTab === 'moreSetting'" />
35+
<ApprovalFlowDesign v-if="activeTab === 'process'" v-model:basicConfig="form.basicConfig" />
36+
<moreSetting v-if="activeTab === 'moreSetting'" v-model:moreConfig="form.moreConfig" />
1337
</template>
1438
</CrmProcessDrawer>
1539
</template>
1640

1741
<script setup lang="ts">
1842
import { ref } from 'vue';
19-
import { useMessage } from 'naive-ui';
43+
import { NTooltip } from 'naive-ui';
2044
2145
import { useI18n } from '@lib/shared/hooks/useI18n';
2246
47+
import CrmEditableText from '@/components/business/crm-editable-text/index.vue';
2348
import CrmProcessDrawer from '@/components/business/crm-process-drawer/index.vue';
2449
import ApprovalFlowDesign from './approvalFlowDesign.vue';
2550
import moreSetting from './moreSetting.vue';
2651
52+
import { defaultBasicForm, defaultMoreConfig } from '@/config/process';
53+
2754
const { t } = useI18n();
2855
2956
const props = defineProps<{
@@ -40,6 +67,13 @@
4067
4168
const activeTab = ref('process');
4269
70+
const form = ref({
71+
basicConfig: defaultBasicForm,
72+
moreConfig: defaultMoreConfig,
73+
});
74+
75+
const editingName = ref('');
76+
4377
const tabList = [
4478
{
4579
name: 'process',
@@ -60,6 +94,49 @@
6094
}
6195
6296
function handleSave() {}
97+
98+
function handleEditName(value: string, done?: () => void) {
99+
form.value.basicConfig.name = value;
100+
editingName.value = value;
101+
done?.();
102+
}
103+
104+
function handleCancelEditName() {
105+
editingName.value = form.value.basicConfig.name ?? '';
106+
}
107+
108+
const errorStatus = computed(() => (editingName.value.trim().length ? '' : 'error'));
109+
function handleInput(value: string) {
110+
editingName.value = value;
111+
}
63112
</script>
64113

65-
<style scoped></style>
114+
<style lang="less">
115+
.process-name-header {
116+
min-width: 0;
117+
> * {
118+
min-width: 0;
119+
max-width: 100%;
120+
flex: 1 1 auto;
121+
}
122+
.table-row-edit {
123+
@apply invisible;
124+
}
125+
&:hover {
126+
.table-row-edit {
127+
color: var(--primary-8);
128+
@apply visible;
129+
}
130+
}
131+
.process-name {
132+
overflow: hidden;
133+
min-width: 0;
134+
max-width: 100%;
135+
font-size: 14px;
136+
font-weight: 400;
137+
border-bottom: 2px solid var(--text-n6);
138+
text-overflow: ellipsis;
139+
white-space: nowrap;
140+
}
141+
}
142+
</style>

0 commit comments

Comments
 (0)