diff --git a/buyer/.babelrc b/buyer/.babelrc deleted file mode 100644 index 3a280ba3..00000000 --- a/buyer/.babelrc +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presets": [ - ["env", { - "modules": false, - "targets": { - "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] - } - }], - "stage-2" - ], - "plugins": ["transform-vue-jsx", "transform-runtime"] -} diff --git a/buyer/MIGRATION-VUE3.md b/buyer/MIGRATION-VUE3.md new file mode 100644 index 00000000..566e1b4a --- /dev/null +++ b/buyer/MIGRATION-VUE3.md @@ -0,0 +1,39 @@ +# Buyer 买家端 Vue 3 迁移说明 + +## 技术栈 + +- Vue 3 + Vue Router 4 + Vuex 4 +- Element Plus(替代 view-design / iView) +- 保留 `$Message` / `$Modal` / `$Notice` 兼容层(`src/utils/message.js`) + +## 启动 + +```bash +cd buyer +yarn install +yarn dev # 默认端口 10000 +yarn build +``` + +## 迁移要点 + +1. **入口** `src/main.js` 使用 `createApp`,全局过滤器改为 `$filters` +2. **路由** `src/router/index.js` 使用 `createRouter` + 动态 `import()` +3. **请求** `src/plugins/request.js` 使用 Element Plus `ElLoading` +4. **iView 组件已全部替换**:轮播 `el-carousel`、下拉 `el-dropdown`、侧栏 `el-menu`、头像 `el-avatar` 等 +5. **图标映射** `src/utils/iconMap.js` — 动态图标(如侧边栏菜单)通过 `resolveIcon()` 使用 +6. **自定义字体图标** `icomoon` 保留为 ``(购物车步骤条、钱包等) +7. **首页装修** `indexDecorate/modelList` 保留买家端展示逻辑(非 manager 编辑版) +8. **特殊替换** + - `vue-piczoom` → `el-image` 预览 + - `mv-count-down` → 内置倒计时 + - 秒杀轮播 → 横向滚动列表 + +## 批量脚本 + +- `scripts/migrate-iview-to-element.js` — iView → Element 模板替换 +- `scripts/fix-filters.js` — Vue 2 过滤器语法修复 +- `scripts/replace-page.js` — `` → `el-pagination` +- `scripts/replace-icon.js` — 静态 `` → `el-icon` +- `scripts/fix-el-icon-click.js` — 修复 `@click` 绑定 +- `scripts/fix-broken-tags.js` — 闭合标签修复 diff --git a/buyer/package.json b/buyer/package.json index 66dd7dba..918cee50 100644 --- a/buyer/package.json +++ b/buyer/package.json @@ -8,35 +8,37 @@ "dev": "vue-cli-service serve" }, "engines": { - "node": ">=14" + "node": ">=16" }, "dependencies": { "@amap/amap-jsapi-loader": "0.0.7", - "axios": "^0.19.2", + "@element-plus/icons-vue": "^2.3.1", + "axios": "^0.21.1", + "core-js": "^3.36.0", "dplayer": "^1.27.1", + "element-plus": "^2.6.3", "js-cookie": "^2.2.1", - "less": "^2.7.0", - "less-loader": "^5.0.0", - "mv-count-down": "^0.1.15", - "sass": "^1.63.6", - "postcss-loader": "^4.3.0", + "nprogress": "^0.2.0", "psl": "^1.8.0", "qs": "^6.9.4", + "sass": "^1.63.6", + "sass-loader": "^10.4.1", + "swiper": "^5.4.5", "uuid": "^8.3.2", - "view-design": "^4.3.2", - "vue": "^2.6.11", - "vue-awesome-swiper": "^3.1.3", - "vue-piczoom": "^1.0.6", - "vue-qr": "^2.3.0", - "vue-router": "^3.0.1", - "vuex": "^3.0.1" + "vue": "^3.4.21", + "vue-awesome-swiper": "^4.1.1", + "vue-qr": "^5.0.3", + "vue-router": "^4.3.0", + "vuex": "^4.1.0" }, "devDependencies": { - "@vue/cli-service": "~4.5.0", - "compression-webpack-plugin": "^5.0.0", - "sass-loader": "^10.4.1", - "uglifyjs-webpack-plugin": "^2.2.0", - "vue-template-compiler": "^2.6.11" + "@vue/cli-plugin-babel": "^5.0.8", + "@vue/cli-service": "^5.0.8", + "@vue/compiler-sfc": "^3.4.21", + "compression-webpack-plugin": "^10.0.0", + "less": "^4.2.0", + "less-loader": "^11.1.4", + "webpack": "^5.95.0" }, "browserslist": [ "> 1%", @@ -45,7 +47,6 @@ ], "resolutions": { "minimatch": "^3.1.2", - "node-sass": "npm:sass@^1.63.6", "@achrinza/node-ipc": "9.2.2" } } diff --git a/buyer/public/index.html b/buyer/public/index.html index 3aad3f9f..1a632be6 100644 --- a/buyer/public/index.html +++ b/buyer/public/index.html @@ -7,20 +7,12 @@ <%= htmlWebpackPlugin.options.title %> - <% for(var css of htmlWebpackPlugin.options.cdn.css) { %> - - <% } %> - <% for(var js of htmlWebpackPlugin.options.cdn.js) { %> - - <% } %> - +
- diff --git a/buyer/scripts/fix-broken-tags.js b/buyer/scripts/fix-broken-tags.js new file mode 100644 index 00000000..01a4e6f7 --- /dev/null +++ b/buyer/scripts/fix-broken-tags.js @@ -0,0 +1,54 @@ +#!/usr/bin/env node +const fs = require("fs"); +const path = require("path"); + +const ROOT = path.join(__dirname, ".."); + +function walkDir(dir, files = []) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) walkDir(full, files); + else if (entry.name.endsWith(".vue")) files.push(full); + } + return files; +} + +function fix(content) { + let c = content; + c = c.replace(/<\/Button>/g, ""); + c = c.replace(/<\/Button\b/g, "/g, ""); + c = c.replace(/<\/Input\b/g, "/g, ""); + c = c.replace(/<\/Option\b/g, "/g, ""); + c = c.replace(/<\/Checkbox\b/g, "/g, ""); + c = c.replace(/<\/Select\b/g, "/g, ""); + c = c.replace(/<\/Modal>/g, ""); + c = c.replace(/@click\.native/g, "@click"); + c = c.replace(/@on-search/g, "@keyup.enter"); + c = c.replace(/\bsearch\b(?=\s+enter-button)/g, ""); + c = c.replace(/\benter-button\b/g, ""); + c = c.replace(//g, ""); + c = c.replace(//g, ""); + c = c.replace(/type="error"/g, 'type="danger"'); + c = c.replace(//g, ""); + c = c.replace(/\{\{([^}|]+)\s*\|\s*secrecyMobile\s*\}\}/g, "{{ $filters.secrecyMobile($1) }}"); + return c; +} + +let count = 0; +for (const file of walkDir(path.join(ROOT, "src"))) { + const original = fs.readFileSync(file, "utf8"); + const fixed = fix(original); + if (fixed !== original) { + fs.writeFileSync(file, fixed); + count++; + } +} +console.log(`Fixed tags in ${count} files`); diff --git a/buyer/scripts/fix-dialog-slots.js b/buyer/scripts/fix-dialog-slots.js new file mode 100644 index 00000000..07b61055 --- /dev/null +++ b/buyer/scripts/fix-dialog-slots.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node +const fs = require("fs"); +const path = require("path"); + +const ROOT = path.join(__dirname, ".."); + +function walkDir(dir, files = []) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) walkDir(full, files); + else if (entry.name.endsWith(".vue")) files.push(full); + } + return files; +} + +function fix(content) { + let c = content; + + c = c.replace(/

/g, "