vue2server7 1.0.1 → 2.1.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.
@@ -1,43 +1,25 @@
1
1
  {
2
- "name": "vue2-app",
3
- "version": "0.1.0",
2
+ "name": "vue3-app",
3
+ "version": "1.0.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "serve": "vue-cli-service serve",
7
- "build": "vue-cli-service build",
8
- "lint": "vue-cli-service lint"
6
+ "dev": "vite",
7
+ "build": "vite build",
8
+ "preview": "vite preview"
9
9
  },
10
10
  "dependencies": {
11
+ "@element-plus/icons-vue": "^2.3.1",
11
12
  "axios": "^1.13.2",
12
- "core-js": "^3.6.5",
13
13
  "echarts": "^5.5.1",
14
- "element-ui": "^2.15.14",
14
+ "element-plus": "^2.8.4",
15
15
  "sortablejs": "^1.15.6",
16
- "vue": "^2.6.11",
17
- "vue-router": "^3.6.5"
16
+ "vue": "^3.5.13",
17
+ "vue-router": "^4.4.5"
18
18
  },
19
19
  "devDependencies": {
20
- "@vue/cli-plugin-babel": "~4.5.0",
21
- "@vue/cli-plugin-eslint": "~4.5.0",
22
- "@vue/cli-service": "~4.5.0",
23
- "babel-eslint": "^10.1.0",
24
- "eslint": "^6.7.2",
25
- "eslint-plugin-vue": "^6.2.2",
26
- "vue-template-compiler": "^2.6.11"
27
- },
28
- "eslintConfig": {
29
- "root": true,
30
- "env": {
31
- "node": true
32
- },
33
- "extends": [
34
- "plugin:vue/essential",
35
- "eslint:recommended"
36
- ],
37
- "parserOptions": {
38
- "parser": "babel-eslint"
39
- },
40
- "rules": {}
20
+ "@vitejs/plugin-vue": "^6.0.0",
21
+ "vite": "^6.0.0",
22
+ "vite-plugin-vue-devtools": "^8.0.6"
41
23
  },
42
24
  "browserslist": [
43
25
  "> 1%",
@@ -8,7 +8,10 @@
8
8
  type="text"
9
9
  @click="isMenuCollapsed = !isMenuCollapsed"
10
10
  >
11
- <i :class="isMenuCollapsed ? 'el-icon-s-unfold' : 'el-icon-s-fold'"></i>
11
+ <el-icon>
12
+ <Expand v-if="isMenuCollapsed" />
13
+ <Fold v-else />
14
+ </el-icon>
12
15
  </el-button>
13
16
  </div>
14
17
  <AppMenu :collapse="isMenuCollapsed" />
@@ -22,6 +25,7 @@
22
25
 
23
26
  <script>
24
27
  import AppMenu from './components/AppMenu.vue'
28
+ import { Expand, Fold } from '@element-plus/icons-vue'
25
29
 
26
30
  export default {
27
31
  name: 'App',
@@ -33,7 +37,9 @@ export default {
33
37
  }
34
38
  },
35
39
  components: {
36
- AppMenu
40
+ AppMenu,
41
+ Expand,
42
+ Fold
37
43
  }
38
44
  }
39
45
  </script>
@@ -12,8 +12,7 @@
12
12
  :key="item.path + '-submenu'"
13
13
  :index="item.path"
14
14
  >
15
- <template slot="title">
16
- <i :class="getIcon(item)"></i>
15
+ <template #title>
17
16
  <span class="menu-title-text">{{ getTitle(item) }}</span>
18
17
  </template>
19
18
  <el-menu-item
@@ -21,7 +20,6 @@
21
20
  :key="child.path"
22
21
  :index="child.path"
23
22
  >
24
- <i :class="getIcon(child)"></i>
25
23
  <span class="menu-title-text">{{ getTitle(child) }}</span>
26
24
  </el-menu-item>
27
25
  </el-submenu>
@@ -31,7 +29,6 @@
31
29
  :key="item.path + '-item'"
32
30
  :index="item.path"
33
31
  >
34
- <i :class="getIcon(item)"></i>
35
32
  <span class="menu-title-text">{{ getTitle(item) }}</span>
36
33
  </el-menu-item>
37
34
  </template>
@@ -61,9 +58,6 @@ export default {
61
58
  getTitle(route) {
62
59
  return (route && route.meta && route.meta.title) || route.name || route.path
63
60
  },
64
- getIcon(route) {
65
- return (route && route.meta && route.meta.icon) || 'el-icon-menu'
66
- },
67
61
  getChildren(route) {
68
62
  if (!route || !Array.isArray(route.children)) return []
69
63
  return route.children.filter(r => this.isMenuVisible(r))
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <div class="column-settings">
3
+ <el-checkbox-group v-model="selected">
4
+ <el-checkbox v-for="col in columns" :key="col.key" :label="col.key">
5
+ {{ col.label }}
6
+ </el-checkbox>
7
+ </el-checkbox-group>
8
+ </div>
9
+ </template>
10
+
11
+ <script>
12
+ export default {
13
+ name: 'ColumnSettings',
14
+ props: {
15
+ columns: {
16
+ type: Array,
17
+ default: () => []
18
+ },
19
+ modelValue: {
20
+ type: Array,
21
+ default: () => []
22
+ }
23
+ },
24
+ emits: ['update:modelValue'],
25
+ data() {
26
+ return {
27
+ selected: [...this.modelValue]
28
+ }
29
+ },
30
+ watch: {
31
+ modelValue(val) {
32
+ this.selected = [...val]
33
+ },
34
+ selected(val) {
35
+ this.$emit('update:modelValue', val)
36
+ }
37
+ }
38
+ }
39
+ </script>
40
+
41
+ <style scoped>
42
+ .column-settings .el-checkbox {
43
+ display: block;
44
+ margin: 6px 0;
45
+ }
46
+ </style>
@@ -124,7 +124,7 @@ export default {
124
124
  mounted() {
125
125
  if (this.autoStart) this.start()
126
126
  },
127
- beforeDestroy() {
127
+ beforeUnmount() {
128
128
  this.stopAuto()
129
129
  window.removeEventListener('resize', this.onResize)
130
130
  },
@@ -1,14 +1,11 @@
1
- import Vue from 'vue'
1
+ import { createApp } from 'vue'
2
2
  import App from './App.vue'
3
-
4
- import ElementUI from 'element-ui'
5
- import 'element-ui/lib/theme-chalk/index.css'
6
3
  import router from './router'
7
4
 
8
- Vue.config.productionTip = false
9
- Vue.use(ElementUI)
5
+ import ElementPlus from 'element-plus'
6
+ import 'element-plus/dist/index.css'
10
7
 
11
- new Vue({
12
- router,
13
- render: h => h(App)
14
- }).$mount('#app')
8
+ const app = createApp(App)
9
+ app.use(router)
10
+ app.use(ElementPlus)
11
+ app.mount('#app')
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <el-cascader
3
+ ref="cascader"
4
+ v-model="value"
5
+ :options="options"
6
+ :props="{ checkStrictly: true,emitPath: true }"
7
+ @change="handleChange"
8
+ />
9
+ </template>
10
+
11
+ <script setup>
12
+ import { ref } from 'vue'
13
+
14
+ const value = ref('')
15
+ const cascader = ref()
16
+
17
+ const options = [
18
+ {
19
+ value: 'zhejiang',
20
+ label: '浙江',
21
+ children: [
22
+ { value: 'hangzhou', label: '杭州' }
23
+ ]
24
+ }
25
+ ]
26
+
27
+ const handleChange = () => {
28
+ const nodes = cascader.value?.getCheckedNodes?.() || []
29
+ const node = nodes[nodes.length - 1]
30
+ const label = node?.label ?? node?.text ?? node?.data?.label
31
+ console.log('label:', label)
32
+ }
33
+ </script>
@@ -0,0 +1,231 @@
1
+ <template>
2
+ <section class="page table-page">
3
+ <h1 class="title">表格页面</h1>
4
+ <div class="toolbar">
5
+ <el-input v-model="keyword" placeholder="按主题搜索" clearable style="width: 240px" />
6
+ <el-select v-model="type" placeholder="类型" clearable style="width: 160px; margin-left: 8px">
7
+ <el-option label="培训" value="TRAINING" />
8
+ <el-option label="晨夕会" value="MORNING" />
9
+ </el-select>
10
+ <el-button type="primary" @click="onSearch" style="margin-left: 8px">查询</el-button>
11
+ <el-button @click="onReset" style="margin-left: 8px">重置</el-button>
12
+ <el-popover placement="bottom" trigger="click" width="260">
13
+ <template #reference>
14
+ <el-button style="margin-left: 8px">列设置</el-button>
15
+ </template>
16
+ <ColumnSettings v-model="visibleKeys" :columns="columns" />
17
+ </el-popover>
18
+ </div>
19
+
20
+ <el-table
21
+ :key="tableKey"
22
+ :data="rows"
23
+ border
24
+ stripe
25
+ size="small"
26
+ style="width: 100%"
27
+ :default-sort="{ prop: 'createTm', order: 'descending' }"
28
+ @sort-change="onSortChange"
29
+ >
30
+ <el-table-column type="index" label="序号" width="60" align="center" />
31
+ <el-table-column
32
+ v-for="col in columns.filter(c => visibleKeys.includes(c.key))"
33
+ :key="col.key"
34
+ :prop="col.prop"
35
+ :label="col.label"
36
+ :width="col.width"
37
+ :min-width="col.minWidth"
38
+ :align="col.align || 'left'"
39
+ :header-align="col.headerAlign || col.align || 'left'"
40
+ :sortable="col.sortable || false"
41
+ :fixed="col.fixed || false"
42
+ >
43
+ <template #default="{ row }">
44
+ <template v-if="col.type === 'type'">
45
+ <el-tag :type="getTypeTag(row.meetTypeCd)" size="small">{{ formatType(row.meetTypeCd) }}</el-tag>
46
+ </template>
47
+ <template v-else-if="col.type === 'date'">
48
+ {{ formatDate(row[col.prop]) }}
49
+ </template>
50
+ <template v-else-if="col.type === 'datetime'">
51
+ {{ formatDateTime(row[col.prop]) }}
52
+ </template>
53
+ <template v-else-if="col.type === 'action'">
54
+ <el-button type="text" size="small" @click="onView(row)">详情</el-button>
55
+ <el-button type="text" size="small" @click="onDelete(row)">删除</el-button>
56
+ </template>
57
+ <template v-else>
58
+ {{ row[col.prop] }}
59
+ </template>
60
+ </template>
61
+ </el-table-column>
62
+ </el-table>
63
+
64
+ <div class="pagination">
65
+ <el-pagination layout="total, prev, pager, next" :current-page="page" :page-size="pageSize" :total="total" @current-change="onPageChange" />
66
+ </div>
67
+
68
+
69
+ </section>
70
+ </template>
71
+
72
+ <script>
73
+ import { get } from '../utils/request'
74
+ import ColumnSettings from '../components/ColumnSettings.vue'
75
+
76
+ export default {
77
+ name: 'TablePage',
78
+ data() {
79
+ return {
80
+ columns: [
81
+ { key: 'organizationName', prop: 'organizationName', label: '所属机构', minWidth: 160 },
82
+ { key: 'meetSubj', prop: 'meetSubj', label: '会议主题', minWidth: 240 },
83
+ { key: 'meetNo', prop: 'meetNo', label: '会议编号', minWidth: 180 },
84
+ { key: 'displayName', prop: 'displayName', label: '创建人', width: 120 },
85
+ { key: 'meetTypeCd', prop: 'meetTypeCd', label: '类型', width: 120, align: 'center', type: 'type' },
86
+ { key: 'createTm', prop: 'createTm', label: '创建日期', width: 160, align: 'center', type: 'date', sortable: 'custom' },
87
+ { key: 'updateTm', prop: 'updateTm', label: '最近编辑时间', width: 190, align: 'center', type: 'datetime', sortable: 'custom' },
88
+ { key: 'action', prop: '', label: '操作', width: 140, align: 'center', fixed: 'right', type: 'action' }
89
+ ],
90
+ visibleKeys: [],
91
+ rows: [],
92
+ total: 0,
93
+ page: 1,
94
+ pageSize: 10,
95
+ sortField: 'createTm',
96
+ sortOrder: 'descending',
97
+ keyword: '',
98
+ type: '',
99
+ tableKey: 0
100
+ }
101
+ },
102
+ components: {
103
+ ColumnSettings
104
+ },
105
+ created() {
106
+ this.visibleKeys = this.columns.map(c => c.key)
107
+ this.fetch()
108
+ },
109
+ methods: {
110
+ async fetch() {
111
+ const params = {
112
+ page: this.page,
113
+ pageSize: this.pageSize,
114
+ sortField: this.sortField,
115
+ sortOrder: this.sortOrder
116
+ }
117
+ if (this.keyword) params.meetSubj = this.keyword
118
+ if (this.type) params.meetTypeCd = this.type
119
+ const res = await get('/meeting/list', params)
120
+ this.rows = Array.isArray(res.list) ? res.list : []
121
+ this.total = Number.isFinite(res.total) ? res.total : this.rows.length
122
+ },
123
+ onSearch() {
124
+ this.page = 1
125
+ this.fetch()
126
+ },
127
+ onReset() {
128
+ this.keyword = ''
129
+ this.type = ''
130
+ this.page = 1
131
+ this.fetch()
132
+ },
133
+ onPageChange(p) {
134
+ this.page = p
135
+ this.fetch()
136
+ },
137
+ onSortChange({ prop, order }) {
138
+ this.sortField = prop || ''
139
+ this.sortOrder = order || ''
140
+ this.page = 1
141
+ this.fetch()
142
+ },
143
+ // 通过改变 key 强制表格重渲染,确保动态列显示/隐藏立即生效
144
+ bumpTableKey() {
145
+ this.tableKey += 1
146
+ },
147
+ onView(row) {
148
+ const lines = [
149
+ `会议主题:${row.meetSubj || ''}`,
150
+ `内容:${row.plainContent || ''}`,
151
+ `所属机构:${row.organizationName || ''}`,
152
+ `会议类型:${this.formatType(row.meetTypeCd)}`,
153
+ `创建人:${row.displayName || ''}`,
154
+ `创建日期:${this.formatDate(row.createTm)}`,
155
+ `最近编辑时间:${this.formatDateTime(row.updateTm)}`
156
+ ]
157
+ this.$alert(lines.join('\n'), '会议详情', { confirmButtonText: '确定' })
158
+ },
159
+ onDelete(row) {
160
+ this.$confirm(`确定删除会议「${row.meetSubj || ''}」吗?`, '提示', {
161
+ type: 'warning',
162
+ confirmButtonText: '确定',
163
+ cancelButtonText: '取消'
164
+ })
165
+ .then(() => {
166
+ this.rows = this.rows.filter(item => item.id !== row.id)
167
+ this.$message({ type: 'success', message: '删除成功' })
168
+ })
169
+ .catch(() => {})
170
+ },
171
+ formatType(code) {
172
+ if (code === 'TRAINING') return '培训'
173
+ if (code === 'MORNING') return '晨夕会'
174
+ return code || ''
175
+ },
176
+ getTypeTag(code) {
177
+ if (code === 'TRAINING') return 'primary'
178
+ if (code === 'MORNING') return 'warning'
179
+ return 'info'
180
+ },
181
+ formatDate(value) {
182
+ if (!value) return ''
183
+ if (value.length === 10) return value
184
+ const d = new Date(value)
185
+ if (Number.isNaN(d.getTime())) return ''
186
+ const yyyy = d.getFullYear()
187
+ const mm = String(d.getMonth() + 1).padStart(2, '0')
188
+ const dd = String(d.getDate()).padStart(2, '0')
189
+ return `${yyyy}-${mm}-${dd}`
190
+ },
191
+ formatDateTime(value) {
192
+ if (!value) return ''
193
+ if (value.length >= 16 && value.includes(' ')) return value
194
+ const d = new Date(value)
195
+ if (Number.isNaN(d.getTime())) return ''
196
+ const yyyy = d.getFullYear()
197
+ const mm = String(d.getMonth() + 1).padStart(2, '0')
198
+ const dd = String(d.getDate()).padStart(2, '0')
199
+ const hh = String(d.getHours()).padStart(2, '0')
200
+ const mi = String(d.getMinutes()).padStart(2, '0')
201
+ const ss = String(d.getSeconds()).padStart(2, '0')
202
+ return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
203
+ }
204
+ }
205
+ ,
206
+ watch: {
207
+ visibleKeys() {
208
+ this.bumpTableKey()
209
+ }
210
+ }
211
+ }
212
+ </script>
213
+
214
+ <style scoped>
215
+ .page.table-page {
216
+ padding: 16px;
217
+ }
218
+ .title {
219
+ font-size: 18px;
220
+ margin-bottom: 12px;
221
+ }
222
+ .toolbar {
223
+ margin-bottom: 12px;
224
+ display: flex;
225
+ align-items: center;
226
+ }
227
+ .pagination {
228
+ margin-top: 12px;
229
+ text-align: right;
230
+ }
231
+ </style>
@@ -1,12 +1,9 @@
1
- import Vue from 'vue'
2
- import Router from 'vue-router'
3
-
1
+ import { createRouter, createWebHashHistory } from 'vue-router'
4
2
  import { routes } from './routes'
5
3
 
6
- Vue.use(Router)
7
-
8
- export default new Router({
9
- mode: 'hash',
4
+ const router = createRouter({
5
+ history: createWebHashHistory(),
10
6
  routes
11
7
  })
12
8
 
9
+ export default router
@@ -1,63 +1,26 @@
1
- // 前端路由配置
2
- // 说明:
3
- // - 本文件只负责声明路由与菜单元信息,不做权限控制
4
- // - AppMenu 会根据 meta.showInMenu 渲染左侧菜单
5
- // - icon 使用 Element UI 内置图标类名
6
- import LineChartPage from '../pages/LineChartPage.vue'
7
- import MedalSetting from '../pages/MedalSetting.vue'
8
- import MeetingListPage from '../pages/MeetingListPage.vue'
9
- import NameScrollPage from '../pages/NameScrollPage.vue'
1
+ import TablePage from '../pages/TablePage.vue'
2
+ import CascaderPage from '../pages/CascaderPage.vue'
10
3
 
11
- // routes 将在 router/index 中被 VueRouter 使用
12
4
  export const routes = [
13
- // 默认路由:根路径重定向到折线图页面
14
5
  {
15
6
  path: '/',
16
- redirect: '/line-chart'
7
+ redirect: '/table'
17
8
  },
18
- // 折线图页面示例
19
9
  {
20
- path: '/line-chart',
21
- name: 'LineChart',
22
- component: LineChartPage,
10
+ path: '/table',
11
+ name: 'Table',
12
+ component: TablePage,
23
13
  meta: {
24
- // 菜单标题
25
- title: '折线图',
26
- // 左侧菜单图标
27
- icon: 'el-icon-data-line',
28
- // 是否在菜单中展示
14
+ title: '表格页面',
29
15
  showInMenu: true
30
16
  }
31
17
  },
32
- // 勋章设置页面
33
18
  {
34
- path: '/medal-setting',
35
- name: 'MedalSetting',
36
- component: MedalSetting,
19
+ path: '/cascader',
20
+ name: 'Cascader',
21
+ component: CascaderPage,
37
22
  meta: {
38
- title: '勋章设置',
39
- icon: 'el-icon-medal',
40
- showInMenu: true
41
- }
42
- },
43
- // 会议列表页面
44
- {
45
- path: '/meeting-list',
46
- name: 'MeetingList',
47
- component: MeetingListPage,
48
- meta: {
49
- title: '会议列表',
50
- icon: 'el-icon-date',
51
- showInMenu: true
52
- }
53
- },
54
- {
55
- path: '/name-scroll',
56
- name: 'NameScroll',
57
- component: NameScrollPage,
58
- meta: {
59
- title: '名字滚动',
60
- icon: 'el-icon-user',
23
+ title: '级联选择',
61
24
  showInMenu: true
62
25
  }
63
26
  }
@@ -1,7 +1,7 @@
1
1
  import axios from 'axios'
2
2
 
3
3
  const service = axios.create({
4
- baseURL: process.env.VUE_APP_API_BASE || 'http://localhost:3000/api',
4
+ baseURL: import.meta.env.VITE_API_BASE || '/api',
5
5
  timeout: 15000
6
6
  })
7
7
 
@@ -0,0 +1,25 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+ import vueDevTools from 'vite-plugin-vue-devtools'
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ vue(),
8
+ vueDevTools()
9
+ ],
10
+ server: {
11
+ port: 5173,
12
+ open: false,
13
+ proxy: {
14
+ '/api': {
15
+ target: 'http://localhost:3000',
16
+ changeOrigin: true,
17
+ rewrite: (path) => path.replace(/^\/api/, '/api')
18
+ }
19
+ }
20
+ },
21
+ build: {
22
+ outDir: 'dist',
23
+ sourcemap: false
24
+ }
25
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2server7",
3
- "version": "1.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "dev": "nodemon --watch src --ext ts --exec \"ts-node src/app.ts\"",
@@ -9,8 +9,9 @@
9
9
  "typecheck": "tsc --noEmit",
10
10
  "lint": "eslint .",
11
11
  "test": "npm run build && node --test",
12
- "front-dev": "cd frontEnd && npm run serve",
12
+ "front-dev": "cd frontEnd && npm run dev",
13
13
  "front-build": "cd frontEnd && npm run build",
14
+ "front-preview": "cd frontEnd && npm run preview",
14
15
  "copy-frontend": "rm -rf dist/public && mkdir -p dist/public && cp -R frontEnd/dist/* dist/public/",
15
16
  "build:all": "npm run build && npm run front-build && npm run copy-frontend",
16
17
  "发布": "npm publish",
package/test/1.js ADDED
@@ -0,0 +1,24 @@
1
+ const fs = require("fs");
2
+
3
+ // 读取 text 文件
4
+ const content = fs.readFileSync("input.txt", "utf8");
5
+
6
+ // 正则:匹配 注解 + 字段
7
+ const regex =
8
+ /@ApiModelProperty\s*\(\s*value\s*=\s*"([^"]+)"\s*\)\s*[\r\n]+\s*private\s+\w+\s+(\w+)\s*;/g;
9
+
10
+ let match;
11
+ const result = [];
12
+
13
+ while ((match = regex.exec(content)) !== null) {
14
+ const label = match[1]; // 中文
15
+ const field = match[2]; // 字段名
16
+
17
+ result.push({
18
+ field,
19
+ label
20
+ });
21
+ }
22
+
23
+ // 输出 JSON
24
+ console.log(JSON.stringify(result, null, 2));
@@ -1,17 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
- <meta name="viewport" content="width=device-width,initial-scale=1.0">
7
- <link rel="icon" href="<%= BASE_URL %>favicon.ico">
8
- <title><%= htmlWebpackPlugin.options.title %></title>
9
- </head>
10
- <body>
11
- <noscript>
12
- <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
13
- </noscript>
14
- <div id="app"></div>
15
- <!-- built files will be auto injected -->
16
- </body>
17
- </html>