vue手机外卖点餐系统
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"presets": ["es2015", "stage-2"],
|
||||||
|
"plugins": ["transform-runtime"],
|
||||||
|
"comments": false
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
|
@ -0,0 +1,2 @@
|
||||||
|
build/*.js
|
||||||
|
config/*.js
|
|
@ -0,0 +1,32 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
|
||||||
|
extends: 'standard',
|
||||||
|
// required to lint *.vue files
|
||||||
|
plugins: [
|
||||||
|
'html'
|
||||||
|
],
|
||||||
|
// add your custom rules here
|
||||||
|
'rules': {
|
||||||
|
// allow paren-less arrow functions
|
||||||
|
'arrow-parens': 0,
|
||||||
|
// allow async-await
|
||||||
|
'generator-star-spacing': 0,
|
||||||
|
// allow debugger during development
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||||
|
'no-multiple-empty-lines': ['error', {
|
||||||
|
max: 2,
|
||||||
|
maxEOF: 2,
|
||||||
|
maxBOF: 2
|
||||||
|
}],
|
||||||
|
'space-before-function-paren':0,
|
||||||
|
'semi':0,
|
||||||
|
'no-new':0,
|
||||||
|
'no-unused-vars':0,
|
||||||
|
'no-undef':0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
resource/
|
||||||
|
dist/
|
||||||
|
npm-debug.log
|
||||||
|
.idea
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 VueDemo_Sell_Eleme
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,145 @@
|
||||||
|
|
||||||
|
#点餐系统
|
||||||
|
> vue2.0、vuex、vue-router、axios、webpack、eslint、better-scroll
|
||||||
|
|
||||||
|
## 组件
|
||||||
|
|
||||||
|
- [x] 购物车
|
||||||
|
- [x] 购买物品小球飞入动画
|
||||||
|
- [x] 评价star组件
|
||||||
|
- [x] 商品添加、删除组件
|
||||||
|
- [x] 优惠图标组件
|
||||||
|
- [x] 目录、列表联动滚动
|
||||||
|
- [x] 画廊
|
||||||
|
- [x] 评论的是否满意和内容筛选
|
||||||
|
- [x] 商品列表页面
|
||||||
|
- [x] 店铺评价页面
|
||||||
|
- [x] 商家介绍页面
|
||||||
|
- [x] 优惠活动页面
|
||||||
|
- [x] 商品详情页面
|
||||||
|
|
||||||
|
## 构建
|
||||||
|
|
||||||
|
vue有自己的脚手架构建工具vue-cli,使用起来非常方便,使用webpack来集成各种开发便捷工具,比如:
|
||||||
|
|
||||||
|
- 代码热更新,修改代码之后网页无刷新改变,对前端开发来说非常的方便
|
||||||
|
- PostCss,再也不用去管兼容性的问题了,只针对chrome写css代码,会自动编译生成支持多款浏览器的css代码
|
||||||
|
- Eslint,统一代码风格,规避低级错误,对于有代码洁癖的人来说是绝对的好东西,不过有些地方的代码校验有时候也挺麻烦的-.-
|
||||||
|
- bable,ES2015出来已经有一段时间了,但是不少浏览器还没有兼容ES6.有了bable,放心使用ES6语法,它会自动转义成ES5语法。
|
||||||
|
- Stylus,类似于SASS/SCSS,但是可以不写{}和“:”,使用起来还是很方便的
|
||||||
|
- ...
|
||||||
|
|
||||||
|
除此之外,vue-cli已经使用node配置了一套本地服务器和安装命令等,本地运行和打包只需要一个命令就可以搞定,非常的方便
|
||||||
|
|
||||||
|
## 开发
|
||||||
|
|
||||||
|
vue非常好的融合了react的组件化思想和angular的指令思想。
|
||||||
|
一个vue的组件将HTML、CSS、JS代码写在一个文件里面,这样既方便编写,也方便管理和修改
|
||||||
|
|
||||||
|
### Axios
|
||||||
|
|
||||||
|
在vue1.x的时候,vue的官方推荐HTTP请求工具是vue-resource,但是在vue2.0的时候将推荐工具改成了axios。
|
||||||
|
|
||||||
|
使用方式都差不多,但需要注意的是:接口返回的res并不直接是返回的数据,而是经过axios本身处理过的json对象。真正的数据在res.data里:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
axios.get(url).then((res)=>{
|
||||||
|
this.data = res.data
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vuex
|
||||||
|
|
||||||
|
vue提供了一个数据管理工具vuex,有点类似于angular中factory和service,可以进行数据上的通信。
|
||||||
|
比如存储一些公共变量或者是不同组件间的数据处理等。
|
||||||
|
|
||||||
|
这个有一些高级用法在这里不细说,想要了解的可以去官方文档看,有中文版本。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const store = new Vuex.Store({
|
||||||
|
state: {
|
||||||
|
count: 0
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
increment(state) {
|
||||||
|
state.count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vue-Router
|
||||||
|
|
||||||
|
vue-router是vue的路由系统,可以用来创建单页应用。基本思想是在主页面中引入<router-view>标签,然后定义路由,把router挂在到app上,然后把各个子页面渲染到view里面。使用起来还是很方便的,
|
||||||
|
跳转页面只需要
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
router.push('test')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取元素节点
|
||||||
|
|
||||||
|
vue2.0废除了v-el指令,所有的节点指令修改为ref,然后通过ref来获取元素节点,如
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div ref="testHook">test</div>
|
||||||
|
...js code
|
||||||
|
this.$ref.testHook
|
||||||
|
```
|
||||||
|
|
||||||
|
### 组件间的通信
|
||||||
|
|
||||||
|
一。如果是和子组件通信,则使用ref就可以实现,如:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<test ref="testHook"></test>
|
||||||
|
...js code
|
||||||
|
this.$ref.testHook.add() //调用test子组件的add方法
|
||||||
|
```
|
||||||
|
|
||||||
|
二。使用emit来发送广播
|
||||||
|
|
||||||
|
vue2提供了一套广播机制,即一边发送广播,一边接收广播来执行相应操作。使用方法如下:
|
||||||
|
|
||||||
|
比如想要给test组件发送一个“相加”广播:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export default {
|
||||||
|
method:{
|
||||||
|
click(){
|
||||||
|
Vue.$emit('add',{}) //第二个参数可作为传递数据传送到监听端口,不需要则传空对象
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
那么test组件中就需要监听,在created方法里写
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export default {
|
||||||
|
created(){
|
||||||
|
Vue.$on('add',this.add)
|
||||||
|
},
|
||||||
|
method:{
|
||||||
|
add(){
|
||||||
|
this.count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 安装步骤
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# serve with hot reload at localhost:8080
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# build for production with minification
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// https://github.com/shelljs/shelljs
|
||||||
|
require('./check-versions')()
|
||||||
|
require('shelljs/global')
|
||||||
|
env.NODE_ENV = 'production'
|
||||||
|
|
||||||
|
var path = require('path')
|
||||||
|
var config = require('../config')
|
||||||
|
var ora = require('ora')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var webpackConfig = require('./webpack.prod.conf')
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
' Tip:\n' +
|
||||||
|
' Built files are meant to be served over an HTTP server.\n' +
|
||||||
|
' Opening index.html over file:// won\'t work.\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
var spinner = ora('building for production...')
|
||||||
|
spinner.start()
|
||||||
|
|
||||||
|
var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
|
||||||
|
rm('-rf', assetsPath)
|
||||||
|
mkdir('-p', assetsPath)
|
||||||
|
cp('-R', 'static/*', assetsPath)
|
||||||
|
|
||||||
|
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')
|
||||||
|
})
|
|
@ -0,0 +1,45 @@
|
||||||
|
var semver = require('semver')
|
||||||
|
var chalk = require('chalk')
|
||||||
|
var packageConfig = require('../package.json')
|
||||||
|
var exec = function (cmd) {
|
||||||
|
return require('child_process')
|
||||||
|
.execSync(cmd).toString().trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionRequirements = [
|
||||||
|
{
|
||||||
|
name: 'node',
|
||||||
|
currentVersion: semver.clean(process.version),
|
||||||
|
versionRequirement: packageConfig.engines.node
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'npm',
|
||||||
|
currentVersion: exec('npm --version'),
|
||||||
|
versionRequirement: packageConfig.engines.npm
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
var warnings = []
|
||||||
|
for (var i = 0; i < versionRequirements.length; i++) {
|
||||||
|
var mod = versionRequirements[i]
|
||||||
|
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||||
|
warnings.push(mod.name + ': ' +
|
||||||
|
chalk.red(mod.currentVersion) + ' should be ' +
|
||||||
|
chalk.green(mod.versionRequirement)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warnings.length) {
|
||||||
|
console.log('')
|
||||||
|
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||||
|
console.log()
|
||||||
|
for (var i = 0; i < warnings.length; i++) {
|
||||||
|
var warning = warnings[i]
|
||||||
|
console.log(' ' + warning)
|
||||||
|
}
|
||||||
|
console.log()
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,108 @@
|
||||||
|
require('./check-versions')()
|
||||||
|
var config = require('../config')
|
||||||
|
if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
|
||||||
|
var path = require('path')
|
||||||
|
var express = require('express')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var opn = require('opn')
|
||||||
|
var proxyMiddleware = require('http-proxy-middleware')
|
||||||
|
var webpackConfig = require('./webpack.dev.conf')
|
||||||
|
|
||||||
|
// default port where dev server listens for incoming traffic
|
||||||
|
var port = process.env.PORT || config.dev.port
|
||||||
|
// Define HTTP proxies to your custom API backend
|
||||||
|
// https://github.com/chimurai/http-proxy-middleware
|
||||||
|
var proxyTable = config.dev.proxyTable
|
||||||
|
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
// 数据mock
|
||||||
|
var appData = require('../static/data.json')
|
||||||
|
var seller = appData.seller
|
||||||
|
var goods = appData.goods
|
||||||
|
var ratings = appData.ratings
|
||||||
|
|
||||||
|
var apiRoutes = express.Router()
|
||||||
|
|
||||||
|
apiRoutes.get('/seller', function(req, res) {
|
||||||
|
res.json({
|
||||||
|
errno: 0,
|
||||||
|
data: seller
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
apiRoutes.get('/goods', function(req, res) {
|
||||||
|
res.json({
|
||||||
|
errno: 0,
|
||||||
|
data: goods
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
apiRoutes.get('/ratings', function(req, res) {
|
||||||
|
res.json({
|
||||||
|
errno: 0,
|
||||||
|
data: ratings
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use('/api', apiRoutes)
|
||||||
|
|
||||||
|
var compiler = webpack(webpackConfig)
|
||||||
|
|
||||||
|
var devMiddleware = require('webpack-dev-middleware')(compiler, {
|
||||||
|
publicPath: webpackConfig.output.publicPath,
|
||||||
|
stats: {
|
||||||
|
colors: true,
|
||||||
|
chunks: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var hotMiddleware = require('webpack-hot-middleware')(compiler)
|
||||||
|
// 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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// proxy api requests
|
||||||
|
Object.keys(proxyTable).forEach(function(context) {
|
||||||
|
var options = proxyTable[context]
|
||||||
|
if (typeof options === 'string') {
|
||||||
|
options = {
|
||||||
|
target: options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.use(proxyMiddleware(context, options))
|
||||||
|
})
|
||||||
|
|
||||||
|
// handle fallback for HTML5 history API
|
||||||
|
app.use(require('connect-history-api-fallback')())
|
||||||
|
|
||||||
|
// serve webpack bundle output
|
||||||
|
app.use(devMiddleware)
|
||||||
|
|
||||||
|
// enable hot-reload and state-preserving
|
||||||
|
// compilation error display
|
||||||
|
app.use(hotMiddleware)
|
||||||
|
|
||||||
|
// serve pure static assets
|
||||||
|
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
|
||||||
|
app.use(staticPath, express.static('./static'))
|
||||||
|
|
||||||
|
module.exports = app.listen(port, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var uri = 'http://localhost:' + port
|
||||||
|
console.log('Listening at ' + uri + '\n')
|
||||||
|
|
||||||
|
// when env is testing, don't need open it
|
||||||
|
if (process.env.NODE_ENV !== 'testing') {
|
||||||
|
opn(uri)
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,61 @@
|
||||||
|
var path = require('path')
|
||||||
|
var config = require('../config')
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||||
|
|
||||||
|
exports.assetsPath = function (_path) {
|
||||||
|
var assetsSubDirectory = process.env.NODE_ENV === 'production'
|
||||||
|
? config.build.assetsSubDirectory
|
||||||
|
: config.dev.assetsSubDirectory
|
||||||
|
return path.posix.join(assetsSubDirectory, _path)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.cssLoaders = function (options) {
|
||||||
|
options = options || {}
|
||||||
|
// generate loader string to be used with extract text plugin
|
||||||
|
function generateLoaders (loaders) {
|
||||||
|
var sourceLoader = loaders.map(function (loader) {
|
||||||
|
var extraParamChar
|
||||||
|
if (/\?/.test(loader)) {
|
||||||
|
loader = loader.replace(/\?/, '-loader?')
|
||||||
|
extraParamChar = '&'
|
||||||
|
} else {
|
||||||
|
loader = loader + '-loader'
|
||||||
|
extraParamChar = '?'
|
||||||
|
}
|
||||||
|
return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
|
||||||
|
}).join('!')
|
||||||
|
|
||||||
|
// Extract CSS when that option is specified
|
||||||
|
// (which is the case during production build)
|
||||||
|
if (options.extract) {
|
||||||
|
return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
|
||||||
|
} else {
|
||||||
|
return ['vue-style-loader', sourceLoader].join('!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
|
||||||
|
return {
|
||||||
|
css: generateLoaders(['css']),
|
||||||
|
postcss: generateLoaders(['css']),
|
||||||
|
less: generateLoaders(['css', 'less']),
|
||||||
|
sass: generateLoaders(['css', 'sass?indentedSyntax']),
|
||||||
|
scss: generateLoaders(['css', 'sass']),
|
||||||
|
stylus: generateLoaders(['css', 'stylus']),
|
||||||
|
styl: generateLoaders(['css', 'stylus'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate loaders for standalone style files (outside of .vue)
|
||||||
|
exports.styleLoaders = function (options) {
|
||||||
|
var output = []
|
||||||
|
var loaders = exports.cssLoaders(options)
|
||||||
|
for (var extension in loaders) {
|
||||||
|
var loader = loaders[extension]
|
||||||
|
output.push({
|
||||||
|
test: new RegExp('\\.' + extension + '$'),
|
||||||
|
loader: loader
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
var path = require('path')
|
||||||
|
var config = require('../config')
|
||||||
|
var utils = require('./utils')
|
||||||
|
var projectRoot = path.resolve(__dirname, '../')
|
||||||
|
|
||||||
|
var env = process.env.NODE_ENV
|
||||||
|
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
|
||||||
|
// various preprocessor loaders added to vue-loader at the end of this file
|
||||||
|
var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
|
||||||
|
var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
|
||||||
|
var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
app: './src/main.js'
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: config.build.assetsRoot,
|
||||||
|
publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
|
||||||
|
filename: '[name].js'
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['', '.js', '.vue'],
|
||||||
|
fallback: [path.join(__dirname, '../node_modules')],
|
||||||
|
alias: {
|
||||||
|
'vue$': 'vue/dist/vue.common.js',
|
||||||
|
'src': path.resolve(__dirname, '../src'),
|
||||||
|
'assets': path.resolve(__dirname, '../src/assets'),
|
||||||
|
'components': path.resolve(__dirname, '../src/components'),
|
||||||
|
'common': path.resolve(__dirname, '../src/common'),
|
||||||
|
'img': path.resolve(__dirname, '../resource/img')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolveLoader: {
|
||||||
|
fallback: [path.join(__dirname, '../node_modules')]
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
preLoaders: [{
|
||||||
|
test: /\.vue$/,
|
||||||
|
loader: 'eslint',
|
||||||
|
include: projectRoot,
|
||||||
|
exclude: /node_modules/
|
||||||
|
}, {
|
||||||
|
test: /\.js$/,
|
||||||
|
loader: 'eslint',
|
||||||
|
include: projectRoot,
|
||||||
|
exclude: /node_modules/
|
||||||
|
}],
|
||||||
|
loaders: [{
|
||||||
|
test: /\.vue$/,
|
||||||
|
loader: 'vue'
|
||||||
|
}, {
|
||||||
|
test: /\.js$/,
|
||||||
|
loader: 'babel',
|
||||||
|
include: projectRoot,
|
||||||
|
exclude: /node_modules/
|
||||||
|
}, {
|
||||||
|
test: /\.json$/,
|
||||||
|
loader: 'json'
|
||||||
|
}, {
|
||||||
|
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||||
|
loader: 'url',
|
||||||
|
query: {
|
||||||
|
limit: 10000,
|
||||||
|
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||||
|
loader: 'url',
|
||||||
|
query: {
|
||||||
|
limit: 10000,
|
||||||
|
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
eslint: {
|
||||||
|
formatter: require('eslint-friendly-formatter')
|
||||||
|
},
|
||||||
|
vue: {
|
||||||
|
loaders: utils.cssLoaders({
|
||||||
|
sourceMap: useCssSourceMap
|
||||||
|
}),
|
||||||
|
postcss: [
|
||||||
|
require('autoprefixer')({
|
||||||
|
browsers: ['last 2 versions']
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
var config = require('../config')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var merge = require('webpack-merge')
|
||||||
|
var utils = require('./utils')
|
||||||
|
var baseWebpackConfig = require('./webpack.base.conf')
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
|
|
||||||
|
// add hot-reload related code to entry chunks
|
||||||
|
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
|
||||||
|
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = merge(baseWebpackConfig, {
|
||||||
|
module: {
|
||||||
|
loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
|
||||||
|
},
|
||||||
|
// eval-source-map is faster for development
|
||||||
|
devtool: '#eval-source-map',
|
||||||
|
plugins: [
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': config.dev.env
|
||||||
|
}),
|
||||||
|
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
|
||||||
|
new webpack.optimize.OccurenceOrderPlugin(),
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new webpack.NoErrorsPlugin(),
|
||||||
|
// https://github.com/ampedandwired/html-webpack-plugin
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: 'index.html',
|
||||||
|
template: 'index.html',
|
||||||
|
inject: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
|
@ -0,0 +1,98 @@
|
||||||
|
var path = require('path')
|
||||||
|
var config = require('../config')
|
||||||
|
var utils = require('./utils')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var merge = require('webpack-merge')
|
||||||
|
var baseWebpackConfig = require('./webpack.base.conf')
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
|
var env = config.build.env
|
||||||
|
|
||||||
|
var webpackConfig = merge(baseWebpackConfig, {
|
||||||
|
module: {
|
||||||
|
loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true })
|
||||||
|
},
|
||||||
|
devtool: config.build.productionSourceMap ? '#source-map' : false,
|
||||||
|
output: {
|
||||||
|
path: config.build.assetsRoot,
|
||||||
|
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||||
|
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||||
|
},
|
||||||
|
vue: {
|
||||||
|
loaders: utils.cssLoaders({
|
||||||
|
sourceMap: config.build.productionSourceMap,
|
||||||
|
extract: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': env
|
||||||
|
}),
|
||||||
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
compress: {
|
||||||
|
warnings: false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.optimize.OccurrenceOrderPlugin(),
|
||||||
|
// extract css into its own file
|
||||||
|
new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
|
||||||
|
// generate dist index.html with correct asset hash for caching.
|
||||||
|
// you can customize output by editing /index.html
|
||||||
|
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: config.build.index,
|
||||||
|
template: 'index.html',
|
||||||
|
inject: true,
|
||||||
|
minify: {
|
||||||
|
removeComments: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeAttributeQuotes: true
|
||||||
|
// more options:
|
||||||
|
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||||
|
},
|
||||||
|
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||||
|
chunksSortMode: 'dependency'
|
||||||
|
}),
|
||||||
|
// split vendor js into its own file
|
||||||
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: 'vendor',
|
||||||
|
minChunks: function (module, count) {
|
||||||
|
// any required modules inside node_modules are extracted to vendor
|
||||||
|
return (
|
||||||
|
module.resource &&
|
||||||
|
/\.js$/.test(module.resource) &&
|
||||||
|
module.resource.indexOf(
|
||||||
|
path.join(__dirname, '../node_modules')
|
||||||
|
) === 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// extract webpack runtime and module manifest to its own file in order to
|
||||||
|
// prevent vendor hash from being updated whenever app bundle is updated
|
||||||
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: 'manifest',
|
||||||
|
chunks: ['vendor']
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
if (config.build.productionGzip) {
|
||||||
|
var CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||||
|
|
||||||
|
webpackConfig.plugins.push(
|
||||||
|
new CompressionWebpackPlugin({
|
||||||
|
asset: '[path].gz[query]',
|
||||||
|
algorithm: 'gzip',
|
||||||
|
test: new RegExp(
|
||||||
|
'\\.(' +
|
||||||
|
config.build.productionGzipExtensions.join('|') +
|
||||||
|
')$'
|
||||||
|
),
|
||||||
|
threshold: 10240,
|
||||||
|
minRatio: 0.8
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = webpackConfig
|
|
@ -0,0 +1,6 @@
|
||||||
|
var merge = require('webpack-merge')
|
||||||
|
var prodEnv = require('./prod.env')
|
||||||
|
|
||||||
|
module.exports = merge(prodEnv, {
|
||||||
|
NODE_ENV: '"development"'
|
||||||
|
})
|
|
@ -0,0 +1,32 @@
|
||||||
|
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||||
|
var path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
build: {
|
||||||
|
env: require('./prod.env'),
|
||||||
|
index: path.resolve(__dirname, '../dist/index.html'),
|
||||||
|
assetsRoot: path.resolve(__dirname, '../dist'),
|
||||||
|
assetsSubDirectory: 'static',
|
||||||
|
assetsPublicPath: '/',
|
||||||
|
productionSourceMap: true,
|
||||||
|
// Gzip off by default as many popular static hosts such as
|
||||||
|
// Surge or Netlify already gzip all static assets for you.
|
||||||
|
// Before setting to `true`, make sure to:
|
||||||
|
// npm install --save-dev compression-webpack-plugin
|
||||||
|
productionGzip: false,
|
||||||
|
productionGzipExtensions: ['js', 'css']
|
||||||
|
},
|
||||||
|
dev: {
|
||||||
|
env: require('./dev.env'),
|
||||||
|
port: 8080,
|
||||||
|
assetsSubDirectory: 'static',
|
||||||
|
assetsPublicPath: '/',
|
||||||
|
proxyTable: {},
|
||||||
|
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||||
|
// with this option, according to the CSS-Loader README
|
||||||
|
// (https://github.com/webpack/css-loader#sourcemaps)
|
||||||
|
// In our experience, they generally work as expected,
|
||||||
|
// just be aware of this issue when enabling this option.
|
||||||
|
cssSourceMap: false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
NODE_ENV: '"production"'
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>sell</title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
|
||||||
|
<link rel="stylesheet" href="static/css/reset.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
"name": "sell",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "sell app",
|
||||||
|
"author": "<simonzhangr@foxmail.com>",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "node build/dev-server.js",
|
||||||
|
"build": "node build/build.js",
|
||||||
|
"lint": "eslint --ext .js,.vue src"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.15.3",
|
||||||
|
"babel-runtime": "^6.9.0",
|
||||||
|
"better-scroll": "^0.1.10",
|
||||||
|
"eslint-config-standard": "^6.2.1",
|
||||||
|
"fastclick": "^1.0.6",
|
||||||
|
"iscroll": "^5.2.0",
|
||||||
|
"moment": "^2.17.1",
|
||||||
|
"stylus": "^0.54.5",
|
||||||
|
"v-tap": "^2.0.2",
|
||||||
|
"vue": "^2.1.0",
|
||||||
|
"vue-resource": "^1.0.3",
|
||||||
|
"vue-router": "^2.1.1",
|
||||||
|
"vue-scroll": "^2.0.1",
|
||||||
|
"vuex": "^2.1.1",
|
||||||
|
"watchpack": "^1.2.0",
|
||||||
|
"webpack": "^1.14.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^6.4.0",
|
||||||
|
"babel-core": "^6.0.0",
|
||||||
|
"babel-eslint": "^7.0.0",
|
||||||
|
"babel-loader": "^6.0.0",
|
||||||
|
"babel-plugin-transform-runtime": "^6.0.0",
|
||||||
|
"babel-preset-es2015": "^6.0.0",
|
||||||
|
"babel-preset-stage-2": "^6.0.0",
|
||||||
|
"babel-register": "^6.0.0",
|
||||||
|
"better-scroll": "^0.1.10",
|
||||||
|
"chalk": "^1.1.3",
|
||||||
|
"connect-history-api-fallback": "^1.1.0",
|
||||||
|
"css-loader": "^0.25.0",
|
||||||
|
"eslint": "^3.7.1",
|
||||||
|
"eslint-config-standard": "^6.1.0",
|
||||||
|
"eslint-friendly-formatter": "^2.0.5",
|
||||||
|
"eslint-loader": "^1.5.0",
|
||||||
|
"eslint-plugin-html": "^1.3.0",
|
||||||
|
"eslint-plugin-promise": "^3.5.0",
|
||||||
|
"eslint-plugin-standard": "^2.0.1",
|
||||||
|
"eventsource-polyfill": "^0.9.6",
|
||||||
|
"express": "^4.13.3",
|
||||||
|
"extract-text-webpack-plugin": "^1.0.1",
|
||||||
|
"file-loader": "^0.9.0",
|
||||||
|
"function-bind": "^1.0.2",
|
||||||
|
"html-webpack-plugin": "^2.8.1",
|
||||||
|
"http-proxy-middleware": "^0.17.2",
|
||||||
|
"json-loader": "^0.5.4",
|
||||||
|
"opn": "^4.0.2",
|
||||||
|
"ora": "^0.3.0",
|
||||||
|
"semver": "^5.3.0",
|
||||||
|
"shelljs": "^0.7.4",
|
||||||
|
"stylus-loader": "^2.1.1",
|
||||||
|
"url-loader": "^0.5.7",
|
||||||
|
"vue-loader": "^10.0.0",
|
||||||
|
"vue-style-loader": "^1.0.0",
|
||||||
|
"vue-template-compiler": "^2.1.0",
|
||||||
|
"webpack": "^1.13.2",
|
||||||
|
"webpack-dev-middleware": "^1.8.3",
|
||||||
|
"webpack-hot-middleware": "^2.12.2",
|
||||||
|
"webpack-merge": "^0.14.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0",
|
||||||
|
"npm": ">= 3.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
<style lang="stylus" rel="stylesheet/stylus">
|
||||||
|
@import 'common/stylus/index'
|
||||||
|
.tab
|
||||||
|
display:flex
|
||||||
|
width:100%
|
||||||
|
height:40px
|
||||||
|
line-height:40px
|
||||||
|
border-1px(rgba(7,17,27,0.1))
|
||||||
|
.tab-item
|
||||||
|
flex:1
|
||||||
|
text-align:center
|
||||||
|
a
|
||||||
|
display:block
|
||||||
|
font-size:14px
|
||||||
|
color rgb(77,85,93)
|
||||||
|
&.active
|
||||||
|
font-size 14px
|
||||||
|
color rgb(240,20,20)
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-header :seller="seller"></v-header>
|
||||||
|
<div class="tab">
|
||||||
|
<div class="tab-item">
|
||||||
|
<router-link to="/goods">商品</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="tab-item">
|
||||||
|
<router-link to="/ratings">评论</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="tab-item">
|
||||||
|
<router-link to="/seller">商家</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<keep-alive>
|
||||||
|
<router-view :seller="seller"></router-view>
|
||||||
|
</keep-alive>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import header from 'components/header/header'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const ERR_OK = 0
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
seller: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
axios.get('static/data.json').then((res) => {
|
||||||
|
this.seller = res.data.seller
|
||||||
|
})
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
'v-header': header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?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>Generated by IcoMoon</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="sell-icon" horiz-adv-x="1024">
|
||||||
|
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||||
|
<missing-glyph horiz-adv-x="1024" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||||
|
<glyph unicode="Ǵ" glyph-name="add_circle" d="M726 406v84h-172v172h-84v-172h-172v-84h172v-172h84v172h172zM512 874q176 0 301-125t125-301-125-301-301-125-301 125-125 301 125 301 301 125z" />
|
||||||
|
<glyph unicode="" glyph-name="arrow_lift" d="M246.784 491.691l443.051 442.709c24.917 24.917 62.464 24.917 87.381 0s24.917-62.464 0-87.381l-399.36-399.36 399.36-399.36c24.917-24.917 24.917-62.464 0-87.381s-62.464-24.917-87.381 0l-443.051 443.392c-24.917 24.917-24.917 62.464 0 87.381z" />
|
||||||
|
<glyph unicode="" glyph-name="check_circle" d="M426 234l384 384-60 62-324-324-152 152-60-60zM512 874q176 0 301-125t125-301-125-301-301-125-301 125-125 301 125 301 301 125z" />
|
||||||
|
<glyph unicode="" glyph-name="close" d="M810 686l-238-238 238-238-60-60-238 238-238-238-60 60 238 238-238 238 60 60 238-238 238 238z" />
|
||||||
|
<glyph unicode="" glyph-name="favorite" d="M512 50l-62 56c-220 200-364 330-364 492 0 132 102 234 234 234 74 0 146-36 192-90 46 54 118 90 192 90 132 0 234-102 234-234 0-162-144-294-364-494z" />
|
||||||
|
<glyph unicode="" glyph-name="keyboard_arrow_right" d="M366 262l196 196-196 196 60 60 256-256-256-256z" />
|
||||||
|
<glyph unicode="" glyph-name="remove_circle_outline" d="M512 106q140 0 241 101t101 241-101 241-241 101-241-101-101-241 101-241 241-101zM512 874q176 0 301-125t125-301-125-301-301-125-301 125-125 301 125 301 301 125zM298 490h428v-84h-428v84z" />
|
||||||
|
<glyph unicode="" glyph-name="shopping_cart" d="M726 192q34 0 59-26t25-60-25-59-59-25-60 25-26 59 26 60 60 26zM42 874h140l40-84h632q18 0 30-13t12-31q0-10-6-20l-152-276q-24-44-74-44h-318l-38-70-2-6q0-10 10-10h494v-86h-512q-34 0-59 26t-25 60q0 20 10 40l58 106-154 324h-86v84zM298 192q34 0 60-26t26-60-26-59-60-25-59 25-25 59 25 60 59 26z" />
|
||||||
|
<glyph unicode="" glyph-name="thumb_down" d="M810 832h172v-512h-172v512zM640 832q34 0 60-26t26-60v-426q0-34-26-60l-280-282-46 46q-18 18-18 44v14l42 196h-270q-34 0-60 25t-26 59l2 4h-2v82q0 16 6 32l130 300q20 52 78 52h384z" />
|
||||||
|
<glyph unicode="" glyph-name="thumb_up" d="M982 534l-2-4h2v-82q0-16-6-32l-130-300q-20-52-78-52h-384q-34 0-60 26t-26 60v426q0 34 26 60l280 282 46-46q18-18 18-44v-14l-42-196h270q34 0 60-25t26-59zM42 64v512h172v-512h-172z" />
|
||||||
|
</font></defs></svg>
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
body,html{
|
||||||
|
font-weight: 200
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
display: inline-block;
|
||||||
|
&:after
|
||||||
|
display: block;
|
||||||
|
content: ''
|
||||||
|
height: 0
|
||||||
|
line-height: 0
|
||||||
|
clear: both;
|
||||||
|
visibility: hidden;
|
|
@ -0,0 +1,55 @@
|
||||||
|
@font-face
|
||||||
|
font-family: 'sell-icon'
|
||||||
|
src: url('common/fonts/sell-icon.eot?nowozp')
|
||||||
|
src: url('common/fonts/sell-icon.eot?nowozp#iefix') format('embedded-opentype'),
|
||||||
|
url('common/fonts/sell-icon.ttf?nowozp') format('truetype'),
|
||||||
|
url('common/fonts/sell-icon.woff?nowozp') format('woff'),
|
||||||
|
url('common/fonts/sell-icon.svg?nowozp#sell-icon') format('svg')
|
||||||
|
font-weight: normal
|
||||||
|
font-style: normal
|
||||||
|
|
||||||
|
|
||||||
|
[class^="icon-"], [class*=" icon-"]
|
||||||
|
/* use !important to prevent issues with browser extensions that change fonts */
|
||||||
|
font-family: 'sell-icon' !important
|
||||||
|
speak: none
|
||||||
|
font-style: normal
|
||||||
|
font-weight: normal
|
||||||
|
font-variant: normal
|
||||||
|
text-transform: none
|
||||||
|
line-height: 1
|
||||||
|
|
||||||
|
/* Better Font Rendering =========== */
|
||||||
|
-webkit-font-smoothing: antialiased
|
||||||
|
-moz-osx-font-smoothing: grayscale
|
||||||
|
|
||||||
|
|
||||||
|
.icon-arrow_lift:before
|
||||||
|
content: "\e900"
|
||||||
|
|
||||||
|
.icon-check_circle:before
|
||||||
|
content: "\e901"
|
||||||
|
|
||||||
|
.icon-close:before
|
||||||
|
content: "\e902"
|
||||||
|
|
||||||
|
.icon-favorite:before
|
||||||
|
content: "\e903"
|
||||||
|
|
||||||
|
.icon-keyboard_arrow_right:before
|
||||||
|
content: "\e904"
|
||||||
|
|
||||||
|
.icon-remove_circle_outline:before
|
||||||
|
content: "\e905"
|
||||||
|
|
||||||
|
.icon-shopping_cart:before
|
||||||
|
content: "\e906"
|
||||||
|
|
||||||
|
.icon-thumb_down:before
|
||||||
|
content: "\e907"
|
||||||
|
|
||||||
|
.icon-thumb_up:before
|
||||||
|
content: "\e908"
|
||||||
|
|
||||||
|
.icon-add_circle:before
|
||||||
|
content: "\1f4"
|
|
@ -0,0 +1,3 @@
|
||||||
|
@import "./base"
|
||||||
|
@import "./icon"
|
||||||
|
@import "./mixin"
|
|
@ -0,0 +1,16 @@
|
||||||
|
border-1px($color)
|
||||||
|
position relative
|
||||||
|
&:after
|
||||||
|
display block
|
||||||
|
position: absolute;
|
||||||
|
left: 0
|
||||||
|
bottom: 0
|
||||||
|
width: 100%
|
||||||
|
border-top: 1px solid $color
|
||||||
|
content: ''
|
||||||
|
|
||||||
|
bg-image($url)
|
||||||
|
background-image: url('img/'+$url+'@2x.png')
|
||||||
|
@media(-webkit-min-device-pixel-ratio:3),(min-device-pixel-ratio:3){
|
||||||
|
background-image: url('img/'+$url+'@3x.png')
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template lang="html">
|
||||||
|
<transition name="fade-backdrop">
|
||||||
|
<div class="backdrop" v-show="isShow"></div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
isShow: Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
.backdrop
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
right 0
|
||||||
|
background rgba(7,17,27,0.6)
|
||||||
|
backdrop-filter blur(10px)
|
||||||
|
z-index 40
|
||||||
|
&.fade-backdrop-enter-active,&.fade-backdrop-leave-active
|
||||||
|
transition opacity 0.5s
|
||||||
|
&.fade-backdrop-enter,&.fade-backdrop-leave-active
|
||||||
|
opacity 0
|
||||||
|
</style>
|
|
@ -0,0 +1,86 @@
|
||||||
|
<template lang="html">
|
||||||
|
|
||||||
|
<div class="cartcontrol">
|
||||||
|
<transition name="fadeRotate">
|
||||||
|
<div class="cart-decrease" v-show="food.count>0" @click.stop.prevent="decreaseCart()">
|
||||||
|
<span class="icon-remove_circle_outline inner"></span>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
<div class="cart-count" v-show="food.count>0">
|
||||||
|
{{food.count}}
|
||||||
|
</div>
|
||||||
|
<div class="cart-add" @click.stop.prevent="addCart($event)">
|
||||||
|
<i class="icon-add_circle"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
food: Object
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addCart(event) {
|
||||||
|
console.log(event.target);
|
||||||
|
if (!event._constructed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!this.food.count) {
|
||||||
|
Vue.set(this.food, 'count', 0)
|
||||||
|
}
|
||||||
|
this.food.count++;
|
||||||
|
this.$root.eventHub.$emit('cart.add', event.target)
|
||||||
|
},
|
||||||
|
decreaseCart() {
|
||||||
|
if (!event._constructed || !this.food.count) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.food.count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
|
||||||
|
.cartcontrol
|
||||||
|
.cart-decrease
|
||||||
|
display inline-block
|
||||||
|
padding 6px
|
||||||
|
transition: all .4s linear
|
||||||
|
.inner
|
||||||
|
line-height 24px
|
||||||
|
font-size 24px
|
||||||
|
color rgb(0,160,220)
|
||||||
|
transition all 0.4s linear
|
||||||
|
&.fadeRotate-enter-active, &.fadeRotate-leave-active
|
||||||
|
transform translate3d(0,0,0)
|
||||||
|
.inner
|
||||||
|
display inline-block
|
||||||
|
transform rotate(0)
|
||||||
|
&.fadeRotate-enter, &.fadeRotate-leave-active
|
||||||
|
opacity: 0
|
||||||
|
transform translate3d(24px,0,0)
|
||||||
|
.inner
|
||||||
|
transform rotate(180deg)
|
||||||
|
.cart-count
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
font-size 10px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
line-height 24px
|
||||||
|
text-align center
|
||||||
|
padding 6px 0
|
||||||
|
.cart-add
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
font-size 24px
|
||||||
|
color rgb(0,160,220)
|
||||||
|
line-height 24px
|
||||||
|
padding 6px
|
||||||
|
</style>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="food" v-show="showDetails">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
food: Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.food
|
||||||
|
position fixed
|
||||||
|
left 0
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
bottom 48px
|
||||||
|
width 100%
|
||||||
|
background white
|
||||||
|
z-index 30
|
||||||
|
</style>
|
|
@ -0,0 +1,323 @@
|
||||||
|
<template lang="html">
|
||||||
|
<transition name="move">
|
||||||
|
<div class="detailWrapper" ref="detailWrapper" v-show="showDetail">
|
||||||
|
<div class="foodDetail">
|
||||||
|
<div class="back" @click="showToggle()">
|
||||||
|
<i class="icon-arrow_lift"></i>
|
||||||
|
</div>
|
||||||
|
<img :src="food.image" height="425" width="100%">
|
||||||
|
<div class="info">
|
||||||
|
<div class="title">{{food.name}}</div>
|
||||||
|
<div class="desc">
|
||||||
|
<span>月售{{food.sellCount}}</span>
|
||||||
|
<span>好评率{{food.rating}}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="price">
|
||||||
|
<span class="unit">¥</span>{{food.price}}
|
||||||
|
<span class="oldPrice" v-show="food.oldPrice">¥{{food.oldPrice}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="shopCart">
|
||||||
|
<transition name="fade">
|
||||||
|
<div class="text" @click="addCart($event)" v-show="!food.count">加入购物车</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<cartcontrol :food="food"></cartcontrol>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="desc">
|
||||||
|
<div class="title">商品介绍</div>
|
||||||
|
<div class="content">{{food.info}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="evaluation">
|
||||||
|
<div class="title">
|
||||||
|
商品评价
|
||||||
|
</div>
|
||||||
|
<div class="classify">
|
||||||
|
<span v-for="(item,index) in classifyArr" class="item" :class="{'active':item.active,'bad':index==2,'badActive':item.active&&index==2}" @click="filterEvel(item)">
|
||||||
|
{{item.name}}<span class="count">{{item.count}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="switch" @click="evelflag=!evelflag">
|
||||||
|
<span class="icon-check_circle" :class="{'on':evelflag}"></span>
|
||||||
|
<span class="text">只看有内容的评价</span>
|
||||||
|
</div>
|
||||||
|
<div class="evel-list">
|
||||||
|
<ul>
|
||||||
|
<li class="evel" v-for="evel in evelArr">
|
||||||
|
<div class="userInfo">
|
||||||
|
<div class="time">{{evel.rateTime | time}}</div>
|
||||||
|
<div class="user">
|
||||||
|
<span>{{evel.username}}</span>
|
||||||
|
<span class="avatar"><img :src="evel.avatar" width="12" height="12"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<span class="icon" :class="evel.rateType?'icon-thumb_down':'icon-thumb_up'"></span>
|
||||||
|
<span class="text">{{evel.text}}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import '../../filter/time.js'
|
||||||
|
import BScroll from 'better-scroll'
|
||||||
|
import cartcontrol from 'components/cartcontrol/cartcontrol'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
cartcontrol
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
food: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showDetail: false,
|
||||||
|
classifyArr: [{
|
||||||
|
name: '全部',
|
||||||
|
count: this.food.ratings.length,
|
||||||
|
active: true
|
||||||
|
}, {
|
||||||
|
name: '推荐',
|
||||||
|
count: this.food.ratings.filter((data) => data.rateType === 0).length,
|
||||||
|
active: false
|
||||||
|
}, {
|
||||||
|
name: '吐槽',
|
||||||
|
count: this.food.ratings.filter((data) => data.rateType).length,
|
||||||
|
active: false
|
||||||
|
}],
|
||||||
|
evelflag: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
evelArr() {
|
||||||
|
let selectIndex = 0
|
||||||
|
this.classifyArr.forEach((data, index) => {
|
||||||
|
if (data.active) {
|
||||||
|
selectIndex = index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (this.detailWrapper) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.detailWrapper.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return selectIndex ? this.food.ratings.filter((data) => this.evelflag ? data.rateType === selectIndex - 1 && data.text : data.rateType === selectIndex - 1) : this.food.ratings.filter((data) => this.evelflag ? data.text : true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showToggle() {
|
||||||
|
this.showDetail = !this.showDetail
|
||||||
|
if (this.showDetail) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this._initScroll()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_initScroll() {
|
||||||
|
if (!this.detailWrapper) {
|
||||||
|
this.detailWrapper = new BScroll(this.$refs.detailWrapper, {
|
||||||
|
click: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.detailWrapper.refresh()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addCart(event) {
|
||||||
|
if (!event._constructed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$set(this.food, 'count', 1)
|
||||||
|
this.$root.eventHub.$emit('cart.add', event.target)
|
||||||
|
},
|
||||||
|
filterEvel(item) {
|
||||||
|
this.classifyArr.forEach((data) => {
|
||||||
|
data.active = false
|
||||||
|
})
|
||||||
|
item.active = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.detailWrapper
|
||||||
|
position fixed
|
||||||
|
left 0
|
||||||
|
top 0
|
||||||
|
bottom 48px
|
||||||
|
width 100%
|
||||||
|
background white
|
||||||
|
transition all 0.4s ease
|
||||||
|
&.move-enter-avtive,&.move-leave-active{
|
||||||
|
transform translate3d(0,0,0)
|
||||||
|
}
|
||||||
|
&.move-enter,&.move-leave-active{
|
||||||
|
transform translate3d(100%,0,0)
|
||||||
|
}
|
||||||
|
.foodDetail
|
||||||
|
.back
|
||||||
|
position absolute
|
||||||
|
color white
|
||||||
|
top 12px
|
||||||
|
left 6px
|
||||||
|
font-size 20px
|
||||||
|
padding 10px
|
||||||
|
.info
|
||||||
|
position relative
|
||||||
|
box-sizing border-box
|
||||||
|
width 100%
|
||||||
|
padding 18px
|
||||||
|
.title
|
||||||
|
font-size 14px
|
||||||
|
font-weight 700
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 14px
|
||||||
|
.desc
|
||||||
|
display flex
|
||||||
|
padding 0
|
||||||
|
padding-top 8px
|
||||||
|
font-size 10px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
line-height 10px
|
||||||
|
span:last-child
|
||||||
|
padding-left 12px
|
||||||
|
.price
|
||||||
|
display flex
|
||||||
|
padding-top 18px
|
||||||
|
font-size 14px
|
||||||
|
font-weight 700
|
||||||
|
color rgb(240,20,20)
|
||||||
|
line-height 24px
|
||||||
|
.unit
|
||||||
|
font-size 10px
|
||||||
|
font-weight normal
|
||||||
|
.oldPrice
|
||||||
|
padding-left 12px
|
||||||
|
font-size 10px
|
||||||
|
font-weight normal
|
||||||
|
color rgb(147,153,159)
|
||||||
|
line-height 24px
|
||||||
|
.shopCart
|
||||||
|
position absolute
|
||||||
|
right 18px
|
||||||
|
bottom 18px
|
||||||
|
height 24px
|
||||||
|
text-align center
|
||||||
|
z-index 2
|
||||||
|
.text
|
||||||
|
box-sizing border-box
|
||||||
|
height 100%
|
||||||
|
line-height 24px
|
||||||
|
color white
|
||||||
|
font-size 10px
|
||||||
|
padding 0 12px
|
||||||
|
border-radius 12px
|
||||||
|
background rgb(0,160,220)
|
||||||
|
&.fade-enter-active,&.fade-leave-active{
|
||||||
|
transition opacity .2s
|
||||||
|
}
|
||||||
|
&.fade-enter,&.fade-leave-active{
|
||||||
|
opacity 0
|
||||||
|
}
|
||||||
|
.cartcontrol
|
||||||
|
position absolute
|
||||||
|
right 12px
|
||||||
|
bottom 12px
|
||||||
|
.desc
|
||||||
|
padding 18px
|
||||||
|
.title
|
||||||
|
font-size 14px
|
||||||
|
font-weight 500
|
||||||
|
color #07111b
|
||||||
|
margin-bottom 6px
|
||||||
|
.content
|
||||||
|
font-size 12px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(77,85,93)
|
||||||
|
line-height 24px
|
||||||
|
padding 0 8px
|
||||||
|
.evaluation
|
||||||
|
padding 18px 0
|
||||||
|
position relative
|
||||||
|
.title
|
||||||
|
padding-left 18px
|
||||||
|
font-size: 14px
|
||||||
|
font-weight 500
|
||||||
|
color: #07111b
|
||||||
|
.classify
|
||||||
|
padding 18px 0
|
||||||
|
margin 0 18px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.item
|
||||||
|
display inline-block
|
||||||
|
font-size 12px
|
||||||
|
padding 8px 12px
|
||||||
|
line-height 16px
|
||||||
|
background rgba(0,160,220,0.2)
|
||||||
|
color rgb(77,85,95)
|
||||||
|
margin-right 8px
|
||||||
|
.count
|
||||||
|
font-size 8px
|
||||||
|
padding-left 2px
|
||||||
|
&.active
|
||||||
|
color white
|
||||||
|
background rgb(0,169,220)
|
||||||
|
&.bad
|
||||||
|
background rgba(77,85,93,0.2)
|
||||||
|
&.badActive
|
||||||
|
background #4d555d
|
||||||
|
.switch
|
||||||
|
font-size 12px
|
||||||
|
width 100%
|
||||||
|
padding 12px 0 12px 18px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.icon-check_circle
|
||||||
|
font-size 24px
|
||||||
|
vertical-align middle
|
||||||
|
&.on
|
||||||
|
color #00c850
|
||||||
|
.evel-list
|
||||||
|
margin 0 18px
|
||||||
|
.evel
|
||||||
|
padding 16px 0
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.userInfo
|
||||||
|
display flex
|
||||||
|
color rgb(147,153,159)
|
||||||
|
font-size 10px
|
||||||
|
line-height 12px
|
||||||
|
.time
|
||||||
|
flex 1
|
||||||
|
.user
|
||||||
|
flex 1
|
||||||
|
text-align right
|
||||||
|
.avatar
|
||||||
|
img
|
||||||
|
padding-left 6px
|
||||||
|
border-radius 50%
|
||||||
|
.content
|
||||||
|
padding-top 6px
|
||||||
|
.icon
|
||||||
|
font-size 12px
|
||||||
|
line-height 24px
|
||||||
|
&.icon-thumb_up
|
||||||
|
color rgb(0,160,220)
|
||||||
|
&.icon-thumb_down
|
||||||
|
color rgb(147,153,159)
|
||||||
|
.text
|
||||||
|
font-size 12px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 16px
|
||||||
|
padding-left 4px
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,256 @@
|
||||||
|
<template lang="html">
|
||||||
|
|
||||||
|
<div class="goods">
|
||||||
|
<div class="menu-wrapper" ref="menuWrapper">
|
||||||
|
<ul>
|
||||||
|
<li v-for="(item,index) in goods" @click="menuClick(index,$event)" :class="index==menuCurrentIndex?'menu-item-selected':'menu-item'">
|
||||||
|
<span class="text">
|
||||||
|
<iconMap v-show="item.type>0" :iconType="item.type"></iconMap>
|
||||||
|
{{item.name}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="foods-wrapper" id="wrapper" ref="foodsWrapper">
|
||||||
|
<ul>
|
||||||
|
<li v-for="item in goods" class="food-list food-list-hook">
|
||||||
|
<h1>{{item.name}}</h1>
|
||||||
|
<ul>
|
||||||
|
<li v-for="food in item.foods" class="food-item" @click="goDetail(food)">
|
||||||
|
<div class="icon">
|
||||||
|
<img width="57" height="57" :src="food.icon"/>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<h2>{{food.name}}</h2>
|
||||||
|
<p class="description" v-show="food.description">{{food.description}}</p>
|
||||||
|
<div class="sell-info">
|
||||||
|
<span class="sellCount">月售{{food.sellCount}}份</span>
|
||||||
|
<span class="rating">好评率{{food.rating}}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="price">
|
||||||
|
<span class="newPrice"><span class="unit">¥</span>{{food.price}}</span>
|
||||||
|
<span v-show="food.oldPrice" class="oldPrice">¥{{food.oldPrice}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="cartcontrol-wrapper">
|
||||||
|
<cartcontrol :food="food"></cartcontrol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<shopCart :deliveryPrice="seller.deliveryPrice" :minPrice = "seller.minPrice" :selectFoods="selectFoods"></shopCart>
|
||||||
|
<foodDetail :food="selectedFood" v-if="selectedFood" ref="myFood"></foodDetail>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import iconMap from 'components/iconMap/iconMap'
|
||||||
|
import BScroll from 'better-scroll'
|
||||||
|
import shopCart from 'components/shopCart/shopCart'
|
||||||
|
import cartcontrol from 'components/cartcontrol/cartcontrol'
|
||||||
|
import foodDetail from 'components/foodDetail/foodDetail'
|
||||||
|
import axios from 'axios'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
const ERR_OK = 0
|
||||||
|
const eventHub = new Vue()
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
seller: Object
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
axios.get('static/data.json').then((res) => {
|
||||||
|
this.goods = res.data.goods
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this._initScroll(); // 初始化scroll
|
||||||
|
this._calculateHeight(); // 初始化列表高度列表
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
goods: [],
|
||||||
|
listHeight: [],
|
||||||
|
foodsScrollY: 0,
|
||||||
|
selectedFood: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
menuCurrentIndex() {
|
||||||
|
for (let i = 0, l = this.listHeight.length; i < l; i++) {
|
||||||
|
let topHeight = this.listHeight[i]
|
||||||
|
let bottomHeight = this.listHeight[i + 1]
|
||||||
|
if (!bottomHeight || (this.foodsScrollY >= topHeight && this.foodsScrollY < bottomHeight)) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
selectFoods() {
|
||||||
|
let foods = []
|
||||||
|
this.goods.forEach((good) => {
|
||||||
|
good.foods.forEach((food) => {
|
||||||
|
if (food.count) {
|
||||||
|
foods.push(food)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return foods
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
_initScroll() {
|
||||||
|
this.menuWrapper = new BScroll(this.$refs.menuWrapper, {
|
||||||
|
click: true
|
||||||
|
});
|
||||||
|
this.foodsScroll = new BScroll(this.$refs.foodsWrapper, {
|
||||||
|
click: true,
|
||||||
|
probeType: 3
|
||||||
|
});
|
||||||
|
// 监控滚动事件
|
||||||
|
this.foodsScroll.on('scroll', (pos) => {
|
||||||
|
this.foodsScrollY = Math.abs(Math.round(pos.y))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_calculateHeight() {
|
||||||
|
let foodList = this.$refs.foodsWrapper.querySelectorAll('.food-list-hook')
|
||||||
|
let height = 0
|
||||||
|
this.listHeight.push(height)
|
||||||
|
for (let i = 0, l = foodList.length; i < l; i++) {
|
||||||
|
let item = foodList[i]
|
||||||
|
height += item.clientHeight
|
||||||
|
this.listHeight.push(height)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
menuClick(index, event) {
|
||||||
|
if (!event._constructed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.foodsScroll.scrollTo(0, -this.listHeight[index], 300)
|
||||||
|
},
|
||||||
|
goDetail(food) {
|
||||||
|
this.selectedFood = food
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.myFood.showToggle()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
iconMap,
|
||||||
|
shopCart,
|
||||||
|
cartcontrol,
|
||||||
|
foodDetail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus">
|
||||||
|
@import '../../common/stylus/mixin'
|
||||||
|
.goods
|
||||||
|
display flex
|
||||||
|
position absolute
|
||||||
|
top 174px
|
||||||
|
bottom 46px
|
||||||
|
width 100%
|
||||||
|
overflow hidden
|
||||||
|
.menu-wrapper
|
||||||
|
flex 0 0 80px
|
||||||
|
width 80px
|
||||||
|
background #F3F5BD
|
||||||
|
margin-top: 2px;
|
||||||
|
.menu-item-selected
|
||||||
|
background white
|
||||||
|
font-weight 700
|
||||||
|
margin-top -1px
|
||||||
|
.menu-item,.menu-item-selected
|
||||||
|
position relative
|
||||||
|
display table
|
||||||
|
height 54px
|
||||||
|
line-height 14px
|
||||||
|
width 56px
|
||||||
|
padding 0 12px
|
||||||
|
&:last-child:after
|
||||||
|
content none
|
||||||
|
.menu-item:after
|
||||||
|
position: absolute
|
||||||
|
content: ''
|
||||||
|
left: 12px
|
||||||
|
width: 56px
|
||||||
|
bottom: 0
|
||||||
|
border-bottom: 1px solid rgba(7,17,27,0.1)
|
||||||
|
.text
|
||||||
|
display table-cell
|
||||||
|
vertical-align middle
|
||||||
|
font-size 12px
|
||||||
|
font-weight 200
|
||||||
|
white-space normal
|
||||||
|
line-height 14px
|
||||||
|
.iconMap
|
||||||
|
vertical-align middle
|
||||||
|
.foods-wrapper
|
||||||
|
flex 1
|
||||||
|
margin-top: 2px;
|
||||||
|
.food-list
|
||||||
|
h1
|
||||||
|
height 26px
|
||||||
|
line-height 26px
|
||||||
|
padding-left 12px
|
||||||
|
font-size 12px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
background #f3f5f7
|
||||||
|
border-left 2px solid #d9dde1
|
||||||
|
.food-item
|
||||||
|
position relative
|
||||||
|
display flex
|
||||||
|
margin: 0 18px;
|
||||||
|
padding: 18px 0;
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.icon
|
||||||
|
flex 0 0 57px
|
||||||
|
&:last-child
|
||||||
|
border-bottom none
|
||||||
|
.content
|
||||||
|
flex 1
|
||||||
|
padding-left 10px
|
||||||
|
h2
|
||||||
|
margin 2px 0 8px 0
|
||||||
|
font-size 14px
|
||||||
|
line-height 14px
|
||||||
|
height 14px
|
||||||
|
font-weight 700
|
||||||
|
color rgb(7,17,27)
|
||||||
|
.sell-info,.description
|
||||||
|
font-size 10px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
line-height 10px
|
||||||
|
.sellCount
|
||||||
|
margin-right 4px
|
||||||
|
.description
|
||||||
|
font-size 10px
|
||||||
|
margin-bottom 8px
|
||||||
|
line-height: 12px
|
||||||
|
.price
|
||||||
|
font-size 10px
|
||||||
|
font-weight 700
|
||||||
|
line-height 24px
|
||||||
|
.newPrice
|
||||||
|
font-size 14px
|
||||||
|
color rgb(240,20,20)
|
||||||
|
.unit
|
||||||
|
font-size 10px
|
||||||
|
font-weight normal
|
||||||
|
.oldPrice
|
||||||
|
text-decoration line-through
|
||||||
|
color rgb(147,153,159)
|
||||||
|
padding-left 4px
|
||||||
|
.cartcontrol-wrapper
|
||||||
|
position: absolute
|
||||||
|
right: 0
|
||||||
|
bottom 12px
|
||||||
|
z-index 20
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,304 @@
|
||||||
|
<template lang="html">
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div class="title">
|
||||||
|
<span class="brand"></span>
|
||||||
|
<span class="name">{{seller.name}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
{{seller.description + ' / ' + seller.deliveryTime + '分钟送达'}}
|
||||||
|
</div>
|
||||||
|
<div class="supports" v-if="seller.supports">
|
||||||
|
<div class="supports_desc">
|
||||||
|
<span class="icon" :class="iconClassMap[seller.supports[0].type]"></span>
|
||||||
|
<span class="text">{{seller.supports[0].description}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="support-count" v-if="seller.supports" @click="showDetails()">
|
||||||
|
<span class="count">{{seller.supports.length+'个'}}</span>
|
||||||
|
<i class="icon-keyboard_arrow_right"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bulletin-wrapper" @click="showDetails()">
|
||||||
|
<span class="bulletin-title"></span>
|
||||||
|
<span class="bulletin-text">{{seller.bulletin}}</span>
|
||||||
|
<i class="icon-keyboard_arrow_right"></i>
|
||||||
|
</div>
|
||||||
|
<div class="background">
|
||||||
|
<img :src="seller.avatar" width="100%" height="100%"/>
|
||||||
|
</div>
|
||||||
|
<transition name="fade">
|
||||||
|
<div v-if="detailShow" class="detail">
|
||||||
|
<div class="detail-wrapper clearfix">
|
||||||
|
<div class="detail-main">
|
||||||
|
<h1 class="name">{{seller.name}}</h1>
|
||||||
|
<div class="star-wrapper">
|
||||||
|
<star :size="48" :score="seller.score"></star>
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
<div class="line"> </div>
|
||||||
|
<div class="text">优惠信息</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
</div>
|
||||||
|
<ul v-if="seller.supports" class="supports">
|
||||||
|
<li class="support-item" v-for="item in seller.supports">
|
||||||
|
<span class="icon" :class="iconClassMap[item.type]"></span>
|
||||||
|
<span class="text">{{item.description}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="title">
|
||||||
|
<div class="line"> </div>
|
||||||
|
<div class="text">商家公告</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
</div>
|
||||||
|
<div class="bulletin">{{seller.bulletin}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-close">
|
||||||
|
<i class="icon-close" @click="hideDetail()"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import star from 'components/star/star'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
seller: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.iconClassMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
star
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
detailShow: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showDetails() {
|
||||||
|
this.detailShow = true;
|
||||||
|
},
|
||||||
|
hideDetail() {
|
||||||
|
this.detailShow = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" rel="stylesheet/stylus">
|
||||||
|
@import '../../common/stylus/mixin'
|
||||||
|
|
||||||
|
|
||||||
|
.header
|
||||||
|
position relative
|
||||||
|
background: steelblue;
|
||||||
|
color #fff
|
||||||
|
blur:10px
|
||||||
|
overflow hidden
|
||||||
|
.content-wrapper
|
||||||
|
position relative
|
||||||
|
display flex
|
||||||
|
padding: 24px 12px 18px 24px
|
||||||
|
font-size 12px
|
||||||
|
.avatar
|
||||||
|
img
|
||||||
|
border-radius 2px
|
||||||
|
.content
|
||||||
|
margin: 0 auto;
|
||||||
|
.title
|
||||||
|
margin 2px 0 8px 0
|
||||||
|
font-size 16px
|
||||||
|
.brand
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
width 30px
|
||||||
|
height 18px
|
||||||
|
bg-image('brand')
|
||||||
|
background-size 30px 18px
|
||||||
|
background-repeat no-repeat
|
||||||
|
.name
|
||||||
|
margin-left 6px
|
||||||
|
font-size 16px
|
||||||
|
line-height 18px
|
||||||
|
font-weight bold
|
||||||
|
.description
|
||||||
|
font-size 12px
|
||||||
|
margin-bottom 10px
|
||||||
|
.supports
|
||||||
|
.icon
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
width 12px
|
||||||
|
height 12px
|
||||||
|
margin-right 4px
|
||||||
|
background-size 12px 12px
|
||||||
|
background-repeat no-repeat
|
||||||
|
&.decrease
|
||||||
|
bg-image('decrease_1')
|
||||||
|
&.discount
|
||||||
|
bg-image('discount_1')
|
||||||
|
&.guarantee
|
||||||
|
bg-image('guarantee_1')
|
||||||
|
&.invoice
|
||||||
|
bg-image('invoice_1')
|
||||||
|
&.special
|
||||||
|
bg-image('special_1')
|
||||||
|
.text
|
||||||
|
line-height 12px
|
||||||
|
font-size 10px
|
||||||
|
.support-count
|
||||||
|
position absolute
|
||||||
|
right 12px
|
||||||
|
bottom 18px
|
||||||
|
padding 0 8px
|
||||||
|
height 24px
|
||||||
|
line-height 24px
|
||||||
|
border-radius 14px
|
||||||
|
background-color rgba(0,0,0,0.2)
|
||||||
|
text-align center
|
||||||
|
.count
|
||||||
|
vertical-align top
|
||||||
|
font-size 10px
|
||||||
|
.icon-keyboard_arrow_right
|
||||||
|
font-size 10px
|
||||||
|
margin-left 2px
|
||||||
|
line-height 24px
|
||||||
|
.bulletin-wrapper
|
||||||
|
position relative
|
||||||
|
height 28px
|
||||||
|
line-height 28px
|
||||||
|
padding 0 22px 0 12px
|
||||||
|
white-space nowrap
|
||||||
|
overflow hidden
|
||||||
|
text-overflow ellipsis
|
||||||
|
background rgba(7,17,27,0.2)
|
||||||
|
.bulletin-title
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
margin-top 8px
|
||||||
|
width 22px
|
||||||
|
height 12px
|
||||||
|
bg-image('bulletin')
|
||||||
|
background-size 100% 100%
|
||||||
|
background-repeat no-repeat
|
||||||
|
.bulletin-text
|
||||||
|
font-size 10px
|
||||||
|
vertical-align middle
|
||||||
|
margin 0 4px
|
||||||
|
.icon-keyboard_arrow_right
|
||||||
|
position absolute
|
||||||
|
font-size 10px
|
||||||
|
right 12px
|
||||||
|
top 8px
|
||||||
|
.background
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
filter blur(10px)
|
||||||
|
z-index -1
|
||||||
|
.detail
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
z-index 100
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
background rgba(7,17,27,0.8)
|
||||||
|
backdrop-filter blur(10px)
|
||||||
|
.detail-wrapper
|
||||||
|
min-height 100%
|
||||||
|
width 100%
|
||||||
|
.detail-main
|
||||||
|
margin-top 64px
|
||||||
|
padding-bottom 64px
|
||||||
|
.name
|
||||||
|
font-size 16px
|
||||||
|
font-weight 700
|
||||||
|
width 100%
|
||||||
|
color rgb(255,255,255)
|
||||||
|
line-height 16px
|
||||||
|
text-align center
|
||||||
|
.star-wrapper
|
||||||
|
margin 16px 11px 28px 0
|
||||||
|
text-align center
|
||||||
|
.title
|
||||||
|
display flex
|
||||||
|
width 80%
|
||||||
|
margin 0 auto 24px auto;
|
||||||
|
.line
|
||||||
|
display inline-block
|
||||||
|
flex 1
|
||||||
|
height 1px
|
||||||
|
background rgba(255,255,255,0.2)
|
||||||
|
margin auto
|
||||||
|
.text
|
||||||
|
padding 0 12px
|
||||||
|
font-size 14px
|
||||||
|
font-weight 700
|
||||||
|
.supports
|
||||||
|
padding 0 0 28px 36px
|
||||||
|
.support-item
|
||||||
|
color white
|
||||||
|
padding 0 6px 12px 16px
|
||||||
|
.text
|
||||||
|
vertical-align middle
|
||||||
|
font-size 12px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(255,255,255)
|
||||||
|
line-height 12px
|
||||||
|
.icon
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
width 16px
|
||||||
|
height 16px
|
||||||
|
margin-right 6px
|
||||||
|
background-size 100% 100%
|
||||||
|
background-repeat no-repeat
|
||||||
|
&.decrease
|
||||||
|
bg-image('decrease_2')
|
||||||
|
&.discount
|
||||||
|
bg-image('discount_2')
|
||||||
|
&.guarantee
|
||||||
|
bg-image('guarantee_2')
|
||||||
|
&.invoice
|
||||||
|
bg-image('invoice_2')
|
||||||
|
&.special
|
||||||
|
bg-image('special_2')
|
||||||
|
.bulletin
|
||||||
|
padding 0 48px
|
||||||
|
font-size 12px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(255,255,255)
|
||||||
|
line-height 24px
|
||||||
|
|
||||||
|
.detail-close
|
||||||
|
position relative
|
||||||
|
width 32px
|
||||||
|
height 32px
|
||||||
|
margin -64px auto 0 auto
|
||||||
|
clear both
|
||||||
|
font-size 32px
|
||||||
|
color rgba(255,255,255,0.5)
|
||||||
|
&.fade-enter-active, &.fade-leave-active {
|
||||||
|
transition: opacity .5s
|
||||||
|
}
|
||||||
|
&.fade-enter, &.fade-leave-active {
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
</style>
|
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,35 @@
|
||||||
|
<template lang="html">
|
||||||
|
<span class="iconMap" :class="iconClassMap[iconType]"></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
iconType: Number
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.iconClassMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '../../common/stylus/mixin'
|
||||||
|
.iconMap
|
||||||
|
display inline-block
|
||||||
|
background-size 100% 100%
|
||||||
|
background-repeat no-repeat
|
||||||
|
width 12px
|
||||||
|
height 12px
|
||||||
|
&.decrease
|
||||||
|
bg-image('decrease_4')
|
||||||
|
&.discount
|
||||||
|
bg-image('discount_4')
|
||||||
|
&.guarantee
|
||||||
|
bg-image('guarantee_4')
|
||||||
|
&.invoice
|
||||||
|
bg-image('invoice_4')
|
||||||
|
&.special
|
||||||
|
bg-image('special_4')
|
||||||
|
</style>
|
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,290 @@
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.ratingsWrapper
|
||||||
|
position: absolute
|
||||||
|
top: 174px
|
||||||
|
bottom: 0
|
||||||
|
left: 0
|
||||||
|
width: 100%
|
||||||
|
overflow: hidden
|
||||||
|
.ratings-content
|
||||||
|
.info
|
||||||
|
display flex
|
||||||
|
.mark
|
||||||
|
flex 0 0 138px
|
||||||
|
margin 18px 0
|
||||||
|
border-right 1px solid rgba(7,17,27,0.1)
|
||||||
|
text-align center
|
||||||
|
.num
|
||||||
|
font-size 24px
|
||||||
|
color rgb(255,153,0)
|
||||||
|
line-height 28px
|
||||||
|
.text
|
||||||
|
padding 6px 0 8px 0
|
||||||
|
font-size 12px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 12px
|
||||||
|
.contrast
|
||||||
|
font-size 10px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 10px
|
||||||
|
margin-bottom 6px
|
||||||
|
.stars
|
||||||
|
padding 18px 24px
|
||||||
|
.serviceScore,.foodScore,.deliveryTime
|
||||||
|
display flex
|
||||||
|
margin-bottom 8px
|
||||||
|
.text
|
||||||
|
font-size 12px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 18px
|
||||||
|
margin-right 12px
|
||||||
|
.num
|
||||||
|
font-size 12px
|
||||||
|
line-height 18px
|
||||||
|
color rgb(255,153,0)
|
||||||
|
padding-left 12px
|
||||||
|
.deliveryTime
|
||||||
|
margin-bottom 0
|
||||||
|
.time
|
||||||
|
font-size 12px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
line-height 18px
|
||||||
|
.evaluation
|
||||||
|
padding 18px 0
|
||||||
|
position relative
|
||||||
|
.classify
|
||||||
|
padding-bottom 18px
|
||||||
|
margin 0 18px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.item
|
||||||
|
display inline-block
|
||||||
|
font-size 12px
|
||||||
|
padding 8px 12px
|
||||||
|
line-height 16px
|
||||||
|
background rgba(0,160,220,0.2)
|
||||||
|
color rgb(77,85,95)
|
||||||
|
margin-right 8px
|
||||||
|
.count
|
||||||
|
font-size 8px
|
||||||
|
padding-left 2px
|
||||||
|
&.active
|
||||||
|
color white
|
||||||
|
background rgb(0,169,220)
|
||||||
|
&.bad
|
||||||
|
background rgba(77,85,93,0.2)
|
||||||
|
&.badActive
|
||||||
|
background #4d555d
|
||||||
|
.switch
|
||||||
|
font-size 12px
|
||||||
|
width 100%
|
||||||
|
padding 12px 0 12px 18px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.icon-check_circle
|
||||||
|
font-size 24px
|
||||||
|
vertical-align middle
|
||||||
|
&.on
|
||||||
|
color #00c850
|
||||||
|
.evel-list
|
||||||
|
.evel
|
||||||
|
display flex
|
||||||
|
padding 18px 0
|
||||||
|
margin 0 18px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.avatar
|
||||||
|
flex 0 0 28px
|
||||||
|
margin-right 12px
|
||||||
|
img
|
||||||
|
border-radius 50%
|
||||||
|
.content
|
||||||
|
flex 1
|
||||||
|
.user
|
||||||
|
font-size 10px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 12px
|
||||||
|
.rateTime
|
||||||
|
position absolute
|
||||||
|
font-weight 200
|
||||||
|
right 18px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
.star-wrapper
|
||||||
|
font-size 0
|
||||||
|
padding-top 4px
|
||||||
|
margin-bottom 6px
|
||||||
|
.star
|
||||||
|
display inline-block
|
||||||
|
.deliveryTime
|
||||||
|
font-size 10px
|
||||||
|
padding-left 6px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(147,153,159)
|
||||||
|
.text
|
||||||
|
font-size 12px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 18px
|
||||||
|
.recommend
|
||||||
|
padding-top 4px
|
||||||
|
.icon
|
||||||
|
font-size 12px
|
||||||
|
color rgb(0,160,220)
|
||||||
|
line-height 16px
|
||||||
|
.dish
|
||||||
|
display inline-block
|
||||||
|
font-size 9px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
line-height 16px
|
||||||
|
border 1px solid rgba(7,17,27,0.1)
|
||||||
|
padding 2px 6px
|
||||||
|
margin-right 8px
|
||||||
|
white-space normal
|
||||||
|
margin-top 4px
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<template lang="html">
|
||||||
|
<div class="ratingsWrapper" ref="ratingsWrapper">
|
||||||
|
<div class="ratings-content">
|
||||||
|
<div class="info">
|
||||||
|
<div class="mark">
|
||||||
|
<div class="num">{{seller.score}}</div>
|
||||||
|
<div class="text">综合评分</div>
|
||||||
|
<div class="contrast">高于周边商家{{seller.rankRate}}%</div>
|
||||||
|
</div>
|
||||||
|
<div class="stars">
|
||||||
|
<div class="serviceScore">
|
||||||
|
<span class="text">服务态度</span>
|
||||||
|
<star :size="36" :score="seller.serviceScore"></star>
|
||||||
|
<span class="num">{{seller.serviceScore}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="foodScore">
|
||||||
|
<span class="text">服务态度</span>
|
||||||
|
<star :size="36" :score="seller.foodScore"></star>
|
||||||
|
<span class="num">{{seller.foodScore}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="deliveryTime">
|
||||||
|
<span class="text">送达时间</span>
|
||||||
|
<span class="time">{{seller.deliveryTime}}分钟</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="evaluation">
|
||||||
|
<div class="classify">
|
||||||
|
<span v-for="(item,index) in classifyArr" class="item" :class="{'active':item.active,'bad':index==2,'badActive':item.active&&index==2}" @click="filterEvel(item)">
|
||||||
|
{{item.name}}<span class="count">{{item.count}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="switch" @click="evelflag=!evelflag">
|
||||||
|
<span class="icon-check_circle" :class="{'on':evelflag}"></span>
|
||||||
|
<span class="text">只看有内容的评价</span>
|
||||||
|
</div>
|
||||||
|
<div class="evel-list">
|
||||||
|
<ul>
|
||||||
|
<li class="evel" v-for="evel in evelArr">
|
||||||
|
<div class="avatar">
|
||||||
|
<img :src="evel.avatar" width="28" height="28">
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="user">
|
||||||
|
<span class="name">{{evel.username}}</span>
|
||||||
|
<span class="rateTime">{{evel.rateTime | time}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="star-wrapper">
|
||||||
|
<star :size="24" :score="evel.score"></star>
|
||||||
|
<span class="deliveryTime">{{evel.deliveryTime}}分钟送达</span>
|
||||||
|
</div>
|
||||||
|
<div class="text">
|
||||||
|
{{evel.text}}
|
||||||
|
</div>
|
||||||
|
<div class="recommend">
|
||||||
|
<span class="icon icon-thumb_up" v-show="evel.recommend.length"></span>
|
||||||
|
<span class="dish" v-for="dish in evel.recommend">{{dish}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios'
|
||||||
|
import star from 'components/star/star'
|
||||||
|
import BScroll from 'better-scroll'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
star: star
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ratings: [],
|
||||||
|
seller: {},
|
||||||
|
classifyArr: [{
|
||||||
|
name: '全部',
|
||||||
|
count: 0,
|
||||||
|
active: true
|
||||||
|
}, {
|
||||||
|
name: '推荐',
|
||||||
|
count: 0,
|
||||||
|
active: false
|
||||||
|
}, {
|
||||||
|
name: '吐槽',
|
||||||
|
count: 0,
|
||||||
|
active: false
|
||||||
|
}],
|
||||||
|
evelflag: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this._init()
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
evelArr() {
|
||||||
|
let selectIndex = 0
|
||||||
|
this.classifyArr.forEach((data, index) => {
|
||||||
|
if (data.active) {
|
||||||
|
selectIndex = index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (this.scroll) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scroll.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return selectIndex ? this.ratings.filter((data) => this.evelflag ? data.rateType === selectIndex - 1 && data.text : data.rateType === selectIndex - 1) : this.ratings.filter((data) => this.evelflag ? data.text : true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
_init() {
|
||||||
|
axios.get('static/data.json').then((res) => {
|
||||||
|
this.ratings = res.data.ratings
|
||||||
|
this.seller = res.data.seller
|
||||||
|
this._initClassifyArr()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scroll = new BScroll(this.$refs.ratingsWrapper, {
|
||||||
|
click: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_initClassifyArr() {
|
||||||
|
this.classifyArr.forEach((data, index) => {
|
||||||
|
if (index) {
|
||||||
|
data.count = this.ratings.filter((temp) => temp.rateType === index - 1).length
|
||||||
|
} else {
|
||||||
|
data.count = this.ratings.length
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
filterEvel(item) {
|
||||||
|
this.classifyArr.forEach((data) => {
|
||||||
|
data.active = false
|
||||||
|
})
|
||||||
|
item.active = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,253 @@
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.seller-wrapper
|
||||||
|
position absolute
|
||||||
|
top 174px
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
overflow hidden
|
||||||
|
.seller-content
|
||||||
|
.info
|
||||||
|
padding 18px 0
|
||||||
|
margin 0 18px
|
||||||
|
.title
|
||||||
|
padding-bottom 18px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.text
|
||||||
|
font-size 14px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 14px
|
||||||
|
.star-wrapper
|
||||||
|
padding-top 8px
|
||||||
|
font-size 0
|
||||||
|
.star
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
.rate-count,.sell-count
|
||||||
|
display inline-block
|
||||||
|
font-size 10px
|
||||||
|
color rgb(77,85,93)
|
||||||
|
line-height 18px
|
||||||
|
.rate-count
|
||||||
|
padding 0 12px 0 8px
|
||||||
|
.collect
|
||||||
|
position absolute
|
||||||
|
top 18px
|
||||||
|
right 8px
|
||||||
|
width 50px
|
||||||
|
text-align center
|
||||||
|
.icon-favorite
|
||||||
|
font-size 24px
|
||||||
|
line-height 24px
|
||||||
|
color #d4d6d9
|
||||||
|
&.active
|
||||||
|
color rgb(240,20,20)
|
||||||
|
.text
|
||||||
|
display block
|
||||||
|
font-size 10px
|
||||||
|
color rgb(77,85,93)
|
||||||
|
line-height 10px
|
||||||
|
padding-top 4px
|
||||||
|
.remark
|
||||||
|
display flex
|
||||||
|
.block
|
||||||
|
flex 1
|
||||||
|
margin-top 18px
|
||||||
|
text-align center
|
||||||
|
border-right 1px solid rgba(7,17,27,0.1)
|
||||||
|
&:last-child
|
||||||
|
border none
|
||||||
|
h2
|
||||||
|
font-size 10px
|
||||||
|
color rgb(147,153,159)
|
||||||
|
line-height 10px
|
||||||
|
margin-bottom 4px
|
||||||
|
.content
|
||||||
|
font-size 10px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 24px
|
||||||
|
font-weight 200
|
||||||
|
.num
|
||||||
|
position relative
|
||||||
|
top 2px
|
||||||
|
font-size 24px
|
||||||
|
.activities
|
||||||
|
padding-top 18px
|
||||||
|
.bulletin
|
||||||
|
margin 0 18px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
h1
|
||||||
|
font-size 14px
|
||||||
|
color #07111b
|
||||||
|
line-height 14px
|
||||||
|
.content
|
||||||
|
padding 8px 12px 16px 12px
|
||||||
|
font-size 12px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(240,20,20)
|
||||||
|
line-height 24px
|
||||||
|
.supports
|
||||||
|
margin 0 18px
|
||||||
|
.item
|
||||||
|
padding 16px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
font-size 0
|
||||||
|
.iconMap
|
||||||
|
width 16px
|
||||||
|
height 16px
|
||||||
|
vertical-align top
|
||||||
|
margin-right 6px
|
||||||
|
.text
|
||||||
|
font-size 12px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 16px
|
||||||
|
.seller-imgs
|
||||||
|
margin 18px
|
||||||
|
white-space nowrap
|
||||||
|
overflow hidden
|
||||||
|
h1
|
||||||
|
font-size 14px
|
||||||
|
line-height 14px
|
||||||
|
margin-bottom 12px
|
||||||
|
img
|
||||||
|
margin-right 6px
|
||||||
|
.seller-info
|
||||||
|
h1
|
||||||
|
margin 0 18px
|
||||||
|
padding 18px 0 12px 0
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.info-list
|
||||||
|
.info
|
||||||
|
font-size 12px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 16px
|
||||||
|
padding 16px 12px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
&:last-child
|
||||||
|
border none
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<template lang="html">
|
||||||
|
<div class="seller-wrapper" ref="sellerWrapper">
|
||||||
|
<div class="seller-content">
|
||||||
|
<div class="info">
|
||||||
|
<div class="title">
|
||||||
|
<div class="text">{{seller.name}}</div>
|
||||||
|
<div class="star-wrapper">
|
||||||
|
<star :size="36" :score="seller.score"></star>
|
||||||
|
<span class="rate-count">({{seller.ratingCount}})</span>
|
||||||
|
<span class="sell-count">月售{{seller.sellCount}}单</span>
|
||||||
|
</div>
|
||||||
|
<div class="collect" @click="collectflag=!collectflag">
|
||||||
|
<span class="icon-favorite" :class="{'active':collectflag}"></span>
|
||||||
|
<span class="text">{{collectflag?'已收藏':'收藏'}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="remark">
|
||||||
|
<div class="block">
|
||||||
|
<h2>起送价</h2>
|
||||||
|
<div class="content">
|
||||||
|
<span class="num">{{seller.minPrice}}</span>元
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<h2>商家配送</h2>
|
||||||
|
<div class="content">
|
||||||
|
<span class="num">{{seller.deliveryPrice}}</span>元
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<h2>平均配送时间</h2>
|
||||||
|
<div class="content">
|
||||||
|
<span class="num">{{seller.deliveryTime}}</span>分钟
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="activities">
|
||||||
|
<div class="bulletin">
|
||||||
|
<h1>公告与活动</h1>
|
||||||
|
<div class="content">
|
||||||
|
{{seller.bulletin}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="supports">
|
||||||
|
<ul>
|
||||||
|
<li class="item" v-for="item in seller.supports">
|
||||||
|
<iconMap :iconType="item.type"></iconMap>
|
||||||
|
<span class="text">{{item.description}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="seller-imgs">
|
||||||
|
<h1>商家实景</h1>
|
||||||
|
<div class="img-wrapper" ref="picsWrapper">
|
||||||
|
<div ref="picList">
|
||||||
|
<img v-for="pic in seller.pics" :src="pic" width="120" height="90">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="seller-info">
|
||||||
|
<h1>商家信息</h1>
|
||||||
|
<ul class="info-list">
|
||||||
|
<li class="info" v-for="info in seller.infos">{{info}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios'
|
||||||
|
import BScroll from 'better-scroll'
|
||||||
|
import star from 'components/star/star'
|
||||||
|
import iconMap from 'components/iconMap/iconMap'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
star: star,
|
||||||
|
iconMap: iconMap
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
seller: {},
|
||||||
|
collectflag: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this._init()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
_init() {
|
||||||
|
axios.get('static/data.json').then((res) => {
|
||||||
|
this.seller = res.data.seller
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.sellerScroll = new BScroll(this.$refs.sellerWrapper, {
|
||||||
|
click: true
|
||||||
|
})
|
||||||
|
this._initPicScroll()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_initPicScroll() {
|
||||||
|
if (this.picsScroll) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const PIC_WIDTH = 120
|
||||||
|
const MARGIN = 6
|
||||||
|
let picLen = this.seller.pics.length
|
||||||
|
this.$refs.picList.style.width = PIC_WIDTH * picLen + MARGIN * (picLen - 1) + 'px'
|
||||||
|
this.picsScroll = new BScroll(this.$refs.picsWrapper, {
|
||||||
|
scrollX: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,393 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="">
|
||||||
|
<div class="shopCart">
|
||||||
|
<div class="content">
|
||||||
|
<div class="content-left" @click="listToggle">
|
||||||
|
<div class="logo-wrapper">
|
||||||
|
<div class="badge" v-show="totalCount">
|
||||||
|
{{totalCount}}
|
||||||
|
</div>
|
||||||
|
<div class="logo" :class="{'active':totalPrice}">
|
||||||
|
<i class="icon-shopping_cart"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="price" :class="{'active':totalPrice}">
|
||||||
|
¥{{totalPrice}}
|
||||||
|
</div>
|
||||||
|
<div class="desc">
|
||||||
|
另需要配送费¥{{deliveryPrice}}元
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-right" :class="{'enough':totalPrice>=minPrice}">
|
||||||
|
{{payDesc}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ball-container">
|
||||||
|
<transition name="drop" v-on:before-enter="beforeEnter"
|
||||||
|
v-on:enter="enter" v-on:after-enter="afterEnter"
|
||||||
|
v-for="(ball,index) in balls">
|
||||||
|
<div class="ball" v-show="ball.show">
|
||||||
|
<div class="inner inner-hook"></div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<transition name="transHeight">
|
||||||
|
<div class="shopcart-list" v-show="listShow">
|
||||||
|
<div class="list-header">
|
||||||
|
<h1 class="title">购物车</h1>
|
||||||
|
<span class="empty" @click="setEmpty()">清空</span>
|
||||||
|
</div>
|
||||||
|
<div class="list-content" ref="foodlist">
|
||||||
|
<ul>
|
||||||
|
<li class="food" v-for="food in selectFoods">
|
||||||
|
<span class="name">{{food.name}}</span>
|
||||||
|
<div class="price">
|
||||||
|
<span>¥{{food.price * food.count}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="cartcontrol-wrapper">
|
||||||
|
<cartcontrol :food="food"></cartcontrol>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<transition name="fade-backdrop">
|
||||||
|
<div class="backdrop" v-show="showBackdrop" @click="hideBackdrop"></div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import cartcontrol from 'components/cartcontrol/cartcontrol'
|
||||||
|
import backdrop from 'components/backdrop/backdrop'
|
||||||
|
import BScroll from 'better-scroll'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
selectFoods: {
|
||||||
|
type: Array,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
deliveryPrice: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
minPrice: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
balls: [{
|
||||||
|
show: false
|
||||||
|
}, {
|
||||||
|
show: false
|
||||||
|
}, {
|
||||||
|
show: false
|
||||||
|
}, {
|
||||||
|
show: false
|
||||||
|
}, {
|
||||||
|
show: false
|
||||||
|
}],
|
||||||
|
dropBalls: [],
|
||||||
|
listShow: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$root.eventHub.$on('cart.add', this.drop)
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
showBackdrop() {
|
||||||
|
if (this.listShow && this.totalPrice) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
this.listShow = false
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
totalPrice() {
|
||||||
|
let total = 0
|
||||||
|
this.selectFoods.forEach((food) => {
|
||||||
|
if (food.count) {
|
||||||
|
total += food.price * food.count
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return total
|
||||||
|
},
|
||||||
|
totalCount() {
|
||||||
|
let count = 0
|
||||||
|
this.selectFoods.forEach((food) => {
|
||||||
|
count += food.count
|
||||||
|
})
|
||||||
|
return count
|
||||||
|
},
|
||||||
|
leftAmount() {
|
||||||
|
if (this.minPrice - this.totalPrice > 0 && totalPrice) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
payDesc() {
|
||||||
|
let diff = this.minPrice - this.totalPrice
|
||||||
|
if (!this.totalPrice) {
|
||||||
|
return `¥${this.totalPrice}起送`
|
||||||
|
} else if (diff > 0) {
|
||||||
|
return `还差¥${diff}元`
|
||||||
|
} else {
|
||||||
|
return '去结算'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
drop(el) {
|
||||||
|
for (let i = 0, l = this.balls.length; i < l; i++) {
|
||||||
|
let ball = this.balls[i]
|
||||||
|
if (!ball.show) {
|
||||||
|
ball.show = true
|
||||||
|
ball.el = el
|
||||||
|
this.dropBalls.push(ball)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setEmpty() {
|
||||||
|
this.selectFoods.forEach((food) => {
|
||||||
|
food.count = 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
hideBackdrop() {
|
||||||
|
this.listShow = false
|
||||||
|
},
|
||||||
|
_initScroll() {
|
||||||
|
this.foodlistScroll = new BScroll(this.$refs.foodlist, {
|
||||||
|
click: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
listToggle() {
|
||||||
|
if (!this.selectFoods.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.listShow = !this.listShow
|
||||||
|
if (this.listShow) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.foodlistScroll) {
|
||||||
|
this._initScroll()
|
||||||
|
} else {
|
||||||
|
this.foodlistScroll.refresh()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeEnter(el) {
|
||||||
|
let count = this.balls.length
|
||||||
|
while (count--) {
|
||||||
|
let ball = this.balls[count]
|
||||||
|
if (ball.show) {
|
||||||
|
let rect = ball.el.getBoundingClientRect()
|
||||||
|
let x = rect.left - 32;
|
||||||
|
let y = -(window.innerHeight - rect.top - 22)
|
||||||
|
el.style.display = ''
|
||||||
|
el.style.webkitTransform = `translate3d(0,${y}px,0)`
|
||||||
|
el.style.transform = `translate3d(0,${y}px,0)`
|
||||||
|
let inner = el.querySelector('.inner-hook')
|
||||||
|
inner.style.webkitTransform = `translate3d(${x}px,0,0)`
|
||||||
|
inner.style.transform = `translate3d(${x}px,0,0)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enter(el) {
|
||||||
|
el.offsetHeight // 触发浏览器重绘,offsetWidth、offsetTop等方法都可以触发
|
||||||
|
this.$nextTick(() => {
|
||||||
|
el.style.webkitTransform = 'translate3d(0,0,0)'
|
||||||
|
el.style.transform = 'translate3d(0,0,0)'
|
||||||
|
let inner = el.querySelector('.inner-hook')
|
||||||
|
inner.style.webkitTransform = 'translate3d(0,0,0)'
|
||||||
|
inner.style.transform = 'translate3d(0,0,0)'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
afterEnter(el) {
|
||||||
|
let ball = this.dropBalls.shift()
|
||||||
|
if (ball) {
|
||||||
|
ball.show = false
|
||||||
|
el.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
cartcontrol,
|
||||||
|
backdrop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.shopCart
|
||||||
|
position fixed
|
||||||
|
left 0
|
||||||
|
bottom 0
|
||||||
|
width 100%
|
||||||
|
height 48px
|
||||||
|
z-index 50
|
||||||
|
.content
|
||||||
|
display flex
|
||||||
|
background #141d27
|
||||||
|
.content-left
|
||||||
|
flex 1
|
||||||
|
height 48px
|
||||||
|
.logo-wrapper
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
position: relative
|
||||||
|
height: 56px
|
||||||
|
line-height: 56px
|
||||||
|
border-radius: 50%
|
||||||
|
width: 56px
|
||||||
|
top: -10px
|
||||||
|
background: #141d27
|
||||||
|
margin:0 12px
|
||||||
|
padding 6px
|
||||||
|
box-sizing border-box
|
||||||
|
text-align: center
|
||||||
|
.badge
|
||||||
|
position absolute
|
||||||
|
top: 0;
|
||||||
|
right 0
|
||||||
|
background: rgb(240,20,20);
|
||||||
|
color: white;
|
||||||
|
width 24px
|
||||||
|
height 16px
|
||||||
|
line-height: 16px;
|
||||||
|
font-size: 9px;
|
||||||
|
box-shadow: 0px 4px 8px 0px rgba(0,0,0,0.4);
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 16px;
|
||||||
|
text-align center
|
||||||
|
.logo
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
background: #2b343c
|
||||||
|
border-radius: 50%
|
||||||
|
font-size: 24px
|
||||||
|
color: #80858a
|
||||||
|
line-height: 44px
|
||||||
|
font-weight: 700
|
||||||
|
&.active
|
||||||
|
background: rgb(0,160,220);
|
||||||
|
color: white;
|
||||||
|
.price
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
font-size 16px
|
||||||
|
margin-top 12px
|
||||||
|
padding-right 12px
|
||||||
|
box-sizing border-box
|
||||||
|
color rgba(255,255,255,0.4)
|
||||||
|
font-weight 700
|
||||||
|
line-height 24px
|
||||||
|
border-right 1px solid rgba(255,255,255,0.1)
|
||||||
|
&.active
|
||||||
|
color white
|
||||||
|
.desc
|
||||||
|
position relative
|
||||||
|
display inline-block
|
||||||
|
vertical-align top
|
||||||
|
margin 12px 0 0 12px
|
||||||
|
font-size 10px
|
||||||
|
color rgba(255,255,255,0.4)
|
||||||
|
font-weight 700
|
||||||
|
line-height 24px
|
||||||
|
.content-right
|
||||||
|
flex 0 0 105px
|
||||||
|
font-size 12px
|
||||||
|
font-weight 700
|
||||||
|
background #2b343c
|
||||||
|
color rgba(255,255,255,0.4)
|
||||||
|
line-height 48px
|
||||||
|
text-align center
|
||||||
|
&.enough
|
||||||
|
background #00b43c
|
||||||
|
color white
|
||||||
|
.ball-container
|
||||||
|
.ball
|
||||||
|
position fixed
|
||||||
|
left 32px
|
||||||
|
bottom 22px
|
||||||
|
z-index 200
|
||||||
|
&.drop-enter,&.drop-enter-active
|
||||||
|
transition all 0.4s cubic-bezier(0.49,-0.29,0.75,0.41)
|
||||||
|
.inner
|
||||||
|
width 16px
|
||||||
|
height 16px
|
||||||
|
border-radius 50%
|
||||||
|
background rgb(0,160,220)
|
||||||
|
transition all 0.4s linear
|
||||||
|
.shopcart-list
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
background white
|
||||||
|
transform translate3d(0,-100%,0)
|
||||||
|
z-index -1
|
||||||
|
&.transHeight-enter-active,&.transHeight-leave-active
|
||||||
|
transition all 0.5s
|
||||||
|
&.transHeight-enter,&.transHeight-leave-active
|
||||||
|
transform translate3d(0,0,0)
|
||||||
|
.list-header
|
||||||
|
height 40px
|
||||||
|
line-height 40px
|
||||||
|
background #f3f5f7
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.title
|
||||||
|
display inline-block
|
||||||
|
font-size 14px
|
||||||
|
font-weight 200
|
||||||
|
color rgb(7,17,27)
|
||||||
|
padding-left 18px
|
||||||
|
.empty
|
||||||
|
position absolute
|
||||||
|
right 8px
|
||||||
|
font-size 12px
|
||||||
|
color rgb(0,160,220)
|
||||||
|
padding 0 10px
|
||||||
|
.list-content
|
||||||
|
max-height 217px
|
||||||
|
overflow hidden
|
||||||
|
.food
|
||||||
|
position relative
|
||||||
|
display flex
|
||||||
|
height 48px
|
||||||
|
margin 0 18px
|
||||||
|
border-bottom 1px solid rgba(7,17,27,0.1)
|
||||||
|
.name
|
||||||
|
flex 1
|
||||||
|
font-size 14px
|
||||||
|
color rgb(7,17,27)
|
||||||
|
line-height 48px
|
||||||
|
font-weight 700
|
||||||
|
.price
|
||||||
|
font-size 14px
|
||||||
|
font-weight 700
|
||||||
|
color rgb(240,20,20)
|
||||||
|
padding 0 12px 0 18px
|
||||||
|
line-height 48px
|
||||||
|
.cartcontrol-wrapper
|
||||||
|
font-size 14px
|
||||||
|
margin-top 6px
|
||||||
|
.backdrop
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
right 0
|
||||||
|
background rgba(7,17,27,0.6)
|
||||||
|
backdrop-filter blur(10px)
|
||||||
|
z-index 40
|
||||||
|
&.fade-backdrop-enter-active,&.fade-backdrop-leave-active
|
||||||
|
transition opacity 0.5s
|
||||||
|
&.fade-backdrop-enter,&.fade-backdrop-leave-active
|
||||||
|
opacity 0
|
||||||
|
</style>
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.8 KiB |