# 目录结构

在[快速入门](#/guide/play)中，大家对框架应该有初步的印象，接下来我们了解下目录结构。  

## 目录规范
Fes.js遵循 **“约定优于配置”** 的原则，推荐的目录结构如下:
```
fes-project
├── .eslintrc.js
├── fes.config.js
├── mock.js(可选)
├── webpack.config.js(可选)
├── package.json
└── src
    ├── app.js
    ├── assets
    │   ├── images
    │   │   ├── bg.png
    │   │   └── logo.png
    │   ├── styles
    │   │   ├── main.scss
    │   │   └── variables.scss
    │   └── utils.js
    ├── components
    │   ├── fesHeader.vue
    │   ├── fesLeft.vue
    │   └── wbDate.vue
    ├── pages
    │   ├── home
    │   │   └── index.vue
    │   └── list
    │       ├── components
    │       │   └── common.vue
    │       ├── edit
    │       │   └── index.vue
    │       └── index.vue
    └── static
        └── favicon.ico
```


## fes.config.js
配置文件
```javascript
module.exports = {
    // 是否采用行内 sso 登录
    sso: false, 
    // 可选有vertical、horizontal，默认vertical
    mode: 'vertical', 
    // 可选有blue、dark，默认blue
    theme: 'blue', 
    // 项目名称
    fesName: 'xx 运营平台', 
    // 图标，可以将 favicon.ico 文件放入 src/static
    favicon: 'static/favicon.ico', 
    // 是否显示左侧区域，默认true
    FesLeft: true, 
    // 是否显示头部区域，默认为false
    FesHeader: false,
    // 是否开启路由懒加载
    lazyRouter: false,
    // 是否开启 gzip 压缩，也可以传一个 obj 声明压缩参数
    compress: false,
    // 环境变量配置, 默认使用local环境
    env: {
        // 本地开发环境
        local: {
            api: ''
        },
        // 开发环境 fes [dev/build] --env=develop
        develop: {
            api: 'http://h.adm-test.webank.io'
        },
        // 生产环境 fes [dev/build] --env=prod 
        prod: {
            api: 'http://adm.webank.io'
        }
    },
    // 配置角色，控制显示的菜单内容和路由权限，使用FesApp.setRole('unLogin')来修改当前用户的角色
    roles: {
        unLogin: ['/home'],
        service: ['/list'],
        admin: ['/list', '/api/fes']
    },
    // 数据字典
    map: {
        status: [['1', '成功'], ['2', '失败']]
    },
    // 左侧菜单配置，仅支持二级菜单
    menu: [
        {   
            // 一级菜单支持配置icon
            icon: 'static/bell.png',
            // 菜单名称
            title: '接口',
            // 菜单对应的路由
            path: '/api',
            // 子菜单
            subMenu: [
                {
                    title: 'Fes',
                    path: '/api/fes'
                },
                {
                    title: 'FesApp',
                    path: '/api/fesApp'
                }
            ]
        }
    ],
    // 国际化 默认为zh-cn
    i18n: {
        locale: 'en', 
        messages: {
            'zh-cn': {
                menu: {
                    interface: '接口'
                },
                overview: '概述',
                i18n: {
                    internationalization: '国际化，基于',
                    achieve: '实现。',
                    ui: 'UI组件'
                },
                title: '标题'
            },
            en: {
                menu: {
                    interface: 'interface'
                },
                overview: 'Overview',
                i18n: {
                    internationalization: 'internationalization，base on',
                    achieve: 'to achieve.',
                    ui: 'UI components'
                },
                title: 'title'
            }
        }
    }
};
```

## mock.js
当运行环境`--env=local`时fes.js启动mock模拟后台服务，配置如下：
```javascript
module.exports = function(cgiMock, Mock, faker) {

    var Random = Mock.Random;

    //前缀，全局（可选）
    cgiMock.prefix = '';

    // 返回一个数字
    cgiMock('/number', 123);

    // 返回一个json
    cgiMock({
        url: '/json',
        result: {
            "code": "400101",
            "msg": "不合法的请求:Missing cookie 'wb_app_id' for method parameter of type String",
            "transactionTime": "20170309171146",
            "success": false
        }
    });

    //利用mock.js 产生随机文本
    cgiMock('/text', Random.cparagraph());

    // 返回一个字符串 利用mock.js 产生随机字符
    cgiMock('/string', Mock.mock({
        "string|1-10": "★"
    }));

    // 正则匹配url, 返回一个字符串
    cgiMock(/\/abc|\/xyz/, 'regexp test!');

    // option.result 参数如果是一个函数, 可以实现自定义返回内容, 接收的参数是是经过 express 封装的 req 和 res 对象.
    cgiMock(/\/function$/, function (req, res) {
        res.send('function test');
    });

    // 返回文本 fs.readFileSync
    cgiMock('/file', cgiMock.file('./test.json'));

    // 更复杂的规则配置
    cgiMock({
        url: /\/who/,
        method: 'GET',
        result: function (req, res) {
            if (req.query.name === 'kwan') {
                res.json({'kwan': '孤独患者'});
            }
            else {
                res.send('Nooooooooooo');
            }
        },
        headers: {
            'Content-Type': 'text/plain',
            'Content-Length': '123',
            'ETag': '12345'
        },
        cookies: [
            {name: 'myname', value: 'kwan', maxAge: 900000, httpOnly: true}
        ],
        //接口随机延迟
        timeout: Mock.mock({
            "number|1000-5000": 1000
        }).number
    });

    // 转发所有请求到指定URL，可以解决跨域
    cgiMock.proxy('http://test-adm.weoa.com/xxx')

}
```
如果希望部分接口使用Mock，其他接口使用请求转发：
```js
module.exports = function(cgiMock, Mock, faker) {

    //新的接口 走数据模拟
    cgiMock('new', function(req, res){
    )}

    //老的接口直接调后台接口
    cgiMock.proxy('http://test-adm.weoa.com/xxx')
}
```

## app.js
在 `fes-core` 初始化内部逻辑后执行导出的函数，建议在`app.js`中编写全局设置，如引入其他第三方插件、注册全局事件、设置应用权限等。
```js
import Vue from 'vue';
import somePlugin from 'somePlugin'
import './assets/styles/main.scss';

Vue.use(somePlugin)

export default function () {

    setTimeout(() => {
        this.FesApp.setRole('admin', false);
    }, 1000);

    // 设置路由钩子
    this.FesApp.setBeforeRouter((from, to, next) => {
        next();
    });
    this.FesApp.setAfterRouter((route) => {
        console.log(`您浏览到了${route.path}`);
    });

    // 设置AJAX配置
    this.FesApi.option({
    });

    // 设置响应结构
    this.FesApi.setResponse({
        successCode: '0',
        codePath: 'code',
        messagePath: 'msg',
        resultPath: 'result'
    });
}
```


## src/assets
存放公共静态资源，例如样式文件、图片资源等。

## src/components
在这个目录下的*.vue组件在编译后会自动注册为全局组件，可以直接在vue单文件的`<template></template>`中使用。 

### 规则
读取文件名称作为组件名称进行注册，例如`src/components/message.vue`注册的全局组件名称为`message`，

### 特殊组件
`fesHeader.vue`      
用于自定义页面头部。参数`FesHeader`控制`fesHeader.vue` 是否显示。在`fes.config.js`中配置`FesHeader`定义全局的，也可以在页面组件中单独配置`FesHeader`。     
`fesLeft.vue`       
用于自定义菜单栏。配置参数`FesLeft`控制`fesLeft.vue` 是否显示。在`fes.config.js`中配置`FesLeft`定义全局的，也可以在页面组件中单独配置`FesLeft`。   

## src/pages
在`pages`目录下编写页面，`Fes.js`会根据`pages`目录以及文件结构自动生成[Vue-router](https://router.vuejs.org/zh-cn/)路由配置。

### 规则
```dir
pages
├── index.vue         # 根路由页面 路径 index.html#/
├── a.vue             # 路径 /a
├── b                 # 文件夹b
│   ├── index.vue     # 路径 /b
│   ├── @id.vue       # 动态路由 /b/:id
│   └── c.vue         # 路径 /b/c
└── layout.vue        # 根路由下所有page共用的外层
```
1. 如果目录下有`layout.vue`，则子目录对应的路径是当前目录对应路径的子路由。如果没有则子目录对应的路径和当前目录对应路径是平级的。
2. 带参数的路径使用`@id.vue`的形式

### 编译
编译后在`.cache`目录下生成`routeConfig.js`，内容如下：
```js
import layout from 'D:\\git\\fes-template\\src\\pages\\layout.vue';
import index from 'D:\\git\\fes-template\\src\\pages\\index.vue';
import a from 'D:\\git\\fes-template\\src\\pages\\a.vue';
import b_index from 'D:\\git\\fes-template\\src\\pages\\b\\index.vue';
import b__id from 'D:\\git\\fes-template\\src\\pages\\b\\@id.vue';
export default { 
    '/': { 
        component: layout,
        subRoutes: {
            '/' : {
                name: 'index', component: index
            },
            '/a' : {
                name: 'a', component: a
            },
            '/b' : {
                name: 'b_index', component: b_index
            },
            '/b/:id' : {
                name: 'b__id', component:  b__id
            }
        }
    }
};
```

## src/static
不需要经过webpack编译处理的静态资源，可以在页面中通过 URL(static/xxx) 直接引用。   
例如有文件`src/static/1.txt`，在页面中使用如下：
```html
<a target="_blank" href="static/1.txt">
    点击下载 1.txt
</a>
```

## webpack.config.js
自定义`webpack`配置      
`mode`是编译命令，`configs`则是编译中的默认配置
```js
const CspHtmlWebpackPlugin = require('csp-html-webpack-plugin');
module.exports = function (mode, configs, webpack) {
    if (mode === 'build') {
        return {
            plugins: [new CspHtmlWebpackPlugin({
                'base-uri': "'self'",
                'object-src': "'none'",
                'script-src': [
                    "'self'"
                ],
                'style-src': [
                    "'self'"
                ],
                'connect-src': [
                    "'self'",
                    'http://adm.webank.io'
                ],
                'img-src': [
                    'data:',
                    "'self'"
                ]
            })]
        };
    }
    return {};
};
```

## 自定义目录结构
除了上述目录结构，也可以根据自己的需要增加其他目录，但不能更改现有的目录结构。
