wl-design-mobile 0.8.3 → 0.8.5

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.
Files changed (40) hide show
  1. package/doc/file/compoents/lambo-file-row.vue +5 -92
  2. package/doc/file-type/file-type.js +286 -0
  3. package/doc/file-type/icons/icon-file-ai.svg +1 -0
  4. package/doc/file-type/icons/icon-file-avi.svg +1 -0
  5. package/doc/file-type/icons/icon-file-default.svg +1 -0
  6. package/doc/file-type/icons/icon-file-epub.svg +1 -0
  7. package/doc/file-type/icons/icon-file-excel.svg +1 -0
  8. package/doc/file-type/icons/icon-file-flv.svg +1 -0
  9. package/doc/file-type/icons/icon-file-gif.svg +1 -0
  10. package/doc/file-type/icons/icon-file-html.svg +1 -0
  11. package/doc/file-type/icons/icon-file-java.svg +1 -0
  12. package/doc/file-type/icons/icon-file-js.svg +1 -0
  13. package/doc/file-type/icons/icon-file-jsp.svg +1 -0
  14. package/doc/file-type/icons/icon-file-mp3.svg +1 -0
  15. package/doc/file-type/icons/icon-file-msi.svg +1 -0
  16. package/doc/file-type/icons/icon-file-pdf.svg +1 -0
  17. package/doc/file-type/icons/icon-file-png.svg +1 -0
  18. package/doc/file-type/icons/icon-file-ppt.svg +1 -0
  19. package/doc/file-type/icons/icon-file-psd.svg +1 -0
  20. package/doc/file-type/icons/icon-file-rar.svg +1 -0
  21. package/doc/file-type/icons/icon-file-raw.svg +1 -0
  22. package/doc/file-type/icons/icon-file-txt.svg +1 -0
  23. package/doc/file-type/icons/icon-file-word.svg +1 -0
  24. package/flow/approval/flow-approval.vue +550 -0
  25. package/flow/comps/flow-attachment-list.vue +230 -0
  26. package/flow/comps/flow-attachment-modal.vue +194 -0
  27. package/flow/comps/flow-copy-users.vue +358 -0
  28. package/flow/comps/flow-node-card.vue +150 -0
  29. package/flow/comps/flow-node-header.vue +172 -0
  30. package/flow/comps/flow-util.js +156 -0
  31. package/flow/comps/icons/approved.svg +11 -0
  32. package/flow/comps/icons/canceled.svg +11 -0
  33. package/flow/comps/icons/rejected.svg +11 -0
  34. package/flow/comps/icons/underway.svg +11 -0
  35. package/flow/detail/flow-detail.vue +466 -0
  36. package/flow/index.js +51 -0
  37. package/flow/lambo-flow-detail.vue +894 -12
  38. package/flow/lambo-flow-edit.vue +6 -21
  39. package/flow/submit/flow-submit.vue +381 -0
  40. package/package.json +1 -1
@@ -0,0 +1,230 @@
1
+ <template>
2
+ <div class="attachment-section" :class="{ 'no-border': !showBorder }">
3
+ <div class="attachment-header" v-if="showTitle || editable">
4
+ <div class="attachment-title" v-if="showTitle">
5
+ <van-icon name="paperclip" size="14" style="margin-right: 4px"></van-icon>
6
+ 附件({{ attachments.length }})
7
+ </div>
8
+ <van-uploader
9
+ v-if="editable"
10
+ :after-read="handleAfterRead"
11
+ :accept="accept"
12
+ :show-preview="false"
13
+ :show-upload-list="false"
14
+ :disabled="uploading"
15
+ >
16
+ <div class="upload-btn" :class="{ uploading }">
17
+ <van-icon :name="uploading ? 'replay' : 'plus'" size="14"></van-icon>
18
+ {{ uploading ? '上传中' : '上传' }}
19
+ </div>
20
+ </van-uploader>
21
+ </div>
22
+ <div class="attachment-list" v-if="attachments && attachments.length > 0">
23
+ <div
24
+ v-for="(file, index) in attachments"
25
+ :key="index"
26
+ class="attachment-item"
27
+ @click="handleClick(file)"
28
+ >
29
+ <van-icon :name="getFileIcon(file.fileName || file.name)" size="14"></van-icon>
30
+ <span class="file-name">{{ file.fileName || file.name }}</span>
31
+ <span class="file-size">{{ formatFileSize(file.fileSize || file.size) }}</span>
32
+ <van-icon
33
+ v-if="editable"
34
+ name="cross"
35
+ class="remove-file"
36
+ @click.stop="handleRemove(index)"
37
+ ></van-icon>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </template>
42
+
43
+ <script>
44
+ import { Toast } from 'vant'
45
+ import { axios } from '../../util/axios'
46
+ import { fileGetUrl, fileUploadUrl } from '../../api/doc'
47
+ import { getFileIcon, formatFileSize } from './flow-util.js'
48
+
49
+ export default {
50
+ name: 'FlowAttachmentList',
51
+ props: {
52
+ uploadEnabled: {
53
+ type: Boolean,
54
+ default: true
55
+ },
56
+ attachments: {
57
+ type: Array,
58
+ default: () => []
59
+ },
60
+ showTitle: {
61
+ type: Boolean,
62
+ default: true
63
+ },
64
+ showBorder: {
65
+ type: Boolean,
66
+ default: true
67
+ },
68
+ editable: {
69
+ type: Boolean,
70
+ default: false
71
+ },
72
+ accept: {
73
+ type: String,
74
+ default: ''
75
+ }
76
+ },
77
+ data() {
78
+ return {
79
+ uploading: false
80
+ }
81
+ },
82
+ methods: {
83
+ getFileIcon,
84
+ formatFileSize,
85
+ handleClick(file) {
86
+ this.$emit('click', file)
87
+ },
88
+ handleAfterRead(fileObj) {
89
+ this.$emit('after-read', fileObj)
90
+ if (!this.uploadEnabled) return
91
+ const file = fileObj.file
92
+ const formData = new FormData()
93
+ formData.append('file', file)
94
+ this.uploading = true
95
+ axios.post(fileUploadUrl, formData).then(res => {
96
+ if (res && res.code === 1 && res.data && res.data.length > 0 && res.data[0].fileId) {
97
+ const resFile = res.data[0]
98
+ const fileData = {
99
+ fileId: resFile.fileId,
100
+ fileName: resFile.fileName || file.name,
101
+ fileSize: resFile.fileSize || file.size,
102
+ fileUrl: resFile.fileUrl || resFile.url || resFile.downloadUrl || fileGetUrl + resFile.fileId
103
+ }
104
+ Toast.success(fileData.fileName + '上传成功')
105
+ this.$emit('upload-success', fileData)
106
+ return
107
+ }
108
+ Toast.fail('文件' + file.name + '上传失败')
109
+ this.$emit('upload-error', res)
110
+ }).catch(error => {
111
+ Toast.fail('文件' + file.name + '上传异常')
112
+ this.$emit('upload-error', error)
113
+ }).finally(() => {
114
+ this.uploading = false
115
+ })
116
+ },
117
+ handleRemove(index) {
118
+ this.$emit('remove', index)
119
+ }
120
+ }
121
+ }
122
+ </script>
123
+
124
+ <style scoped>
125
+ .attachment-section {
126
+ margin-top: 12px;
127
+ padding-top: 12px;
128
+ border-top: 1px solid #f0f0f0;
129
+ }
130
+
131
+ .attachment-section.no-border {
132
+ margin-top: 0;
133
+ padding-top: 0;
134
+ border-top: none;
135
+ }
136
+
137
+ .attachment-header {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ margin-bottom: 8px;
142
+ }
143
+
144
+ .attachment-title {
145
+ font-size: 13px;
146
+ font-weight: 500;
147
+ color: #595959;
148
+ display: flex;
149
+ align-items: center;
150
+ }
151
+
152
+ .attachment-list {
153
+ display: flex;
154
+ flex-wrap: wrap;
155
+ gap: 8px;
156
+ }
157
+
158
+ .attachment-item {
159
+ display: inline-flex;
160
+ align-items: center;
161
+ padding: 6px 10px;
162
+ background: #fff;
163
+ border: 1px solid #e8e8e8;
164
+ border-radius: 4px;
165
+ cursor: pointer;
166
+ transition: all 0.2s;
167
+ font-size: 12px;
168
+ gap: 6px;
169
+ color: #595959;
170
+ height: 32px;
171
+ }
172
+
173
+ .attachment-item:hover {
174
+ background: #f0f0f0;
175
+ color: #262626;
176
+ }
177
+
178
+ .attachment-item .van-icon {
179
+ color: #8c8c8c;
180
+ vertical-align: middle;
181
+ flex-shrink: 0;
182
+ }
183
+
184
+ .file-name {
185
+ max-width: 120px;
186
+ overflow: hidden;
187
+ text-overflow: ellipsis;
188
+ white-space: nowrap;
189
+ }
190
+
191
+ .file-size {
192
+ color: #bfbfbf;
193
+ flex-shrink: 0;
194
+ font-size: 11px;
195
+ }
196
+
197
+ .remove-file {
198
+ color: #bfbfbf;
199
+ margin-left: 4px;
200
+ }
201
+
202
+ .remove-file:hover {
203
+ color: #ee0a24;
204
+ }
205
+
206
+ .upload-btn {
207
+ display: inline-flex;
208
+ align-items: center;
209
+ gap: 4px;
210
+ height: 26px;
211
+ padding: 0 10px;
212
+ border: 1px dashed #c8c9cc;
213
+ border-radius: 4px;
214
+ font-size: 12px;
215
+ color: #646566;
216
+ background: #fff;
217
+ cursor: pointer;
218
+ transition: all 0.2s;
219
+ }
220
+
221
+ .upload-btn:hover {
222
+ border-color: #1989fa;
223
+ color: #1989fa;
224
+ }
225
+
226
+ .upload-btn.uploading {
227
+ color: #969799;
228
+ border-color: #dcdee0;
229
+ }
230
+ </style>
@@ -0,0 +1,194 @@
1
+ <template>
2
+ <van-popup :value="visible" position="center" round :style="{ width: modalWidth, maxWidth: '90%' }" @input="handleVisibleChange">
3
+ <div class="modal-header">
4
+ <span class="modal-title">{{ fileName }}</span>
5
+ <van-icon name="cross" class="close-icon" @click="handleClose"></van-icon>
6
+ </div>
7
+ <div class="modal-content" v-show="visible">
8
+ <!-- 图片预览 -->
9
+ <div v-if="isImage" class="image-preview">
10
+ <img :src="fileUrl" alt="图片预览" />
11
+ </div>
12
+ <!-- TXT 文本预览 -->
13
+ <div v-else-if="fileType === 'txt'" class="text-preview">
14
+ <pre>{{ textContent }}</pre>
15
+ </div>
16
+ <!-- 其他文件-->
17
+ <div v-else class="preview-placeholder">
18
+ <van-icon name="description" size="64" color="#ccc"></van-icon>
19
+ <p>{{ fileName }}</p>
20
+ </div>
21
+ </div>
22
+ <div class="modal-footer">
23
+ <van-button type="default" size="small" @click="handleDownload">下载</van-button>
24
+ <van-button type="primary" size="small" @click="handleClose">关闭</van-button>
25
+ </div>
26
+ </van-popup>
27
+ </template>
28
+
29
+ <script>
30
+ export default {
31
+ name: 'FlowAttachmentModal',
32
+ props: {
33
+ visible: {
34
+ type: Boolean,
35
+ default: false
36
+ },
37
+ fileUrl: {
38
+ type: String,
39
+ default: ''
40
+ },
41
+ fileName: {
42
+ type: String,
43
+ default: ''
44
+ },
45
+ modalWidth: {
46
+ type: String,
47
+ default: '320px'
48
+ }
49
+ },
50
+ data() {
51
+ return {
52
+ textContent: '' // 存放 TXT 内容
53
+ }
54
+ },
55
+ computed: {
56
+ fileType() {
57
+ if (!this.fileName) return ''
58
+ return this.fileName.split('.').pop().toLowerCase()
59
+ },
60
+ isImage() {
61
+ const imgTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp']
62
+ return imgTypes.includes(this.fileType)
63
+ }
64
+ },
65
+ watch: {
66
+ visible(val) {
67
+ if (val) {
68
+ this.loadPreview()
69
+ }
70
+ }
71
+ },
72
+ methods: {
73
+ handleVisibleChange(val) {
74
+ this.$emit('update:visible', val)
75
+ },
76
+ handleClose() {
77
+ this.$emit('update:visible', false)
78
+ },
79
+ handleDownload() {
80
+ if (!this.fileUrl) {
81
+ this.$toast({ message: '文件地址不存在', position: 'middle' })
82
+ return
83
+ }
84
+ let downloadFileName = this.fileName
85
+ if (!downloadFileName) {
86
+ downloadFileName = this.fileUrl.split('/').pop() || 'download'
87
+ }
88
+ const link = document.createElement('a')
89
+ link.href = this.fileUrl
90
+ link.download = downloadFileName
91
+ document.body.appendChild(link)
92
+ link.click()
93
+ document.body.removeChild(link)
94
+ },
95
+
96
+ // 加载 TXT 预览
97
+ async loadPreview() {
98
+ if (this.fileType === 'txt') {
99
+ try {
100
+ const res = await fetch(this.fileUrl)
101
+ this.textContent = await res.text()
102
+ } catch (e) {
103
+ this.textContent = '文本加载失败'
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ </script>
110
+
111
+ <style scoped>
112
+ .modal-header {
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: space-between;
116
+ padding: 16px;
117
+ border-bottom: 1px solid #f0f0f0;
118
+ }
119
+
120
+ .modal-title {
121
+ font-size: 16px;
122
+ font-weight: 600;
123
+ color: #323233;
124
+ flex: 1;
125
+ overflow: hidden;
126
+ text-overflow: ellipsis;
127
+ white-space: nowrap;
128
+ }
129
+
130
+ .close-icon {
131
+ font-size: 20px;
132
+ color: #c8c9cc;
133
+ cursor: pointer;
134
+ flex-shrink: 0;
135
+ margin-left: 12px;
136
+ }
137
+
138
+ .close-icon:hover {
139
+ color: #969799;
140
+ }
141
+
142
+ .modal-content {
143
+ padding: 40px 20px;
144
+ min-height: 200px;
145
+ max-height: 70vh;
146
+ overflow-y: auto;
147
+ }
148
+
149
+ .preview-placeholder {
150
+ display: flex;
151
+ flex-direction: column;
152
+ align-items: center;
153
+ justify-content: center;
154
+ color: #969799;
155
+ }
156
+
157
+ .preview-placeholder p {
158
+ margin-top: 12px;
159
+ font-size: 14px;
160
+ }
161
+
162
+ .modal-footer {
163
+ padding: 16px;
164
+ border-top: 1px solid #f0f0f0;
165
+ display: flex;
166
+ justify-content: flex-end;
167
+ gap: 12px;
168
+ }
169
+
170
+ /* 图片预览 */
171
+ .image-preview {
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ }
176
+ .image-preview img {
177
+ max-width: 100%;
178
+ max-height: 60vh;
179
+ object-fit: contain;
180
+ border-radius: 4px;
181
+ }
182
+
183
+ /* TXT 预览 */
184
+ .text-preview {
185
+ padding: 10px;
186
+ background: #f7f8fa;
187
+ border-radius: 4px;
188
+ max-height: 60vh;
189
+ overflow-y: auto;
190
+ white-space: pre-wrap;
191
+ font-size: 14px;
192
+ line-height: 1.6;
193
+ }
194
+ </style>