Authored by biao

init for blk project

root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
... ...
**/bundle/**/*.js
**/dist/**/*.js
coverage
... ...
{
"extends": "yoho",
"parserOptions": {
"sourceType": "module"
}
}
... ...
# Created by https://www.gitignore.io/api/node,webstorm,netbeans,sublimetext,vim
### Node ###
# Logs
!logs/README.md
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### WebStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
### NetBeans ###
nbproject/
build/
nbbuild/
dist/
nbdist/
nbactions.xml
.nb-gradle/
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
### Vim ###
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
### YOHO ###
dist
public/css/*
public/bundle/*
.eslintcache
*.log.*
.vscode/
... ...
**/css/**/*.css
**/dist/**/*.css
... ...
{
"extends": "stylelint-config-yoho"
}
... ...
# 基于Express的有货前端开发框架
## 规约
### js模块命令规范
* JS按大模块放置,比如:home或者guang
* 页面加载的JS使用`*.page.js`, 且page的JS只能放在模块下的一级目录
* 非页面加载的JS直接`*.js`
### 页面加载静态资源规范
* 传模块名`module`和页面名`page`,即模块的目录名和当前页面的JS文件名的前缀,以加载对应页面的JS
\ No newline at end of file
... ...
/**
* yohobuy app
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/4/25
*/
'use strict';
const config = require('./config/common');
// use one apm
if (config.useOneapm) {
require('oneapm');
}
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const favicon = require('serve-favicon');
const session = require('express-session');
const memcached = require('connect-memcached');
const hbs = require('express-handlebars');
const pkg = require('./package.json');
const yohoLib = require('yoho-node-lib');
const app = express();
const MemcachedStore = memcached(session);
// 全局注册library
yohoLib.global(config);
global.middleware = path.resolve('./doraemon/middleware');
global.utils = path.resolve('./utils');
// 向模板注入变量
app.locals.devEnv = app.get('env') === 'development';
app.locals.version = pkg.version;
app.set('view engine', '.hbs');
app.set('views', './doraemon/views');
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: './doraemon/views',
partialsDir: './doraemon/views/partial',
helpers: global.yoho.helpers
}));
app.use(favicon(path.join(__dirname, '/public/favicon.ico')));
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(session({
proxy: true,
resave: false,
saveUninitialized: true,
unset: 'destroy',
secret: '82dd7e724f2c6870472c89dfa43cf48d',
name: 'yohoblk-session',
cookie: {
domain: 'yohoblk.com',
httpOnly: false
},
store: new MemcachedStore({
hosts: config.memcache.session,
prefix: 'yohoblk_session:'
})
}));
app.use((req, res, next) => {
req.user = {}; // 全局的用户数据
req.yoho = {}; // req和res绑定yoho对象,用于传递全局数据, 如req.yoho.channel等
next();
});
const logger = global.yoho.logger;
// dispatcher
try {
const mobileCheck = require('./doraemon/middleware/mobile-check');
const user = require('./doraemon/middleware/user');
const seo = require('./doraemon/middleware/seo');
const setYohoData = require('./doraemon/middleware/set-yoho-data');
const errorHanlder = require('./doraemon/middleware/error-handler');
const setPageInfo = require('./doraemon/middleware/set-pageinfo');
// YOHO 前置中间件
app.use(mobileCheck());
app.use(setYohoData());
app.use(user());
app.use(seo());
app.use(setPageInfo());
require('./dispatch')(app);
app.all('*', errorHanlder.notFound()); // 404
// YOHO 后置中间件
app.use(errorHanlder.serverError());
} catch (err) {
logger.error(err);
}
// listener
app.listen(config.port, function() {
logger.info('yohobuy start');
});
... ...
# sub app
## 构建方法
* `npm i -g generator-subapp`
* `cd apps`
* `yo subapp`
... ...
/**
* 系统配置
*
* @author hbomb qiqi.zhou@yoho.cn
* @date 2016/05/06
*/
const isProduction = process.env.NODE_ENV === 'production';
const isTest = process.env.NODE_ENV === 'test';
module.exports = {
port: 6003,
siteUrl: 'http://www.yohoblkcom',
domains: {
api: 'http://devapi.yoho.cn:58078/', // devapi.yoho.cn:58078 testapi.yoho.cn:28078 devapi.yoho.cn:58078
service: 'http://devservice.yoho.cn:28077/', // testservice.yoho.cn:28077 devservice.yoho.cn:58077
search: 'http://192.168.102.216:8080/yohosearch/'
},
useOneapm: false,
useCache: false,
memcache: {
master: ['192.168.102.163:11213'],
slave: ['192.168.102.163:11213'],
session: ['192.168.102.161:11213'],
timeout: 1000,
retries: 0
},
loggers: {
infoFile: {
name: 'info',
level: 'info',
filename: 'logs/info.log'
},
errorFile: {
name: 'error',
level: 'error',
filename: 'logs/error.log',
handleExceptions: true
},
udp: { // send by udp
level: 'debug', // logger level
host: '192.168.102.162', // influxdb host
port: '4444' // influxdb port
},
console: {
level: 'debug',
colorize: 'all',
prettyPrint: true
}
},
thirdLogin: {
wechat: {
appID: 'wx3ae21dcbb82ad672',
appSecret: 'e78afb2321e6a19085767e1a0f0d52c1'
}
}
};
if (isProduction) {
Object.assign(module.exports, {
appName: 'www.yohoblk.com',
domains: {
api: 'http://api.yoho.yohoops.org/',
service: 'http://service.yoho.yohoops.org/'
},
memcache: {
master: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111', 'memcache3.yohoops.org:12111'],
slave: ['memcache1.yohoops.org:12112', 'memcache2.yohoops.org:12112', 'memcache3.yohoops.org:12112'],
session: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111', 'memcache3.yohoops.org:12111'],
timeout: 3000
},
useOneapm: true,
useCache: true
});
} else if (isTest) {
Object.assign(module.exports, {
appName: 'www.yohoblk.com for test',
domains: {
api: 'http://testapi.yoho.cn:28078/',
service: 'http://testservice.yoho.cn:28077/',
search: 'http://192.168.102.216:8080/yohosearch/'
},
useOneapm: true,
useCache: true,
memcache: {
master: ['127.0.0.1:12111'],
slave: ['127.0.0.1:12112'],
session: ['127.0.0.1:12111'],
timeout: 1000,
retries: 0
}
});
}
... ...
/**
* 路由分发
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/4/27
*/
module.exports = app => {
// 公共服务
// 业务模块
};
... ...
# 代码规范说明文档
开发前请务必仔细阅读,遵守规范,保持团队代码风格统一
## 文件命名
* 中划线分隔小写单词
* Ex: `your-file`
## 缩进
* 统一4个Space
* 建议将编辑器Tab映射成4个Space
## 注释
* 为每个你创建的JS文件添加注释
```
/**
* 对文件实现功能的描述
* @date: 2016-11-11
* @author: name<emial@yoho.cn>
*/
```
* 为重要的函数添加注释
```
/**
* 对函数功能的说明
* @params name paramType 参数描述
* @return name returnType 返回值描述
*/
```
* 为重要的代码、逻辑复杂的代码或者有特殊处理的代码添加注释
```
// Your comments for the code
```
* 减少不必要的注释
类似于:**进入循环****循环结束**等垃圾话的注释请谨慎添加,大家都是程序员,不用你注释也能知道的
## html
* 见名知意,不要有1,2,3这种名字出现
* class、id等属性命名为中划线分隔小写单词
* html中请不要出现不必要的嵌套以及不要将标签滥用,比如使用`a`标签作为不跳转的按钮的标签
* 属性按顺序出现:`id -> class -> name -> data-* -> src,for,type,href -> title,alt -> aria-*,role`
## js
[Link](http://git.yoho.cn/fe/yohobuywap-node/blob/develop/doc/code-norm/js.md)
## css
[Link](http://git.yoho.cn/fe/yohobuywap-node/blob/develop/doc/code-norm/css.md)
\ No newline at end of file
... ...
# doraemon for everything
## middleware
## views contains **layout、header and footer** partial
\ No newline at end of file
... ...
/**
* 登录判断
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/4/25
*/
'use strict';
module.exports = (req, res, next) => {
next();
};
... ...
/**
* 404 错误
* @return {[type]}
*/
const headerModel = require('../models/header');
const logger = global.yoho.logger;
exports.notFound = () => {
return (req, res, next) => {
if (req.xhr) {
return res.status(404).json({
code: 404,
message: '抱歉,页面不存在!'
});
}
headerModel.requestHeaderData().then((result) => {
return res.render('error/404', {
module: 'common',
page: 'error',
title: '页面不存在 | Yoho!Buy有货 | 潮流购物逛不停',
headerData: result.headerData
});
}).catch(next);
};
};
/**
* 服务器错误
* @return {[type]}
*/
exports.serverError = () => {
return (err, req, res, next) => {
logger.error(`error at path: ${req.url}`);
logger.error(err);
if (!res.headersSent) {
if (req.xhr) {
return res.status(500).json({
code: 500,
message: '服务器错误!'
});
}
const renderErrPage = (result) => {
res.render('error/500', {
module: 'common',
page: 'error',
err: err,
title: '服务器错误 | Yoho!Buy有货 | 潮流购物逛不停',
headerData: result.headerData
});
};
return headerModel.requestHeaderData().then(renderErrPage).catch(() => {
renderErrPage();
});
}
next(err);
};
};
... ...
/**
* 手机访问检测
*/
'use strict';
module.exports = () => {
return (req, res, next) => {
let isMobile = /(nokia|iphone|android|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\.browser|up\.link|htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|jig\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\s*mobi|opera\*mini|320x320|240x320|176x220)/i.test(req.get('user-agent')); // eslint-disable-line
if (isMobile) {
return res.redirect(`//m.yohobuy.com${req.url}`);
}
next();
};
};
... ...
'use strict';
// 页面seo相关属性的配置文件
const seoMap = require('./seoConfig');
/**
* 设置seo相关的数据,包括title, keywords, description
* @param {undefined}
* @return {Function} 中间件函数,用于给res.locals对象添加属性
*/
module.exports = () => {
return (req, res, next) => {
if (!req.xhr) {
Object.assign(res.locals, seoMap[req.path] || seoMap['/']);
}
next();
};
};
... ...
const seoMap = {
/* eslint-disable */
'/': {
title: 'YOHO!有货 | 年轻人潮流购物中心,中国潮流购物风向标,官方授权正品保证',
keywords: 'Yoho! 有货官网,潮流志,潮流男装,潮牌,美国潮牌,日本潮牌,香港潮牌,潮牌店,新品首发,欧美潮流,全球购,代购,时尚,流行,特卖,B2C,正品,购物网站,网上购物,货到付款',
description: 'YOHO! 有货,年轻人潮流购物中心,中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。作为YOHO!旗下的购物平台,汇集了全球潮流时尚商品和中国最流行的商品,也是国内最大的原创文化商品平台,也是香港,台湾地区流行商品的集中地。同时包含日本、韩国等众多国外潮流品牌,带给您全新潮流购物体验。'
},
'/woman': {
title: '女生|时尚潮流女装,日韩女装,潮牌女装全球购|YOHO!BUY有货 100%正品保证',
keywords: '女生服饰,时尚潮流女装,日韩女装,女装正品购物网站,女装全球购',
description: 'YOHO!BUY有货官网女生频道汇集了全球女装潮流时尚,提供时尚潮流女装,日版女装,韩版女装,潮牌女装正品全球购。YOHO!BUY有货购物100%正品保证,支持货到付款。'
},
'/kids': {
title: '潮童|男童装,女童装,韩版童装,儿童服装服饰|YOHO!BUY有货 100%正品保证',
keywords: '潮童,男童装,女童装,韩版童装,儿童服装服饰',
description: 'YOHO!BUY有货官网潮童频道汇集了全球潮童潮流时尚,提供新款男童装,女童装,韩版童装,儿童服装服饰正品全球购。YOHO!BUY有货购物100%正品保证,支持货到付款。'
},
'/lifestyle': {
title: '创意生活|创意生活馆,潮流创意家居,家居生活用品|YOHO!BUY有货 100%正品保证',
keywords: '创意生活,创意生活馆,潮流家居,潮流创意家居,家居生活用品,YOHO!有货',
description: 'YOHO!BUY有货官网创意生活频道汇集了创意生活馆,潮流创意家居,家居生活用品等正品网购,给您的生活带来更多创意。YOHO!BUY有货购物100%正品保证,支持货到付款。'
}
/* eslint-enable */
};
module.exports = seoMap;
... ...
/**
* 设置页面的module,page默认值
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/6/22
*/
'use strict';
module.exports = () => {
return (req, res, next) => {
if (!req.xhr) {
const arr = req.path.substring(1).split('/');
Object.assign(res.locals, {
module: arr[0],
page: arr[1]
});
}
next();
};
};
... ...
/**
* 设置 YOHO 数据
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/6/16
*/
'use strict';
module.exports = () => {
return (req, res, next) => {
let yoho = {
pageChannel: {}
};
const channel = req.query.channel || req.cookies._Channel || 'boys';
yoho.pageChannel[channel] = true;
yoho.isApp = req.query.app_version || req.query.appVersion;
Object.assign(res.locals, yoho);
Object.assign(req.yoho, yoho);
next();
};
};
... ...
const _ = require('lodash');
const cookie = global.yoho.cookie;
module.exports = () => {
return (req, res, next) => {
// 从 SESSION 中获取到当前登录用户的 UID
if (req.session && _.isNumber(req.session._LOGIN_UID)) {
req.user.uid = req.session._LOGIN_UID;
}
// session 没有读取到的时候,从 cookie 读取 UID
if (!req.user.uid && req.cookies._UID) {
req.user.uid = cookie.getUid(req);
}
next();
};
};
... ...
/**
* 通用路由处理
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/4/28
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
// const cRoot = './controllers';
module.exports = router;
... ...
<div class="wrapper screen">
<div class="clear wrapper-404">
<div class="left"><img src="http://static.yohobuy.com/images/v3/index/404.png"></div>
<div class="right right-tips">
<p class="text1">很抱歉,您访问的页面不存在!</p>
<p class="text2">
</p>
<p class="text3"><a href="http://www.yohobuy.com/product/new" class="button">浏览新品</a>&nbsp;&nbsp;<a href="http://www.yohobuy.com/" class="button">返回首页</a></p>
</div>
</div>
</div>
\ No newline at end of file
... ...
<div class="wrapper screen">
<div class="clear wrapper-404">
<div class="left"><img src="http://static.yohobuy.com/images/v3/index/404.png"></div>
<div class="right right-tips">
<p class="text1">服务器错误!</p>
<p class="text2">
</p>
<p class="text3"><a href="http://www.yohobuy.com/product/new" class="button">浏览新品</a>&nbsp;&nbsp;<a href="http://www.yohobuy.com/" class="button">返回首页</a></p>
</div>
</div>
</div>
\ No newline at end of file
... ...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<meta name="keywords" content="{{keywords}}">
<meta name="description" content="{{description}}">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta http-equiv="cleartype" content="on">
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta content="telephone=no" name="format-detection" />
<meta content="email=no" name="format-detection" />
<link rel="dns-prefetch" href="//cdn.yoho.cn">
<link rel="dns-prefetch" href="//static.yohobuy.com">
<link rel="dns-prefetch" href="//img12.static.yhbimg.com">
<link rel="dns-prefetch" href="//img13.static.yhbimg.com">
{{#if devEnv}}
<link rel="stylesheet" href="//localhost:5002/css/index.css">
{{^}}
<link rel="stylesheet" href="//cdn.yoho.cn/yohobuy-node/{{version}}/index.css">
{{/if}}
</head>
<body>
{{> header}}
{{#if pageErr}}
{{> 404}}
{{^}}
{{{body}}}
{{/if}}
{{> footer}}
{{#if devEnv}}
<script src="//localhost:5002/libs.js"></script>
<script src="//localhost:5002/{{module}}.{{page}}.js"></script>
{{^}}
<script src="//cdn.yoho.cn/yohobuy-node/{{version}}/libs.js"></script>
<script src="//cdn.yoho.cn/yohobuy-node/{{version}}/{{module}}.{{page}}.js"></script>
{{/if}}
</body>
</html>
... ...
const shelljs = require('shelljs');
const path = require('path');
const changeFiles = {
js: shelljs.exec('git diff --cached --name-only --diff-filter=ACM | grep .js$', {silent: true}).stdout,
css: shelljs.exec('git diff --cached --name-only --diff-filter=ACM | grep .css$', {silent: true}).stdout
};
const lintPath = {
js: path.resolve('./node_modules/.bin/eslint'),
css: path.resolve('./node_modules/.bin/stylelint')
};
const lintResult = {
js: {},
css: {}
};
const ext = process.platform === 'win32' ? '.cmd' : ''; // Windows 平台需要加后缀
// 在执行检查脚本的时候,不显示 NPM 错误日志
if (!shelljs.grep('npm run -s', path.resolve('./.git/hooks/pre-commit')).stdout.trim()) {
shelljs.sed('-i', 'npm run', 'npm run -s', path.resolve('./.git/hooks/pre-commit'));
}
if (changeFiles.js) {
changeFiles.js = changeFiles.js.replace(/\n/g, ' ');
lintResult.js = shelljs.exec(`${lintPath.js}${ext} -c .eslintrc --cache --fix ${changeFiles.js}`);
}
if (changeFiles.css) {
changeFiles.css = changeFiles.css.replace(/\n/g, ' ');
lintResult.css = shelljs.exec(`${lintPath.css}${ext} --config .stylelintrc ${changeFiles.css}`);
}
if (lintResult.js.code || lintResult.css.code) {
process.exit(lintResult.js.code || lintResult.css.code); // eslint-disable-line
}
... ...
/**
* OneAPM agent configuration
*/
const commonConfig = require('./config/common');
exports.config = {
app_name: [commonConfig.appName],
license_key: 'BwEGA1dRDlQ6357HHQ1AD1xJVkbc9fNfWRtQUwhQG41c5QFWGFIDSQoHc0e8AgMaUlcUVw0=',
logging: {
level: 'info'
},
transaction_events: {
enabled: true
}
};
... ...
{
"name": "yoho-blk",
"version": "0.0.1",
"private": true,
"description": "A New Yohoblk Project With Express",
"repository": {
"type": "git",
"url": "http://git.yoho.cn/fe/yoho-blk.git"
},
"scripts": {
"start": "node app.js",
"dev": "nodemon -e js,hbs -i public/ app.js",
"online": "NODE_ENV=\"production\" node app.js",
"debug": "DEBUG=\"express:*\" nodemon -e js,hbs -i public/ app.js",
"lint-js": "eslint -c .eslintrc --cache --fix .",
"lint-css": "stylelint --config .stylelintrc public/scss/**/*.css",
"precommit": "node lint.js",
"test": "NODE_ENV=test nyc ./node_modules/.bin/ava",
"posttest": "nyc report --reporter=html"
},
"ava": {
"tap": true,
"require": [
"babel-register"
],
"babel": {
"presets": [
"es2015"
]
}
},
"license": "MIT",
"dependencies": {
"bluebird": "^3.4.0",
"body-parser": "^1.15.0",
"connect-memcached": "^0.2.0",
"cookie-parser": "^1.4.3",
"express": "^4.13.1",
"express-handlebars": "^3.0.0",
"express-session": "^1.13.0",
"influxdb-winston": "^1.0.1",
"lodash": "^4.13.1",
"md5": "^2.1.0",
"memcached": "^2.2.2",
"moment": "^2.13.0",
"morgan": "^1.7.0",
"oneapm": "^1.2.20",
"passport": "^0.3.2",
"passport-weixin": "^0.1.0",
"request-promise": "^3.0.0",
"serve-favicon": "^2.3.0",
"uuid": "^2.0.2",
"winston": "^2.2.0",
"winston-daily-rotate-file": "^1.1.4",
"yoho-node-lib": "0.0.5"
},
"devDependencies": {
"autoprefixer": "^6.3.6",
"ava": "^0.15.2",
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.9.0",
"eslint": "^2.12.0",
"eslint-config-yoho": "^1.0.1",
"gulp": "^3.9.1",
"gulp-cssnano": "^2.1.2",
"gulp-ftp": "^1.1.0",
"gulp-postcss": "^6.1.0",
"gulp-sourcemaps": "^2.0.0-alpha",
"gulp-util": "^3.0.7",
"husky": "^0.11.4",
"nodemon": "1.9.2",
"nyc": "^6.6.1",
"postcss-assets": "^4.0.1",
"postcss-cachebuster": "^0.1.3",
"postcss-calc": "^5.2.1",
"postcss-center": "^1.0.0",
"postcss-clearfix": "^1.0.0",
"postcss-crip": "^2.0.0",
"postcss-opacity": "^3.0.0",
"postcss-position": "^0.5.0",
"postcss-pxtorem": "^3.3.1",
"postcss-short": "^1.4.0",
"postcss-sprites": "^3.1.2",
"postcss-use": "^2.0.2",
"precss": "^1.4.0",
"rewire": "^2.5.1",
"shelljs": "^0.7.0",
"stylelint": "^6.6.0",
"stylelint-config-yoho": "^1.2.4",
"webpack": "^1.13.1",
"webpack-dev-server": "^1.14.1",
"webpack-stream": "^3.1.0",
"yoho-handlebars": "^4.0.5",
"yoho-jquery": "^1.12.4",
"yoho-jquery-lazyload": "^1.9.7",
"yoho-slider": "0.0.2"
}
}
... ...
{
"apps": [
{
"name": "yohoblk-node",
"script": "app.js",
"instances": "max",
"exec_mode": "cluster",
"env": {
"PORT": 6003
}
}
]
}
\ No newline at end of file
... ...
No preview for this file type
No preview for this file type
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>
Created by FontForge 20120731 at Tue Jun 7 15:10:09 2016
By admin
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
panose-1="2 0 6 3 0 0 0 0 0 0"
ascent="896"
descent="-128"
x-height="792"
bbox="0 -212 1158 896"
underline-thickness="50"
underline-position="-100"
unicode-range="U+0078-E619"
/>
<missing-glyph horiz-adv-x="374"
d="M34 0v682h272v-682h-272zM68 34h204v614h-204v-614z" />
<glyph glyph-name=".notdef" horiz-adv-x="374"
d="M34 0v682h272v-682h-272zM68 34h204v614h-204v-614z" />
<glyph glyph-name=".null" horiz-adv-x="0"
/>
<glyph glyph-name="nonmarkingreturn" horiz-adv-x="341"
/>
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="uniE600" unicode="&#xe600;" horiz-adv-x="1048"
d="M832 -126.5q0 -35.5 -25 -60.5t-60.5 -25t-60.5 25t-25 60.5t25 60.5t60.5 25t60.5 -25t25 -60.5zM533 -126.5q0 -35.5 -25 -60.5t-60 -25t-60 25t-25 60.5t25 60.5t60 25t60 -25t25 -60.5zM277 620l-35 159q-3 14 -15 23.5t-27 9.5h-147q-22 0 -37.5 -15.5t-15.5 -37.5
t15.5 -38t37.5 -16h54l157 -627q6 -25 25.5 -40t44.5 -15h527q25 0 44.5 15t25.5 40l113 452q9 34 -13 62t-57 28h-697z" />
<glyph glyph-name="uniE601" unicode="&#xe601;"
d="M505 337l2 -2q2 -1 3.5 -1t3.5 1l430 364q2 2 1 5.5t-5 3.5h-435h-424q-4 0 -5 -3.5t1 -5.5zM72 669q-3 2 -6 0.5t-3 -4.5v-584q0 -4 3.5 -5t5.5 1l288 346zM953 669.5q-3 1.5 -5 -0.5l-288 -246l287 -346q3 -2 6 -1t3 5v584q0 3 -3 4.5zM641 406l-131 -111l-5 5
l-125 103l-275 -328q-2 -3 -1 -6t5 -3h396h407q4 0 5 3t-1 6z" />
<glyph glyph-name="uniE602" unicode="&#xe602;" horiz-adv-x="1048"
d="M297.5 528q-20.5 0 -35 -14.5t-14.5 -35t14.5 -35.5t35 -15t35.5 15t15 35.5t-15 35t-35.5 14.5zM381 251q0 96 84 164t202 68t202 -68t84 -163.5t-84 -163.5t-202 -68t-202 68t-84 163zM286 251q0 -17 2 -35v1q-88 42 -140.5 114t-52.5 157t51.5 157t139.5 114t192 42
q142 0 249.5 -76.5t128.5 -189.5q-88 43 -189 43q-104 0 -191.5 -43.5t-138.5 -119t-51 -164.5zM953 36q95 93 95 215t-94 214q2 20 2 23q0 111 -64 205t-174.5 148.5t-240 54.5t-239.5 -54.5t-174 -148.5t-64 -205q0 -78 33 -148.5t93 -125.5l-77 -123q-8 -12 -6.5 -26
t10.5 -25q13 -15 32 -15q9 0 18 4l180 80q4 2 7 4q20 -7 39 -12q48 -80 138.5 -128t199.5 -48q75 0 145 25q1 -1 2 -1l140 -62q8 -4 17 -4q20 0 32 15q10 10 11 24t-7 26zM527 282q-16 0 -27.5 -11t-11.5 -27t11.5 -27.5t27.5 -11.5t27.5 11.5t11.5 27.5t-11.5 27t-27.5 11z
M667 282q-16 0 -27.5 -11t-11.5 -27t11.5 -27.5t27.5 -11.5t27.5 11.5t11.5 27.5t-11.5 27t-27.5 11zM806 282q-16 0 -27 -11t-11 -27t11 -27.5t27 -11.5t27.5 11.5t11.5 27.5t-11.5 27t-27.5 11z" />
<glyph glyph-name="uniE603" unicode="&#xe603;" horiz-adv-x="1158"
d="M1069 181h-245v378h132l113 -169v-209zM1158 417l-155 231h-268v-467h-45v508q0 20 -14 34t-34 14h-63l-67 -89h89v-467h-512v467h45l22 89h-108q-20 0 -34 -14t-14 -34v-549q0 -20 14 -34t34 -14h139q-33 -37 -33 -87q0 -53 37.5 -91t91 -38t91.5 38t38 91q0 50 -34 87
h264h191q-34 -37 -34 -87q0 -53 38 -91t91.5 -38t91 38t37.5 91q0 50 -33 87h134v325zM326 668q-89 -153 -94 -296v-12h129v12q0 43 17 112q17 68 39 116q27 61 67.5 118t62.5 79l4 3v96h-390l-2 -114h245q-33 -40 -78 -114z" />
<glyph glyph-name="uniE604" unicode="&#xe604;"
d="M875 126l-363 -164l-363 164v610q247 75 363 75t363 -75v-610zM930 808q-34 11 -84.5 26t-159.5 38.5t-174 23.5t-174 -23.5t-159.5 -38.5t-84.5 -26q-14 -4 -22 -15.5t-8 -25.5v-669q0 -27 25 -39l405 -183q9 -3 18 -3t18 3l405 183q25 12 25 39v669q0 14 -8 25.5
t-22 15.5zM751 552v83h-473v-83h206v-298h-72v237h-87v-237h-66v-84h506v84h-193v119h151v83h-151v96h179z" />
<glyph glyph-name="uniE605" unicode="&#xe605;"
d="M903 577l-68 69l-388 -388l-231 230l-68 -68l299 -298l65 65z" />
<glyph glyph-name="uniE606" unicode="&#xe606;"
d="M512 599q47 0 88 -18t72 -49t49 -72t18 -89q0 -46 -18 -87t-49 -72t-72 -49t-88 -18t-88 18t-72 49t-49 72t-18 87q0 48 18 89t49 72t72 49t88 18v0zM512 599z" />
<glyph glyph-name="uniE607" unicode="&#xe607;"
d="M797 219.5q0 -7.5 -5 -13.5l-29 -28q-6 -6 -13.5 -6t-12.5 6l-225 224l-224 -224q-6 -6 -13.5 -6t-13.5 6l-28 28q-6 6 -6 13.5t6 13.5l266 266q6 6 13.5 6t12.5 -6l267 -266q5 -6 5 -13.5z" />
<glyph glyph-name="uniE608" unicode="&#xe608;"
d="M284 15q-14 -14 -14 -33t13.5 -32.5t32.5 -13.5t32 14l397 401q13 14 13 33t-13 33l-397 401q-13 14 -32 14t-32.5 -13.5t-13.5 -32.5t13 -33l351 -369l-350 -369v0zM284 15z" />
<glyph glyph-name="uniE609" unicode="&#xe609;"
d="M745 753q13 14 13 33t-13.5 32.5t-32.5 13.5t-32 -14l-396 -401q-14 -14 -14 -33t14 -33l396 -401q14 -14 32.5 -14t32 13.5t13.5 32.5t-13 33l-351 369l351 369v0zM745 753z" />
<glyph glyph-name="uniE60A" unicode="&#xe60a;"
d="M512.5 750q-12.5 0 -21.5 -9t-9 -22v-311h-249q-12 0 -21.5 -9t-9.5 -22t9.5 -22t21.5 -9h280q12 0 21.5 9t9.5 22v342q0 13 -9.5 22t-22 9zM513 874q-101 0 -193 -39.5t-158.5 -105.5t-106 -158t-39.5 -193t39.5 -193t106 -158.5t158.5 -105.5t193 -39t192.5 39
t158 105.5t106 158.5t39.5 193t-39.5 193t-106 158t-158 105.5t-192.5 39.5zM513 -56q-118 0 -218 58t-158.5 158t-58.5 218t58.5 218t158.5 158t218 58t218 -58t158 -158t58 -218t-58 -218t-158 -158t-218 -58z" />
<glyph glyph-name="uniE60B" unicode="&#xe60b;"
d="M286 509l-46 -45l272 -272l272 272l-46 45l-226 -226z" />
<glyph glyph-name="uniE60C" unicode="&#xe60c;"
d="M387 158l45 -46l272 272l-272 272l-45 -46l226 -226z" />
<glyph glyph-name="uniE60D" unicode="&#xe60d;"
d="M766 193l-196 192l196 192l-61 60l-196 -193l-196 193l-60 -60l196 -192l-196 -192l60 -59l196 192l196 -192z" />
<glyph glyph-name="uniE60E" unicode="&#xe60e;"
d="M704 665l-41 39l-343 -320l343 -320l41 39l-301 281z" />
<glyph glyph-name="uniE60F" unicode="&#xe60f;"
d="M892 125l-236 236q54 77 54 172q0 124 -87 211t-210.5 87t-211 -87t-87.5 -211t87.5 -211t210.5 -87q95 0 173 55l236 -236q16 -17 38 -18t37 14t14 37t-18 38zM213 533q0 82 58.5 140.5t140.5 58.5t140.5 -58.5t58.5 -140.5t-58.5 -140.5t-140.5 -58.5t-140.5 58.5
t-58.5 140.5z" />
<glyph glyph-name="uniE610" unicode="&#xe610;"
d="M512 77l-439 614h878z" />
<glyph glyph-name="uniE611" unicode="&#xe611;" horiz-adv-x="1173"
d="M569 607q-28 66 -74 108.5t-95 56.5t-100 2.5t-91.5 -45t-66 -94t-26.5 -137.5q0 -36 8.5 -67.5t26 -58t35.5 -47t46.5 -41t49 -34t52.5 -32.5t48 -30q71 -47 124.5 -106.5t63.5 -93.5q7 31 62.5 92t124.5 110q19 14 55.5 36.5t61 38.5t55 42.5t49 52t31.5 62.5t13 79
q0 76 -26.5 136t-67 92t-92 42.5t-101 -4t-94.5 -56t-72 -104.5z" />
<glyph glyph-name="uniE612" unicode="&#xe612;"
d="M0 896h1024v-1024h-1024v1024zM85 -43h854v854h-854v-854z" />
<glyph glyph-name="uniE613" unicode="&#xe613;"
d="M1024 -128h-1024v1024h1024v-1024zM947 674l-34 34q-13 13 -30 13t-30 -13l-486 -495l-196 205q-13 13 -30 13t-30 -13l-34 -34q-13 -13 -13 -30t13 -30l256 -256q23 -22 51 -8q9 0 9 8l546 546q18 13 20 30.5t-12 29.5z" />
<glyph glyph-name="uniE614" unicode="&#xe614;"
d="M160 996zM865 247q0 -18 -13 -31l-308 -308q-13 -13 -31 -13t-31 13l-309 308q-13 13 -13 31t13.5 31t30.5 13h617q18 0 31 -13t13 -31zM865 511q0 -18 -13 -31t-31 -13h-617q-18 0 -31 13t-13 31t13 31l309 309q13 13 31 13t31 -13l308 -309q13 -13 13 -31z" />
<glyph glyph-name="uniE615" unicode="&#xe615;"
d="M951 77h-878l439 614z" />
<glyph glyph-name="uniE616" unicode="&#xe616;"
d="M877 896h-730q-61 0 -104 -43t-43 -104v-730q0 -61 43 -104t104 -43h730q61 0 104 43t43 104v730q0 61 -43 104t-104 43zM939 19q0 -26 -18 -44t-44 -18h-730q-26 0 -44 18t-18 44v730q0 26 18 44t44 18h730q26 0 44 -18t18 -44v-730z" />
<glyph glyph-name="uniE617" unicode="&#xe617;"
d="M939 -128h-854q-35 0 -60 25t-25 60v854q0 35 25 60t60 25h854q35 0 60 -25t25 -60v-854q0 -35 -25 -60t-60 -25zM939 789q0 10 -6 16t-16 6h-810q-10 0 -16 -6t-6 -16v-810q0 -10 6 -16t16 -6h810q10 0 16 6t6 16v810zM457 183q-10 -12 -30.5 -12t-29.5 12l-171 171
q-13 10 -13 30q0 18 12.5 30.5t30.5 12.5q20 0 30 -13l141 -141l311 312q13 12 30 12q18 0 30.5 -12.5t12.5 -29.5q0 -21 -13 -30z" />
<glyph glyph-name="uniE618" unicode="&#xe618;" horiz-adv-x="1000"
d="M662 662v-499l-299 250zM662 662z" />
<glyph glyph-name="uniE619" unicode="&#xe619;"
d="M384 640v-513l321 257z" />
</font>
</defs></svg>
... ...
No preview for this file type
No preview for this file type
/**
* Yohobuy 构建脚本
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/4/25
*/
'use strict';
const gulp = require('gulp');
const gutil = require('gulp-util');
const ftp = require('gulp-ftp');
const postcss = require('gulp-postcss');
const sourcemaps = require('gulp-sourcemaps');
const cssnano = require('gulp-cssnano');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const webpackConfig = require('./webpack.config.js');
const env = {
dev: Symbol('development'),
pro: Symbol('production')
};
const config = require('../package.json');
const ftpConfig = {
host: '218.94.75.58',
user: 'php',
pass: 'yoho9646'
};
const distRoot = `dist/${config.name}`;
const dist = {
js: `${distRoot}/${config.version}`,
css: `${distRoot}/${config.version}`,
assets: `${distRoot}/assets`,
img: `${distRoot}/assets/img`,
font: `${distRoot}/assets/font`
};
/**
* postcss plugins for both dev and pro
* @parem et Symbol
*/
const postcssPlugin = (et) => {
var sprites = {
spritesmith: {
padding: 2
},
groupBy(file) {
var group = file.url.split('/')[1];
group = group === '' ? 'yo' : group;
return group ? Promise.resolve(group) : Promise.reject(group);
}
},
assets,
plugins;
// assets & sprites config in both dev and pro
if (et === env.pro) {
assets = {
loadPaths: [dist.img, dist.font],
relativeTo: dist.css
};
Object.assign(sprites, {
basePath: dist.img,
stylesheetPath: dist.css,
spritePath: dist.img
});
} else if (et === env.dev) {
assets = {
loadPaths: ['img/', 'font/'],
relativeTo: 'css/'
};
Object.assign(sprites, {
basePath: 'img/',
stylesheetPath: 'css/',
spritePath: 'img/'
});
}
plugins = [
require('autoprefixer')({
browsers: ['> 1%']
}),
require('precss'),
require('postcss-sprites').default(sprites),
require('postcss-assets')(assets),
require('postcss-calc'),
require('postcss-opacity'),
// 可选
require('postcss-use')({
modules: ['postcss-clearfix', 'postcss-crip', 'postcss-short', 'postcss-center', 'postcss-position']
})
];
if (et === env.pro) {
plugins.push(require('postcss-cachebuster')({
imagesPath: `/${dist.img}`,
cssPath: `/${dist.css}`
}));
}
return plugins;
};
// default
gulp.task('default', ['postcss-dev', 'postcss-watch', 'webpack-dev-server']);
// ge
gulp.task('ge', ['postcss', 'webpack']);
// dist
gulp.task('dist', ['ge'], () => {
var ftpstream = ftp(ftpConfig);
return gulp.src('dist/**/')
.pipe(ftpstream)
.pipe(gutil.noop());
});
// postcss compile in dev
gulp.task('postcss-dev', () => {
return gulp.src('scss/index.css')
.pipe(sourcemaps.init())
.pipe(postcss(postcssPlugin(env.dev)))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('css/'));
});
// postcss file watch
gulp.task('postcss-watch', () => {
gulp.watch('scss/**/*.css', ['postcss-dev']);
});
// copy assets
gulp.task('assets', ['img', 'font']);
// copy img
gulp.task('img', () => {
return gulp.src('img/**/*')
.pipe(gulp.dest(dist.img));
});
// copy font
gulp.task('font', () => {
return gulp.src('font/*')
.pipe(gulp.dest(dist.font));
});
// postcss compile in pro
gulp.task('postcss', ['assets'], () => {
return gulp.src('scss/index.css')
.pipe(postcss(postcssPlugin(env.pro)))
.pipe(cssnano())
.pipe(gulp.dest(dist.css));
});
// webpack dev server
gulp.task('webpack-dev-server', () => {
var devConfig = Object.assign({}, webpackConfig, {
debug: true
});
new WebpackDevServer(webpack(devConfig), {
contentBase: '.',
publicPath: '//localhost:5003/',
hot: true,
stats: {
colors: true
},
headers: {
'Access-Control-Allow-Origin': '*'
}
}).listen(5003, '0.0.0.0', (err) => {
if (err) {
throw new gutil.PluginError('webpack-dev-server', err);
}
gutil.log('[webpack-serve]', 'http://localhost:5003/');
});
});
// webpack compile in pro
gulp.task('webpack', (done) => {
var proConfig = Object.assign({}, webpackConfig);
proConfig.output.path = dist.js;
proConfig.plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}));
webpack(proConfig, (err, stats) => {
if (err) {
throw new gutil.PluginError('webpack', err);
}
gutil.log('[webpack compile]:', stats.endTime - stats.startTime, 'ms');
done();
});
});
... ...
var Slide = require('./yohoui/YH.slide');
var $ = require('yoho-jquery');
var $contain = $('.slide-accordion');
var $item = $contain.find('li');
var width = $item.width(),
spn = parseInt($('.home-page').width(), 10) === 1150 ? (120 + 5) : (102 + 5),
slide;
function switchfun(to) {
$item.each(function(index) {
$(this).css('zIndex', index);
if (index <= to) {
$(this).stop().animate({
left: index * spn
}, 400);
} else {
$(this).stop().animate({
left: (to) * spn + width + spn * (index - to - 1)
}, 400);
}
});
}
switchfun(0);
slide = new Slide({
length: 5,
loop: false,
auto: false,
timeout: 2,
index: 0
});
slide.on('change', function(data) {
switchfun(data.to);
});
$item.mouseover(function() {
slide.go($(this).index());
});
slide.init();
... ...
/**
* 弹框公共组件
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/2/24
*/
var $ = require('yoho-jquery'),
Handlebars = require('yoho-handlebars');
var defaultOptions = {
mask: true,
closeIcon: true
};
var tpl =
'<div class="yoho-dialog {{className}} hide">' +
'{{#if closeIcon}}' +
'<span class="close">' +
'<i class="iconfont">&#xe602;</i>' +
'</span>' +
'{{/if}}' +
'<div class="content">' +
'{{{content}}}' +
'</div>' +
'<div class="btns">' +
'{{# btns}}' +
'<span {{#if id}}id="dialog-{{id}}"{{/if}} class="btn{{#each btnClass}} {{.}}{{/each}}">' +
'{{name}}' +
'</span>' +
'{{/ btns}}' +
'</div>' +
'</div>';
var tplFn = Handlebars.compile(tpl);
// 背景蒙版
function createMask() {
if ($('.body-mask').length === 0) {
$('body').append('<div class="body-mask hide"></div>');
}
return $('.body-mask').css({
height: $(document).height(),
width: $(document).width()
});
}
function createDialog(data) {
$('body').append(tplFn(data));
return $('.yoho-dialog');
}
function Dialog(options) {
var opt = $.extend({}, defaultOptions, options);
var that = this,
i;
// 实现继承时,只返回实例,不生成html
if (opt.inherit) {
return this;
}
if (opt.mask) {
that.$mask = createMask();
}
that.$el = createDialog(opt);
// 绑定x关闭事件
that.$el.find('.close').click(function() {
that.close();
});
function bindBtnEvt(index) {
that.$el.find('#dialog-' + opt.btns[index].id).on('click', function() {
opt.btns[index].cb && opt.btns[index].cb();
});
}
// 绑定按钮事件
if (opt.btns) {
for (i = 0; i < opt.btns.length; i++) {
bindBtnEvt(i);
}
}
}
Dialog.prototype.close = function() {
this.$mask && this.$mask.addClass('hide');
this.$el.remove();
};
Dialog.prototype.show = function() {
this.$mask && this.$mask.removeClass('hide');
this.$el.removeClass('hide').css({
'margin-top': -this.$el.height() / 2,
'margin-left': -this.$el.width() / 2
});
};
exports.Dialog = Dialog;
// Alert
function Alert(content) {
var that = this;
var option = {
content: content,
className: 'alert-dialog',
btns: [
{
id: 'alert-sure',
btnClass: ['alert-sure'],
name: '确定',
cb: function() {
that.close();
}
}
]
};
Dialog.call(this, option);
}
Alert.prototype = new Dialog({
inherit: true
});
Alert.prototype.constructor = Alert;
exports.Alert = Alert;
// Confirm
function Confirm(opt) {
var that = this;
var option = {
content: opt.content,
className: 'confirm-dialog',
btns: [
{
id: 'confirm-sure',
btnClass: ['confirm-sure'],
name: '确定',
cb: opt.cb
},
{
id: 'confirm-cancel',
btnClass: ['confirm-cancel'],
name: '取消',
cb: function() {
that.close();
}
}
]
};
Dialog.call(this, option);
}
Confirm.prototype = new Dialog({
inherit: true
});
Confirm.prototype.constructor = Confirm;
exports.Confirm = Confirm;
... ...
/**
* 商品筛选逻辑
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2015/12/4
*/
var $ = require('yoho-jquery'),
Handlebars = require('yoho-handlebars');
var checkUnicode = {
unchecked: '&#xe612;',
checked: '&#xe613;'
},
moreUnicode = {
up: '&#xe607;',
down: '&#xe60b;'
};
// 品牌相关变量
var $brandDefault = $('.brand .default'),
$brandInput = $('#brand-search-input'),
$brandPanel = $('.brand .brand-panel'),
$brandAttrs = $brandPanel.find('.attr'),
$brandMore = $('#brand-more'),
$brandMulti = $('#brand-multi'),
$brandsIndex = $('.brands-index');
var $brandMoreTxt, $brandMoreIcon;
// 价格相关变量
var $udPrice = $('.ud-price-range'),
interReg = /^\d+$/,
$limit, $min, $max, $btn;
// 分类相关变量
var $sortSub = $('.sort-sub-wrap');
// 高级选项相关变量
var $seniorSubWrap = $('.senior-sub-wrap'),
$seniorAttrWrap = $('.senior-attr-wrap');
var seniorHoverTime, hoveredIndex;
// 尺寸 handlebars 模板
var sizeTmpl = '{{# size}}<a class="attr {{#if checked}}checked{{/if}}" href="{{href}}">{{name}}</a>{{/ size}}',
sizeCache = {}, // 缓存一下尺寸信息
$sizeWrap = $('.filter-box .size');
sizeTmpl = Handlebars.compile(sizeTmpl);
// 清除checkbox选中状态[页面默认checked不清空]
function clearChecked($checkbox, $checked) {
$checkbox.each(function() {
if ($(this).closest('.attr').hasClass('checked')) {
return;
}
$(this).removeClass('checked').html(checkUnicode.unchecked);
});
$checked.find('.checkbox').addClass('checked').html(checkUnicode.checked);
}
// 显示更多品牌面板
function brandShowMore() {
$brandDefault.addClass('hide');
$brandPanel.removeClass('hide');
}
// 隐藏更多品牌面板
function brandHideMore() {
$brandPanel.addClass('hide');
$brandDefault.removeClass('hide');
// 清除索引状态
$brandsIndex.children('span').removeClass('hover');
}
// url构造&跳转
function uriLoc(attr, val) {
var qs = window.queryString(),
newHref;
qs[attr] = val;
delete qs.page;
newHref = '?' + decodeURIComponent($.param(qs));
window.location.href = newHref;
}
// 隐藏高级选项面板
function hideSeniorPanel() {
$seniorSubWrap.children('.senior-sub:eq(' + hoveredIndex + ')').addClass('hide');
$seniorAttrWrap.children('.attr:eq(' + hoveredIndex + ')').removeClass('hover');
hoveredIndex = -1;
}
// 屏蔽筛选项双击文字选中
$('.filter-box').on('selectstart', '.attr, .brands-index span', function() {
return false;
});
// 【分类】
$('.sort-pre').on('click', 'li', function() {
var $this = $(this),
index = $this.index(),
id = $this.data('id');
if ($this.hasClass('active')) {
// 选中时,再次点击取消选中
$this.removeClass('active');
$sortSub.children(':eq(' + index + ')').addClass('hide');
$sizeWrap.addClass('hide');
return;
}
$this.siblings('.active').removeClass('active');
$this.addClass('active');
$sortSub.children(':not(.hide)').addClass('hide');
$sortSub.children(':eq(' + index + ')').removeClass('hide');
if (!$sizeWrap.data('load')) {
if (sizeCache[id]) {
$sizeWrap.find('.attr-content').html(sizeCache[id]);
$sizeWrap.removeClass('hide');
} else {
$.ajax({
url: '/product/search/sortSize',
data: {
msort: id
}
}).then(function(res) {
if ($.type(res) === 'array' && res.length) {
sizeCache[id] = sizeTmpl({
size: res
});
$sizeWrap.find('.attr-content').html(sizeCache[id]);
$sizeWrap.removeClass('hide');
} else {
$sizeWrap.addClass('hide');
}
});
}
}
});
// 【品牌】
if ($brandMore.length > 0) {
$brandMoreTxt = $brandMore.children('em');
$brandMoreIcon = $brandMore.children('.iconfont');
}
// 【品牌】多选
$brandMulti.click(function() {
if ($brandPanel.css('display') === 'none') {
// 显示品牌面板
$brandMore.trigger('click');
}
brandShowMore();
$brandPanel.removeClass('hide').addClass('multi'); // 显示出checkbox
$(this).addClass('hide');
// 隐藏更多按钮
$brandMore.addClass('hide');
});
// 【品牌】更多
$brandMore.click(function() {
var $this = $(this);
if ($this.hasClass('more')) {
brandHideMore();
$brandMoreTxt.text('更多');
$brandMoreIcon.html(moreUnicode.down);
// 清空搜索框和搜索结果
$brandInput.val('').trigger('keyup');
} else {
brandShowMore();
$brandMoreTxt.text('收起');
$brandMoreIcon.html(moreUnicode.up);
}
$(this).toggleClass('more');
});
// 【品牌】索引
$brandsIndex.on('mouseenter', 'span', function() {
var $this = $(this),
index = $this.data('index');
if ($this.hasClass('hover')) {
return;
}
setTimeout(function() {
$this.siblings('span.hover').removeClass('hover');
$this.addClass('hover');
if ($this.index() === 0) {
// 全部
$brandAttrs.removeClass('hide');
} else {
$brandAttrs.addClass('hide').filter('[data-index=' + index + ']').removeClass('hide');
}
}, 120);
});
// 【品牌】搜索
$brandInput.keyup(function() {
var val = $(this).val().toLowerCase();
if (val === '') {
$brandAttrs.removeClass('hide');
} else {
$brandAttrs.addClass('hide').filter('[data-key*="' + val + '"]').removeClass('hide');
}
});
// 【品牌】多选确定
$('#brand-multi-ok').click(function() {
var val = [];
if ($(this).hasClass('dis')) {
return;
}
$brandPanel.find('span.checked').each(function() {
val.push($(this).data('id'));
});
uriLoc('brand', val.join(','));
});
// 【品牌/高级选项】多选取消
$('.multi-select-cancel').click(function() {
var $panel = $(this).closest('.multi');
if ($panel.hasClass('brand-panel')) {
$brandMulti.removeClass('hide'); // 显示多选按钮
$brandMore.trigger('click');
// 显示出更多按钮
$brandMore.removeClass('hide');
// 清空搜索框和搜索结果
$brandInput.val('').trigger('keyup');
brandHideMore();
}
$panel.addClass('hide').removeClass('multi');
clearChecked($panel.find('.checkbox.checked'), $panel.find('.attr.checked')); // 清除选中状态
// 确定按钮状态恢复dis
$(this).siblings('.multi-select-ok').addClass('dis');
});
// 【品牌/高级选项】checkbox
$('.check-container').on('click', '.attr', function() {
var $this = $(this),
$check = $this.find('.checkbox'),
$btnOk = $this.closest('.brand-panel, .senior-sub').find('.multi-select-ok');
$check.toggleClass('checked');
if ($check.hasClass('checked')) {
$check.html(checkUnicode.checked);
} else {
$check.html(checkUnicode.unchecked);
}
// 更新按钮状态
if ($check.hasClass('checked') ||
$this.siblings('.attr').find('.checked').length > 0) {
$btnOk.removeClass('dis');
} else {
$btnOk.addClass('dis');
}
});
// 【品牌/高级选项】当多选时阻止链接默认跳转
$('.brand, .senior').on('click', '.attr > a', function(e) {
if ($(this).closest('.multi').length > 0) {
e.preventDefault();
}
});
// 已选中的条件,点击不刷新
$('.filter-box').on('click', 'li.checked, a.checked', function(e) {
e.preventDefault();
});
// 【价格】用户定义价格处理
if ($udPrice.length > 0) {
$limit = $udPrice.find('.limit');
$min = $limit.filter('.min');
$max = $limit.filter('.max');
$btn = $udPrice.find('.price-sure');
// 【价格】输入
$limit.keyup(function() {
var thisPrice = $.trim($(this).val()),
isPriInt = interReg.test(thisPrice),
intPrice,
min,
max;
if (!isPriInt) {
intPrice = parseInt(thisPrice, 10);
$(this).val(isNaN(intPrice) ? '' : intPrice);
}
min = $.trim($min.val());
max = $.trim($max.val());
if (min !== '' || max !== '') {
$btn.removeClass('hide');
} else {
$btn.addClass('hide');
}
});
// 【价格】多项查询
$btn.click(function() {
var min = $.trim($min.val()),
max = $.trim($max.val()),
tmp;
// 对于min大于max的情况,交换位置
if (min !== '' && max !== '' && +min > +max) {
tmp = max;
max = min;
min = tmp;
}
uriLoc('price', min + ',' + max);
});
}
// 【高级选项】鼠标移入显示子项
$seniorAttrWrap.on('mouseenter', '.attr', function() {
var $this = $(this);
var index = $this.index();
if ($this.hasClass('no-sub')) {
return;
}
$this.addClass('hover').siblings().removeClass('hover');
$seniorSubWrap.children('.senior-sub:eq(' + index + ')').removeClass('hide').siblings().addClass('hide');
}).on('mouseleave', '.attr', function() {
var $this = $(this),
index = $this.index();
if ($this.hasClass('no-sub')) {
return;
}
hoveredIndex = index;
seniorHoverTime = setTimeout(function() {
hideSeniorPanel();
}, 100);
});
// 【高级选项】多选
$('.senior-sub').on('click', '.multi-select', function() {
$(this).closest('.senior-sub').addClass('multi');
}).on('click', '.multi-select-ok', function() {
var $btn2 = $(this),
$sub = $btn2.closest('.senior-sub'),
val = [];
if ($btn2.hasClass('dis')) {
return;
}
$sub.find('span.checked').each(function() {
val.push($(this).data('id'));
});
uriLoc($sub.data('attr'), val.join(','));
}).on('mouseenter', function() {
clearTimeout(seniorHoverTime);
}).on('mouseleave', function() {
hideSeniorPanel();
});
... ...
/**
* 分享
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/3/1
*/
var $ = require('yoho.jquery');
function shareBase(options) {
var openUrl = '';
var defOption = {
title: '',
url: window.location.href,
weixinUrl: '',
image: '',
desc: '',
channel: ''
};
var shareChannels = ['weibo', 'tweibo', 'qzone', 'renren', 'qq', 'douban', 'weixin'];
var sharebox;
var shareCon = '<em><i></i></em>';
defOption = $.extend(defOption, options);
if ($.inArray(defOption.channel, shareChannels) === -1) {
alert('不存在的分享平台!');
return false;
}
switch (defOption.channel) {
case 'weibo':
openUrl = 'http://service.weibo.com/share/share.php?url=' + defOption.url + '&title=' +
defOption.title + '&appkey=3739328910&searchPic=true&pic=' + defOption.image;
break;
case 'tweibo':
openUrl = 'http://share.v.t.qq.com/index.php?c=share&a=index&url=' + defOption.url + '&title=' +
defOption.title + '&appkey=c0af9c29e0900813028c2ccb42021792&pic=' + defOption.image;
break;
case 'qzone':
openUrl = 'http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=' + defOption.url + '&title=' +
defOption.title + '&desc=&summary=' + defOption.desc + '&site=YOHO!有货&pics=' + defOption.image;
break;
case 'renren':
openUrl = 'http://widget.renren.com/dialog/share?resourceUrl=' + defOption.url + '&srcUrl=' +
defOption.url + '&desc=' + defOption.desc + '&title=' + defOption.title + '&description=' +
defOption.desc + '&pic=' + defOption.image;
break;
case 'qq':
openUrl = 'http://connect.qq.com/widget/shareqq/index.html?url=' + defOption.url + '&desc=' +
defOption.desc + '&title=' + defOption.title.replace('%', '') + '&desc=&summary=' +
defOption.desc + '&site=YOHO!有货&pics=' + defOption.image;
break;
case 'weixin':
openUrl = 'http://s.jiathis.com/qrcode.php?url=' + defOption.weixinUrl + '&desc=' +
defOption.desc + '&title=' + defOption.title + '&description=' +
defOption.desc + '&pic=' + defOption.image;
break;
case 'douban':
openUrl = 'http://www.douban.com/share/service?href=' + defOption.url + '&text=' +
defOption.desc + '&image=' + defOption.image + '&title=' + defOption.title + '&comment=';
break;
}
if (defOption.channel === 'weixin') {
if (!defOption.self) {
return;
}
sharebox = defOption.self.closest('.share-to').find('.weixin-share-box');
if (sharebox.length > 0) {
shareCon += '<div class="con"><h2>分享到微信朋友圈</h2><p class="pic">' +
'<img src="' + openUrl + '" /></p><p class="w">打开微信,点击底部得“发现”,使用<br/>“扫一扫“即可将网页分享到我的朋友圈。</p>' +
'<a href="javascript:void(0)" class="close">x</a></div>';
sharebox.find('div').length > 0 ? sharebox.show() : sharebox.html(shareCon).show();
sharebox.find('.close').click(function() {
$(this).closest('.weixin-share-box').hide();
});
}
} else {
window.open(encodeURI(openUrl));
}
}
function share(channel, self) {
var title = document.title.replace(/(^\s*)|(\s*$)/g, '');
var desc = $('#share-desc').val();
var image = $('#share-img').val();
var weixinUrl = $('#weixin-url').val();
if (channel === 'weibo' || channel === 'tqq') {
shareBase({
channel: channel,
title: title,
image: image
});
} else {
shareBase({
channel: channel,
title: title,
desc: desc,
image: image,
self: self,
weixinUrl: weixinUrl
});
}
}
$('.share-wrapper').on('click', 'i', function() {
var $el = $(this),
type = $el.data('type');
if (type === 'weixin') {
share(type, $el);
} else {
share(type);
}
});
... ...
/**
* 首页banner轮播
* @author: liuyue(yue.liu@yoho.cn)
* @date: 2015/12/04
*/
var $ = require('yoho-jquery'),
lazyLoad = require('yoho-jquery-lazyload');
(function() {
var Slider = function(element, options) {
this.$element = $(element);
this.options = $.extend({}, $.fn.slider.defaults, options);
this.bigItem = this.$element.find('.slide-wrapper').find('li');
this.smallItem = null;
this.len = this.bigItem.size();
this.index = 0;
this.timer = null;
this.init();
};
Slider.prototype = {
init: function() {
if (!this.$element) {
return;
}
if (this.len <= 1) {
lazyLoad(this.$element.find('img.lazy'));
return;
}
if (this.options.pagination) {
this.smallItem = $(this.options.pagination).find('li');
} else {
this._createPage();
}
if (this.options.orient) {
this._createOrient();
}
this._slideShow();
this._bindEvent();
this._autoplay();
},
_createOrient: function() {
var orientHtml = '<div class="slide-switch">' +
'<a class="prev" href="javascript:;"><span class="iconfont">&#xe609;</span></a>' +
'<a class="next" href="javascript:;"><span class="iconfont">&#xe608;</span></a>' +
'</div>';
if (this.$element.find('.slide-switch').length > 0) {
return;
}
this.$element.append(orientHtml);
},
_createPage: function() {
var pageHtml = '<div class="slide-pagination"><div class="slide-pagination-inner">' +
'<div class="slide-shade"></div><div class="slide-pagination-last">',
i = 0;
if (this.len <= 1) {
return;
}
for (i = 0; i < this.len; i++) {
pageHtml += '<span></span>';
}
pageHtml += '</div></div></div>';
this.$element.append($(pageHtml));
this.smallItem = this.$element.find('.slide-pagination-inner span');
},
_bindEvent: function() {
var that = this;
this.$element.find('.slide-switch').on('click', '.next', function() {
that._nextSlide();
clearInterval(that.timer);
that.timer = setInterval(function() {
that._nextSlide();
}, that.options.time);
}).on('click', '.prev', function() {
that._prevSlide();
clearInterval(that.timer);
that.timer = setInterval(function() {
that._nextSlide();
}, that.options.time);
});
this.smallItem.on('mouseenter', function() {
that.index = $(this).index();
clearInterval(that.timer);
that._slideShow();
}).on('mouseleave', function() {
that._autoplay();
});
this.$element.on('mouseenter', function() {
$(this).find('.slide-switch').addClass('show');
}).on('mouseleave', function() {
$(this).find('.slide-switch').removeClass('show');
});
},
_nextSlide: function() {
if (this.index === this.len - 1) {
this.index = 0;
} else {
this.index++;
}
this._slideShow();
},
_prevSlide: function() {
if (this.index === 0) {
this.index = this.len - 1;
} else {
this.index--;
}
this._slideShow();
},
_slideShow: function() {
var $img = this.bigItem.eq(this.index).find('img.lazy');
// 未加载图片的及时显示
if ($img.attr('src') !== $img.data('original')) {
lazyLoad($img, {
event: 'sporty'
});
$img.trigger('sporty');
}
this.smallItem.eq(this.index).addClass('focus').siblings().removeClass('focus');
this.bigItem.eq(this.index).show().stop().animate({
opacity: 1
}, function() {
// 修正IE下切换时文字会重叠的问题
$(this).find('.slide-tips > p').removeClass('hide');
}).siblings().stop().animate({
opacity: 0
}, function() {
$(this).hide().find('.slide-tips > p').addClass('hide');
});
},
_autoplay: function() {
var that = this;
clearInterval(this.timer);
this.timer = setInterval(function() {
that._nextSlide();
}, this.options.time);
}
};
$.fn.slider = function(option) {
return this.each(function() {
var $this = $(this),
data = $this.data('Slider'),
options = typeof option === 'object' && option;
if (!data) {
$this.data('Slider', (data = new Slider(this, options)));
}
if (typeof option === 'string') {
data[option]();
}
});
};
$.fn.slider.Constructor = Slider;
$.fn.slider.defaults = {
time: 5000,
orient: true, // 左右切换箭头的显示
pagination: null
};
}($));
... ...
/**
* 筛选页和列表页操作banner中的每页N个商品选择逻辑
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2015/12/4
*/
var $ = require('yoho-jquery');
var $countPerPage = $('#count-per-page'),
$countChose = $countPerPage.next('ul');
var SLIDETIME = 200;
$(document).click(function(e) {
if ($(e.target).closest('.page-count').length > 0) {
return;
}
$countChose && $countChose.slideUp(SLIDETIME);
});
$countPerPage.click(function() {
if ($countChose.css('display') === 'none') {
$countChose.slideDown(SLIDETIME);
} else {
$countChose.slideUp(SLIDETIME);
}
});
... ...
/**
* webpack config
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/4/25
*/
'use strict';
const webpack = require('webpack');
const path = require('path');
const _ = require('lodash');
const shelljs = require('shelljs');
var entries = {};
// 构建各模块子页面JS。生成规则module.page.js
shelljs.ls(path.join(__dirname, '/js/**/*.page.js')).forEach((f) => {
var dir = _.slice(f.split('/'), -2); // [modulename, xx.page.js]
// Important
// 生成规则:module.page: './js/module/xx.page.js'
entries[`${dir[0]}.${dir[1].match(/(.*).page.js/)[1]}`] = `./js/${dir.join('/')}`;
entries.libs = [
'yoho-handlebars',
'yoho-jquery',
'yoho-jquery-lazyload',
'yoho-slider'
];
});
module.exports = {
entry: entries,
output: {
path: path.join(__dirname, 'bundle'), // absolute path
filename: '[name].js'
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'libs',
filename: 'libs.js'
}),
new webpack.ProvidePlugin({
$: 'yoho-jquery',
jQuery: 'yoho-jquery',
'window.jQuery': 'yoho-jquery'
})
]
};
... ...
/**
* 图片处理
* @author: 梁校松
* @date: 2016/4/25
*/
'use strict';
const _ = require('lodash');
const // domain = '.static.yhbimg.com',
defaultImage = '/2015/08/25/02/01dd632a6e07bfef457ce4beda21dd6413.png',
qiniuDomain = 'yhfair.qiniudn.com',
domainList = {
'01': ['img10.static.yhbimg.com', 'img11.static.yhbimg.com'],
'02': ['img12.static.yhbimg.com', 'img13.static.yhbimg.com'],
'yhb-head': 'head.static.yhbimg.com'
};
const regexp = /(?:\{)([a-zA-z][^\s\}]+)(?:\})/g;
const myReplace = (tem, data) => {
return tem.replace(regexp, function(fullMatch, capture) {
if (data[capture]) {
return data[capture];
} else {
return fullMatch;
}
});
};
const makeBaseUrl = (domain, key) =>{
encodeURIComponent(key).replace('%2F', '/');
return `http://${domain}/${key}`;
};
const makeTemplateRequest = url => {
let ops = ['{mode}', 'w/{width}', 'h/{height}'];
if (ops.length === 0) {
return url;
}
return url + '?imageView/' + ops.join('/');
};
const getImgTemplateUrl = (fileName, mode, domain) =>{
// 默认值
if (!mode) {
mode = 1;
}
if (!domain) {
domain = null;
}
if (domain === null) {
domain = qiniuDomain;
}
let baseUrl = makeBaseUrl(domain, fileName);
return makeTemplateRequest(baseUrl);
};
const getDomain = (bucket, fileName) => {
let domain = '';
if (bucket in domainList) {
domain = domainList.bucket;
} else {
let node = fileName.substr(15, 2);
if (node in domainList) {
let urlList = domainList[node];
let nodeNum = _.random(urlList.length - 1);
domain = urlList[nodeNum];
}
}
return domain;
};
const url = (fileName, bucket, mode) =>{
// 默认值
if (!mode) {
mode = 1;
}
if (!bucket) {
bucket = 'yhfair';
}
let domain = getDomain(bucket, fileName);
return getImgTemplateUrl(bucket + fileName, mode, domain);
};
const template = (fileName, bucket, mode) => {
// 默认值
if (!mode) {
mode = 1;
}
if (!bucket) {
bucket = 'yhfair';
}
return url(fileName, bucket, mode);
};
exports.getImageUrl = (fileName, width, height, mode, bucket) => {
// 默认值
if (!mode) {
mode = 2;
}
if (!bucket) {
bucket = 'goodsimg';
}
if (typeof(fileName) !== 'string') {
return template(defaultImage, bucket, mode);
}
if (fileName.indexOf('http://') !== 0) {
fileName = template(fileName, bucket, mode);
}
return myReplace(fileName, {width: width, height: height, mode: mode});
};
... ...
'use strict';
const _ = require('lodash');
const camelCase = global.yoho.camelCase;
const helpers = global.yoho.helpers;
const images = require(`${global.utils}/images`);
/**
* 根据性别来决定 默认图片获取字段 如果是 2、3
*
* 则优先从cover2 --》 cover1 -- 》 images_url
* 否则优先从cover1 --》 cover2 -- 》 images_url
*
*/
const procProductImg = (product, gender) => {
if (gender === '2,3') {
return product.cover2 || product.cover1 || product.imagesUrl || '';
}
return product.cover1 || product.cover2 || product.imagesUrl || '';
};
/**
* 处理列表大图周边小图数据,拼接添加 href
* @param origin goods 原始数据
* @returns {{}} goods 输出数据,结构不变
*/
const handleGoodsListData = (origin) => {
_.forEach(origin, (value, key) => {
if (!_.isEmpty(value.goodsList)) {
_.forEach(value.goodsList, (subValue, subKey) => {
origin[key].goodsList[subKey].url =
helpers.urlFormat(`/product/pro_${value.productId}_${subValue.goodsId}/${value.cnAlphabet}.html`);
});
}
});
return origin;
};
/**
* 商品搜索商品数据处理
*/
exports.processProductList = (list, options) => {
const pruductList = [];
options = Object.assign({
showTags: true,
showNew: true,
showSale: true,
width: 290,
height: 388,
isApp: false,
showPoint: true,
gender: '2,3'
}, options);
list = camelCase(list);
_.forEach(list, (product) => {
// 商品信息有问题,则不显示
if (!product.productSkn || !product.goodsList.length) {
return;
}
// 如果库存为0,显示已抢完
if (product.storageNum === 0 && product.status === 1) {
product.isSaleOut = true;
}
// 市场价和售价一样,则不显示市场价, 不显示折扣信息
if (product.marketPrice === product.salesPrice) {
delete product.marketPrice;
} else {
product.discount = (product.salesPrice / product.marketPrice * 10).toFixed(1);
}
// 判别默认的商品是否将默认的图片URL赋值到skn
let flag = false;
// 如果设置了默认图片,就取默认的图片
_.forEach(product.goodsList, (goods) => {
if (flag) {
return;
}
if (goods.isDefault === 'Y') {
product.defaultImages = procProductImg(goods);
flag = true;
}
});
// 如果还未赋值,则取第一个skc产品的默认图片
if (!flag) {
product.defaultImages = procProductImg(product.goodsList[0]);
}
product = Object.assign(product, {
id: product.productSkn,
thumb: product.defaultImages
});
product.isSoonSoldOut = product.isSoonSoldOut === 'Y';
product.url = helpers.urlFormat(`/product/pro_${product.productId}_${product.goodsList[0].goodsId}/${product.cnAlphabet}.html`); // eslint-disable-line
// tar add 1606071146 品牌链接处理
product.brandUrl = helpers.urlFormat('', '', product.brandDomain);
// APP访问需要加附加的参数
// 备注:如果以后APP的接口太多,可以把这边参数提取出来,变成一个公共的方法来生成,便于以后管理维护
if (options.isApp) {
product.url += `?openby:yohobuy={"action":"go.productDetail","params":{"product_skn":'${product.productId}'}}`; // eslint-disable-line
}
if (options.showTags) {
product.tags = {};
product.tags.isNew = options.showNew && product.isNew === 'Y'; // 新品
product.tags.isDiscount = options.showSale && product.isDiscount === 'Y'; // 在售
product.tags.isLimited = product.isLimited === 'Y'; // 限量
product.tags.isYohood = product.isYohood === 'Y'; // YOHOOD
product.tags.midYear = product.midYear === 'Y'; // 年中
product.tags.yearEnd = product.yearEnd === 'Y'; // 年末
product.tags.isAdvance = product.isAdvance === 'Y'; // 再到着
// 打折与即将售完组合显示打折
if (product.isSoonSoldOut && product.tags.isDiscount) {
product.tags.isFew = false;
} else if (product.tags.isDiscount &&
(product.tags.isFew || product.tags.isLimited || product.tags.isYohood || product.tags.isAdvance)) {
// 打折与其它组合则隐藏打折
product.tags.isDiscount = false;
} else if (product.tags.isYohood && product.tags.isFew) {
// YOHOOD和新品组合显示YOHOOD
product.tags.isFew = false;
}
}
pruductList.push(product);
});
return handleGoodsListData(pruductList);
};
/**
* 处理筛选数据
* @param list
* @param string | options
* @return array 处理之后的筛选数据
*/
exports.processFilter = (list, options) => {
const filters = {
classify: []
};
const filtersType = {
brand: {
name: '所有品牌',
title: '品牌',
dataId: 'id',
subsName: 'brandName',
firstSub: 0,
dataType: 'brand'
},
color: {
name: '所有颜色',
title: '颜色',
dataId: 'colorId',
subsName: 'colorName',
firstSub: 0,
dataType: 'color'
},
discount: {
name: '所有商品',
title: '折扣',
dataId: 'key',
subsName: 'name',
firstSub: '0.1,0.9',
dataType: 'p_d'
},
gender: {
name: '所有性别',
title: '性别',
dataId: 'key',
subsName: 'flag',
firstSub: '1,2,3',
dataType: 'gender'
},
groupSort: {
name: '所有品类',
title: '品类',
dataId: 'relationParameter',
subsName: 'categoryName',
firstSub: 0,
dataType: 'sort'
},
priceRange: {
name: '所有价格',
title: '价格',
dataId: 'key',
subsName: 'flag',
firstSub: 0,
dataType: 'price'
},
size: {
name: '所有尺码',
title: '尺码',
dataId: 'sizeId',
subsName: 'sizeName',
firstSub: 0,
dataType: 'size'
}
};
options = Object.assign({
gender: '1,2,3', // 默认选择的性别,默认1,2,3表示所有
exclude: null // 需要排除的字段
}, options);
list = camelCase(list);
_.forEach(list, (item, key) => {
let classify = {
subs: []
};
if (key === 'group_sort') {
return;
}
if ((options.hideSize && key === 'size') || (options.hideSort && key === 'groupSort')) {
return;
}
classify.dataType = filtersType[key].dataType;
classify.name = filtersType[key].name;
classify.title = filtersType[key].title;
classify.subs.push({
chosed: true,
dataId: filtersType[key].firstSub,
name: filtersType[key].name
});
_.forEach(item, (sub, index) => {
let subs = {};
if (filtersType[key].dataId === 'key') {
subs.dataId = index;
} else if (filtersType[key].dataId === 'relationParameter') {
subs.dataId = sub.relationParameter['sort']; // eslint-disable-line
} else {
subs.dataId = sub[filtersType[key].dataId];
}
if (filtersType[key].subsName === 'flag') {
subs.name = sub;
} else {
subs.name = sub[filtersType[key].subsName];
if (key === 'discount') {
subs.name = subs.name + '折商品';
}
}
classify.subs.push(subs);
});
filters.classify.push(classify);
});
return filters;
};
// 处理单个商品
exports.processProduct = (productData, options) => {
let result = {};
options = Object.assign({
showTags: true,
showNew: true,
showSale: true,
width: 290,
height: 388,
showPoint: true
}, options);
// 商品信息有问题,则不显示
if (!productData.product_skn || !productData.goods_list[0]) {
return false;
}
// 市场价和售价一样,则不显示市场价
if (parseInt(productData.market_price, 0) === parseInt(productData.sales_price, 0)) {
productData.market_price = false;
}
// 设置默认图片
_.forEach(productData.goods_list, item => {
if (item.is_default === 'Y') {
productData.default_images = item.images_url;
}
});
if (!productData.default_images) {
productData.default_images = productData.goods_list[0].images_url;
}
result.id = productData.product_skn;
result.product_id = productData.product_id;
result.thumb = images.getImageUrl(productData.default_images, options.width, options.height);
result.name = productData.product_name;
result.price = !productData.market_price ? false : productData.market_price;
result.salePrice = productData.sales_price;
if (options.showPoint) {
result.price += '.00';
result.salePrice += '.00';
}
result.is_soon_sold_out = (productData.is_soon_sold_out === 'Y');
result.url = helpers.urlFormat(`/product/pro_${productData.product_id}_${productData.goods_list[0].goods_id}/${productData.cn_alphabet}.html`, null, 'list'); // eslint-disable-line
if (options.showTags) {
result.tags = {};
result.tags.is_new = options.showNew && productData.is_new && productData.is_new === 'Y'; // 新品
result.tags.is_discount = options.showSale && productData.is_discount && productData.is_discount === 'Y'; // 在售
result.tags.is_limited = productData.is_limited && productData.is_limited === 'Y'; // 限量
result.tags.is_yohood = productData.is_yohood && productData.is_yohood === 'Y'; // YOHOOD
result.tags.midYear = productData['mid-year'] && productData['mid-year'] === 'Y'; // 年中
result.tags.yearEnd = productData['year-end'] && productData['year-end'] === 'Y'; // 年末
result.tags.is_advance = productData.is_advance && productData.is_advance === 'Y'; // 再到着
if (result.is_soon_sold_out && result.tags.is_discount) {
result.tags.is_new = false;// 打折与即将售完组合显示打折
} else if (result.tags.is_discount &&
(result.tags.is_new || result.tags.is_limited || result.tags.is_yohood || result.tags.is_advance)) {
result.tags.is_discount = false;// 打折与其它组合则隐藏打折
} else if (result.tags.is_yohood && result.tags.is_new) {
result.tags.is_new = false;// YOHOOD和新品组合显示YOHOOD
}
}
return result;
};
... ...
const _ = require('lodash');
const camelCase = global.yoho.camelCase;
/**
* 处理楼层数据
* @param {[array]} list
* @return {[array]}
*/
module.exports = (list) => {
const formatData = [];
list = list || [];
list = camelCase(list);
_.forEach(list, (floor) => {
floor[_.camelCase(floor.templateName)] = true;
// 特殊资源位处理
// PLUS
if (floor.singleNameImage && floor.data) {
floor.data.title = {
title: floor.data.title
};
}
// 潮流时装/经典裤裙/时尚靴履/潮人配饰/潮流上装
if (floor.recommendContentOne && floor.data) {
if (floor.data.bigImage && floor.data.bigImage.length > 1) {
floor.data.bigImage = {
bigList: floor.data.bigImage
};
}
}
formatData.push(floor);
});
return formatData;
};
... ...
'use strict';
const _ = require('lodash');
module.exports = seoData => {
const defaultSeoData = {
/* eslint-disable */
title: 'YOHO!有货 | 年轻人潮流购物中心,中国潮流购物风向标,官方授权正品保证',
keywords: 'Yoho! 有货官网,潮流志,潮流男装,潮牌,美国潮牌,日本潮牌,香港潮牌,潮牌店,新品首发,欧美潮流,全球购,代购,时尚,流行,特卖,B2C,正品,购物网站,网上购物,货到付款',
description: 'YOHO! 有货,年轻人潮流购物中心,中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。作为YOHO!旗下的购物平台,汇集了全球潮流时尚商品和中国最流行的商品,也是国内最大的原创文化商品平台,也是香港,台湾地区流行商品的集中地。同时包含日本、韩国等众多国外潮流品牌,带给您全新潮流购物体验。'
/* eslint-enable */
};
_.merge(defaultSeoData, seoData);
return (req, res, next) => {
if (!req.xhr) {
res.locals.title = seoData.title;
res.locals.keywords = seoData.keywords;
res.locals.description = seoData.description;
}
next();
};
};
... ...