zant-admin 1.0.0

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 (82) hide show
  1. package/README.en.md +36 -0
  2. package/README.md +248 -0
  3. package/SCAFFOLD_README.md +215 -0
  4. package/bin/cli.js +99 -0
  5. package/bin/generator.js +503 -0
  6. package/bin/prompts.js +159 -0
  7. package/bin/utils.js +134 -0
  8. package/package.json +74 -0
  9. package/public/logo.png +0 -0
  10. package/src/App.vue +16 -0
  11. package/src/api/methods/logError.js +8 -0
  12. package/src/api/methods/logOperation.js +8 -0
  13. package/src/api/methods/login.js +6 -0
  14. package/src/api/methods/quartz.js +36 -0
  15. package/src/api/methods/region.js +16 -0
  16. package/src/api/methods/sysAccount.js +30 -0
  17. package/src/api/methods/sysDict.js +29 -0
  18. package/src/api/methods/sysDictItem.js +26 -0
  19. package/src/api/methods/sysMenu.js +42 -0
  20. package/src/api/methods/sysRole.js +35 -0
  21. package/src/api/methods/sysUser.js +25 -0
  22. package/src/api/methods/system.js +16 -0
  23. package/src/api/request.js +225 -0
  24. package/src/assets/css/style.css +70 -0
  25. package/src/assets/css/zcui.css +340 -0
  26. package/src/assets/imgs/loginbackground.svg +69 -0
  27. package/src/assets/imgs/logo.png +0 -0
  28. package/src/assets/imgs/md/1.png +0 -0
  29. package/src/assets/imgs/md/10.png +0 -0
  30. package/src/assets/imgs/md/11.png +0 -0
  31. package/src/assets/imgs/md/2.png +0 -0
  32. package/src/assets/imgs/md/3.png +0 -0
  33. package/src/assets/imgs/md/4.png +0 -0
  34. package/src/assets/imgs/md/5.png +0 -0
  35. package/src/assets/imgs/md/6.png +0 -0
  36. package/src/assets/imgs/md/7.png +0 -0
  37. package/src/assets/imgs/md/8.png +0 -0
  38. package/src/assets/imgs/md/9.png +0 -0
  39. package/src/components/FormTable.vue +875 -0
  40. package/src/components/IconPicker.vue +344 -0
  41. package/src/components/MainPage.vue +957 -0
  42. package/src/components/details/logErrorDetails.vue +58 -0
  43. package/src/components/details/logOperationDetails.vue +76 -0
  44. package/src/components/edit/QuartzEdit.vue +221 -0
  45. package/src/components/edit/SysAccountEdit.vue +178 -0
  46. package/src/components/edit/SysDictEdit.vue +114 -0
  47. package/src/components/edit/SysDictItemEdit.vue +134 -0
  48. package/src/components/edit/SysRoleEdit.vue +109 -0
  49. package/src/components/edit/sysMenuEdit.vue +305 -0
  50. package/src/config/index.js +74 -0
  51. package/src/directives/permission.js +45 -0
  52. package/src/main.js +38 -0
  53. package/src/router/index.js +270 -0
  54. package/src/stores/config.js +37 -0
  55. package/src/stores/dict.js +33 -0
  56. package/src/stores/menu.js +57 -0
  57. package/src/stores/user.js +21 -0
  58. package/src/utils/baseEcharts.js +661 -0
  59. package/src/utils/dictTemplate.js +26 -0
  60. package/src/utils/regionUtils.js +169 -0
  61. package/src/utils/useFormCRUD.js +60 -0
  62. package/src/views/baiscstatis/center.vue +463 -0
  63. package/src/views/baiscstatis/iframePage.vue +31 -0
  64. package/src/views/baiscstatis/notFound.vue +192 -0
  65. package/src/views/console.vue +771 -0
  66. package/src/views/demo/importexport.vue +123 -0
  67. package/src/views/demo/region.vue +240 -0
  68. package/src/views/demo/statistics.vue +195 -0
  69. package/src/views/home.vue +7 -0
  70. package/src/views/login.vue +272 -0
  71. package/src/views/operations/log/logError.vue +78 -0
  72. package/src/views/operations/log/logLogin.vue +66 -0
  73. package/src/views/operations/log/logOperation.vue +103 -0
  74. package/src/views/operations/log/logQuartz.vue +57 -0
  75. package/src/views/operations/quartz.vue +181 -0
  76. package/src/views/operations/serviceMonitoring.vue +134 -0
  77. package/src/views/system/sysAccount.vue +123 -0
  78. package/src/views/system/sysDict.vue +156 -0
  79. package/src/views/system/sysDictItem.vue +118 -0
  80. package/src/views/system/sysMenu.vue +223 -0
  81. package/src/views/system/sysRole.vue +184 -0
  82. package/templates/env.production +2 -0
@@ -0,0 +1,875 @@
1
+ <template>
2
+ <div>
3
+ <div class="table-search" v-if="formState">
4
+ <a-form
5
+ name="advanced_search"
6
+ ref="tableformRef"
7
+ :model="formState"
8
+ @finish="onFinish"
9
+ >
10
+ <a-row :gutter="24">
11
+ <template v-for="(v, key, index) in formState" :key="key">
12
+ <a-col v-show="state.expand || index < formStateShow" :span="6">
13
+ <a-form-item :name="`${key}`" :label="`${v.label}`">
14
+ <a-input
15
+ v-if="v.type === 'text'"
16
+ v-model:value="v.value"
17
+ placeholder="请输入"
18
+ />
19
+ <a-input-number
20
+ v-if="v.type === 'number'"
21
+ :min="v.min"
22
+ :max="v.max"
23
+ :step="v.step"
24
+ style="width: 100%"
25
+ v-model:value="v.value"
26
+ placeholder="请输入"
27
+ />
28
+ <a-select
29
+ v-if="v.type === 'select'"
30
+ v-model:value="v.value"
31
+ placeholder="请选择"
32
+ :options="v.data"
33
+ showSearch
34
+ @change="v.onChange"
35
+ :filter-option="filterOption"
36
+ />
37
+ <a-radio-group
38
+ v-if="v.type === 'radio'"
39
+ v-model:value="v.value"
40
+ :options="v.data"
41
+ >
42
+ </a-radio-group>
43
+ <a-range-picker
44
+ v-if="v.type === 'time'"
45
+ v-model:value="v.value"
46
+ style="width: 100%"
47
+ :format="
48
+ v.picker === 'month'
49
+ ? 'YYYY-MM'
50
+ : v.picker === 'year'
51
+ ? 'YYYY'
52
+ : 'YYYY-MM-DD'
53
+ "
54
+ :value-format="
55
+ v.picker === 'month'
56
+ ? 'YYYY-MM'
57
+ : v.picker === 'year'
58
+ ? 'YYYY'
59
+ : 'YYYY-MM-DD'
60
+ "
61
+ :picker="v.picker"
62
+ />
63
+ <a-date-picker
64
+ v-if="v.type === 'datetime'"
65
+ v-model:value="v.value"
66
+ style="width: 100%"
67
+ :picker="v.picker"
68
+ />
69
+ </a-form-item>
70
+ </a-col>
71
+ </template>
72
+ <a-col
73
+ :span="forminputbuttonspan"
74
+ style="text-align: right; margin-bottom: 24px"
75
+ >
76
+ <a-space>
77
+ <a
78
+ style="font-size: 12px; margin-right: 20px"
79
+ v-if="formStatelength > formStateShow"
80
+ @click="state.expand = !state.expand"
81
+ >
82
+ <template v-if="state.expand"> <UpOutlined />收起 </template>
83
+ <template v-else> <DownOutlined />更多搜索条件 </template>
84
+ </a>
85
+ <a-button type="primary" html-type="submit">搜索</a-button>
86
+ <a-button style="margin: 0 8px" @click="resetForm">重置</a-button>
87
+ </a-space>
88
+ </a-col>
89
+ </a-row>
90
+ </a-form>
91
+ </div>
92
+ <div class="card-table">
93
+ <a-table
94
+ :columns="filteredColumns"
95
+ :dataSource="state.dataSource"
96
+ :pagination="page ? pagination : false"
97
+ :rowSelection="rowSelect ? rowSelection : null"
98
+ :loading="state.loading"
99
+ rowKey="id"
100
+ :size="formConfig.tableSize"
101
+ :bordered="formConfig.tableBordered"
102
+ @change="handleTableChange"
103
+ :scroll="scroll"
104
+ @resizeColumn="handleResizeColumn"
105
+ >
106
+ <!-- 插槽部分 -->
107
+ <template #bodyCell="{ column, record, index }">
108
+ <template v-if="column.key === 'num'">
109
+ <span>{{
110
+ (pagination.current - 1) * pagination.pageSize + index + 1
111
+ }}</span>
112
+ </template>
113
+ <template v-if="column.key === 'operation'">
114
+ <!-- 插槽内容 -->
115
+ <a v-permission="getPermission('details')" @click="details(record)">详情</a>
116
+ <a-divider
117
+ v-if="hasDetailsPermission && (hasEditPermission || hasDeletePermission)"
118
+ type="vertical"
119
+ />
120
+ <a v-permission="getPermission('update')" @click="edit(record)">修改</a>
121
+ <a-divider v-if="hasEditPermission && hasDeletePermission" type="vertical" />
122
+ <a-popconfirm
123
+ title="确定要删除该数据吗?"
124
+ @confirm="tabelDelete(record)"
125
+ >
126
+ <a v-permission="getPermission('delete')">删除</a>
127
+ </a-popconfirm>
128
+ </template>
129
+ <!-- 如果父组件传递了对应列的插槽,则渲染插槽内容,否则默认展示数据 -->
130
+ <slot
131
+ :name="`custom-${column.key}`"
132
+ :record="record"
133
+ :column="column"
134
+ >
135
+ {{ record[column.dataIndex] }}
136
+ </slot>
137
+ </template>
138
+ <!-- 新增合计插槽 -->
139
+ <template #summary>
140
+ <slot
141
+ name="summary"
142
+ :dataSource="state.dataSource"
143
+ :columns="filteredColumns"
144
+ ></slot>
145
+ </template>
146
+ <template #title>
147
+ <div class="flex-row">
148
+ <div class="flex-item text-align-left">
149
+ <a-space>
150
+ <a-button
151
+ type="primary"
152
+ v-permission="getPermission('add')"
153
+ @click="edit"
154
+ size="middle"
155
+ >
156
+ <PlusOutlined />新增
157
+ </a-button>
158
+ <a-button
159
+ type="primary"
160
+ v-permission="getPermission('delete')"
161
+ @click="batchDelete"
162
+ :disabled="!hasSelected"
163
+ danger
164
+ size="middle"
165
+ >
166
+ <DeleteOutlined />删除
167
+ </a-button>
168
+ <a-button
169
+ type="primary"
170
+ v-permission="getPermission('export')"
171
+ @click="dataExport"
172
+ size="middle"
173
+ >
174
+ <ExportOutlined />导出
175
+ </a-button>
176
+ <a-button
177
+ type="primary"
178
+ v-permission="getPermission('import')"
179
+ @click="showImportModal"
180
+ size="middle"
181
+ >
182
+ <ImportOutlined />导入
183
+ </a-button>
184
+ <slot :name="'table-toolbar'"></slot>
185
+ </a-space>
186
+ </div>
187
+ <div class="flex-item text-align-right padding-right-10">
188
+ <a-space size="large">
189
+ <a class="text-color-black" @click.prevent @click="tableLoad">
190
+ <RedoOutlined />
191
+ </a>
192
+ <a-dropdown class="">
193
+ <a class="text-color-black" @click.prevent>
194
+ <ColumnHeightOutlined />
195
+ </a>
196
+ <template #overlay>
197
+ <a-menu @click="dropdownonTableSizeClick">
198
+ <a-menu-item key="large"> 默认 </a-menu-item>
199
+ <a-menu-item key="middle"> 中等 </a-menu-item>
200
+ <a-menu-item key="small"> 紧凑 </a-menu-item>
201
+ </a-menu>
202
+ </template>
203
+ </a-dropdown>
204
+ <a-dropdown
205
+ :trigger="['click']"
206
+ :open="dropdownVisible"
207
+ @openChange="handleDropdownVisibility"
208
+ >
209
+ <a class="text-color-black" @click.prevent>
210
+ <AlignCenterOutlined />
211
+ </a>
212
+ <template #overlay>
213
+ <a-checkbox-group
214
+ v-model:value="checkedColumns"
215
+ @change="handleColumnChange"
216
+ >
217
+ <a-menu>
218
+ <a-menu-item
219
+ v-for="col in columns"
220
+ :key="col.dataIndex"
221
+ >
222
+ <a-checkbox :value="col.dataIndex" @click.stop>{{
223
+ col.title
224
+ }}</a-checkbox>
225
+ </a-menu-item>
226
+ </a-menu>
227
+ </a-checkbox-group>
228
+ </template>
229
+ </a-dropdown>
230
+ </a-space>
231
+ </div>
232
+ </div>
233
+ </template>
234
+ </a-table>
235
+ </div>
236
+
237
+ <!-- 导入模态框 -->
238
+ <a-modal
239
+ v-model:open="importModalVisible"
240
+ title="数据导入"
241
+ :footer="null"
242
+ width="650px"
243
+ >
244
+ <div class="import-modal-content">
245
+ <a-space direction="vertical" style="width: 100%">
246
+ <a-card title="" :bordered="false">
247
+ <a-upload-dragger
248
+ :file-list="fileList"
249
+ :before-upload="beforeUpload"
250
+ @remove="handleRemove"
251
+ accept=".xlsx,.xls"
252
+ :multiple="false"
253
+ >
254
+ <p class="ant-upload-drag-icon">
255
+ <InboxOutlined />
256
+ </p>
257
+ <p class="ant-upload-text">点击或拖拽文件到此区域上传</p>
258
+ <p class="ant-upload-hint">
259
+ 仅支持单个文件上传,只接受.xlsx、.xls格式的Excel文件,文件大小不能超过10MB
260
+ </p>
261
+ </a-upload-dragger>
262
+ <div class="upload-actions">
263
+ <a-button
264
+ type="primary"
265
+ :disabled="fileList.length === 0"
266
+ :loading="uploading"
267
+ @click="handleUpload"
268
+ >
269
+ {{ uploading ? '上传中...' : '开始导入' }}
270
+ </a-button>
271
+ </div>
272
+ </a-card>
273
+
274
+ <a-card title="导入模板" :bordered="false" class="template-card">
275
+ <div class="template-content">
276
+ <div class="template-info">
277
+ <FileExcelOutlined class="template-icon" />
278
+ <div class="template-description">
279
+ <h4>Excel导入模板</h4>
280
+ <p>请先下载导入模板,按照模板格式填写数据后再进行导入</p>
281
+ <ul class="template-tips">
282
+ <li>支持.xlsx、.xls格式的Excel文件</li>
283
+ <li>请按照模板中的字段顺序填写数据</li>
284
+ <li>必填字段不能为空</li>
285
+ <li>数据格式需与模板保持一致</li>
286
+ </ul>
287
+ </div>
288
+ </div>
289
+ <div class="template-action">
290
+ <a-button type="primary" size="large" @click="downloadTemplate">
291
+ <DownloadOutlined />
292
+ 下载模板
293
+ </a-button>
294
+ </div>
295
+ </div>
296
+ </a-card>
297
+ </a-space>
298
+ </div>
299
+ </a-modal>
300
+ </div>
301
+ </template>
302
+ <script setup>
303
+ import { ref, defineProps, defineEmits, reactive, computed } from 'vue'
304
+ import { configStore } from '@/stores/config'
305
+ import { menuStore } from '@/stores/menu'
306
+ import { post, get, downloadFilePost } from '@/api/request'
307
+ import { Modal, message } from 'ant-design-vue'
308
+ import { saveAs } from 'file-saver'
309
+ import {
310
+ PlusOutlined,
311
+ DeleteOutlined,
312
+ ExportOutlined,
313
+ ImportOutlined,
314
+ UploadOutlined,
315
+ DownloadOutlined,
316
+ RedoOutlined,
317
+ ColumnHeightOutlined,
318
+ AlignCenterOutlined,
319
+ UpOutlined,
320
+ DownOutlined,
321
+ InboxOutlined,
322
+ FileExcelOutlined
323
+ } from '@ant-design/icons-vue'
324
+
325
+ const config = configStore()
326
+ const menu = menuStore()
327
+ const formConfig = reactive({
328
+ tableSize: config.tableSize,
329
+ tableBordered: config.tableBordered,
330
+ })
331
+ // 根据模块路径生成权限标识
332
+ const getPermission = (action) => {
333
+ // 优先使用 permissionModulePath,如果不存在则使用 modulePath
334
+ const pathToUse = props.permissionModulePath || props.modulePath
335
+ if (!pathToUse) return `table:${action}`
336
+ // 将路径转换为权限标识,例如:/sys/user -> sys:user
337
+ const pathPermission = pathToUse.replace(/^\//, '').replace(/\//g, ':')
338
+ return `${pathPermission}:${action}`
339
+ }
340
+
341
+ // 检查按钮权限的计算属性
342
+ const hasDetailsPermission = computed(() => {
343
+ return menu.hasPermission(getPermission('details'))
344
+ })
345
+
346
+ const hasEditPermission = computed(() => {
347
+ return menu.hasPermission(getPermission('update'))
348
+ })
349
+
350
+ const hasDeletePermission = computed(() => {
351
+ return menu.hasPermission(getPermission('delete'))
352
+ })
353
+
354
+ // 定义 props
355
+ const props = defineProps({
356
+ formStateShow: {
357
+ //筛选条件控制显示隐藏数量
358
+ type: Int32Array,
359
+ default: 7,
360
+ },
361
+ formState: Object, //表格上面搜索条件
362
+ modulePath: String, //模块路径
363
+ permissionModulePath: String, //权限模块路径,用于生成权限标识
364
+ pageAction: {
365
+ //分页方法名称
366
+ type: String,
367
+ default: 'page',
368
+ },
369
+ where: {
370
+ //查询条件
371
+ type: Array,
372
+ default: [],
373
+ },
374
+ ftableAdd: {
375
+ //新增方法名称
376
+ type: Boolean,
377
+ default: false,
378
+ },
379
+ ftableDeleteAction: {
380
+ //删除方法名称
381
+ type: String,
382
+ default: 'delete',
383
+ },
384
+ ftableExportAction: {
385
+ //导出方法名称
386
+ type: String,
387
+ default: 'export',
388
+ },
389
+ method: {
390
+ //请求方法
391
+ type: String,
392
+ default: 'post',
393
+ },
394
+ page: {
395
+ //是否分页
396
+ type: Boolean,
397
+ default: true,
398
+ },
399
+ columns: {
400
+ type: Array,
401
+ default: () => [],
402
+ }, //列
403
+ rowSelect: {
404
+ //选择行
405
+ type: Boolean,
406
+ default: true,
407
+ },
408
+ scroll: {
409
+ //选择行
410
+ type: Object,
411
+ default: () => ({
412
+ x: 1200,
413
+ }),
414
+ },
415
+ })
416
+ const state = reactive({
417
+ loading: false, //表格加载中状态
418
+ params: {}, //表单请求条件参数
419
+ expand: false, // 表单判断是从合并展开属性
420
+ dataSource: [], //定义表格数据源
421
+ selectedRowKeys: [], //表格选中行
422
+ })
423
+
424
+ // 导入相关状态
425
+ const importModalVisible = ref(false) // 导入模态框显示状态
426
+ const fileList = ref([]) // 上传文件列表
427
+ const uploading = ref(false) // 上传状态
428
+ // // 根据模块路径生成权限标识
429
+ // const getPermission = (action) => {
430
+
431
+ // return `${props.permissionModulePath}:${action}`
432
+ // }
433
+ const formStatelength = ref(
434
+ props.formState ? Object.keys(props.formState).length : 0,
435
+ )
436
+ const forminputbuttonspan = computed(() => {
437
+ const values = [18, 12, 6, 24]
438
+ if (state.expand) {
439
+ const index = (formStatelength.value - 1) % values.length
440
+ return values[index]
441
+ } else {
442
+ if (formStatelength.value >= props.formStateShow) {
443
+ return 6
444
+ } else if (formStatelength.value < props.formStateShow) {
445
+ const index = (formStatelength.value - 1) % values.length
446
+ return values[index]
447
+ }
448
+ }
449
+ })
450
+ // 表单选择器属性
451
+ const filterOption = (input, option) => {
452
+ return option.label.toLowerCase().includes(input.toLowerCase())
453
+ }
454
+ //表单重置属性
455
+ const tableformRef = ref(null)
456
+ //表单搜索按钮事件
457
+ const onFinish = values => {
458
+ pagination.value.current = 1
459
+ tableLoad()
460
+ }
461
+ //重置表单
462
+ const resetForm = () => {
463
+ Object.keys(props.formState).forEach(key => {
464
+ props.formState[key].value = props.formState[key].defaultvalue // 重置输入框
465
+ })
466
+ onFinish()
467
+ }
468
+ //列的显示/隐藏选择器
469
+ const dropdownVisible = ref(false)
470
+ const handleDropdownVisibility = visible => {
471
+ dropdownVisible.value = visible
472
+ }
473
+ const handleColumnChange = checkedValues => {
474
+ checkedColumns.value = checkedValues
475
+ }
476
+ const initialCheckedColumns = props.columns.map(col => col.dataIndex)
477
+ const checkedColumns = ref([...initialCheckedColumns])
478
+ const filteredColumns = computed(() =>
479
+ props.columns.filter(col => checkedColumns.value.includes(col.dataIndex)),
480
+ )
481
+ // 分页配置
482
+ const pagination = ref({
483
+ current: 1,
484
+ pageSize: 10,
485
+ total: 0,
486
+ showTotal: total => `共 ${total} 条`,
487
+ showSizeChanger: true,
488
+ pageSizeOptions: ['10', '20', '30', '40'],
489
+ showQuickJumper: true,
490
+ })
491
+ const hasSelected = computed(() => state.selectedRowKeys.length > 0)
492
+ //选择行事件
493
+ const rowSelection = {
494
+ onChange: (selectedRowKeys, selectedRows) => {
495
+ state.selectedRowKeys = selectedRowKeys
496
+ },
497
+ }
498
+ // 分页和表格变化处理
499
+ const handleTableChange = newPagination => {
500
+ pagination.value = {
501
+ ...pagination.value,
502
+ ...newPagination,
503
+ }
504
+ tableLoad()
505
+ }
506
+
507
+ const loaddata = () => {
508
+ var values = props.formState
509
+ if (values) {
510
+ const newValues = Object.keys(values).reduce((acc, key) => {
511
+ const item = values[key]
512
+ switch (key) {
513
+ case 'createTime':
514
+ if (Array.isArray(item.value) && item.value.length > 0) {
515
+ acc[key] = item.value.join('~')
516
+ } else {
517
+ acc[key] = item.value
518
+ }
519
+ break
520
+ case 'finishTime':
521
+ if (Array.isArray(item.value) && item.value.length > 0) {
522
+ acc[key] = item.value.join('~')
523
+ } else {
524
+ acc[key] = item.value
525
+ }
526
+ break
527
+ case 'year':
528
+ if (item.value != null) {
529
+ // 确保是 dayjs 对象
530
+ acc[key] = item.value?.format?.('YYYY') || item.value
531
+ } else {
532
+ acc[key] = null
533
+ }
534
+ break
535
+ default:
536
+ acc[key] = values[key].value
537
+ break
538
+ }
539
+ return acc
540
+ }, {})
541
+ state.params = newValues
542
+ }
543
+
544
+ var data = state.params
545
+ Object.assign(data, props.where)
546
+ return data
547
+ }
548
+ //加载表格数据
549
+ const tableLoad = () => {
550
+ state.loading = true
551
+ var data = loaddata()
552
+ if (props.page) {
553
+ data.page = pagination.value.current
554
+ data.size = pagination.value.pageSize
555
+ }
556
+ if (props.method == 'post') {
557
+ post(props.modulePath + '/' + props.pageAction, data).then(res => {
558
+ state.loading = false
559
+ state.dataSource = res.data
560
+ if (props.page) pagination.value.total = res.count // 计算总条数
561
+ emit('load-complete', res.data) // 发射事件
562
+ })
563
+ }
564
+ if (props.method == 'get') {
565
+ get(props.modulePath + '/' + props.pageAction, data).then(res => {
566
+ state.loading = false
567
+ state.dataSource = res.data
568
+ if (props.page) pagination.value.total = res.count // 计算总条数
569
+ emit('load-complete', res.data) // 发射事件
570
+ })
571
+ }
572
+ }
573
+ tableLoad()
574
+ //表格选择size
575
+ const dropdownonTableSizeClick = ({ key }) => {
576
+ config.tableSize = key
577
+ formConfig.tableSize = key
578
+ }
579
+ const emit = defineEmits(['edit', 'details'])
580
+ //编辑
581
+ const edit = record => {
582
+ // 触发自定义事件,父组件会监听这个事件
583
+ emit('edit', record)
584
+ }
585
+ const details = record => {
586
+ emit('details', record)
587
+ }
588
+ //批量删除
589
+ const batchDelete = () => {
590
+ Modal.confirm({
591
+ title: '提示',
592
+ content: '确定要删除这些数据吗?',
593
+ onOk() {
594
+ return new Promise((resolve, reject) => {
595
+ setTimeout(Math.random() > 0.5 ? resolve : reject, 1000)
596
+ }).catch(() => {
597
+
598
+ get(props.modulePath + '/' + props.ftableDeleteAction, {
599
+ ids: state.selectedRowKeys,
600
+ }).then(() => {
601
+ message.success({
602
+ content: '成功',
603
+ duration: 1,
604
+ onClose: () => {
605
+ state.selectedRowKeys = []
606
+ tableLoad()
607
+ },
608
+ })
609
+ })
610
+ })
611
+ },
612
+ onCancel() {},
613
+ })
614
+ }
615
+ const tabelDelete = record => {
616
+ get(
617
+ props.modulePath + '/' + props.ftableDeleteAction,
618
+ {
619
+ ids: record.id,
620
+ },
621
+ true,
622
+ '删除中...',
623
+ ).then(() => {
624
+ message.success({
625
+ content: '成功',
626
+ duration: 1,
627
+ onClose: () => {
628
+ tableLoad()
629
+ },
630
+ })
631
+ })
632
+ }
633
+ //导出
634
+ const dataExport = () => {
635
+ Modal.confirm({
636
+ title: '提示',
637
+ content: '确定要导出这些数据吗?',
638
+ onOk() {
639
+ return new Promise((resolve, reject) => {
640
+ var data = loaddata()
641
+ downloadFilePost(
642
+ props.modulePath + '/' + props.ftableExportAction,
643
+ data,
644
+ ).then(res => {
645
+ saveAs(res.blob, res.fileName)
646
+ resolve() // 请求成功后再关闭对话框
647
+ }).catch(error => {
648
+ console.log('Oops errors!')
649
+ reject(error) // 请求失败时保持对话框打开
650
+ })
651
+ })
652
+ },
653
+ onCancel() {},
654
+ })
655
+ }
656
+ const handleResizeColumn = (w, col) => {
657
+ col.width = w
658
+ }
659
+ const getSelectedRows = () => state.selectedRowKeys
660
+ const getDataSource = () => state.dataSource
661
+ // const getBtns = () => btns
662
+
663
+ /**
664
+ * 显示导入模态框
665
+ */
666
+ const showImportModal = () => {
667
+ importModalVisible.value = true
668
+ fileList.value = []
669
+ }
670
+
671
+ /**
672
+ * 文件上传前的处理
673
+ * @param {Object} file - 上传的文件对象
674
+ * @returns {Boolean} - 返回false阻止自动上传
675
+ */
676
+ const beforeUpload = file => {
677
+ // 检查文件类型
678
+ const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
679
+ file.type === 'application/vnd.ms-excel'
680
+
681
+ if (!isExcel) {
682
+ message.error('只能上传Excel文件!')
683
+ return false
684
+ }
685
+
686
+ // 检查文件大小(限制为10MB)
687
+ const isLt10M = file.size / 1024 / 1024 < 10
688
+ if (!isLt10M) {
689
+ message.error('文件大小不能超过10MB!')
690
+ return false
691
+ }
692
+
693
+ // 确保只能上传一个文件,替换已有文件
694
+ fileList.value = [file]
695
+ return false // 阻止自动上传
696
+ }
697
+
698
+ /**
699
+ * 移除上传的文件
700
+ * @param {Object} file - 要移除的文件对象
701
+ */
702
+ const handleRemove = file => {
703
+ const index = fileList.value.indexOf(file)
704
+ const newFileList = fileList.value.slice()
705
+ newFileList.splice(index, 1)
706
+ fileList.value = newFileList
707
+ }
708
+
709
+ /**
710
+ * 处理文件上传
711
+ */
712
+ const handleUpload = () => {
713
+ if (fileList.value.length === 0) {
714
+ message.warning('请先选择文件')
715
+ return
716
+ }
717
+
718
+ const formData = new FormData()
719
+ fileList.value.forEach(file => {
720
+ formData.append('file', file)
721
+ })
722
+
723
+ uploading.value = true
724
+
725
+ // 这里假设导入接口为模块路径下的import方法
726
+ post(props.modulePath + '/import', formData, {
727
+ }).then(res => {
728
+ uploading.value = false
729
+ message.success({
730
+ content: '导入成功',
731
+ duration: 1,
732
+ onClose: () => {
733
+ importModalVisible.value = false
734
+ // 刷新表格数据
735
+ tableLoad()
736
+ },
737
+ })
738
+
739
+ }).catch(error => {
740
+ uploading.value = false
741
+ })
742
+ }
743
+
744
+ /**
745
+ * 下载导入模板
746
+ */
747
+ const downloadTemplate = () => {
748
+ // 这里假设模板下载接口为模块路径下的template方法
749
+ downloadFilePost(props.modulePath + '/importTemplate', {})
750
+ .then(res => {
751
+ saveAs(res.blob, res.fileName || '导入模板.xlsx')
752
+ })
753
+ .catch(error => {
754
+ message.error('模板下载失败: ' + (error.message || '未知错误'))
755
+ })
756
+ }
757
+
758
+ // 将配置和方法暴露给父组件
759
+ defineExpose({
760
+ edit,
761
+ tableLoad,
762
+ getSelectedRows,
763
+ getDataSource,
764
+ // getBtns,
765
+ })
766
+ </script>
767
+ <style scoped>
768
+ .table-search {
769
+ margin-bottom: 8px;
770
+ padding: 24px 24px 0px;
771
+ background-color: white;
772
+ border-radius: 3px;
773
+ }
774
+ .card-table {
775
+ padding: 24px;
776
+ background-color: white;
777
+ border-radius: 3px;
778
+ }
779
+ .table-operations {
780
+ margin-bottom: 16px;
781
+ }
782
+ .table-operations > button {
783
+ margin-right: 8px;
784
+ }
785
+
786
+ /* 导入模态框样式 */
787
+ .import-modal-content {
788
+ padding: 10px 0;
789
+ }
790
+
791
+ .upload-actions {
792
+ margin-top: 16px;
793
+ text-align: center;
794
+ }
795
+
796
+ /* 导入模板卡片样式 */
797
+ .template-card {
798
+ margin-top: 16px;
799
+ }
800
+
801
+ .template-content {
802
+ display: flex;
803
+ align-items: center;
804
+ justify-content: space-between;
805
+ }
806
+
807
+ .template-info {
808
+ display: flex;
809
+ align-items: flex-start;
810
+ flex: 1;
811
+ }
812
+
813
+ .template-icon {
814
+ font-size: 48px;
815
+ color: #1890ff;
816
+ margin-right: 16px;
817
+ }
818
+
819
+ .template-description {
820
+ flex: 1;
821
+ }
822
+
823
+ .template-description h4 {
824
+ margin: 0 0 8px 0;
825
+ font-size: 16px;
826
+ font-weight: 500;
827
+ }
828
+
829
+ .template-description p {
830
+ margin: 0 0 12px 0;
831
+ color: rgba(0, 0, 0, 0.65);
832
+ }
833
+
834
+ .template-tips {
835
+ margin: 0;
836
+ padding-left: 20px;
837
+ color: rgba(0, 0, 0, 0.45);
838
+ font-size: 12px;
839
+ }
840
+
841
+ .template-tips li {
842
+ margin-bottom: 4px;
843
+ }
844
+
845
+ .template-action {
846
+ margin-left: 16px;
847
+ }
848
+
849
+ /* 表格工具栏样式 */
850
+ .flex-row {
851
+ display: flex;
852
+ justify-content: space-between;
853
+ align-items: center;
854
+ }
855
+
856
+ .flex-item {
857
+ flex: 1;
858
+ }
859
+
860
+ .text-align-left {
861
+ text-align: left;
862
+ }
863
+
864
+ .text-align-right {
865
+ text-align: right;
866
+ }
867
+
868
+ .padding-right-10 {
869
+ padding-right: 10px;
870
+ }
871
+
872
+ .text-color-black {
873
+ color: rgba(0, 0, 0, 0.88);
874
+ }
875
+ </style>