Commit bfe2bc7f authored by JacenShi's avatar JacenShi

项目模版初始化

parent 255d7b5b
# framework-tempalte
# 框架(基座)项目类型模版
## Project setup
![![venus-fetch](https://www.npmjs.com/package/venus-fetch)](https://img.shields.io/badge/venus--fetch-^1.1.13-green)
![![iview](https://www.iviewui.com/docs/guide/install)](https://img.shields.io/badge/iview-^3.4.2-green)
![![iview](https://www.npmjs.com/package/eslint-config-venus/v/1.0.2)](https://img.shields.io/badge/eslint--config--venus-^1.0.2-green)
基于TQ-TPL快速生成的框架(基座)项目类型模版.
## 目录
- [脚本](#脚本)
- [结构](#结构)
- [特性](#特性)
## 脚本
### 本地开发
```shell
$ npm run serve
```
npm install
### 构建打包
```shell
$ npm run framework
```
### Compiles and hot-reloads for development
### 配合云效的相关脚本
- _加入新的环境变量_
```shell
$ npm run framework -- --app_env='{"DNS_CODE": "test"}'
```
npm run serve
- _使用环境变量_
```javascript
// 网关 api 后缀根据 DNS_CODE 的值在编译时进行修改
const FORMAT_API_CODE = (s) => {
const suffix = isDev ? '_test' : `_${`${process.env.DNS_CODE}` || 'pro'}`
return `${s}${suffix}`
}
```
### Compiles and minifies for production
- _生成`nginx.conf`配置文件_
```shell
$ npm run build -- --port=8181 --root=/home/8181/mnt/dist
```
npm run build
- _配置文件效果_
```nginx
server {
listen 8181;
#证书文件
#ssl_certificate /home/8181/mnt/dist/server.pem;
#私钥文件
#ssl_certificate_key /home/8181/mnt/dist/server.pem;
#ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#ssl_ciphers HIGH:!aNULL:!MD5;
root /home/8181/mnt/dist;
location / {
try_files $uri @fallback;
}
location @fallback {
rewrite .* /index.html break;
}
location /api/ {
proxy_read_timeout 300; # Some requests take more than 30 seconds.
proxy_connect_timeout 300; # Some requests take more than 30 seconds.
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_pass http://192.168.110.187:17000/api/;
}
index index.html index.htm index.cgi index.php index.php5;
}
```
### Lints and fixes files
>__`nginx.conf`中相关代理的配置与 dev service里proxy的配置保持一致。__
### 构建分析
```shell
$ npm run framework -- --analyzer
```
npm run lint
## 结构
```shell
+-- public -------------------------------------------> 公用文件
|
+-- src ----------------------------------------------> 源码目录
| +-- assets ---------------------------------------> 全局资源目录
| | +-- css --------------------------------------> 样式资源文件
| | +-- img --------------------------------------> 图片资源文件
| | +-- font -------------------------------------> 全局字体库
| | +-- json -------------------------------------> 静态json文件
| |
| +-- components -----------------------------------> 全局组件目录
| | +-- App --------------------------------------> App 组件目录 双驼峰
| | | +-- index.vue ----------------------------> 组件主入口
| | | +-- cell ---------------------------------> 组件下子组件文件夹
| | +-- Other Component
| | +-- index.js ---------------------------------> 全局注册入口
| |
| +-- directives -----------------------------------> 全局指令目录
| | +-- resize-dom -------------------------------> 指令目录 命名采用连字符
| | | +-- index.js -----------------------------> 组件主入口
| | +-- Other Directive
| | +-- index.js ---------------------------------> 全局注册入口
| |
| +-- lib ------------------------------------------> 第三方库资源文件
| | +-- event-bus --------------------------------> 基于订阅者模式的事件总线(主要用于支撑基座-应用、应用-应用之间的数据传递)
| | +-- load-worker ------------------------------> 用于子应用资源多线程预加载的实现库
| | +-- http -------------------------------------> 请求客户端
| |
| +-- utils ----------------------------------------> 工具目录
| | +-- index.js ---------------------------------> 注册入口
| |
| +-- router ---------------------------------------> 路由配置,基座的路由基本在控制数量范围内,故采用统一配置的形式
| |
| +-- store ----------------------------------------> vuex配置
| | +-- globe ------------------------------------> 持久化缓存的全局状态目录
| | | +-- modules ------------------------------> globe/[module] 状态模块
| | | +-- index.js -----------------------------> globe 入口文件
| | +-- common -----------------------------------> 全局状态目录
| | | +-- modules ------------------------------> common/[module] 状态模块
| | | +-- index.js -----------------------------> common 入口文件
| | +-- index.js ---------------------------------> store 入口文件
| |
| +-- views ----------------------------------------> 视图目录(*框架的视图文目录不需要遵守模块化目录的结构)
| | +-- home -------------------------------------> home路由目录
| | | +-- components ---------------------------> home路由所依赖的相关组件
| | | +-- index.vue ----------------------------> home视图入口
| | +-- router-wrapper ---------------------------> 子应用加载的视图容器,不建议修改
| | +-- dev-router-wrapper -----------------------> 本地开发模式下提供的子应用联调模式视图容器
| |
| +-- app.js ---------------------------------------> app Vue实例生成,各全局组件/指令/函数注册
| +-- main.js --------------------------------------> 项目主入口
| +-- normalize.js ---------------------------------> 项目适配,主要实现各种 polyfill
| +-- pwa.js ---------------------------------------> pwa相关配置
|
+-- babel.config.js ----------------------------------> babel相关配置
+-- vue.config.js ------------------------------------> vue cli 相关配置
+-- .gitignore ---------------------------------------> git ignore 配置
+-- package.json -------------------------------------> 项目信息/版本/npm脚本/依赖/eslint配置/postcss配置/游览器适配配置
+-- README.md ----------------------------------------> 说明文档
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
## 特性
### 本地联调模式
- 主要是配合子应用类型项目的 `dev:module` 模式使用
- 子应用类型的项目在`dev:module`会开启一个热更新服务,并生成一组加密字符串
- 将字符串拷贝到框架类型项目的`/dev`路由页面中的对应输入框中,即可实现本地联调模式的建立
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
['@vue/cli-plugin-babel/preset', {
useBuiltIns: 'usage',
}],
],
plugins: [
'import-glob',
'@babel/plugin-syntax-dynamic-import',
],
}
This diff is collapsed.
......@@ -5,34 +5,59 @@
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
"lint": "vue-cli-service lint",
"framework": "vue-cli-service framework"
},
"dependencies": {
"@ourea/fetch": "^1.2.2",
"core-js": "^3.6.4",
"vue": "^2.6.11"
"normalize.css": "^8.0.1",
"offline-plugin": "^5.0.6",
"socket.io-client": "~2.2.0",
"view-design": "^4.0.0",
"vue": "^2.6.11",
"vue-router": "^3.0.2",
"vuex": "^3.0.1",
"vuex-persistedstate": "^2.5.4",
"vuex-router-sync": "^5.0.0",
"whatwg-fetch": "^3.0.0"
},
"devDependencies": {
"@ourea/eslint-config-tq": "^0.0.2",
"@vue/cli-plugin-babel": "^4.3.0",
"@vue/cli-plugin-eslint": "^4.3.0",
"@vue/cli-service": "^4.3.0",
"babel-eslint": "^10.1.0",
"babel-plugin-import-glob": "^2.0.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11"
"less": "^3.0.4",
"less-loader": "^4.1.0",
"node-sass": "^4.10.0",
"node-sass-magic-importer": "^5.2.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
"postcss-url": "^8.0.0",
"precss": "^4.0.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"vue-cli-plugin-tq-tpl": "^1.1.11",
"vue-template-compiler": "^2.6.11",
"worker-loader": "^2.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"extends": "@ourea/eslint-config-tq/vue",
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
"rules": {
"no-magic-numbers": 0,
"no-console": 0
}
},
"browserslist": [
"> 1%",
......
module.exports = {
plugins: {
'postcss-import': {},
'postcss-url': {},
autoprefixer: {},
precss: {},
},
}
{
"code": 200,
"success": true,
"message": "",
"data": {
"ssToken": "eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1c2VyIiwiYXVkIjoiYXVkaWVuY2UiLCJ0ZW5hbnRfaWQiOiIwMDAwMDAiLCJhZG1pbmlzdHJhdG9yIjp0cnVlLCJ1c2VyX2lkIjoiMTE2NDczMzM3OTY1ODk2NjI1OCIsInVzZXJfbmFtZSI6ImFkbWluNiIsIm9yZ19pZCI6IjEiLCJ0b2tlbl90eXBlIjoiYWNjZXNzX3Rva2VuIiwicm9sZV9pZHMiOltdLCJleHAiOjE1ODMyMzAzNzgsIm5iZiI6MTU4MzIyNjc3OH0.1vXyZ3EiE8xjSiYujMDGrp007weX0J7UQIkwXw4yZrA",
"tokenType": "bearer",
"refreshToken": "eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpc3N1c2VyIiwiYXVkIjoiYXVkaWVuY2UiLCJ0ZW5hbnRfaWQiOiIwMDAwMDAiLCJhZG1pbmlzdHJhdG9yIjp0cnVlLCJ1c2VyX2lkIjoiMTE2NDczMzM3OTY1ODk2NjI1OCIsInVzZXJfbmFtZSI6ImFkbWluNiIsIm9yZ19pZCI6IjEiLCJ0b2tlbl90eXBlIjoicmVmcmVzaF90b2tlbiIsInJvbGVfaWRzIjpbXSwiZXhwIjoxNTgzODMxNTc4LCJuYmYiOjE1ODMyMjY3Nzh9.zaE5MxuN6HmKnIV4e3Yj8L5mkHY71qOTO6XjYRB9M5o",
"userId": 1164733379658966300,
"userName": "admin6",
"password": "",
"orgId": 1,
"tenantId": "",
"expiresIn": 3600,
"grantType": ""
}
}
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
import Vue from 'vue'
// 第三方组件
import { sync } from 'vuex-router-sync'
import ViewUI from 'view-design'
// 文件目录
import components from './components' // 全局组件注册入口
import directives from './directives' // 全局指令注册入口
import utils from './utils' // 全局工具注册入口
// http 框架
import Http from './lib/http'
// 路由
import router from './router'
// 状态管理框架
import store from './store'
// reset css
import 'normalize.css'
import './assets/css/init.css'
// 入口组件
import App from './components/App/index.vue'
import EventBus from './lib/event-bus'
import LoadWorker from './lib/load-worker'
Vue.use(ViewUI)
Vue.use(components)
Vue.use(directives)
Vue.use(utils)
Vue.use(Http)
Vue.use(LoadWorker)
sync(store, router)
const bus = new EventBus()
const app = new Vue({
router,
store,
provide: { bus },
...App,
})
export default app
html, body{
width: 100%;
height: 100%;
}
<template>
<div class="cell simple-wrapper">
<slot />
</div>
</template>
<script>
export default {
name: 'SimpleWrapper',
}
</script>
<style lang="scss" scoped>
.simple-wrapper{
width: 100%;
height: 100%;
}
</style>
<template>
<theme-provider id="app" :theme="theme">
<i :is="layout">
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
</i>
</theme-provider>
</template>
<script>
import SimpleWrapper from './cell/simple-wrapper'
export default {
name: 'App',
components: {
SimpleWrapper,
},
data() {
return {}
},
computed: {
currentPath() {
return this.$route.path
},
theme() {
return this.$store.getters['globe/user/userTheme']
},
layout() {
const { $options: { components }} = this // 已经注册到app的layout组件
const { meta, matched: [firstMatch] } = this.$route // route携带的显示信息
const { layoutVisible = true } = meta // route携带的显示信息
const env = 'normal' // 项目环境
const [nouse, path] = this.currentPath.split('/') //eslint-disable-line
const layoutName = `${this.formatName(path)}-layout`
const defaultLayout = 'simple-wrapper'
const hasLayoutComponent = Object.keys(components)
.findIndex(key => this.formatName(components[key].name) === layoutName) >= 0
const isLayoutNotExits = !hasLayoutComponent && !(firstMatch && firstMatch.meta.layout)
if (env === 'normal') {
if (isLayoutNotExits || !path || !layoutVisible) {
return defaultLayout
} else {
return firstMatch ? firstMatch.meta.layout : layoutName
}
} else {
return null
}
},
},
methods: {
formatName(name) {
const firstIndex = 0
const deleteCount = 1
const str = name.replace(/([A-Z])/g, '-$1').toLowerCase()
return str.indexOf('-') === firstIndex ? str.substr(deleteCount) : str
},
},
}
</script>
<style lang="scss">
:global {
html,
body {
height: 100%;
overflow-y: hidden;
overflow-x: auto;
}
#app {
height: 100%;
overflow-x: auto;
}
body {
margin: 0;
// font-size: 1rem;
font-family: -apple-system, BlinkMacSystemFont, "avenir next", avenir,
helvetica, "helvetica neue", Ubuntu, "segoe ui", arial, sans-serif;
}
.page {
text-align: center;
}
code {
background-color: #f0f0f0;
padding: 3px 5px;
border-radius: 2px;
}
}
</style>
<template>
<div class="base-layout">
<Layout :style="{minHeight: '100vh'}">
<Sider collapsible :collapsed-width="78" v-model="isCollapsed">
<Menu
active-name="1-2"
theme="dark"
width="auto"
:class="menuitemClasses">
<MenuItem name="1-1" to="/home">
<Icon type="ios-home" />
<span>首页</span>
</MenuItem>
<MenuItem name="1-2" to="/dev">
<Icon type="ios-bug" />
<span>本地联调页面</span>
</MenuItem>
<MenuItem name="1-3" to="/error/404">
<Icon type="ios-warning" />
<span>404</span>
</MenuItem>
<MenuItem name="1-4" to="/error/403">
<Icon type="md-warning" />
<span>403</span>
</MenuItem>
</Menu>
</Sider>
<Layout>
<Header :style="{background: '#fff', boxShadow: '0 2px 3px 2px rgba(0,0,0,.1)'}">
<h2>TQ-TPL framework project template</h2>
</Header>
<Content :style="{padding: '16px'}">
<slot />
</Content>
</Layout>
</Layout>
</div>
</template>
<script>
export default {
name: 'BaseLayout',
data() {
return {
isCollapsed: false,
}
},
computed: {
menuitemClasses: function () {
return [
'menu-item',
this.isCollapsed ? 'collapsed-menu' : '',
]
},
},
}
</script>
<style lang="scss" scoped>
.base-layout{
width: 100%;
height: 100%;
& /deep/ .menu-item {
height: 100%;
}
& /deep/ .menu-item span{
display: inline-block;
overflow: hidden;
width: 90px;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: bottom;
transition: width .2s ease .2s;
}
& /deep/ .collapsed-menu span{
width: 0px;
transition: width .2s ease;
}
& /deep/ .menu-item i{
transform: translateX(0px);
transition: font-size .2s ease, transform .2s ease;
vertical-align: middle;
font-size: 16px;
}
& /deep/ .collapsed-menu i{
transform: translateX(5px);
transition: font-size .2s ease .2s, transform .2s ease .2s;
vertical-align: middle;
font-size: 22px;
}
& /deep/ .ivu-layout-sider-trigger i{
color: #333;
}
}
</style>
<template>
<div class="page page-404">
<img class="error-img" :src="info.img" alt="img">
<p class="error-msg">{{info.msg}}</p>
<Button v-if="info.btn" type="primary" @click="info.btn">返回</Button>
</div>
</template>
<script>
export default {
name: 'NotFound',
computed: {
info() {
const { params: { code }} = this.$route
switch (code) {
case '404':
return {
img: require('./assets/404.png'),
msg: '抱歉,你访问的页面不存在!',
btn: () => this.$router.go(-1),
}
case '403':
return {
img: require('./assets/403.png'),
msg: '抱歉,你当前权限无法访问该页面!',
btn: () => this.$router.go(-1),
}
case '500':
default:
return {
img: require('./assets/500.png'),
msg: '抱歉,系统错误,请联系管理员!',
btn: () => this.$router.go(-1),
}
}
},
},
}
</script>