vue2server7 5.0.9 → 6.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.
@@ -0,0 +1,146 @@
1
+ <!-- ImportTableDialog.vue -->
2
+ <template>
3
+ <el-dialog
4
+ v-model="dialogVisible"
5
+ :title="title"
6
+ width="520px"
7
+ destroy-on-close
8
+ @closed="handleClosed"
9
+ >
10
+ <el-upload
11
+ ref="uploadRef"
12
+ v-model:file-list="fileList"
13
+ drag
14
+ action="#"
15
+ :auto-upload="false"
16
+ :limit="1"
17
+ :accept="accept"
18
+ :on-change="handleChange"
19
+ :on-exceed="handleExceed"
20
+ :on-remove="handleRemove"
21
+ >
22
+ <el-icon class="el-icon--upload">
23
+ <UploadFilled />
24
+ </el-icon>
25
+
26
+ <div class="el-upload__text">
27
+ 将表格文件拖到这里,或 <em>点击选择</em>
28
+ </div>
29
+
30
+ <template #tip>
31
+ <div class="el-upload__tip">
32
+ 仅支持 xls / xlsx / csv,每次只允许上传 1 个文件
33
+ </div>
34
+ </template>
35
+ </el-upload>
36
+
37
+ <template #footer>
38
+ <el-button :disabled="loading" @click="dialogVisible = false">
39
+ 取消
40
+ </el-button>
41
+ <el-button type="primary" :loading="loading" @click="handleConfirm">
42
+ 导入
43
+ </el-button>
44
+ </template>
45
+ </el-dialog>
46
+ </template>
47
+
48
+ <script setup>
49
+ import { computed, ref } from 'vue'
50
+ import { ElMessage, genFileId } from 'element-plus'
51
+ import { UploadFilled } from '@element-plus/icons-vue'
52
+
53
+ const props = defineProps({
54
+ modelValue: {
55
+ type: Boolean,
56
+ default: false,
57
+ },
58
+ title: {
59
+ type: String,
60
+ default: '导入表格',
61
+ },
62
+ loading: {
63
+ type: Boolean,
64
+ default: false,
65
+ },
66
+ })
67
+
68
+ const emit = defineEmits(['update:modelValue', 'confirm'])
69
+
70
+ const dialogVisible = computed({
71
+ get: () => props.modelValue,
72
+ set: (value) => emit('update:modelValue', value),
73
+ })
74
+
75
+ const uploadRef = ref()
76
+ const fileList = ref([])
77
+ const selectedFile = ref(null)
78
+
79
+ const accept = '.xls,.xlsx,.csv'
80
+ const allowExt = ['xls', 'xlsx', 'csv']
81
+
82
+ const isTableFile = (file) => {
83
+ const ext = file.name?.split('.').pop()?.toLowerCase()
84
+ return allowExt.includes(ext)
85
+ }
86
+
87
+ const reset = () => {
88
+ selectedFile.value = null
89
+ fileList.value = []
90
+ uploadRef.value?.clearFiles()
91
+ }
92
+
93
+ const handleChange = (uploadFile, uploadFiles) => {
94
+ const rawFile = uploadFile.raw
95
+ if (!rawFile) return
96
+
97
+ if (!isTableFile(rawFile)) {
98
+ ElMessage.error('只能上传表格文件(xls、xlsx、csv)')
99
+ reset()
100
+ return
101
+ }
102
+
103
+ // 只保留最后一个文件
104
+ fileList.value = uploadFiles.slice(-1)
105
+ selectedFile.value = rawFile
106
+ }
107
+
108
+ const handleExceed = (files) => {
109
+ const file = files[0]
110
+
111
+ if (!isTableFile(file)) {
112
+ ElMessage.error('只能上传表格文件(xls、xlsx、csv)')
113
+ return
114
+ }
115
+
116
+ // 超出 1 个时,用新文件覆盖旧文件
117
+ uploadRef.value?.clearFiles()
118
+ file.uid = genFileId()
119
+ uploadRef.value?.handleStart(file)
120
+ }
121
+
122
+ const handleRemove = () => {
123
+ selectedFile.value = null
124
+ }
125
+
126
+ const handleConfirm = () => {
127
+ if (!selectedFile.value) {
128
+ ElMessage.warning('请先选择一个表格文件')
129
+ return
130
+ }
131
+
132
+ // 把原始 File 对象交给父组件
133
+ emit('confirm', selectedFile.value)
134
+ }
135
+
136
+ const handleClosed = () => {
137
+ reset()
138
+ }
139
+ </script>
140
+
141
+ <style scoped>
142
+ :deep(.el-upload),
143
+ :deep(.el-upload-dragger) {
144
+ width: 100%;
145
+ }
146
+ </style>
@@ -0,0 +1,164 @@
1
+ <template>
2
+ <section class="page import-page">
3
+ <h1 class="title">导入表格</h1>
4
+
5
+ <el-card class="upload-card">
6
+ <template #header>
7
+ <div class="card-header">
8
+ <span>数据导入</span>
9
+ </div>
10
+ </template>
11
+
12
+ <div class="upload-content">
13
+ <el-button type="primary" @click="showImportDialog">
14
+ 导入表格文件
15
+ </el-button>
16
+
17
+ <div v-if="uploadedFile" class="file-info">
18
+ <el-alert
19
+ title="已选择文件"
20
+ type="success"
21
+ :closable="false"
22
+ show-icon
23
+ >
24
+ <div class="file-details">
25
+ <p><strong>文件名:</strong>{{ uploadedFile.name }}</p>
26
+ <p><strong>文件大小:</strong>{{ formatFileSize(uploadedFile.size) }}</p>
27
+ <p><strong>文件类型:</strong>{{ uploadedFile.type || '未知' }}</p>
28
+ </div>
29
+ </el-alert>
30
+
31
+ <el-button type="danger" size="small" @click="clearFile" style="margin-top: 12px">
32
+ 清除文件
33
+ </el-button>
34
+ </div>
35
+
36
+ <div v-else class="upload-tips">
37
+ <el-alert
38
+ title="上传说明"
39
+ type="info"
40
+ :closable="false"
41
+ show-icon
42
+ >
43
+ <template #default>
44
+ <ul>
45
+ <li>支持的文件格式:xls、xlsx、csv</li>
46
+ <li>每次只能上传一个文件</li>
47
+ <li>上传后可以在这里查看文件信息</li>
48
+ </ul>
49
+ </template>
50
+ </el-alert>
51
+ </div>
52
+ </div>
53
+ </el-card>
54
+
55
+ <!-- 导入对话框 -->
56
+ <ImportTableDialog
57
+ v-model="dialogVisible"
58
+ :loading="loading"
59
+ @confirm="handleImportConfirm"
60
+ />
61
+ </section>
62
+ </template>
63
+
64
+ <script setup>
65
+ import { ref } from 'vue'
66
+ import { ElMessage } from 'element-plus'
67
+ import ImportTableDialog from '../components/ImportTableDialog.vue'
68
+
69
+ const dialogVisible = ref(false)
70
+ const loading = ref(false)
71
+ const uploadedFile = ref(null)
72
+
73
+ const showImportDialog = () => {
74
+ dialogVisible.value = true
75
+ }
76
+
77
+ const handleImportConfirm = (file) => {
78
+ loading.value = true
79
+
80
+ // 模拟上传处理
81
+ setTimeout(() => {
82
+ loading.value = false
83
+ dialogVisible.value = false
84
+ uploadedFile.value = file
85
+
86
+ ElMessage.success(`文件 "${file.name}" 上传成功!`)
87
+
88
+ // 这里可以添加实际的文件上传逻辑
89
+ console.log('准备上传的文件:', file)
90
+ // 例如:调用 API 上传文件
91
+ // uploadFileApi(file).then(res => { ... })
92
+ }, 1000)
93
+ }
94
+
95
+ const clearFile = () => {
96
+ uploadedFile.value = null
97
+ ElMessage.info('已清除文件')
98
+ }
99
+
100
+ const formatFileSize = (bytes) => {
101
+ if (!bytes) return '0 B'
102
+ const sizes = ['B', 'KB', 'MB', 'GB']
103
+ const i = Math.floor(Math.log(bytes) / Math.log(1024))
104
+ return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`
105
+ }
106
+ </script>
107
+
108
+ <style scoped>
109
+ .page.import-page {
110
+ padding: 16px;
111
+ }
112
+
113
+ .title {
114
+ font-size: 18px;
115
+ margin-bottom: 12px;
116
+ }
117
+
118
+ .upload-card {
119
+ max-width: 800px;
120
+ }
121
+
122
+ .card-header {
123
+ display: flex;
124
+ justify-content: space-between;
125
+ align-items: center;
126
+ }
127
+
128
+ .upload-content {
129
+ min-height: 200px;
130
+ display: flex;
131
+ flex-direction: column;
132
+ align-items: center;
133
+ justify-content: center;
134
+ }
135
+
136
+ .file-info {
137
+ margin-top: 16px;
138
+ text-align: center;
139
+ }
140
+
141
+ .file-details {
142
+ text-align: left;
143
+ }
144
+
145
+ .file-details p {
146
+ margin: 8px 0;
147
+ font-size: 14px;
148
+ }
149
+
150
+ .upload-tips {
151
+ margin-top: 16px;
152
+ width: 100%;
153
+ }
154
+
155
+ .upload-tips ul {
156
+ margin: 8px 0;
157
+ padding-left: 20px;
158
+ }
159
+
160
+ .upload-tips li {
161
+ margin: 4px 0;
162
+ font-size: 14px;
163
+ }
164
+ </style>
@@ -1,6 +1,7 @@
1
1
  import TablePage from '../pages/TablePage.vue'
2
2
  import CascaderPage from '../pages/CascaderPage.vue'
3
3
  import ExportExcelPage from '../pages/ExportExcelPage.vue'
4
+ import ImportTablePage from '../pages/ImportTablePage.vue'
4
5
 
5
6
  export const routes = [
6
7
  {
@@ -30,7 +31,16 @@ export const routes = [
30
31
  name: 'ExportExcel',
31
32
  component: ExportExcelPage,
32
33
  meta: {
33
- title: '导出Excel',
34
+ title: '导出 Excel',
35
+ showInMenu: true
36
+ }
37
+ },
38
+ {
39
+ path: '/import-table',
40
+ name: 'ImportTable',
41
+ component: ImportTablePage,
42
+ meta: {
43
+ title: '导入表格',
34
44
  showInMenu: true
35
45
  }
36
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2server7",
3
- "version": "5.0.9",
3
+ "version": "6.0.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "dev": "nodemon --watch src --ext ts --exec \"ts-node src/app.ts\"",
@@ -30,19 +30,18 @@
30
30
  "author": "",
31
31
  "license": "ISC",
32
32
  "dependencies": {
33
- "@element-plus/icons-vue": "^2.3.1",
34
- "axios": "^1.13.2",
35
33
  "cors": "^2.8.5",
36
34
  "dotenv": "^16.6.1",
37
- "echarts": "^5.5.1",
38
- "element-plus": "^2.8.4",
39
35
  "express": "^5.2.1",
40
36
  "helmet": "^8.1.0",
41
37
  "morgan": "^1.10.1",
38
+ "@element-plus/icons-vue": "^2.3.1",
39
+ "axios": "^1.13.2",
40
+ "echarts": "^5.5.1",
41
+ "element-plus": "^2.8.4",
42
42
  "sortablejs": "^1.15.6",
43
43
  "vue": "^3.5.13",
44
- "vue-router": "^4.4.5",
45
- "vue2webstrom": "^0.0.1"
44
+ "vue-router": "^4.4.5"
46
45
  },
47
46
  "devDependencies": {
48
47
  "@types/cors": "^2.8.19",
@@ -51,12 +50,12 @@
51
50
  "@types/node": "^22.10.5",
52
51
  "@typescript-eslint/parser": "^8.19.0",
53
52
  "@vitejs/plugin-vue": "^6.0.0",
54
- "concurrently": "^9.1.0",
55
53
  "eslint": "^9.18.0",
56
- "nodemon": "^3.0.1",
57
- "supertest": "^7.1.4",
58
54
  "ts-node": "^10.9.2",
55
+ "supertest": "^7.1.4",
59
56
  "typescript": "^5.7.3",
57
+ "nodemon": "^3.0.1",
58
+ "concurrently": "^9.1.0",
60
59
  "vite": "^6.0.0",
61
60
  "vite-plugin-vue-devtools": "^8.0.6"
62
61
  },