前章
1.开发环境搭建
2.项目环境搭建
3.golang项目基础框架-前篇
4.golang项目基础框架-后篇
开始
本篇开始搭建管理后台的前端项目,其实纯讲前端好像没啥营养,刚好本项目是采用旧项目改造的基础框架搭建的,就讲讲改造过程中,遇到难点,以及如何处理!主要是笔者工作期间的产物,在给后端项目做前后端技术分离时,将bootstrap+jquery+adminLTE改造成vue项目。
当前项目:
https://github.com/liaz-repo/liaz-admin-web.git
先附上app成品的gitee地址(漫画/轻小说app):
https://gitee.com/liaz-app/liaz-android/releases/download/1.0.0/app-arm64-v8a-release.apk
先说一句,这后台样式可能会有大部分人熟悉,像是某某音、某某聊、某某界的运营后台;如果有幸认识它的话,贵公司有机会能尝试笔者这旧改方案哦!(改造项目已上线运行)
难点
1.jquery与vue兼容
2.adminLTE菜单加载
3.第三方插件库兼容
4.批量代码搬迁
改造过程中,第三方库的兼容问题是最大,困扰了笔者好几天,最后还好能解决。
技术架构
{
"name": "liaz-admin-web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve --mode development",
"build": "vue-cli-service build --mode production",
"lint": "vue-cli-service lint"
},
"dependencies": {
"admin-lte": "^2.3.2",
"axios": "^1.5.1",
"bootstrap": "^3.3.5",
"core-js": "^3.8.3",
"font-awesome": "^4.6.3",
"ionicons": "^2.0.1",
"jquery": "^2.2.0",
"jquery.md5": "^1.0.0",
"knockout": "^3.5.1",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"popper.js": "^1.16.1",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"sass": "^1.32.7",
"sass-loader": "^12.0.0"
}
}
涉及的第三库改造,定制化本地引入
bootstrap-combobox
bootstrap-datepicker
bootstrap-datetimepicker
bootstrap-multiselect
bootstrap-select
bootstrap-table
bootstrap-treeview
clipboard
jquery.form
jQuery-Validation-Engine
vue项目创建
vue create liaz-admin-web
手动搭建
Vue CLI v5.0.8
? Please pick a preset: (Use arrow keys)
Default ([Vue 3] babel, eslint)
Default ([Vue 2] babel, eslint)
❯ Manually select features
选择Router、Vuex、CSS Pre-processors、Linter / Formatter
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: (Press <space> to select, <a> to
toggle all, <i> to invert selection, and <enter> to proceed)
❯◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◉ Router
◉ Vuex
◉ CSS Pre-processors
◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
选vue3(Vue当然是用最新的,哈哈哈)
? Choose a version of Vue.js that you want to start the project with (Use arrow
keys)
❯ 3.x
2.x
路由的访问方式(姑且没什么讲究,就选了no)
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS
Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback
in production) (Y/n) n
less样式加载器(再新入新库时,有些需要依赖)
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS
Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback
in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported
by default):
Sass/SCSS (with dart-sass)
❯ Less
Stylus
ESLint选择推荐就好,标准会有问题(ESLint真的是个有爱有恨的角色)
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS
Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback
in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported
by default): Less
? Pick a linter / formatter config:
❯ ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS
Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback
in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported
by default): Less
? Pick a linter / formatter config: Standard
? Pick additional lint features: (Press <space> to select, <a> to toggle all,
<i> to invert selection, and <enter> to proceed)
❯◉ Lint on save
◯ Lint and fix on commit
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS
Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback
in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported
by default): Less
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files
In package.json
Vue CLI v5.0.8
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS
Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback
in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported
by default): Less
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated
config files
? Save this as a preset for future projects? (y/N) N
到这一步,vue项目已经创建好,开始旧改的艰难旅程。。。
npm安装
经过一段时间摸索,原先旧项目中有部分库在npm中可安装使用,便直接安装,但需指定版本。(因选择eslint标准配置,存在版本不兼容报错,需强制执行–force)
npm install [email protected] --force
npm install [email protected] --force
npm install [email protected] --force
npm install [email protected] --force
npm install [email protected] --force
npm install [email protected] --force
npm install [email protected] --force
npm install [email protected] --force
npm install axios --force
顺带改一下package.json执行命令
"scripts": {
"dev": "vue-cli-service serve --mode development",
"build": "vue-cli-service build --mode production",
"lint": "vue-cli-service lint"
},
jquery和adminLTE整合
在main.js导入关键依赖
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'jquery'
import 'jquery.md5'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min'
import 'font-awesome/css/font-awesome.min.css'
import 'ionicons/css/ionicons.min.css'
import 'admin-lte/dist/css/AdminLTE.min.css'
import 'admin-lte/dist/css/skins/skin-red-light.min.css'
import 'admin-lte/dist/js/app.min'
createApp(App).use(store).use(router).mount('#app')
public/index.html添加adminLTE的样式class:hold-transition skin-blue sidebar-mini
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body class="hold-transition skin-blue sidebar-mini">
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
难点一:jquery与vue兼容
第一个难点来了,vue识别不出jquery,在执行npm run dev后页面报错
index.js:235 Uncaught ReferenceError: jQuery is not defined
at eval (index.js:235:4)
at ./node_modules/jquery.md5/index.js (chunk-vendors.js:202:1)
at __webpack_require__ (app.js:313:32)
at fn (app.js:564:21)
at eval (main.js:8:68)
at ./src/main.js (app.js:74:1)
at __webpack_require__ (app.js:313:32)
at app.js:1488:109
at __webpack_require__.O (app.js:355:23)
at app.js:1489:53
需修改eslint的配置
.eslintrc.js
添加jquery:true
module.exports = {
root: true,
env: {
node: true,
jquery: true
},
extends: [
'plugin:vue/vue3-essential',
'@vue/standard'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
vue.config.js
添加webpack插件,指定全局jquery变量
const { defineConfig } = require('@vue/cli-service')
var webpack = require('webpack')
module.exports = defineConfig({
transpileDependencies: true,
chainWebpack: config => {
config
.plugin('html')
.tap(args => {
args[0].title = '管理后台'
return args
})
},
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'windows.jQuery': 'jquery',
Popper: ['popper.js', 'default']
}),
],
},
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {
'/': {
ws: false,
target: process.env.VUE_APP_API_BASE_URL,
changeOrigin: true,
pathRewrite: {
'^/': ''
}
}
},
},
})
到此,jquery与vue的兼容问题解决。
难点二:adminLTE加载菜单
adminLTE是个模版框架,并没有提供兼容vue写法,只能自己想办法处理。
笔者想到利用vue的component标签,通过切换组件名称,实现页面切换,因此需要将所有的vue页面预先加载到vue中。
<component :is="componentName"></component>
components.js
export default {
install: function (Vue) {
const files = require.context('@/views', true, /\.vue$/);
let components = {};
files.keys().forEach(key => {
components[key.replace(/(\.\/|\.vue)/g, '')] = files(key).default;
});
Object.keys(components).forEach(item => {
if (components[item].name) {
Vue.component(components[item].name, components[item]);
}
});
},
}
在main.js导入
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'jquery'
import 'jquery.md5'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min'
import 'font-awesome/css/font-awesome.min.css'
import 'ionicons/css/ionicons.min.css'
import 'admin-lte/dist/css/AdminLTE.min.css'
import 'admin-lte/dist/css/skins/skin-red-light.min.css'
import 'admin-lte/dist/js/app.min'
import components from '@/utils/components'
createApp(App).use(store).use(router).use(components).mount('#app')
也同时将adminLTE提供的模版页面(node_modules/admin-lte/index.html),拆解成三部分
footer
<template>
<footer class="main-footer">
<div class="pull-right hidden-xs">
<b>Version</b> 2.0.0
</div>
<strong>Copyright © 2024 <a href="">LIAZ</a>.</strong> All rights
reserved.
</footer>
</template>
<script>
export default {
name: 'FooterView',
components: {
}
}
</script>
<style scoped>
</style>
header
<template>
<!-- Main Header -->
<header class="main-header">
<!-- Logo -->
<a class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><b>L</b>Z</span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"><b>LIAZ</b>管理系统</span>
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top" role="navigation">
<!-- Sidebar toggle button-->
<a class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<!-- Navbar Right Menu -->
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a class="dropdown-toggle" data-toggle="dropdown" style="height:50px;">
<img :src="avatar" class="user-image" :alt="name">
<span class="hidden-xs"><span :text="name">{{ name }}</span></span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img :src="avatar" class="img-circle" :alt="name" />
<p>
<span :text="name">{{ name }}</span>
<small>上次登录:<span :text="lastTime">{{ lastTime }}</span></small>
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-right">
<a @click="logout" class="btn btn-default btn-flat" id="signOut">退出</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img :src="avatar" class="img-circle" :alt="name">
</div>
<div class="pull-left info">
<p><span :text="name">{{ name }}</span></p>
<a><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
<ul class="sidebar-menu" id="mainMenu">
<li class="header">MAIN NAVIGATION</li>
</ul>
</section>
<!-- /.sidebar -->
</aside>
</template>
<script>
import store from '@/store';
export default {
name: 'HeaderView',
data() {
return {
name: "",
avatar: "",
lastTime: ""
};
},
created() {
this.name = store.getters.name;
this.avatar = store.getters.avatar;
this.lastTime = store.getters.lastTime;
},
methods: {
logout() {
store.dispatch('logout');
}
}
}
</script>
<style scoped></style>
maintainer
这部分是重点,将菜单加载改成使用component标签切换,点击菜单时修改局部变量componentName,实现vue组件切换。
<template>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar" style="height: 100%; overflow: hidden; overflow: scroll;">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel">
<div class="pull-left image">
<img :src="avatar" class="img-circle" :alt="name">
</div>
<div class="pull-left info">
<p>{{ name }}</p>
<!-- Status -->
<a href="#"><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
<!-- search form (Optional) -->
<form method="get" class="sidebar-form" onsubmit="return false;">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search..." @input="search">
<span class="input-group-btn">
<button name="search" id="search-btn" class="btn btn-flat" @click="search">
<i class="fa fa-search"></i>
</button>
</span>
</div>
</form>
<!-- /.search form -->
<!-- Sidebar Menu -->
<ul class="sidebar-menu">
<li class="header">主导航</li>
<!-- Optionally, you can add icons to the links -->
<li v-for="(parent, parentIndex) in parentMenus" :key="parent" :data-index="parentIndex" class="treeview">
<a>
<i :class="[parent.icon ? parent.icon : 'fa fa-link']"></i>
<span>{{ parent.name }}</span>
<span class="label pull-right bg-yellow" :id="['pid_' + parent.menuId]">
{{ getChildLength(parent.menuId) }}
</span>
</a>
<ul class="treeview-menu" :id="['cid_' + parent.menuId]">
<li v-for="(child, childIndex) in getChilds(parent.menuId)" :key="child" :data-index="childIndex">
<a :data-url="child.path" @click="handleClick(child)">
<i
:class="[child.icon && child.icon != '' ? child.icon : 'fa fa-circle-o text-yellow']"></i>
<span>{{ child.name }}</span>
</a>
</li>
</ul>
</li>
</ul>
<!-- /.sidebar-menu -->
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper" style="height: 100%;">
<!-- Content Header (Page header) -->
<section class="content-header"
:style="[childMenu.name && childMenu.name != '' ? 'display:block;' : 'display:none;']">
<h1>
{{ childMenu.name }}
<small>{{ childMenu.description }}</small>
</h1>
<ol class="breadcrumb">
<li><a><i class="fa fa-dashboard"></i> {{ childMenu.parentName }}</a></li>
<li class="active">{{ childMenu.name }}</li>
</ol>
</section>
<!-- Main content -->
<section class="content" style="height: 100%; overflow: hidden; overflow: scroll;">
<!-- Your Page Content Here -->
<component :is="componentName"></component>
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
<div class="modal fade" id="tipModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">提示信息</h4>
</div>
<div class="modal-body" id="tipMsg"></div>
</div>
</div>
</div>
<div class="modal fade" id="confirmModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel"
data-backdrop="static">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">确认信息</h4>
</div>
<div class="modal-body" id="confirmMsg"></div>
</div>
</div>
</div>
<div class="modal fade loading" style="background: #00000040;" tabindex="-1" role="dialog"
aria-labelledby="loadingModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="loadingGif" style="position: fixed;"><img :src="loading"></div>
</div>
</template>
<script>
import store from '@/store';
import { getStore } from '@/utils/store';
import loading from '@/assets/images/loading.gif';
export default {
name: 'MaintainerView',
data() {
return {
componentName: "",
name: "",
avatar: "",
parentMenus: [],
childMenus: [],
childMenu: {
name: "",
parentName: "",
description: "",
}
};
},
created() {
this.getMenu();
this.name = store.getters.name;
this.avatar = store.getters.avatar;
},
methods: {
getMenu() {
this.parentMenus = getStore({ name: 'parent_menus' });
this.childMenus = getStore({ name: 'child_menus' });
store.dispatch('getMenu').then(res => {
console.log(res);
this.parentMenus = getStore({ name: 'parent_menus' });
this.childMenus = getStore({ name: 'child_menus' });
});
},
getChilds(parentId) {
return this.childMenus.filter(v => v.parentId == parentId);
},
getChildLength(parentId) {
return this.childMenus.filter(v => v.parentId == parentId).length;
},
handleClick(menu) {
this.childMenu.name = menu.name;
this.childMenu.parentName = this.parentMenus.filter(v => v.menuId == menu.parentId)[0].name;
this.childMenu.description = menu.description;
store.dispatch('getViewComponent', menu.path).then(componentName => {
console.log(componentName);
this.componentName = componentName;
});
},
search() {
let text = $("input[type='text']").val();
this.childMenus = store.getters.childMenus.filter(v => v.name.indexOf(text) >= 0);
if (!this.childMenus.length || this.childMenus.length == 0) {
this.parentMenus = store.getters.parentMenus.filter(v => v.name.indexOf(text) >= 0);
} else {
let parentIds = this.childMenus.map(v => v.parentid);
let parentMenus = store.getters.parentMenus.filter(v => v.name.indexOf(text) >= 0);
if (parentMenus && parentMenus.length > 0) {
parentMenus.forEach(v => {
parentIds.push(v.id);
});
}
this.parentMenus = store.getters.parentMenus.filter(v1 => parentIds.filter(v2 => v1.id == v2).length > 0);
}
}
},
}
/**
* 扩展date函数
* author:c3gen
*/
Date.prototype.format = function (format) {
var o = {
"M+": this.getMonth() + 1,
"d+": this.getDate(),
"h+": this.getHours(),
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"q+": Math.floor((this.getMonth() + 3) / 3),
"S": this.getMilliseconds()
}
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
}
</script>
<style scoped>
@import '@/css/main.css';
</style>
难点三:第三方库改造
旧项目中使用的第三方插件库,大部分都因年代久远,失去维护;在baidu与google上都找不到相同的源代码,巨头疼。
尝试以下几个方案:
一、npm安装接近版本或类似的库,因旧代码报错,无法兼容(失败)
二、直接复制源代码过来,在main.js导入,代码无法加载,都报undefined(失败)
最后经过好几天摸索,发现以下两种方式可解决。
1.插件库改造
对插件库代码进行改造,套上nodejs的加载结构,导入便可自动执行,主要都是jquery的插件库。
(function (factory) {
var define;
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports === 'object') {
factory(require('jquery'));
} else {
factory(jQuery);
}
}(function ($, obj = undefined) {
// 此次放原先插件库的代码
...
}));
在main.js导入
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import 'jquery'
import 'jquery.md5'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min'
import 'font-awesome/css/font-awesome.min.css'
import 'ionicons/css/ionicons.min.css'
import 'admin-lte/dist/css/AdminLTE.min.css'
import 'admin-lte/dist/css/skins/skin-red-light.min.css'
import 'admin-lte/dist/js/app.min'
import '@/assets/plugins/bootstrap-table/css/bootstrap-table.css'
import '@/assets/plugins/bootstrap-table/js/bootstrap-table'
import '@/assets/plugins/bootstrap-table/js/locale/bootstrap-table-zh-CN'
import '@/assets/plugins/bootstrap-table/js/extensions/editable/bootstrap-table-editable'
import '@/assets/plugins/jquery/jquery.form'
import '@/assets/plugins/bootstrap-datepicker/css/datepicker3.css'
import '@/assets/plugins/bootstrap-datepicker/js/bootstrap-datepicker'
import '@/assets/plugins/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css'
import '@/assets/plugins/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min'
import '@/assets/plugins/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN'
import '@/assets/plugins/bootstrap-combobox/css/bootstrap-combobox.css'
import '@/assets/plugins/bootstrap-combobox/js/bootstrap-combobox'
import '@/assets/plugins/bootstrap-select/css/bootstrap-select.css'
import '@/assets/plugins/bootstrap-select/js/bootstrap-select'
import '@/assets/plugins/bootstrap-multiselect/css/bootstrap-multiselect.css'
import '@/assets/plugins/bootstrap-multiselect/js/bootstrap-multiselect'
import '@/assets/plugins/jQuery-Validation-Engine/css/validationEngine.jquery.css'
import '@/assets/plugins/jQuery-Validation-Engine/js/jquery.validationEngine'
import '@/assets/plugins/jQuery-Validation-Engine/js/languages/jquery.validationEngine-zh_CN'
import '@/assets/plugins/bootstrap-treeview/css/bootstrap-treeview.min.css'
import '@/assets/plugins/bootstrap-treeview/js/bootstrap-treeview.min'
import components from '@/utils/components'
createApp(App).use(store).use(router).use(components).mount('#app')
.eslintrc.js
还需关闭eslint一些校验,旧代码不规范,直接启动不了,害
module.exports = {
root: true,
env: {
node: true,
jquery: true
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-unused-vars': 'off',
'no-useless-escape': 'off',
'no-control-regex': 'off',
}
}
2.public/index.html导入
在index.html内通过
难点三:登录后的样式污染
目前就这个问题,笔者这三脚猫的前端技术,实在无法攻克;只能通过一种取巧的办法,先跳转到个空白页面,再判断是否链接跳转来源方式,从而重定向回首页。
views/login/index.vue
登录函数代码
export default {
name: "LoginView",
created() {
this.init();
this.bubble();
},
methods: {
bubble() {
var $ul = $('<ul id="bubble-wrapper"/>');
for (var i = 0; i < 10; i++) {
$ul.append($('<li/>'));
}
$("body").append($ul);
},
init() {
this.$nextTick(function () {
let $this = this;
$("#loginBtn").click(function () {
$this.login();
});
});
},
login() {
let username = $("#username").val();
let password = $("#password").val();
if (username.trim() == '') {
$(".loginTips").html("账号不能为空!");
return;
} else if (password.trim() == '') {
$(".loginTips").html("密码不能为空!");
return;
}
login({
'username': username,
'password': $.md5(password),
}).then(res => {
if (res.code == global.HTTP_STATUS.OK) {
console.log("login success.");
this.storeAccessToken(res.data.accessToken);
this.getSysConfs();
} else {
$(".loginTips").html(res.message).css("padding", "3px 5px");
}
});
},
storeAccessToken(accessToken) {
setStore({
name: "access_token",
content: accessToken,
type: "session"
});
},
getSysConfs() {
store.dispatch('getSysConfs').then(res => {
console.log(res);
this.updateUser();
});
},
updateUser() {
store.dispatch('getUser').then(res => {
console.log(res);
window.location.href = '#/blank';
});
},
},
};
BlankView.vue
<template>
<div></div>
</template>
<script>
import router from '@/router';
export default {
name: 'BlankView',
created() {
//处理css样式污染问题
if (window.performance.navigation.type == window.performance.navigation.TYPE_RELOAD) {
router.push('/home');
} else {
router.go(0);
}
}
}
</script>
<style scoped></style>
难点四:代码迁移
以下附上JAVA批量处理脚本,将原项目中的所有页面批量转化为vue页面。
import java.io.*;
public class VueScriptTest {
public static void main(String[] args) {
String sourcePath = "/html";
String targetPath = "/views";
File sourceDirectory = new File(sourcePath);
File targetDirectory = new File(targetPath);
if (!targetDirectory.exists()) {
boolean isMkdir = targetDirectory.mkdirs();
}
processFiles(sourceDirectory, targetDirectory);
}
private static void processFiles(File sourceDirectory, File targetDirectory) {
File[] files = sourceDirectory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
File newTargetDirectory = new File(targetDirectory, file.getName());
boolean isMkdir = newTargetDirectory.mkdirs();
processFiles(file, newTargetDirectory);
} else if (file.getName().endsWith(".html")) {
try {
String newFileNameBase = toCamelCase(file.getName().replace(".html", "")).trim();
String componentName = upperFirst(newFileNameBase) + "View";
BufferedReader reader = new BufferedReader(new FileReader(file));
StringBuilder content = new StringBuilder();
StringBuilder styleContent = new StringBuilder();
StringBuilder scriptContent = new StringBuilder();
boolean inStyleTag = false;
boolean inScriptTag = false;
String line;
while ((line = reader.readLine()) != null) {
if (isNonEmptyNonCommentLine(line)) {
if (line.trim().startsWith("<style>")) {
inStyleTag = true;
line = line.replace("<style>", "<style scoped>");
}
if (line.trim().startsWith("<script>")) {
inScriptTag = true;
}
if (inStyleTag) {
styleContent.append(line).append("\n");
} else if (inScriptTag) {
if (line.trim().startsWith("<script>")) {
scriptContent.append(line).append("\n");
scriptContent.append("export default {\n" + " name: \"").append(componentName).append("\",\n").append(" setup() {},\n").append(" created() {\n").append(" this.$nextTick(function () {\n").append(" this.initData();\n").append(" });\n").append(" },\n").append(" methods: {\n").append(" initData() {\n");
} else {
if (line.contains(".bootstrapTable") && line.contains("destroy")) {
scriptContent.append(line).append("\n");
scriptContent.append("// 清空分页组件的容器\n");
scriptContent.append("$('.fixed-table-pagination').empty();\n");
} else {
if (line.trim().contains("</script>")) {
scriptContent.append(" }\n" + " }\n" + "};\n");
}
scriptContent.append(line).append("\n");
}
}
} else {
// 添加import语句
if (line.contains("<script src=\"/static/js/admin/style.js\"></script>")) {
// 在scriptContent中找到<script>的位置,下一行插入import语句
int scriptIndex = scriptContent.indexOf("<script>");
if (scriptIndex != -1) {
scriptContent.insert(scriptIndex + "<script>".length(), "\nimport '@/utils/style.js';");
}
} else {
content.append(line).append("\n");
}
}
} else {
content.append(line).append("\n");
}
if (inStyleTag && line.trim().endsWith("</style>")) {
inStyleTag = false;
}
if (inScriptTag && line.trim().endsWith("</script>")) {
inScriptTag = false;
}
}
reader.close();
content.insert(0, "<template>\n");
content.append("</template>\n");
content = new StringBuilder(content.toString().replace("type=\"date\"", ""));
// 添加import语句
if (scriptContent.toString().contains("TableHelper")) {
// 在scriptContent中找到<script>的位置,下一行插入import语句
int scriptIndex = scriptContent.indexOf("<script>");
if (scriptIndex != -1) {
scriptContent.insert(scriptIndex + "<script>".length(), "\nimport TableHelper from '@/utils/bootstrap-table-helper';");
}
}
content.append(scriptContent).append("\n");
content.append(styleContent);
String newFileName = upperFirst(newFileNameBase) + "View.vue";
File newFile = new File(targetDirectory, newFileName);
FileWriter writer = new FileWriter(newFile);
writer.write(content.toString());
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
private static boolean isNonEmptyNonCommentLine(String line) {
return !line.trim().isEmpty() && !line.trim().startsWith("<!--");
}
private static String toCamelCase(String fileName) {
StringBuilder result = new StringBuilder();
boolean nextUpperCase = false;
for (char c : fileName.toCharArray()) {
if (c == '-' || c == '_') {
nextUpperCase = true;
} else {
if (nextUpperCase) {
result.append(Character.toUpperCase(c));
nextUpperCase = false;
} else {
result.append(Character.toLowerCase(c));
}
}
}
return result.toString();
}
private static String upperFirst(String name) {
char[] ch = name.toCharArray();
ch[0] -= 32;
return new String(ch);
}
}
总结
整个项目改造真的细节很多,写可能很大部分没写全,文章前面已经放上git地址,大伙可下载代码,自行参透。
标签:vue,进化史,name,APP,bootstrap,js,css,import,服务端 From: https://blog.csdn.net/qq_37450508/article/details/137193899