提交 75f5c3b5 编写于 作者: B BingBlog

frontend first commit

上级 f1b20155
{
"presets": [
["es2015", {"modules": false}],
"stage-0"
],
"plugins": ["transform-class-properties"]
}
node_modules/**/*
dep/**/*
test/**/*
mock/**/*
example/**/*
output/**/*
{
"files": [
"./src/**/*.san",
"./src/**/*.js",
"./template/**/*.html"
],
"eslint": {
"rules": {
"fecs-esnext-ext": [
"2",
[
"js",
"san"
]
],
"fecs-valid-jsdoc": [
"0"
]
}
},
"csshint": {},
"htmlcs": {},
"jformatter": {},
"esformatter": {},
"csscomb": {}
}
/node_modules
/dist
/.vscode
package-lock.json
/**
* frontend mock data
*
* @param {string} path request path
* @param {Object} queryParam params
* @param {Object} postParam post post params
* @return {Object}
*/
module.exports = function (path, queryParam, postParam) {
return {
// delay
_timeout: 0,
// http code status
_status: 200,
// response data
_data: {
// 0 for sucsuss, others for error
status: 0,
// error msg
msg: '',
data: ''
}
};
};
{
"name": "front",
"version": "1.0.1",
"description": "VisualDL frontend",
"author": "ecomfe",
"private": true,
"scripts": {
"release": "cross-env NODE_ENV=production node ./tool/build.js",
"build": "cross-env NODE_ENV=dev node ./tool/build.js",
"dev": "cross-env NODE_ENV=dev node tool/dev-server.js",
"deploy": "fis3 release --root=./dist/ --file=./tool/fis-conf.js",
"analyzer": "npm run release -- analyzer",
"lint": "./node_modules/fecs/bin/fecs --rule",
"precommit": "npm run lint",
"prepush": "npm run lint"
},
"engines": {
"node": ">= 6.4.0"
},
"dependencies": {
"axios": "^0.16.1",
"dom-align": "^1.5.3",
"file-saver": "^1.3.3",
"lodash": "^4.17.4",
"lodash.debounce": "^4.0.8",
"lodash.merge": "^4.6.0",
"lodash.omit": "^4.5.0",
"lodash.throttle": "^4.1.1",
"normalize.css": "^6.0.0",
"qs": "^6.5.1",
"san": "3.2.3",
"san-mui": "^1.0.4",
"san-router": "^1.1.1",
"san-store": "^1.0.1",
"san-update": "^2.1.0",
"text-encoding": "^0.6.4",
"xlsx": "^0.11.3"
},
"devDependencies": {
"autoprefixer": "^6.7.7",
"autoresponse": "^0.2.0",
"babel-core": "^6.25.0",
"babel-loader": "^6.2.7",
"babel-plugin-transform-class-properties": "^6.19.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-stage-0": "^6.16.0",
"babel-preset-stage-2": "^6.0.0",
"babel-register": "^6.0.0",
"babel-runtime": "^6.26.0",
"case-sensitive-paths-webpack-plugin": "^2.1.1",
"chalk": "^1.1.3",
"connect-history-api-fallback": "^1.1.0",
"copy-webpack-plugin": "^4.0.1",
"cross-env": "^4.0.0",
"css-loader": "^0.28.0",
"ejs-render-loader": "^1.0.0",
"empty-module": "0.0.2",
"eventsource-polyfill": "^0.9.6",
"extract-text-webpack-plugin": "^2.1.0",
"fecs": "^1.5.2",
"file-loader": "^0.11.1",
"font-awesome": "^4.7.0",
"friendly-errors-webpack-plugin": "^1.6.1",
"glob": "^7.1.1",
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.4",
"husky": "^0.14.3",
"json-loader": "^0.5.4",
"opn": "^5.1.0",
"optimize-css-assets-webpack-plugin": "^1.3.2",
"ora": "^1.1.0",
"postcss-loader": "^1.3.3",
"raw-loader": "^0.5.1",
"rider": "^2.0.0",
"san-loader": "^0.0.4",
"style-loader": "^0.16.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"url-loader": "^0.5.8",
"webpack": "^2.4.1",
"webpack-bundle-analyzer": "^2.8.2",
"webpack-dev-server": "^2.4.2",
"webpack-hot-middleware": "^2.19.1",
"webpack-merge": "^4.1.0",
"yargs": "^8.0.2"
}
}
<template>
<div id="app">
<san-appbar title="VisualDL">
<div class="user-info app-follow-list" slot="right"></div>
</san-appbar>
<div id="content-container" class="content-container">
<div id="app-content" class="app-content">
<div id="content"></div>
</div>
</div>
<div class="footer">
<div class="ftext"><a href="https://github.com/VisualDL/VisualDL" target="_blank" class="bluetext">GitHub</a></div>
<div class="ftext ftext3">VisualDL © MIT</div>
</div>
</div>
</template>
<script>
import Appbar from 'san-mui/AppBar';
import {resizeContentHeight} from './common/fun/adjustConentHeight';
export default {
components: {
'san-appbar': Appbar
},
initData() {
return {};
},
attached() {
resizeContentHeight();
}
};
</script>
<style lang="stylus">
.footer
float: left;
position:relative;
width: 100%
background: #fff
padding-top: 0
margin-top: -2px
background #fff
border solid 1px #e4e4e4
.ftext
width: 30%
float: left
text-align: center
padding: 30px 0
font-size: 14px
.ftext3
margin-left: 5%
.bluetext
text-decoration: none
color: #398bfb
</style>
const adjustConentHeight = function () {
let elContentContainer = document.getElementById('content-container');
let contentHeight = window.innerHeight - 140;
elContentContainer.style.minHeight = contentHeight + 'px';
};
const resizeContentHeightWhenScroll = function () {
window.addEventListener('resize', () => {
adjustConentHeight();
});
};
export const resizeContentHeight = function () {
adjustConentHeight();
resizeContentHeightWhenScroll();
};
import XLSX from 'xlsx';
import FileSaver from 'file-saver';
// const JSON_TO_SHEET = XLSX.utils.json_to_sheet;
const aoaToSheet = XLSX.utils.aoa_to_sheet;
const saveAs = FileSaver.saveAs;
function s2ab(s) {
if (typeof ArrayBuffer !== 'undefined') {
let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf);
for (let i = 0; i !== s.length; ++i) {
view[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}
let buf = new Array(s.length);
for (let i = 0; i !== s.length; ++i) {
buf[i] = s.charCodeAt(i) & 0xFF;
}
return buf;
}
/**
* download Excel
*
* @desc transform data like [['A', 'B', 'C'], ['1', '2', '3'],[['1-1', '2-1', '3-1']]] to xlsx and download
* @param {Array} data the data for the xlsx
* @param {string} name filename
*/
export const generateXLSXandAutoDownload = function (data, name) {
let wopts = {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
};
let ws = aoaToSheet(data);
let wb = {
SheetNames: ['Export'],
Sheets: {},
Props: {}
};
wb.Sheets.Export = ws;
let wbout = XLSX.write(wb, wopts);
saveAs(
new Blob(
[s2ab(wbout)],
{
type: 'application/octet-stream'
}
),
name + '.xlsx' || 'sheetjs.xlsx'
);
};
<template>
<article>
<h1>
welcome
</h1>
</article>
</template>
<script>
export default {
};
</script>
import {router} from 'san-router';
import HomePage from './Home';
router.add({
target: '#content',
rule: '/home',
Component: HomePage
});
import 'normalize.css/normalize.css';
import 'san-mui/index.css';
let App = require('./App');
new App({
data: {
titleName: 'VisualDL'
}
}).attach(document.getElementById('root'));
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>VisualDL</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
</head>
<body>
<div id="root"></div>
</body>
</html>
function noopReplace (val) { return val; }
function HtmlReplacePlugin(options) {
this.replacer = options.replacer || noopReplace;
}
HtmlReplacePlugin.prototype.apply = function(compiler) {
var replacer = this.replacer;
compiler.plugin('compilation', function(compilation) {
compilation.plugin('html-webpack-plugin-after-html-processing', function(htmlPluginData, callback) {
htmlPluginData.html = replacer(htmlPluginData.html, htmlPluginData);
callback(null, htmlPluginData);
});
});
};
module.exports = HtmlReplacePlugin;
const webpack = require('webpack');
const rm = require('rimraf');
const ora = require('ora');
const chalk = require('chalk');
const HtmlReplacePlugin = require('./HtmlReplacePlugin');
// env 'production'
process.env.WEBPACK_ENV = 'production';
let webpackConfig = require('./webpack.prod.config');
let spinner = ora('building for production...');
spinner.start();
let feRoots = {
'index': '/dist/'
};
webpackConfig.plugins = webpackConfig.plugins.concat([
new HtmlReplacePlugin({
replacer: function(html, opt) {
var name = opt.outputName.replace(/\.html$/, '');
var feRoot = feRoots[name];
if (feRoot) {
html = html
.replace(/href="/g, 'href="' + feRoot)
.replace(/src="/g, 'src="' + feRoot);
}
return html;
}
})
]);
rm(webpackConfig.output.path, err => {
if (err) throw err;
webpack(webpackConfig, function(err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n');
console.log(chalk.cyan(' Build complete.\n'));
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
));
})
});
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})
process.env.NODE_ENV = 'dev';
let devPort = 8999;
let opn = require('opn');
let express = require('express');
let webpack = require('webpack');
let proxyMiddleware = require('http-proxy-middleware');
let webpackConfig = require('./webpack.dev.config');
let autoresponse = require('autoresponse');
let path = require('path');
let port = devPort;
let autoOpenBrowser = false;
let app = express();
let compiler = webpack(webpackConfig);
let devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
disableHostCheck: true,
quiet: false,
noInfo: false,
stats: {
colors: true
},
headers: {'Access-Control-Allow-Origin': '*'}
});
let hotMiddleware = require('webpack-hot-middleware')(compiler, {
heartbeat: 2000
});
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({
action: 'reload'
});
cb();
});
});
// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')());
var context = [
'/example',
];
var proxypath = '';
var options = {
target: proxypath,
changeOrigin: true,
};
if (context.length) {
// app.use(proxyMiddleware(context, options));
app.use('/example', proxyMiddleware({
target: 'www.baidu.com',
changeOrigin: true,
}));
}
app.use(autoresponse({
logLevel: 'debug',
root: path.dirname(__dirname),
rules: [
{
match: '/example/:id',
method: ['get']
}
],
post: {
match: function (reqPathName) {
return !/\.(html|js|map)$/.test(reqPathName) && /^\/(api)(.*)/.test(reqPathName);
}
},
delete: {
match: function () {
return true;
}
},
get: {
match: function (reqPathName) {
return !/\.(html|js|map)$/.test(reqPathName) && /^\/(api)(.*)/.test(reqPathName);
}
}
}));
// serve webpack bundle output
app.use(devMiddleware);
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware);
let uri = 'http://localhost:' + port;
let _resolve;
let readyPromise = new Promise(resolve => {
_resolve = resolve;
});
console.log('> Starting dev server...');
devMiddleware.waitUntilValid(() => {
console.log('> Listening at ' + uri + '\n');
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri);
}
_resolve();
});
let server = app.listen(port);
module.exports = {
ready: readyPromise,
close() {
server.close();
}
};
const path = require('path');
const projectPath = path.resolve(__dirname, '..');
const HtmlWebpackPlugin = require('html-webpack-plugin');
/**
* default apps
*
* @type {Array}
*/
let defaultApps = [
{
name: 'index',
feRoot: '/dist'
}
];
/**
* get entry js file
*
* @param {Array} apps appname
* @return {string} file path
*/
function getModules(apps) {
let modules = {};
apps.forEach(function (item) {
let app = item.name;
modules[app] = path.join(projectPath, 'src/' + app + '.js');
});
return modules;
}
/**
* get HtmlWebpackPlugin
*
* @param {string} app appname
* @param {boolan} template use template
* @return {HtmlWebpackPlugin} HtmlWebpackPlugin
*/
function getTemplate(app, template) {
let templateUrl = 'template/index.html';
if (template) {
templateUrl = `ejs-render-loader!template/${template}.ejs`;
}
return new HtmlWebpackPlugin({
filename: app + '.html',
template: templateUrl
});
}
/**
* get entry config
*
* @param {string} app appname
* @param {boolan} template use template
* @return {Object} config
*/
function getEntry(app, template) {
let buildApps = defaultApps.filter(function (item) {
let name = item.name;
return name === app;
});
buildApps = buildApps.length > 0 ? buildApps : defaultApps;
return {
module: getModules(buildApps),
template: buildApps.map(item => getTemplate(item.name, template))
};
}
module.exports.get = getEntry;
module.exports.entry = defaultApps;
const webpack = require('webpack');
const path = require('path');
const rider = require('rider');
const projectPath = path.resolve(__dirname, '..');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const argv = require('yargs').argv;
const isDev = process.env.NODE_ENV === 'dev';
const entry = require('./entry');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
function getLoaders(isDev, ext) {
let arr = ['css-loader'];
if (ext) {
arr.push(ext + '-loader');
}
if (isDev) {
arr.unshift('style-loader');
return arr;
}
return ExtractTextPlugin.extract({
use: arr,
fallback: 'style-loader'
});
}
/**
* entry config
*
* @type {Object}
*/
const ENTR_CONFIG = entry.get(argv.app, argv.template);
/**
* webpack config
*
* @type {Object}
*/
const config = {
entry: ENTR_CONFIG.module,
output: {
path: path.resolve(projectPath, 'dist'),
filename: '[name].[hash].js'
},
resolve: {
alias: {
'san-mui': 'san-mui/lib',
moment: 'moment/min/moment-with-locales.min.js',
axios: 'axios/dist/axios.min.js'
},
extensions: ['.js', '.json', '.styl', '.css', '.html', '.san']
},
module: {
noParse: [
/moment-with-locales/,
/node_modules\/(san|axios)\//
],
rules: [
{
test: /\.san$/,
loader: 'san-loader',
options: {
loaders: {
stylus: getLoaders(isDev, 'stylus')
}
}
},
{
test: /\.js$/,
exclude: /node_modules/,
include: [
path.resolve(projectPath, 'src')
],
loader: 'babel-loader'
},
{
test: /\.json$/,
loader: 'json-loader'
},
{
test: /\.html/,
loader: 'html-loader',
options: {
minimize: false
}
},
{
test: /\.css$/,
use: getLoaders(isDev)
},
{
test: /\.styl$/,
use: getLoaders(isDev, 'stylus')
},
{
test: /\.(gif|png|jpe?g)$/i,
loader: 'file-loader',
options: {
name: 'images/[name].[hash].[ext]'
}
},
{
test: /\.woff2?$/,
loader: 'url-loader',
options: {
name: 'fonts/[name].[hash].[ext]',
limit: '10000',
mimetype: 'application/font-woff'
}
},
{
test: /\.(ttf|eot|svg)$/,
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash].[ext]'
}
}
]
},
plugins: [
new CaseSensitivePathsPlugin(),
new webpack.LoaderOptionsPlugin({
test: /\.(styl|san)$/,
stylus: {
default: {
use: [rider()]
}
}
})
]
};
// template config
config.plugins = config.plugins.concat(ENTR_CONFIG.template);
// analyzer
if (argv.analyzer) {
config.plugins.push(new BundleAnalyzerPlugin());
}
module.exports = config;
const webpack = require('webpack');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
let merge = require('webpack-merge');
let baseWebpackConfig = require('./webpack.config');
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./tool/dev-client'].concat(baseWebpackConfig.entry[name]);
});
/**
* dev config
*
* @type {Object}
*/
module.exports = merge(baseWebpackConfig, {
// cheap-module-eval-source-map is faster for development
devtool: '#cheap-module-eval-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': '"dev"'
}
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new FriendlyErrorsPlugin()
]
});
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const autoprefixer = require('autoprefixer');
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
const bizCss = new ExtractTextPlugin('biz.[chunkhash].css');
let merge = require('webpack-merge');
let baseWebpackConfig = require('./webpack.config');
const autoPrefixOptions = {
browsers: [
'iOS >= 7',
'Android >= 4.0',
'ExplorerMobile >= 10',
'ie >= 9'
]
};
/**
* pro config
*
* @type {Object}
*/
module.exports = merge(baseWebpackConfig, {
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': 'production'
}
}),
new webpack.LoaderOptionsPlugin({
test: /\.(styl|san)$/,
san: {
autoprefixer: autoPrefixOptions
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.[chunkhash].js',
minChunks: function (module, count) {
const resPath = module.resource;
return resPath && /\.js$/.test(resPath)
&& resPath.indexOf(
path.join(__dirname, '../node_modules')
) === 0;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
'screw_ie8': true, // no ie6/7/8
'warnings': false
},
comments: false,
sourceMap: false
}),
bizCss,
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
})
]
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册