adtec-core-package 2.9.7 → 2.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/api/workflow/workflowCommentApi.ts +5 -2
- package/src/components/RichTextEditor/UmoRichTextEditor.vue +257 -0
- package/src/components/RichTextEditor/richTextHtmlUtils.ts +166 -0
- package/src/components/upload/FileView.vue +22 -18
- package/src/components/workflow/components/AddOrMinusMultiDialog.vue +17 -2
- package/src/components/workflow/components/CheckDialog.vue +3 -0
- package/src/components/workflow/components/ProcessInstanceStep.vue +17 -2
- package/src/types/umoteam-editor.d.ts +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adtec-core-package",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"@element-plus/icons-vue": "^2.3.1",
|
|
17
17
|
"@onlyoffice/document-editor-vue": "1.4.0",
|
|
18
18
|
"@types/echarts": "^5.0.0",
|
|
19
|
+
"@umoteam/editor": "^10.2.1",
|
|
19
20
|
"@vueuse/components": "^13.0.0",
|
|
20
21
|
"@vueuse/core": "^13.0.0",
|
|
21
22
|
"@vxe-ui/plugin-export-pdf": "4.2.4",
|
|
@@ -17,7 +17,10 @@ export default {
|
|
|
17
17
|
addOrEditComment(tpReviewBatch: ITpReviewBatch) {
|
|
18
18
|
return request.post<ITpReviewBatch>('/api/tm/tpApplyCheck/updateBatchInfo', tpReviewBatch)
|
|
19
19
|
},
|
|
20
|
-
getBatchInfo(tpApplyId: string) {
|
|
21
|
-
return request.get<ITpReviewBatch>('/api/tm/tpApplyCheck/getBatchInfo', { tpApplyId })
|
|
20
|
+
getBatchInfo(tpApplyId: string, batchPrefer: 'max' | 'min' = 'max') {
|
|
21
|
+
return request.get<ITpReviewBatch>('/api/tm/tpApplyCheck/getBatchInfo', { tpApplyId, batchPrefer })
|
|
22
|
+
},
|
|
23
|
+
getBatchInfoByTaskId(taskId: string) {
|
|
24
|
+
return request.get<ITpReviewBatch>('/api/tm/tpApplyCheck/getBatchInfoByTaskId', { taskId })
|
|
22
25
|
},
|
|
23
26
|
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
<!-- Umo Editor 封装:与 RichTextEditor 保持相同对外 API,供结题验收等模块试点 -->
|
|
2
|
+
<template>
|
|
3
|
+
<el-flex ref="rootRef" class="umo-rich-text-editor-root" :style="{ height: '100%', width: '100%' }">
|
|
4
|
+
<UmoEditor
|
|
5
|
+
ref="editorRef"
|
|
6
|
+
class="umo-rich-text-editor"
|
|
7
|
+
v-bind="umoOptions"
|
|
8
|
+
@created="onEditorCreated"
|
|
9
|
+
@changed="onEditorChanged"
|
|
10
|
+
@blur="onEditorBlur"
|
|
11
|
+
/>
|
|
12
|
+
</el-flex>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
import { UmoEditor } from '@umoteam/editor'
|
|
17
|
+
import '@umoteam/editor/style'
|
|
18
|
+
import { ElMessage } from 'element-plus'
|
|
19
|
+
import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
|
|
20
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
21
|
+
import { finalizeHtml, initHtml } from './richTextHtmlUtils'
|
|
22
|
+
|
|
23
|
+
export type RichTextEditorConfig = {
|
|
24
|
+
placeholder?: string
|
|
25
|
+
editable?: boolean
|
|
26
|
+
[key: string]: unknown
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const emit = defineEmits<{
|
|
30
|
+
onBlur: [payload: { html: string; text: string }]
|
|
31
|
+
onChange: [payload: { html: string; text: string }]
|
|
32
|
+
}>()
|
|
33
|
+
|
|
34
|
+
const props = withDefaults(
|
|
35
|
+
defineProps<{
|
|
36
|
+
showToolbar?: boolean
|
|
37
|
+
borderWidth?: string
|
|
38
|
+
editorConfig?: RichTextEditorConfig
|
|
39
|
+
isEdit?: boolean
|
|
40
|
+
}>(),
|
|
41
|
+
{
|
|
42
|
+
showToolbar: true,
|
|
43
|
+
borderWidth: '1px',
|
|
44
|
+
editorConfig: () => ({}),
|
|
45
|
+
isEdit: true,
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const model = defineModel<string | null | undefined>({
|
|
50
|
+
required: false,
|
|
51
|
+
default: null,
|
|
52
|
+
})
|
|
53
|
+
const text = defineModel<string>('text', {
|
|
54
|
+
required: false,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const editorRef = ref<InstanceType<typeof UmoEditor> | null>(null)
|
|
58
|
+
const rootRef = ref<HTMLElement | null>(null)
|
|
59
|
+
const editorKey = `umo-${uuidv4()}`
|
|
60
|
+
const ready = ref(false)
|
|
61
|
+
const syncingContent = ref(false)
|
|
62
|
+
|
|
63
|
+
const placeholderText = computed(() => {
|
|
64
|
+
const raw = props.editorConfig?.placeholder
|
|
65
|
+
if (typeof raw === 'string' && raw.trim()) return raw
|
|
66
|
+
return '请输入内容...'
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const editable = computed(() => {
|
|
70
|
+
if (!props.isEdit) return false
|
|
71
|
+
if (props.editorConfig?.editable === false) return false
|
|
72
|
+
return true
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const umoOptions = computed(() => ({
|
|
76
|
+
editorKey,
|
|
77
|
+
locale: 'zh-CN',
|
|
78
|
+
theme: 'light',
|
|
79
|
+
height: '100%',
|
|
80
|
+
toolbar: {
|
|
81
|
+
showSaveLabel: false,
|
|
82
|
+
defaultMode: 'classic',
|
|
83
|
+
menus: props.showToolbar ? ['base', 'insert', 'table', 'tools'] : ['base'],
|
|
84
|
+
},
|
|
85
|
+
page: {
|
|
86
|
+
layouts: ['web'],
|
|
87
|
+
},
|
|
88
|
+
document: {
|
|
89
|
+
title: '',
|
|
90
|
+
content: initHtml(model.value ?? ''),
|
|
91
|
+
placeholder: {
|
|
92
|
+
zh_CN: placeholderText.value,
|
|
93
|
+
},
|
|
94
|
+
readOnly: !editable.value,
|
|
95
|
+
enableMarkdown: false,
|
|
96
|
+
enableSpellcheck: false,
|
|
97
|
+
autoSave: {
|
|
98
|
+
enabled: false,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
shareUrl: typeof location !== 'undefined' ? location.href : '',
|
|
102
|
+
async onFileUpload(file: File) {
|
|
103
|
+
if (file.type.startsWith('image/') && file.size > 2 * 1024 * 1024) {
|
|
104
|
+
ElMessage.warning('图片大小不能超过2M')
|
|
105
|
+
throw new Error('image too large')
|
|
106
|
+
}
|
|
107
|
+
const url = await readFileAsDataUrl(file)
|
|
108
|
+
return {
|
|
109
|
+
id: uuidv4(),
|
|
110
|
+
url,
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
}))
|
|
114
|
+
|
|
115
|
+
function readFileAsDataUrl(file: File) {
|
|
116
|
+
return new Promise<string>((resolve, reject) => {
|
|
117
|
+
const reader = new FileReader()
|
|
118
|
+
reader.onload = () => resolve(String(reader.result ?? ''))
|
|
119
|
+
reader.onerror = () => reject(reader.error)
|
|
120
|
+
reader.readAsDataURL(file)
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function getEditorInstance() {
|
|
125
|
+
return editorRef.value as unknown as {
|
|
126
|
+
getHTML?: () => string
|
|
127
|
+
getText?: () => string
|
|
128
|
+
setContent?: (content: string, options?: Record<string, unknown>) => void
|
|
129
|
+
setReadOnly?: (value: boolean) => void
|
|
130
|
+
setToolbar?: (payload: { mode?: string; show?: boolean }) => void
|
|
131
|
+
setLayout?: (layout: 'page' | 'web') => void
|
|
132
|
+
} | null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getRawHtml() {
|
|
136
|
+
const editor = getEditorInstance()
|
|
137
|
+
return editor?.getHTML?.() ?? ''
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getPlainText() {
|
|
141
|
+
const editor = getEditorInstance()
|
|
142
|
+
return editor?.getText?.() ?? ''
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function applyEditorHtml(html: string, focus = false) {
|
|
146
|
+
const editor = getEditorInstance()
|
|
147
|
+
editor?.setContent?.(initHtml(html), {
|
|
148
|
+
emitUpdate: false,
|
|
149
|
+
focusPosition: focus ? 'end' : false,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function syncEditorPresentation() {
|
|
154
|
+
const editor = getEditorInstance()
|
|
155
|
+
if (!editor) return
|
|
156
|
+
editor.setLayout?.('web')
|
|
157
|
+
editor.setReadOnly?.(!editable.value)
|
|
158
|
+
editor.setToolbar?.({
|
|
159
|
+
mode: 'classic',
|
|
160
|
+
show: props.showToolbar && editable.value,
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function publishModelFromEditor(finalize = false) {
|
|
165
|
+
const editor = getEditorInstance()
|
|
166
|
+
if (!editor) return
|
|
167
|
+
let html = getRawHtml()
|
|
168
|
+
if (finalize) {
|
|
169
|
+
html = finalizeHtml(html, rootRef.value)
|
|
170
|
+
}
|
|
171
|
+
syncingContent.value = true
|
|
172
|
+
model.value = html
|
|
173
|
+
text.value = getPlainText()
|
|
174
|
+
syncingContent.value = false
|
|
175
|
+
emit('onChange', { html, text: getPlainText() })
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function onEditorCreated() {
|
|
179
|
+
ready.value = true
|
|
180
|
+
syncEditorPresentation()
|
|
181
|
+
applyEditorHtml(model.value ?? '', false)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function onEditorChanged() {
|
|
185
|
+
if (!ready.value || syncingContent.value) return
|
|
186
|
+
publishModelFromEditor(false)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function onEditorBlur() {
|
|
190
|
+
publishModelFromEditor(true)
|
|
191
|
+
emit('onBlur', { html: model.value ?? '', text: getPlainText() })
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
watch(
|
|
195
|
+
() => model.value,
|
|
196
|
+
(value) => {
|
|
197
|
+
if (!ready.value || syncingContent.value) return
|
|
198
|
+
const current = getRawHtml()
|
|
199
|
+
if ((value ?? '') === current) return
|
|
200
|
+
applyEditorHtml(value ?? '', false)
|
|
201
|
+
},
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
watch(
|
|
205
|
+
() => [editable.value, props.showToolbar] as const,
|
|
206
|
+
async () => {
|
|
207
|
+
if (!ready.value) return
|
|
208
|
+
await nextTick()
|
|
209
|
+
syncEditorPresentation()
|
|
210
|
+
},
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
watch(placeholderText, () => {
|
|
214
|
+
if (!ready.value) return
|
|
215
|
+
getEditorInstance()?.setContent?.(getRawHtml(), { emitUpdate: false })
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
defineExpose({
|
|
219
|
+
getHtml: () => {
|
|
220
|
+
if (!ready.value) return model.value ?? ''
|
|
221
|
+
return finalizeHtml(getRawHtml(), rootRef.value)
|
|
222
|
+
},
|
|
223
|
+
getText: () => {
|
|
224
|
+
if (!ready.value) return text.value ?? ''
|
|
225
|
+
return getPlainText()
|
|
226
|
+
},
|
|
227
|
+
setContent: (html: string, focus = false) => {
|
|
228
|
+
model.value = html
|
|
229
|
+
if (ready.value) {
|
|
230
|
+
applyEditorHtml(html, focus)
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
onUnmounted(() => {
|
|
236
|
+
ready.value = false
|
|
237
|
+
})
|
|
238
|
+
</script>
|
|
239
|
+
|
|
240
|
+
<style scoped lang="scss">
|
|
241
|
+
.umo-rich-text-editor-root {
|
|
242
|
+
min-height: 280px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
:deep(.umo-rich-text-editor) {
|
|
246
|
+
width: 100%;
|
|
247
|
+
height: 100%;
|
|
248
|
+
border-width: v-bind(borderWidth);
|
|
249
|
+
border-style: solid;
|
|
250
|
+
border-color: #dcdfe6;
|
|
251
|
+
box-sizing: border-box;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
:deep(.umo-editor-container) {
|
|
255
|
+
min-height: 240px;
|
|
256
|
+
}
|
|
257
|
+
</style>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const fontSizes = [
|
|
2
|
+
{ name: '八号', pt: 5, px: 5 },
|
|
3
|
+
{ name: '七号', pt: 5.5, px: 6 },
|
|
4
|
+
{ name: '小六', pt: 6.5, px: 8 },
|
|
5
|
+
{ name: '六号', pt: 7.5, px: 9 },
|
|
6
|
+
{ name: '小五', pt: 9, px: 11 },
|
|
7
|
+
{ name: '五号', pt: 10.5, px: 12 },
|
|
8
|
+
{ name: '小四', pt: 12, px: 14 },
|
|
9
|
+
{ name: '四号', pt: 14, px: 16 },
|
|
10
|
+
{ name: '小三', pt: 15, px: 18 },
|
|
11
|
+
{ name: '三号', pt: 16, px: 19 },
|
|
12
|
+
{ name: '小二', pt: 18, px: 21 },
|
|
13
|
+
{ name: '二号', pt: 22, px: 26 },
|
|
14
|
+
{ name: '小一', pt: 24, px: 28 },
|
|
15
|
+
{ name: '一号', pt: 26, px: 30 },
|
|
16
|
+
{ name: '小初', pt: 36, px: 42 },
|
|
17
|
+
{ name: '初号', pt: 42, px: 49 },
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
function setDefaultPx(html: string) {
|
|
21
|
+
const tempDiv = document.createElement('div')
|
|
22
|
+
tempDiv.innerHTML = html
|
|
23
|
+
tempDiv.querySelectorAll('p').forEach((p) => {
|
|
24
|
+
if (!p.style.fontSize) {
|
|
25
|
+
p.style.fontSize = '14px'
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
return tempDiv.innerHTML
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function convertPxToPt(html: string) {
|
|
32
|
+
html = setDefaultPx(html)
|
|
33
|
+
const regex = /font-size: (\d+)px/g
|
|
34
|
+
return html.replace(regex, (match, pxValue) => {
|
|
35
|
+
const px = parseInt(pxValue, 10)
|
|
36
|
+
const entry = fontSizes.find((item) => item.px === px)
|
|
37
|
+
if (entry) {
|
|
38
|
+
return `font-size: ${entry.pt}pt`
|
|
39
|
+
}
|
|
40
|
+
return match
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function convertPtToPx(html: string) {
|
|
45
|
+
const regex = /font-size: (\d+)pt/g
|
|
46
|
+
return html.replace(regex, (match, ptValue) => {
|
|
47
|
+
const pt = parseFloat(ptValue)
|
|
48
|
+
const entry = fontSizes.find((item) => item.pt === pt)
|
|
49
|
+
if (entry) {
|
|
50
|
+
return `font-size: ${entry.px}px`
|
|
51
|
+
}
|
|
52
|
+
return match
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function normalizeStyleString(style: string): string {
|
|
57
|
+
style = style.trim()
|
|
58
|
+
if (!style) return ''
|
|
59
|
+
if (style.charAt(style.length - 1) !== ';') {
|
|
60
|
+
style += ';'
|
|
61
|
+
}
|
|
62
|
+
return style
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function removeAllFontFamilyStyles(html: string): string {
|
|
66
|
+
const tempDiv = document.createElement('div')
|
|
67
|
+
tempDiv.innerHTML = html
|
|
68
|
+
|
|
69
|
+
function processElement(element: Element) {
|
|
70
|
+
if (element.hasAttribute('style')) {
|
|
71
|
+
let style = element.getAttribute('style') || ''
|
|
72
|
+
style = normalizeStyleString(style)
|
|
73
|
+
style = style.replace(
|
|
74
|
+
/(?:font-family|mso-(?:ascii|hansi|bidi|fareast|east-asian|font)-font-family)\s*:\s*[^;]+;/gi,
|
|
75
|
+
'',
|
|
76
|
+
)
|
|
77
|
+
style = style.trim()
|
|
78
|
+
if (style) {
|
|
79
|
+
element.setAttribute('style', style)
|
|
80
|
+
} else {
|
|
81
|
+
element.removeAttribute('style')
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const fontFamilyAttrs = [
|
|
86
|
+
'font-family',
|
|
87
|
+
'face',
|
|
88
|
+
'mso-hansi-font-family',
|
|
89
|
+
'mso-bidi-font-family',
|
|
90
|
+
'mso-ascii-font-family',
|
|
91
|
+
'mso-fareast-font-family',
|
|
92
|
+
'mso-east-asian-font-family',
|
|
93
|
+
'mso-font-font-family',
|
|
94
|
+
]
|
|
95
|
+
fontFamilyAttrs.forEach((attr) => element.removeAttribute(attr))
|
|
96
|
+
Array.from(element.children).forEach((child) => processElement(child))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
Array.from(tempDiv.children).forEach((child) => processElement(child))
|
|
100
|
+
return tempDiv.innerHTML
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function customStringReplacement(
|
|
104
|
+
str: string,
|
|
105
|
+
startMarker: string,
|
|
106
|
+
endMarker: string,
|
|
107
|
+
target: string,
|
|
108
|
+
replacement: string,
|
|
109
|
+
): string {
|
|
110
|
+
let result = ''
|
|
111
|
+
let currentIndex = 0
|
|
112
|
+
|
|
113
|
+
while (true) {
|
|
114
|
+
const startIndex = str.indexOf(startMarker, currentIndex)
|
|
115
|
+
if (startIndex === -1) {
|
|
116
|
+
result += str.slice(currentIndex)
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const endIndex = str.indexOf(endMarker, startIndex + startMarker.length)
|
|
121
|
+
if (endIndex === -1) {
|
|
122
|
+
result += str.slice(currentIndex)
|
|
123
|
+
break
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
result += str.slice(currentIndex, startIndex + startMarker.length)
|
|
127
|
+
const middle = str.slice(startIndex + startMarker.length, endIndex)
|
|
128
|
+
const newMiddle = middle.replace(new RegExp(target, 'g'), replacement)
|
|
129
|
+
result += newMiddle
|
|
130
|
+
result += endMarker
|
|
131
|
+
currentIndex = endIndex + 1
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return result
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function calcImagesHeight(html: string, root?: HTMLElement | null) {
|
|
138
|
+
if (!root) return html
|
|
139
|
+
const map = new Map<string, number>()
|
|
140
|
+
const imageElements = root.querySelectorAll('img')
|
|
141
|
+
imageElements.forEach((imageElement) => {
|
|
142
|
+
const width = imageElement.offsetWidth
|
|
143
|
+
const height = imageElement.offsetHeight
|
|
144
|
+
if (width > 1 && height > 1) {
|
|
145
|
+
const src =
|
|
146
|
+
imageElement.src.length > 100 ? imageElement.src.substring(0, 100) : imageElement.src
|
|
147
|
+
map.set(src, height)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
for (const [key, value] of map) {
|
|
151
|
+
html = customStringReplacement(html, `${key}`, '>', 'height="auto"', `height="${value}"`)
|
|
152
|
+
}
|
|
153
|
+
return html
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function initHtml(html: string) {
|
|
157
|
+
if (!html) return ''
|
|
158
|
+
html = customStringReplacement(html, '<img', '>', 'height="[0-9]+"', 'height="auto"')
|
|
159
|
+
return convertPtToPx(html)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function finalizeHtml(html: string, root?: HTMLElement | null) {
|
|
163
|
+
let result = html || ''
|
|
164
|
+
result = calcImagesHeight(result, root)
|
|
165
|
+
return convertPxToPt(result)
|
|
166
|
+
}
|
|
@@ -115,27 +115,31 @@ const title = useVModel(props, 'title', emit)
|
|
|
115
115
|
const fileId = useVModel(props, 'fileId', emit)
|
|
116
116
|
const url = useVModel(props, 'url', emit)
|
|
117
117
|
const edit = useVModel(props, 'edit', emit)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return '
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return '
|
|
118
|
+
|
|
119
|
+
const resolveFileExt = (type?: string, name?: string, fileUrl?: string) => {
|
|
120
|
+
const normalizedType = (type || '').toLowerCase().replace(/^\./, '')
|
|
121
|
+
if (normalizedType) {
|
|
122
|
+
return normalizedType === 'doc' ? 'docx' : normalizedType
|
|
123
|
+
}
|
|
124
|
+
const fromName = (name || '').match(/\.([a-z0-9]+)$/i)?.[1]?.toLowerCase()
|
|
125
|
+
if (fromName) {
|
|
126
|
+
return fromName === 'doc' ? 'docx' : fromName
|
|
127
127
|
}
|
|
128
|
+
const urlStr = (fileUrl || '').toLowerCase()
|
|
129
|
+
if (urlStr.includes('.docx') || urlStr.includes('.doc')) return 'docx'
|
|
130
|
+
if (urlStr.includes('.pdf')) return 'pdf'
|
|
131
|
+
if (urlStr.includes('.xlsx') || urlStr.includes('.xls')) return 'xlsx'
|
|
132
|
+
if (urlStr.includes('.pptx') || urlStr.includes('.ppt')) return 'pptx'
|
|
133
|
+
return ''
|
|
128
134
|
}
|
|
135
|
+
|
|
136
|
+
const getFileType = () => resolveFileExt(undefined, title.value, url.value)
|
|
129
137
|
const getDocumentType = () => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return 'cell'
|
|
136
|
-
} else if ((url.value + '').indexOf('.pptx') !== -1 || (url.value + '').indexOf('.pptx') !== -1) {
|
|
137
|
-
return 'slide'
|
|
138
|
-
}
|
|
138
|
+
const ext = getFileType()
|
|
139
|
+
if (ext === 'docx' || ext === 'doc') return 'word'
|
|
140
|
+
if (ext === 'pdf') return 'pdf'
|
|
141
|
+
if (ext === 'xlsx' || ext === 'xls') return 'cell'
|
|
142
|
+
if (ext === 'pptx' || ext === 'ppt') return 'slide'
|
|
139
143
|
}
|
|
140
144
|
const download= async ()=>{
|
|
141
145
|
try{
|
|
@@ -54,6 +54,7 @@ import { ElMessage } from 'element-plus'
|
|
|
54
54
|
import { userInfoStore } from '../../../stores/userInfoStore'
|
|
55
55
|
import type { ISysUserInfo } from '../../../interface/ISysUserInfo'
|
|
56
56
|
import frameworkUtils from '../../../utils/FrameworkUtils.ts'
|
|
57
|
+
const emit = defineEmits<{ success: [] }>()
|
|
57
58
|
const userInfo = userInfoStore()
|
|
58
59
|
const data = ref<IWfTaskUsersVo>({
|
|
59
60
|
assignee: [],
|
|
@@ -81,6 +82,11 @@ const open = async (taskId: string, type: string) => {
|
|
|
81
82
|
data.value = { ...(await workflowInstApi.getTaskCandidate(taskId)), assignee: [] }
|
|
82
83
|
//设置当前的审批人
|
|
83
84
|
defaultAssignee.value = data.value.candidate?.map((e:ISysUserInfo) => e.id!) || []
|
|
85
|
+
if (type === 'minusMulti' && defaultAssignee.value.length <= 1) {
|
|
86
|
+
ElMessage.warning('至少需保留一名审核人,当前仅剩一名审核人,无法减签')
|
|
87
|
+
dialogVisible.value = false
|
|
88
|
+
return
|
|
89
|
+
}
|
|
84
90
|
data.value.assignee = [...defaultAssignee.value]
|
|
85
91
|
} catch (err: any) {
|
|
86
92
|
frameworkUtils.messageError(err)
|
|
@@ -122,18 +128,26 @@ const disableFunc = (val: string) => {
|
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
const save = async () => {
|
|
125
|
-
if (data.value.candidate?.length == 0
|
|
131
|
+
if (data.value.candidate?.length == 0) {
|
|
126
132
|
ElMessage.warning('请指定执行人!')
|
|
127
133
|
return
|
|
128
134
|
}
|
|
129
135
|
//加签逻辑判断
|
|
130
136
|
if (dialogType.value === 'addMulti') {
|
|
137
|
+
if (data.value.assignee.length == 0) {
|
|
138
|
+
ElMessage.warning('请指定执行人!')
|
|
139
|
+
return
|
|
140
|
+
}
|
|
131
141
|
if (data.value.assignee.length <= defaultAssignee.value.length) {
|
|
132
142
|
ElMessage.warning('加签必须选择非原审批人!')
|
|
133
143
|
return
|
|
134
144
|
}
|
|
135
145
|
} else if (dialogType.value === 'minusMulti') {
|
|
136
|
-
|
|
146
|
+
//减签逻辑判断:至少保留一名审核人
|
|
147
|
+
if (data.value.assignee.length < 1) {
|
|
148
|
+
ElMessage.warning('至少需保留一名审核人')
|
|
149
|
+
return
|
|
150
|
+
}
|
|
137
151
|
if (data.value.assignee.length >= defaultAssignee.value.length) {
|
|
138
152
|
ElMessage.warning('减签必须取消选中原审批人!')
|
|
139
153
|
return
|
|
@@ -145,6 +159,7 @@ const save = async () => {
|
|
|
145
159
|
await workflowInstApi.addOrMinusAssignee(data.value)
|
|
146
160
|
ElMessage.success('操作成功')
|
|
147
161
|
dialogVisible.value = false
|
|
162
|
+
emit('success')
|
|
148
163
|
} catch (err: any) {
|
|
149
164
|
frameworkUtils.messageError(err)
|
|
150
165
|
} finally {
|
|
@@ -201,6 +201,9 @@ const open = async (
|
|
|
201
201
|
//转办
|
|
202
202
|
dialogLabel.value = '转办原因'
|
|
203
203
|
dialogTitle.value = '转办审批'
|
|
204
|
+
// 每次打开转办弹窗清空原因与转办人,避免复用 taskVo 上次的审批/转办意见
|
|
205
|
+
task.value.comment = ''
|
|
206
|
+
transferUser.value = { id: '', userName: '' }
|
|
204
207
|
}
|
|
205
208
|
loading.value = false
|
|
206
209
|
}
|
|
@@ -220,13 +220,28 @@ const getStatus = (task: IWfTaskVo) => {
|
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
const compParam = ref()
|
|
223
|
+
const resolveReviewBatch = async (task: IWfTaskVo) => {
|
|
224
|
+
if (task.taskId) {
|
|
225
|
+
const byTask = await workflowcommentApi.getBatchInfoByTaskId(task.taskId)
|
|
226
|
+
if (byTask?.revMeetingId) {
|
|
227
|
+
return byTask
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const batchPrefer = task.taskName === '专家会审(答辩)' ? 'max' : 'min'
|
|
231
|
+
return workflowcommentApi.getBatchInfo(task.businessId ?? '', batchPrefer)
|
|
232
|
+
}
|
|
223
233
|
const viewDetail = async (task: IWfTaskVo) => {
|
|
224
234
|
if (task.taskName === '专家评审' || task.taskName === '专家会审(答辩)') {
|
|
225
|
-
|
|
226
|
-
|
|
235
|
+
const batchInfo = await resolveReviewBatch(task)
|
|
236
|
+
if (!batchInfo?.revMeetingId) {
|
|
237
|
+
ElMessage.warning('暂无评审详情')
|
|
238
|
+
return
|
|
239
|
+
}
|
|
227
240
|
const taskInfo = {
|
|
228
241
|
businessId: batchInfo.revMeetingId + '_' + (task.businessId ?? ''),
|
|
229
242
|
nodeTag: 'TECPM:/src/views/meetingHost/componets/comp.viewResult.vue',
|
|
243
|
+
nodeParam: batchInfo.reviewType ?? '10',
|
|
244
|
+
taskName: task.taskName,
|
|
230
245
|
}
|
|
231
246
|
await showTodoDialog(ref(taskInfo as IWfTaskVo), compParam)
|
|
232
247
|
} else {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare module '@umoteam/editor' {
|
|
2
|
+
import type { DefineComponent } from 'vue'
|
|
3
|
+
|
|
4
|
+
export const UmoEditor: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>
|
|
5
|
+
export function useUmoEditor(options?: Record<string, unknown>): void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare module '@umoteam/editor/style'
|