├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Makefile ├── README.md ├── build ├── bin │ ├── delete.js │ └── new.js ├── build.js ├── check-versions.js ├── dev-client.js ├── dev-server.js ├── utils.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── config ├── dev.env.js ├── index.js └── prod.env.js ├── index.html ├── package-lock.json ├── package.json ├── package ├── README.md ├── package.json └── src │ ├── assets │ ├── beauty_1.png │ ├── beauty_2.png │ ├── beauty_3.png │ ├── beauty_4.png │ ├── beauty_5.png │ ├── close.png │ ├── dialog.gif │ ├── dropdown.png │ ├── logo.png │ ├── picker.gif │ ├── scroller.gif │ ├── search.gif │ ├── search.png │ ├── select.gif │ ├── swiper.gif │ └── table.png │ ├── coms │ ├── dialog │ │ ├── index.js │ │ ├── message │ │ │ ├── Message.vue │ │ │ └── index.js │ │ └── modal │ │ │ ├── Modal.vue │ │ │ └── index.js │ ├── dropdown │ │ ├── Dropdown.vue │ │ ├── DropdownList.vue │ │ └── DropdownMenu.vue │ ├── picker │ │ ├── Custom.vue │ │ ├── Datetime.vue │ │ ├── Picker.vue │ │ ├── animate.js │ │ ├── scroller.js │ │ └── utils.js │ ├── scroller │ │ ├── Arrow.vue │ │ ├── Spinner.vue │ │ ├── core.js │ │ ├── index.vue │ │ └── render.js │ ├── search │ │ ├── SearchList.vue │ │ └── index.vue │ ├── select │ │ └── Select.vue │ ├── swipe │ │ ├── index.vue │ │ ├── swipe-item.vue │ │ └── swipe-menu.vue │ ├── switch │ │ └── Switch.vue │ ├── table │ │ ├── Table.vue │ │ └── TableColumn.vue │ └── vui.common.js │ ├── css │ └── _common.css │ └── js │ ├── clickoutside.js │ ├── dom.js │ └── emitter.js ├── src ├── App.vue ├── assets │ ├── beauty_1.png │ ├── beauty_2.png │ ├── beauty_3.png │ ├── beauty_4.png │ ├── beauty_5.png │ ├── close.png │ ├── dialog.gif │ ├── dropdown.png │ ├── logo.png │ ├── picker.gif │ ├── qrcode.png │ ├── scroller.gif │ ├── search.gif │ ├── search.png │ ├── select.gif │ ├── swiper.gif │ ├── switch.gif │ └── table.png ├── components │ ├── components.json │ ├── dialog │ │ ├── index.js │ │ ├── message │ │ │ ├── Message.vue │ │ │ └── index.js │ │ └── modal │ │ │ ├── Modal.vue │ │ │ └── index.js │ ├── dropdown │ │ ├── Dropdown.vue │ │ ├── DropdownList.vue │ │ └── DropdownMenu.vue │ ├── index.js │ ├── picker │ │ ├── Custom.vue │ │ ├── Datetime.vue │ │ ├── Picker.vue │ │ ├── animate.js │ │ ├── scroller.js │ │ └── utils.js │ ├── scroller │ │ ├── Arrow.vue │ │ ├── Spinner.vue │ │ ├── core.js │ │ ├── index.vue │ │ └── render.js │ ├── search │ │ ├── SearchList.vue │ │ └── index.vue │ ├── select │ │ └── Select.vue │ ├── swiper │ │ ├── Swiper.vue │ │ ├── SwiperItem.vue │ │ └── index.vue │ ├── switch │ │ └── Switch.vue │ └── table │ │ ├── Table.vue │ │ └── TableColumn.vue ├── main.js ├── mixins │ └── emitter.js ├── pages │ ├── dialog.vue │ ├── dropdown.vue │ ├── index.vue │ ├── picker.vue │ ├── scroller.vue │ ├── search.vue │ ├── select.vue │ ├── swiper.vue │ ├── switch.vue │ └── table.vue ├── router │ ├── index.js │ └── router.config.js ├── stylus │ ├── _common.styl │ └── _reset.styl └── utils │ ├── clickoutside.js │ └── dom.js ├── static └── .gitkeep └── vue-part.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | parserOptions: { 5 | sourceType: 'module' 6 | }, 7 | // required to lint *.vue files 8 | plugins: [ 9 | 'html' 10 | ], 11 | // add your custom rules here 12 | 'rules': { 13 | // allow debugger during development 14 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.vue linguist-language=vue -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6 4 | os: linux 5 | cache: 6 | yarn: true 7 | branches: 8 | only: 9 | - master 10 | - dev 11 | install: 12 | - | 13 | npm install 14 | script: 15 | - | 16 | npm run lint && npm run build 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## v1.2.0(2017-12-16) 4 | 5 | ### Features 6 | 7 | - **Usage/Switch:** update the usage of the component, add switch component . [commit](https://github.com/xuqiang521/vui/commit/067f1c7a5ef0242136258a57226110c422b466cf) 8 | - **Switch:** Improve the switch component, make the data use v-model . [commit](https://github.com/xuqiang521/vui/commit/17897ef331895a02334be5a9bee99baf44df93ea) 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test build 2 | 3 | default: help 4 | 5 | install: 6 | npm install 7 | 8 | install-cn: 9 | npm install --registry=http://registry.npm.taobao.org 10 | 11 | install-cnpm: 12 | cnpm install 13 | 14 | build: 15 | npm run build 16 | 17 | opn: 18 | node build/bin/opn.js 19 | 20 | new: 21 | node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS)) 22 | 23 | del: 24 | node build/bin/delete.js $(filter-out $@,$(MAKECMDGOALS)) 25 | 26 | dev: 27 | npm run dev 28 | 29 | # clean: 30 | # rm ./dist/*.js 31 | 32 | help: 33 | @echo " \033[35mmake\033[0m \033[1m命令使用说明\033[0m" 34 | @echo " \033[35mmake install\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 安装依赖" 35 | @echo " \033[35mmake install-cn\033[0m\t\033[0m\t\033[0m\t--- 安装淘宝镜像" 36 | @echo " \033[35mmake install-cnpm\033[0m\t\033[0m\t\033[0m\t--- 淘宝镜像安装依赖" 37 | @echo " \033[35mmake build\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 脚本打包" 38 | @echo " \033[35mmake new [中文名]\033[0m\t--- 创建新全局组件 生成对应文件 例如 'make new button 'button组件'" 39 | @echo " \033[35mmake del \033[0m\t\033[0m\t--- 删除组件 删除对应文件 例如 'make del button'" 40 | @echo " \033[35mmake dev\033[0m\t\033[0m\t\033[0m\t\033[0m\t--- 开发模式" -------------------------------------------------------------------------------- /build/bin/delete.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/build/bin/delete.js -------------------------------------------------------------------------------- /build/bin/new.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | console.log(); 4 | process.on('exit', () => { 5 | console.log(); 6 | }); 7 | 8 | if (!process.argv[2]) { 9 | console.error('[组件名]必填 - Please enter new component name'); 10 | process.exit(1); 11 | } 12 | 13 | const path = require('path'); 14 | const fileSave = require('file-save'); 15 | const uppercamelcase = require('uppercamelcase'); 16 | const componentname = process.argv[2]; 17 | const chineseName = process.argv[3] || componentname; 18 | const ComponentName = uppercamelcase(componentname); 19 | 20 | const filePath = path.resolve(__dirname, '../../src'); 21 | const Files = [ 22 | { 23 | filename: `components/${componentname}/${ComponentName}.vue`, 24 | content: ` 27 | 28 | 33 | 34 | ` 39 | }, 40 | { 41 | filename: `pages/${componentname}.vue`, 42 | content: ` 45 | 46 | 51 | 52 | ` 57 | } 58 | ]; 59 | // 创建 组件文件 60 | Files.forEach(file => { 61 | fileSave(path.join(filePath, file.filename)) 62 | .write(file.content, 'utf8') 63 | .end('\n'); 64 | }); 65 | 66 | const componentsFile = require('../../src/components/components.json'); 67 | 68 | if (componentsFile[ComponentName]) { 69 | console.error(`${componentname} 已存在.`); 70 | process.exit(1); 71 | } 72 | 73 | componentsFile[ComponentName] = `./${componentname}/${ComponentName}` 74 | 75 | // fileSavefileSave(path.join(filePath, file.filename)) 76 | fileSave(path.join(__dirname, '../../src/components/components.json')) 77 | .write(JSON.stringify(componentsFile, null, ' '), 'utf8') 78 | .end('\n'); 79 | 80 | // console.log(componentsFile, ComponentName) 81 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | // https://github.com/shelljs/shelljs 2 | require('./check-versions')() 3 | require('shelljs/global') 4 | env.NODE_ENV = 'production' 5 | 6 | var path = require('path') 7 | var config = require('../config') 8 | var ora = require('ora') 9 | var webpack = require('webpack') 10 | var webpackConfig = require('./webpack.prod.conf') 11 | console.log( 12 | ' Tip:\n' + 13 | ' Built files are meant to be served over an HTTP server.\n' + 14 | ' Opening index.html over file:// won\'t work.\n' 15 | ) 16 | 17 | var spinner = ora('building for production...') 18 | spinner.start() 19 | 20 | var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory) 21 | rm('-rf', assetsPath) 22 | mkdir('-p', assetsPath) 23 | cp('-R', 'static/*', assetsPath) 24 | 25 | webpack(webpackConfig, function (err, stats) { 26 | spinner.stop() 27 | if (err) throw err 28 | process.stdout.write(stats.toString({ 29 | colors: true, 30 | modules: false, 31 | children: false, 32 | chunks: false, 33 | chunkModules: false 34 | }) + '\n') 35 | }) 36 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | var semver = require('semver') 2 | var chalk = require('chalk') 3 | var packageConfig = require('../package.json') 4 | var exec = function (cmd) { 5 | return require('child_process') 6 | .execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | }, 15 | { 16 | name: 'npm', 17 | currentVersion: exec('npm --version'), 18 | versionRequirement: packageConfig.engines.npm 19 | } 20 | ] 21 | 22 | module.exports = function () { 23 | var warnings = [] 24 | for (var i = 0; i < versionRequirements.length; i++) { 25 | var mod = versionRequirements[i] 26 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 27 | warnings.push(mod.name + ': ' + 28 | chalk.red(mod.currentVersion) + ' should be ' + 29 | chalk.green(mod.versionRequirement) 30 | ) 31 | } 32 | } 33 | 34 | if (warnings.length) { 35 | console.log('') 36 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 37 | console.log() 38 | for (var i = 0; i < warnings.length; i++) { 39 | var warning = warnings[i] 40 | console.log(' ' + warning) 41 | } 42 | console.log() 43 | process.exit(1) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /build/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | require('eventsource-polyfill') 3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 4 | 5 | hotClient.subscribe(function (event) { 6 | if (event.action === 'reload') { 7 | window.location.reload() 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | var config = require('../config') 3 | if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 4 | var path = require('path') 5 | var express = require('express') 6 | var webpack = require('webpack') 7 | var opn = require('opn') 8 | var proxyMiddleware = require('http-proxy-middleware') 9 | var webpackConfig = require('./webpack.dev.conf') 10 | 11 | // default port where dev server listens for incoming traffic 12 | var port = process.env.PORT || config.dev.port 13 | // Define HTTP proxies to your custom API backend 14 | // https://github.com/chimurai/http-proxy-middleware 15 | var proxyTable = config.dev.proxyTable 16 | 17 | var app = express() 18 | var compiler = webpack(webpackConfig) 19 | 20 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 21 | publicPath: webpackConfig.output.publicPath, 22 | stats: { 23 | colors: true, 24 | chunks: false 25 | } 26 | }) 27 | 28 | var hotMiddleware = require('webpack-hot-middleware')(compiler) 29 | // force page reload when html-webpack-plugin template changes 30 | compiler.plugin('compilation', function (compilation) { 31 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 32 | hotMiddleware.publish({ action: 'reload' }) 33 | cb() 34 | }) 35 | }) 36 | 37 | // proxy api requests 38 | Object.keys(proxyTable).forEach(function (context) { 39 | var options = proxyTable[context] 40 | if (typeof options === 'string') { 41 | options = { target: options } 42 | } 43 | app.use(proxyMiddleware(context, options)) 44 | }) 45 | 46 | // handle fallback for HTML5 history API 47 | app.use(require('connect-history-api-fallback')()) 48 | 49 | // serve webpack bundle output 50 | app.use(devMiddleware) 51 | 52 | // enable hot-reload and state-preserving 53 | // compilation error display 54 | app.use(hotMiddleware) 55 | 56 | // serve pure static assets 57 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 58 | app.use(staticPath, express.static('./static')) 59 | 60 | module.exports = app.listen(port, function (err) { 61 | if (err) { 62 | console.log(err) 63 | return 64 | } 65 | var uri = 'http://localhost:' + port 66 | console.log('Listening at ' + uri + '\n') 67 | 68 | // when env is testing, don't need open it 69 | if (process.env.NODE_ENV !== 'testing') { 70 | opn(uri) 71 | } 72 | }) 73 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | // generate loader string to be used with extract text plugin 15 | function generateLoaders (loaders) { 16 | var sourceLoader = loaders.map(function (loader) { 17 | var extraParamChar 18 | if (/\?/.test(loader)) { 19 | loader = loader.replace(/\?/, '-loader?') 20 | extraParamChar = '&' 21 | } else { 22 | loader = loader + '-loader' 23 | extraParamChar = '?' 24 | } 25 | return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '') 26 | }).join('!') 27 | 28 | // Extract CSS when that option is specified 29 | // (which is the case during production build) 30 | if (options.extract) { 31 | return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) 32 | } else { 33 | return ['vue-style-loader', sourceLoader].join('!') 34 | } 35 | } 36 | 37 | // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html 38 | return { 39 | css: generateLoaders(['css']), 40 | postcss: generateLoaders(['css']), 41 | less: generateLoaders(['css', 'less']), 42 | sass: generateLoaders(['css', 'sass?indentedSyntax']), 43 | scss: generateLoaders(['css', 'sass']), 44 | stylus: generateLoaders(['css', 'stylus']), 45 | styl: generateLoaders(['css', 'stylus']) 46 | } 47 | } 48 | 49 | // Generate loaders for standalone style files (outside of .vue) 50 | exports.styleLoaders = function (options) { 51 | var output = [] 52 | var loaders = exports.cssLoaders(options) 53 | for (var extension in loaders) { 54 | var loader = loaders[extension] 55 | output.push({ 56 | test: new RegExp('\\.' + extension + '$'), 57 | loader: loader 58 | }) 59 | } 60 | return output 61 | } 62 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var utils = require('./utils') 4 | var projectRoot = path.resolve(__dirname, '../') 5 | 6 | var env = process.env.NODE_ENV 7 | // check env & config/index.js to decide weither to enable CSS Sourcemaps for the 8 | // various preprocessor loaders added to vue-loader at the end of this file 9 | var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap) 10 | var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap) 11 | var useCssSourceMap = cssSourceMapDev || cssSourceMapProd 12 | 13 | module.exports = { 14 | entry: { 15 | app: './src/main.js' 16 | }, 17 | output: { 18 | path: config.build.assetsRoot, 19 | publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, 20 | filename: '[name].js' 21 | }, 22 | resolve: { 23 | extensions: ['', '.js', '.vue'], 24 | fallback: [path.join(__dirname, '../node_modules')], 25 | alias: { 26 | // '@': path.resolve(__dirname, '../src'), 27 | 'vue$': 'vue/dist/vue', 28 | '@': path.resolve(__dirname, '../src'), 29 | 'assets': path.resolve(__dirname, '../src/assets'), 30 | 'components': path.resolve(__dirname, '../src/components'), 31 | 'utils': path.resolve(__dirname, '../src/utils'), 32 | 'pages': path.resolve(__dirname, '../src/pages'), 33 | 'stylus': path.resolve(__dirname, '../src/stylus') 34 | } 35 | }, 36 | resolveLoader: { 37 | fallback: [path.join(__dirname, '../node_modules')] 38 | }, 39 | module: { 40 | preLoaders: [ 41 | { 42 | test: /\.vue$/, 43 | loader: 'eslint', 44 | include: projectRoot, 45 | exclude: /node_modules/ 46 | }, 47 | { 48 | test: /\.js$/, 49 | loader: 'eslint', 50 | include: projectRoot, 51 | exclude: /node_modules/ 52 | } 53 | ], 54 | loaders: [ 55 | { 56 | test: /\.vue$/, 57 | loader: 'vue' 58 | }, 59 | { 60 | test: /\.js$/, 61 | loader: 'babel', 62 | include: projectRoot, 63 | exclude: /node_modules/ 64 | }, 65 | { 66 | test: /\.json$/, 67 | loader: 'json' 68 | }, 69 | { 70 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 71 | loader: 'url', 72 | query: { 73 | limit: 10000, 74 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 75 | } 76 | }, 77 | { 78 | test: /\.styl$/, 79 | use: [ 80 | { 81 | loader: 'style-loader' 82 | }, { 83 | loader: 'css-loader' 84 | }, { 85 | loader: 'stylus-loader' 86 | } 87 | ] 88 | }, 89 | { 90 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 91 | loader: 'url', 92 | query: { 93 | limit: 10000, 94 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 95 | } 96 | } 97 | ] 98 | }, 99 | eslint: { 100 | formatter: require('eslint-friendly-formatter') 101 | }, 102 | vue: { 103 | loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }), 104 | postcss: [ 105 | require('autoprefixer')({ 106 | browsers: ['last 2 versions'] 107 | }) 108 | ] 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var webpack = require('webpack') 3 | var merge = require('webpack-merge') 4 | var utils = require('./utils') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var HtmlWebpackPlugin = require('html-webpack-plugin') 7 | 8 | // add hot-reload related code to entry chunks 9 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 10 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 11 | }) 12 | 13 | module.exports = merge(baseWebpackConfig, { 14 | module: { 15 | loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 16 | }, 17 | // eval-source-map is faster for development 18 | devtool: '#eval-source-map', 19 | plugins: [ 20 | new webpack.DefinePlugin({ 21 | 'process.env': config.dev.env 22 | }), 23 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 24 | new webpack.optimize.OccurenceOrderPlugin(), 25 | new webpack.HotModuleReplacementPlugin(), 26 | new webpack.NoErrorsPlugin(), 27 | // https://github.com/ampedandwired/html-webpack-plugin 28 | new HtmlWebpackPlugin({ 29 | filename: 'index.html', 30 | template: 'index.html', 31 | inject: true 32 | }) 33 | ] 34 | }) 35 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var baseWebpackConfig = require('./webpack.base.conf') 7 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 8 | var HtmlWebpackPlugin = require('html-webpack-plugin') 9 | var env = config.build.env 10 | 11 | var webpackConfig = merge(baseWebpackConfig, { 12 | module: { 13 | loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) 14 | }, 15 | devtool: config.build.productionSourceMap ? '#source-map' : false, 16 | output: { 17 | path: config.build.assetsRoot, 18 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 19 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 20 | }, 21 | vue: { 22 | loaders: utils.cssLoaders({ 23 | sourceMap: config.build.productionSourceMap, 24 | extract: true 25 | }) 26 | }, 27 | plugins: [ 28 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 29 | new webpack.DefinePlugin({ 30 | 'process.env': env 31 | }), 32 | new webpack.optimize.UglifyJsPlugin({ 33 | compress: { 34 | warnings: false 35 | } 36 | }), 37 | new webpack.optimize.OccurrenceOrderPlugin(), 38 | // extract css into its own file 39 | new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')), 40 | // generate dist index.html with correct asset hash for caching. 41 | // you can customize output by editing /index.html 42 | // see https://github.com/ampedandwired/html-webpack-plugin 43 | new HtmlWebpackPlugin({ 44 | filename: config.build.index, 45 | template: 'index.html', 46 | inject: true, 47 | minify: { 48 | removeComments: true, 49 | collapseWhitespace: true, 50 | removeAttributeQuotes: true 51 | // more options: 52 | // https://github.com/kangax/html-minifier#options-quick-reference 53 | }, 54 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 55 | chunksSortMode: 'dependency' 56 | }), 57 | // split vendor js into its own file 58 | new webpack.optimize.CommonsChunkPlugin({ 59 | name: 'vendor', 60 | minChunks: function (module, count) { 61 | // any required modules inside node_modules are extracted to vendor 62 | return ( 63 | module.resource && 64 | /\.js$/.test(module.resource) && 65 | module.resource.indexOf( 66 | path.join(__dirname, '../node_modules') 67 | ) === 0 68 | ) 69 | } 70 | }), 71 | // extract webpack runtime and module manifest to its own file in order to 72 | // prevent vendor hash from being updated whenever app bundle is updated 73 | new webpack.optimize.CommonsChunkPlugin({ 74 | name: 'manifest', 75 | chunks: ['vendor'] 76 | }) 77 | ] 78 | }) 79 | 80 | if (config.build.productionGzip) { 81 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 82 | 83 | webpackConfig.plugins.push( 84 | new CompressionWebpackPlugin({ 85 | asset: '[path].gz[query]', 86 | algorithm: 'gzip', 87 | test: new RegExp( 88 | '\\.(' + 89 | config.build.productionGzipExtensions.join('|') + 90 | ')$' 91 | ), 92 | threshold: 10240, 93 | minRatio: 0.8 94 | }) 95 | ) 96 | } 97 | 98 | module.exports = webpackConfig 99 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../docs/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../docs'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 8080, 22 | assetsSubDirectory: 'static', 23 | assetsPublicPath: '/', 24 | proxyTable: {}, 25 | // CSS Sourcemaps off by default because relative paths are "buggy" 26 | // with this option, according to the CSS-Loader README 27 | // (https://github.com/webpack/css-loader#sourcemaps) 28 | // In our experience, they generally work as expected, 29 | // just be aware of this issue when enabling this option. 30 | cssSourceMap: false 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vui", 3 | "version": "1.2.0", 4 | "description": "A Private Component Library for Vue.js.", 5 | "author": "qiangdada", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "build": "node build/build.js", 10 | "lint": "eslint --ext .js,.vue src" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/xuqiang521/vui.git" 15 | }, 16 | "keywords": [ 17 | "element", 18 | "vue", 19 | "components" 20 | ], 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/xuqiang521/vui/issues" 24 | }, 25 | "dependencies": { 26 | "vue": "^2.5.2", 27 | "vue-router": "^3.0.1" 28 | }, 29 | "devDependencies": { 30 | "autoprefixer": "^6.4.0", 31 | "babel-core": "^6.0.0", 32 | "babel-eslint": "^7.0.0", 33 | "babel-loader": "^6.0.0", 34 | "babel-plugin-transform-runtime": "^6.0.0", 35 | "babel-preset-es2015": "^6.0.0", 36 | "babel-preset-stage-2": "^6.0.0", 37 | "babel-register": "^6.0.0", 38 | "chalk": "^1.1.3", 39 | "connect-history-api-fallback": "^1.1.0", 40 | "css-loader": "^0.25.0", 41 | "eslint": "^3.7.1", 42 | "eslint-friendly-formatter": "^2.0.5", 43 | "eslint-loader": "^1.5.0", 44 | "eslint-plugin-html": "^1.3.0", 45 | "eventsource-polyfill": "^0.9.6", 46 | "express": "^4.13.3", 47 | "extract-text-webpack-plugin": "^1.0.1", 48 | "file-loader": "^0.9.0", 49 | "file-save": "^0.2.0", 50 | "function-bind": "^1.0.2", 51 | "html-webpack-plugin": "^2.8.1", 52 | "http-proxy-middleware": "^0.17.2", 53 | "json-loader": "^0.5.4", 54 | "opn": "^4.0.2", 55 | "ora": "^0.3.0", 56 | "semver": "^5.3.0", 57 | "shelljs": "^0.7.4", 58 | "style-loader": "^0.19.0", 59 | "stylus": "^0.54.5", 60 | "stylus-loader": "^3.0.1", 61 | "uppercamelcase": "^1.1.0", 62 | "url-loader": "^0.5.7", 63 | "vue-loader": "^9.4.0", 64 | "vue-style-loader": "^1.0.0", 65 | "webpack": "^1.13.2", 66 | "webpack-dev-middleware": "^1.8.3", 67 | "webpack-hot-middleware": "^2.12.2", 68 | "webpack-merge": "^0.14.1" 69 | }, 70 | "engines": { 71 | "node": ">= 4.0.0", 72 | "npm": ">= 3.0.0" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.2.0", 3 | "author": { 4 | "name": "qiangdada" 5 | }, 6 | "bugs": { 7 | "url": "https://github.com/xuqiang521/vui/issues" 8 | }, 9 | "description": "A Private Component Library for Vue.js.", 10 | "homepage": "https://github.com/xuqiang521/vui.git", 11 | "keywords": [ 12 | "element", 13 | "vue", 14 | "components" 15 | ], 16 | "license": "MIT", 17 | "main": "src/coms/vui.common.js", 18 | "name": "x-vui", 19 | "private": false, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/xuqiang521/vui.git" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /package/src/assets/beauty_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/beauty_1.png -------------------------------------------------------------------------------- /package/src/assets/beauty_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/beauty_2.png -------------------------------------------------------------------------------- /package/src/assets/beauty_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/beauty_3.png -------------------------------------------------------------------------------- /package/src/assets/beauty_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/beauty_4.png -------------------------------------------------------------------------------- /package/src/assets/beauty_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/beauty_5.png -------------------------------------------------------------------------------- /package/src/assets/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/close.png -------------------------------------------------------------------------------- /package/src/assets/dialog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/dialog.gif -------------------------------------------------------------------------------- /package/src/assets/dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/dropdown.png -------------------------------------------------------------------------------- /package/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/logo.png -------------------------------------------------------------------------------- /package/src/assets/picker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/picker.gif -------------------------------------------------------------------------------- /package/src/assets/scroller.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/scroller.gif -------------------------------------------------------------------------------- /package/src/assets/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/search.gif -------------------------------------------------------------------------------- /package/src/assets/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/search.png -------------------------------------------------------------------------------- /package/src/assets/select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/select.gif -------------------------------------------------------------------------------- /package/src/assets/swiper.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/swiper.gif -------------------------------------------------------------------------------- /package/src/assets/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/package/src/assets/table.png -------------------------------------------------------------------------------- /package/src/coms/dialog/index.js: -------------------------------------------------------------------------------- 1 | import Msg from './message'; 2 | import Modal from './modal'; 3 | 4 | export { 5 | Msg, 6 | Modal, 7 | }; 8 | -------------------------------------------------------------------------------- /package/src/coms/dialog/message/Message.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 36 | 37 | 84 | -------------------------------------------------------------------------------- /package/src/coms/dialog/message/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | const MsgConstructor = Vue.extend(require('./Message.vue').default); 4 | 5 | const instance = new MsgConstructor().$mount(document.createElement('div')); 6 | 7 | // 这里可以销毁组件实例,也能通过在message组件内部通过this.$el进行销毁 8 | MsgConstructor.prototype.closeMsg = function () { 9 | const el = instance.$el; 10 | el.parentNode && el.parentNode.removeChild(el); 11 | document.getElementsByTagName('html')[0].style.overflow = 'initial' 12 | typeof this.callback === 'function' && this.callback(); 13 | }; 14 | 15 | const Message = (options = {}) => { 16 | instance.msg = options.msg; 17 | instance.timeout = options.timeout || 2000; 18 | instance.icon = options.icon; 19 | instance.callback = options.callback; 20 | document.body.appendChild(instance.$el); 21 | document.getElementsByTagName('html')[0].style.overflow = 'hidden' 22 | 23 | const timer = setTimeout(() => { 24 | clearTimeout(timer); 25 | instance.closeMsg(); 26 | }, instance.timeout); 27 | }; 28 | 29 | export default Message; 30 | -------------------------------------------------------------------------------- /package/src/coms/dialog/modal/Modal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 103 | 104 | 123 | 124 | 147 | -------------------------------------------------------------------------------- /package/src/coms/dialog/modal/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | const ModalConstructor = Vue.extend(require('./Modal.vue').default); 4 | 5 | const instance = new ModalConstructor({ 6 | el: document.createElement('div'), 7 | }); 8 | ModalConstructor.prototype.closeModal = function (stay, callback) { 9 | typeof callback === 'function' && callback(); 10 | if (stay) return; 11 | this.$emit('update:show', false); 12 | const el = instance.$el; 13 | el.parentNode && el.parentNode.removeChild(el); 14 | document.getElementsByTagName('html')[0].style.overflow = 'initial' 15 | }; 16 | 17 | const Modal = (options = {}) => { 18 | instance.title = options.title; 19 | instance.content = options.content; 20 | instance.cancelText = options.cancelText; 21 | instance.okText = options.okText; 22 | instance.onCancel = options.onCancel; 23 | instance.onOk = options.onOk; 24 | instance.showCloseIcon = options.showCloseIcon || true; 25 | instance.show = true; 26 | document.body.appendChild(instance.$el); 27 | document.getElementsByTagName('html')[0].style.overflow = 'hidden' 28 | }; 29 | 30 | export default Modal; 31 | -------------------------------------------------------------------------------- /package/src/coms/dropdown/Dropdown.vue: -------------------------------------------------------------------------------- 1 | 10 | 20 | 21 | 102 | -------------------------------------------------------------------------------- /package/src/coms/dropdown/DropdownList.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 26 | -------------------------------------------------------------------------------- /package/src/coms/dropdown/DropdownMenu.vue: -------------------------------------------------------------------------------- 1 | 21 | 26 | 27 | 39 | -------------------------------------------------------------------------------- /package/src/coms/picker/Custom.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 167 | -------------------------------------------------------------------------------- /package/src/coms/picker/Picker.vue: -------------------------------------------------------------------------------- 1 | 135 | 151 | 152 | 336 | -------------------------------------------------------------------------------- /package/src/coms/picker/animate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Scroller 3 | * http://github.com/zynga/scroller 4 | * 5 | * Copyright 2011, Zynga Inc. 6 | * Licensed under the MIT License. 7 | * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt 8 | * 9 | * Based on the work of: Unify Project (unify-project.org) 10 | * http://unify-project.org 11 | * Copyright 2011, Deutsche Telekom AG 12 | * License: MIT + Apache (V2) 13 | */ 14 | 15 | /** 16 | * Generic animation class with support for dropped frames both optional easing and duration. 17 | * 18 | * Optional duration is useful when the lifetime is defined by another condition than time 19 | * e.g. speed of an animating object, etc. 20 | * 21 | * Dropped frame logic allows to keep using the same updater logic independent from the actual 22 | * rendering. This eases a lot of cases where it might be pretty complex to break down a state 23 | * based on the pure time difference. 24 | */ 25 | const time = Date.now || function () { 26 | return +new Date(); 27 | }; 28 | 29 | let running = {}; 30 | let counter = 1; 31 | const desiredFrames = 60; 32 | const millisecondsPerSecond = 1000; 33 | 34 | module.exports = { 35 | /** 36 | * A requestAnimationFrame wrapper / polyfill. 37 | * 38 | * @param callback {Function} The callback to be invoked before the next repaint. 39 | * @param root {HTMLElement} The root element for the repaint 40 | */ 41 | requestAnimationFrame: (() => { 42 | // Check for request animation Frame support 43 | const requestFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame; 44 | let isNative = !!requestFrame; 45 | 46 | if (requestFrame && !/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString())) { 47 | isNative = false; 48 | } 49 | 50 | if (isNative) { 51 | return (callback, root) => { 52 | requestFrame(callback, root); 53 | }; 54 | } 55 | 56 | const TARGET_FPS = 60; 57 | const requests = {}; 58 | let requestCount = 0; 59 | let rafHandle = 1; 60 | let intervalHandle = null; 61 | let lastActive = +new Date(); 62 | 63 | return (callback, root) => { 64 | const callbackHandle = rafHandle++; 65 | 66 | // Store callback 67 | requests[callbackHandle] = callback; 68 | requestCount++; 69 | 70 | // Create timeout at first request 71 | if (intervalHandle === null) { 72 | intervalHandle = setInterval(() => { 73 | const time = +new Date(); 74 | const currentRequests = requests; 75 | // Reset data structure before executing callbacks 76 | requests; 77 | requestCount = 0; 78 | /* eslint-disable no-restricted-syntax */ 79 | /* eslint-disable guard-for-in */ 80 | for (const key in currentRequests) { 81 | /* eslint-disable no-prototype-builtins */ 82 | if (currentRequests.hasOwnProperty(key)) { 83 | currentRequests[key](time); 84 | lastActive = time; 85 | } 86 | } 87 | 88 | // Disable the timeout when nothing happens for a certain 89 | // period of time 90 | if (time - lastActive > 2500) { 91 | clearInterval(intervalHandle); 92 | intervalHandle = null; 93 | } 94 | }, 1000 / TARGET_FPS); 95 | } 96 | 97 | return callbackHandle; 98 | }; 99 | })(), 100 | 101 | /** 102 | * Stops the given animation. 103 | * 104 | * @param id {Integer} Unique animation ID 105 | * @return {Boolean} Whether the animation was stopped (aka, was running before) 106 | */ 107 | stop(id) { 108 | const cleared = running[id] != null; 109 | if (cleared) { 110 | running[id] = null; 111 | } 112 | return cleared; 113 | }, 114 | 115 | /** 116 | * Whether the given animation is still running. 117 | * 118 | * @param id {Integer} Unique animation ID 119 | * @return {Boolean} Whether the animation is still running 120 | */ 121 | isRunning(id) { 122 | return running[id] != null; 123 | }, 124 | 125 | 126 | /** 127 | * Start the animation. 128 | * 129 | * @param stepCallback {Function} Pointer to function which is executed on every step. 130 | * Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }` 131 | * @param verifyCallback {Function} Executed before every animation step. 132 | * Signature of the method should be `function() { return continueWithAnimation; }` 133 | * @param completedCallback {Function} 134 | * Signature of the method should be `function(droppedFrames, finishedAnimation) {}` 135 | * @param duration {Integer} Milliseconds to run the animation 136 | * @param easingMethod {Function} Pointer to easing function 137 | * Signature of the method should be `function(percent) { return modifiedValue; }` 138 | * @param root {Element ? document.body} Render root, when available. Used for internal 139 | * usage of requestAnimationFrame. 140 | * @return {Integer} Identifier of animation. Can be used to stop it any time. 141 | */ 142 | start(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) { 143 | const _this = this; 144 | const start = time(); 145 | let lastFrame = start; 146 | let percent = 0; 147 | let dropCounter = 0; 148 | const id = counter++; 149 | 150 | if (!root) { 151 | root = document.body; 152 | } 153 | 154 | // Compacting running db automatically every few new animations 155 | if (id % 20 === 0) { 156 | const newRunning = {}; 157 | /* eslint-disable no-restricted-syntax */ 158 | /* eslint-disable guard-for-in */ 159 | for (const usedId in running) { 160 | newRunning[usedId] = true; 161 | } 162 | running = newRunning; 163 | } 164 | 165 | // This is the internal step method which is called every few milliseconds 166 | const step = (virtual) => { 167 | // Normalize virtual value 168 | const render = virtual !== true; 169 | 170 | // Get current time 171 | const now = time(); 172 | 173 | // Verification is executed before next animation step 174 | if (!running[id] || (verifyCallback && !verifyCallback(id))) { 175 | running[id] = null; 176 | completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false); 177 | return; 178 | } 179 | 180 | // For the current rendering to apply let's update omitted steps in memory. 181 | // This is important to bring internal state variables up-to-date with progress in time. 182 | if (render) { 183 | const droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1; 184 | 185 | for (let j = 0; j < Math.min(droppedFrames, 4); j++) { 186 | step(true); 187 | 188 | dropCounter++; 189 | } 190 | } 191 | 192 | // Compute percent value 193 | if (duration) { 194 | percent = (now - start) / duration; 195 | if (percent > 1) { 196 | percent = 1; 197 | } 198 | } 199 | 200 | // Execute step callback, then... 201 | const value = easingMethod ? easingMethod(percent) : percent; 202 | if ((stepCallback(value, now, render) === false || percent === 1) && render) { 203 | running[id] = null; 204 | completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, percent === 1 || duration == null); 205 | } else if (render) { 206 | lastFrame = now; 207 | _this.requestAnimationFrame(step, root); 208 | } 209 | }; 210 | 211 | // Mark as running 212 | running[id] = true; 213 | 214 | // Init first step 215 | _this.requestAnimationFrame(step, root); 216 | 217 | // Return unique animation ID 218 | return id; 219 | }, 220 | }; 221 | -------------------------------------------------------------------------------- /package/src/coms/picker/utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | 3 | export default { 4 | isDateTimeString(str) { 5 | return /^\d{4}((\.|-|\/)(0[1-9]|1[0-2]))((\.|-|\/)(0[1-9]|[12][0-9]|3[0-1]))( ([01][0-9]|2[0-3]):([012345][0-9]))?$/.test(str); 6 | }, 7 | isTimeString(str) { 8 | return /^([01][0-9]|2[0-3]):([012345][0-9])$/.test(str); 9 | }, 10 | mentStr(str) { 11 | return (`${100 + ~~str}`).substr(1, 2); 12 | }, 13 | getYearItems(config) { 14 | const years = []; 15 | const today = new Date(); 16 | 17 | let start = today.getFullYear(); 18 | let end = today.getFullYear() + 10; 19 | 20 | if (config.startYear !== 0) { 21 | start = ~~config.startYear; 22 | } 23 | 24 | if (config.endYear !== 0) { 25 | end = ~~config.endYear; 26 | } 27 | 28 | if (end < start) { 29 | end = start + 10; 30 | } 31 | 32 | if (config.startDate) { 33 | start = new Date(config.startDate.replace(/-/g, '/')).getFullYear(); 34 | } 35 | 36 | if (config.endDate) { 37 | end = new Date(config.endDate.replace(/-/g, '/')).getFullYear(); 38 | } 39 | 40 | while (start <= end) { 41 | years.push({ value: start, name: config.format.replace('{value}', start) }); 42 | start++; 43 | } 44 | 45 | return years; 46 | }, 47 | getMonthItems(config) { 48 | const months = []; 49 | let startMonth = 1; 50 | let endMonth = 12; 51 | 52 | if (config.startDate) { 53 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 54 | if (startDate.getFullYear() === config.currentYear) { 55 | startMonth = startDate.getMonth() + 1; 56 | endMonth = 12; 57 | } 58 | } 59 | 60 | if (config.endDate) { 61 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 62 | if (endDate.getFullYear() === config.currentYear) { 63 | startMonth = 1; 64 | endMonth = endDate.getMonth() + 1; 65 | } 66 | } 67 | 68 | while (startMonth <= endMonth) { 69 | const t = this.mentStr(startMonth); 70 | months.push({ value: t, name: config.format.replace('{value}', t) }); 71 | startMonth++; 72 | } 73 | 74 | return months; 75 | }, 76 | getDateItems(config) { 77 | const dates = []; 78 | const today = new Date(); 79 | let year = today.getFullYear(); 80 | let month = today.getMonth(); 81 | 82 | if (config.currentYear) year = ~~config.currentYear; 83 | 84 | if (config.currentMonth) month = ~~config.currentMonth - 1; 85 | 86 | let end = 30; 87 | 88 | if ([0, 2, 4, 6, 7, 9, 11].indexOf(month) > -1) { 89 | end = 31; 90 | } else if (month === 1) { 91 | end = year % 100 === 0 ? (year % 400 === 0 ? 29 : 28) : (year % 4 === 0 ? 29 : 28); 92 | } 93 | if (config.endDate) { 94 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 95 | if (endDate.getMonth() + 1 === config.currentMonth && endDate.getFullYear() === config.currentYear && endDate.getDate() < end) { 96 | end = endDate.getDate(); 97 | } 98 | } 99 | 100 | let d = 1; 101 | if (config.startDate) { 102 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 103 | if (startDate.getMonth() + 1 === config.currentMonth && startDate.getFullYear() === config.currentYear) { 104 | d = startDate.getDate(); 105 | } 106 | } 107 | 108 | while (d <= end) { 109 | const t = this.mentStr(d); 110 | dates.push({ value: t, name: config.format.replace('{value}', t) }); 111 | d++; 112 | } 113 | 114 | return dates; 115 | }, 116 | getHourItems(config) { 117 | const hours = []; 118 | let start = ~~config.startHour; 119 | let end = ~~config.endHour; 120 | 121 | if (end < start) { 122 | end = 23; 123 | } 124 | 125 | if (config.startDate) { 126 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 127 | if (startDate.getFullYear() === config.currentYear && startDate.getMonth() + 1 === config.currentMonth && startDate.getDate() === config.currentDay) { 128 | start = startDate.getHours(); 129 | } 130 | } 131 | 132 | if (config.endDate) { 133 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 134 | if (endDate.getFullYear() === config.currentYear && endDate.getMonth() + 1 === config.currentMonth && endDate.getDate() === config.currentDay) { 135 | end = endDate.getHours(); 136 | } 137 | } 138 | 139 | while (start <= end) { 140 | const t = this.mentStr(start); 141 | hours.push({ value: t, name: config.format.replace('{value}', t) }); 142 | start++; 143 | } 144 | 145 | return hours; 146 | }, 147 | getMinuteItems(config) { 148 | const minute = []; 149 | let start = ~~config.startMinute; 150 | let end = ~~config.endMinute; 151 | 152 | if (config.startDate) { 153 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 154 | if (startDate.getFullYear() === config.currentYear && startDate.getMonth() + 1 === config.currentMonth && startDate.getDate() === config.currentDay && startDate.getHours() === config.currentHour) { 155 | start = startDate.getMinutes(); 156 | } 157 | } 158 | 159 | if (config.endDate) { 160 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 161 | if (endDate.getFullYear() === config.currentYear && endDate.getMonth() + 1 === config.currentMonth && endDate.getDate() === config.currentDay && endDate.getHours() === config.currentHour) { 162 | end = endDate.getMinutes(); 163 | } 164 | } 165 | 166 | while (start <= end) { 167 | const t = this.mentStr(start); 168 | minute.push({ value: t, name: config.format.replace('{value}', t) }); 169 | start += config.timeStep; 170 | } 171 | return minute; 172 | }, 173 | }; 174 | -------------------------------------------------------------------------------- /package/src/coms/scroller/Arrow.vue: -------------------------------------------------------------------------------- 1 | 79 | 89 | -------------------------------------------------------------------------------- /package/src/coms/scroller/Spinner.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /package/src/coms/scroller/render.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | function getContentRender(content) { 3 | var global = window; 4 | 5 | var docStyle = document.documentElement.style; 6 | 7 | var engine; 8 | if (global.opera && Object.prototype.toString.call(opera) === '[object Opera]') { 9 | engine = 'presto'; 10 | } else if ('MozAppearance' in docStyle) { 11 | engine = 'gecko'; 12 | } else if ('WebkitAppearance' in docStyle) { 13 | engine = 'webkit'; 14 | } else if (typeof navigator.cpuClass === 'string') { 15 | engine = 'trident'; 16 | } 17 | 18 | var vendorPrefix = { 19 | trident: 'ms', 20 | gecko: 'Moz', 21 | webkit: 'Webkit', 22 | presto: 'O' 23 | }[engine]; 24 | 25 | var helperElem = document.createElement("div"); 26 | var undef; 27 | 28 | var perspectiveProperty = vendorPrefix + "Perspective"; 29 | var transformProperty = vendorPrefix + "Transform"; 30 | 31 | if (helperElem.style[perspectiveProperty] !== undef) { 32 | 33 | return function (left, top, zoom) { 34 | content.style[transformProperty] = 'translate3d(' + (-left) + 'px,' + (-top) + 'px,0) scale(' + zoom + ')'; 35 | }; 36 | 37 | } else if (helperElem.style[transformProperty] !== undef) { 38 | 39 | return function (left, top, zoom) { 40 | content.style[transformProperty] = 'translate(' + (-left) + 'px,' + (-top) + 'px) scale(' + zoom + ')'; 41 | }; 42 | 43 | } else { 44 | 45 | return function (left, top, zoom) { 46 | content.style.marginLeft = left ? (-left / zoom) + 'px' : ''; 47 | content.style.marginTop = top ? (-top / zoom) + 'px' : ''; 48 | content.style.zoom = zoom || ''; 49 | }; 50 | 51 | } 52 | } 53 | 54 | module.exports = getContentRender; 55 | -------------------------------------------------------------------------------- /package/src/coms/search/SearchList.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | -------------------------------------------------------------------------------- /package/src/coms/search/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 42 | 65 | 155 | -------------------------------------------------------------------------------- /package/src/coms/select/Select.vue: -------------------------------------------------------------------------------- 1 | 2 | 121 | 148 | 197 | -------------------------------------------------------------------------------- /package/src/coms/swipe/index.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 112 | 113 | 185 | -------------------------------------------------------------------------------- /package/src/coms/swipe/swipe-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /package/src/coms/switch/Switch.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 26 | 27 | 86 | -------------------------------------------------------------------------------- /package/src/coms/table/Table.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 39 | 40 | 61 | 62 | -------------------------------------------------------------------------------- /package/src/coms/table/TableColumn.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /package/src/coms/vui.common.js: -------------------------------------------------------------------------------- 1 | const Css = require('../css/_common.css') 2 | 3 | const Dropdown = require('./dropdown/Dropdown.vue').default; 4 | const DropdownList = require('./dropdown/DropdownList.vue').default; 5 | const DropdownMenu = require('./dropdown/DropdownMenu.vue').default; 6 | const Scroller = require('./scroller/index.vue').default; 7 | const Select = require('./select/Select.vue').default; 8 | const Swiper = require('./swipe/index.vue').default; 9 | const Swipe = require('./swipe/swipe-menu.vue').default; 10 | const SwipeItem = require('./swipe/swipe-item.vue').default; 11 | const Table = require('./table/Table.vue').default; 12 | const TableColumn = require('./table/TableColumn.vue').default; 13 | const Search = require('./search/index.vue').default; 14 | const SearchList = require('./search/SearchList.vue').default; 15 | const Picker = require('./picker/Picker.vue').default; 16 | const Modal = require('./dialog/modal/Modal.vue').default; 17 | const Switch = require('./switch/Switch.vue').default; 18 | const $msg = require('./dialog/message/index.js').default; 19 | const $modal = require('./dialog/modal//index.js').default; 20 | 21 | const VUICOMS = [ 22 | Dropdown, 23 | DropdownList, 24 | DropdownMenu, 25 | Scroller, 26 | Select, 27 | Swiper, 28 | Swipe, 29 | SwipeItem, 30 | Table, 31 | TableColumn, 32 | Search, 33 | SearchList, 34 | Picker, 35 | Modal, 36 | Switch 37 | ] 38 | 39 | module.exports = { 40 | Dropdown, 41 | DropdownList, 42 | DropdownMenu, 43 | Scroller, 44 | Select, 45 | Swiper, 46 | Swipe, 47 | SwipeItem, 48 | Table, 49 | TableColumn, 50 | Search, 51 | SearchList, 52 | Picker, 53 | Modal, 54 | Switch, 55 | VUICOMS, 56 | $msg, 57 | $modal 58 | }; -------------------------------------------------------------------------------- /package/src/css/_common.css: -------------------------------------------------------------------------------- 1 | *, 2 | *:before, 3 | *:after { 4 | box-sizing: border-box; 5 | outline: none; 6 | text-size-adjust: none; 7 | } 8 | html, body { 9 | height: 100%; 10 | font-size: 50px; 11 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 12 | } 13 | 14 | body { 15 | background-color: #f7f7f7; 16 | font-size: 16px; 17 | -webkit-font-smoothing: antialiased; 18 | font-family: arial, sans-serif; 19 | } 20 | 21 | body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td, iframe { 22 | margin: 0; 23 | padding: 0; 24 | } 25 | 26 | img, article, aside, details, figcaption, figure, footer, header, menu, nav, section, summary, time, mark, audio, video { 27 | display: block; 28 | margin: 0; 29 | padding: 0; 30 | } 31 | 32 | h1, h2, h3, h4, h5, h6 { 33 | font-size: 100%; 34 | } 35 | 36 | fieldset, img { 37 | border: 0; 38 | } 39 | 40 | address, caption, cite, dfn, em, th, var, i, em { 41 | font-style: normal; 42 | font-weight: normal; 43 | } 44 | 45 | ol, ul { 46 | list-style: none; 47 | } 48 | 49 | a { 50 | text-decoration: none; 51 | color: inherit; 52 | &:hover { 53 | text-decoration: none; 54 | } 55 | } 56 | 57 | a, label, button, input, select { 58 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 59 | } 60 | 61 | input, select, button { 62 | font:100% tahoma, "\5b8b\4f53", arial; 63 | vertical-align: baseline; 64 | border-radius: 0; 65 | background-color: transparent; 66 | } 67 | 68 | select { 69 | -webkit-appearance: none; 70 | -moz-appearance: none; 71 | } 72 | 73 | button::-moz-focus-inner, 74 | input[type="reset"]::-moz-focus-inner, 75 | input[type="button"]::-moz-focus-inner, 76 | input[type="submit"]::-moz-focus-inner, 77 | input[type="file"] > input[type="button"]::-moz-focus-inner { 78 | border: none; 79 | } 80 | 81 | input[type=checkbox], input[type=radio] { 82 | vertical-align: middle; 83 | } 84 | 85 | input[type="number"]::-webkit-outer-spin-button, 86 | input[type="number"]::-webkit-inner-spin-button { 87 | -webkit-appearance: none !important; 88 | -moz-appearance: none !important; 89 | margin: 0; 90 | } 91 | 92 | input:-webkit-autofill { 93 | -webkit-box-shadow: 0 0 0 1000px white inset; 94 | } 95 | 96 | input[type=search],input[type=tel],input[type=text],input { 97 | -webkit-appearance: none; /*去除系统默认的样式*/ 98 | -webkit-box-sizing: content-box; 99 | font-family: inherit; 100 | font-size: 100%; 101 | box-sizing: border-box; 102 | } 103 | 104 | input::-webkit-search-decoration, 105 | input::-webkit-search-cancel-button { 106 | display: none; 107 | } 108 | 109 | textarea { 110 | outline: none; 111 | border-radius: 0; 112 | -webkit-appearance: none; 113 | -moz-appearance: none; 114 | overflow: auto; 115 | resize: none; 116 | font: 100% tahoma, "\5b8b\4f53", arial; 117 | } 118 | 119 | 120 | textarea::-webkit-input-placeholder, input::-webkit-input-placeholder { /* WebKit browsers */ 121 | color: #CCC; 122 | } 123 | textarea:-moz-placeholder ,input:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ 124 | color: #CCC; 125 | opacity: 1; 126 | } 127 | textarea::-moz-placeholder, input::-moz-placeholder { /* Mozilla Firefox 19+ */ 128 | color: #CCC; 129 | opacity: 1; 130 | } 131 | textarea:-ms-input-placeholder, input:-ms-input-placeholder { /* Internet Explorer 10+ */ 132 | color: #CCC; 133 | } 134 | 135 | .l { 136 | float: left; 137 | } 138 | .r { 139 | float: right; 140 | } 141 | .gray { 142 | color: #999; 143 | } 144 | .clearfix::after { 145 | display: block; 146 | content: ' '; 147 | clear: both; 148 | } 149 | .placeholder { 150 | font-size: 14px; 151 | color: #CCC 152 | } -------------------------------------------------------------------------------- /package/src/js/clickoutside.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { on } from './dom.js'; 3 | 4 | const nodeList = []; 5 | const ctx = '@@clickoutsideContext'; 6 | 7 | !Vue.prototype.$isServer && on(document, 'click', e => { 8 | nodeList.forEach(node => node[ctx].documentHandler(e)); 9 | }); 10 | /** 11 | * v-clickoutside 12 | * @desc 点击元素外面才会触发的事件 13 | * @example 14 | * ```vue 15 | *
16 | * ``` 17 | */ 18 | export default { 19 | bind(el, binding, vnode) { 20 | const id = nodeList.push(el) - 1; 21 | const documentHandler = function(e) { 22 | if (!vnode.context || 23 | el.contains(e.target) || 24 | (vnode.context.popperElm && 25 | vnode.context.popperElm.contains(e.target))) return; 26 | 27 | if (binding.expression && 28 | el[ctx].methodName && 29 | vnode.context[el[ctx].methodName]) { 30 | vnode.context[el[ctx].methodName](); 31 | } else { 32 | el[ctx].bindingFn && el[ctx].bindingFn(); 33 | } 34 | }; 35 | el[ctx] = { 36 | id, 37 | documentHandler, 38 | methodName: binding.expression, 39 | bindingFn: binding.value 40 | }; 41 | }, 42 | 43 | update(el, binding) { 44 | el[ctx].methodName = binding.expression; 45 | el[ctx].bindingFn = binding.value; 46 | }, 47 | 48 | unbind(el) { 49 | let len = nodeList.length; 50 | 51 | for (let i = 0; i < len; i++) { 52 | if (nodeList[i][ctx].id === el[ctx].id) { 53 | nodeList.splice(i, 1); 54 | break; 55 | } 56 | } 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /package/src/js/dom.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | 3 | import Vue from 'vue'; 4 | 5 | const isServer = Vue.prototype.$isServer; 6 | const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; 7 | const MOZ_HACK_REGEXP = /^moz([A-Z])/; 8 | const ieVersion = isServer ? 0 : Number(document.documentMode); 9 | 10 | /* istanbul ignore next */ 11 | const trim = function(string) { 12 | return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, ''); 13 | }; 14 | /* istanbul ignore next */ 15 | const camelCase = function(name) { 16 | return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { 17 | return offset ? letter.toUpperCase() : letter; 18 | }).replace(MOZ_HACK_REGEXP, 'Moz$1'); 19 | }; 20 | 21 | /* istanbul ignore next */ 22 | export const on = (function() { 23 | if (!isServer && document.addEventListener) { 24 | return function(element, event, handler) { 25 | if (element && event && handler) { 26 | element.addEventListener(event, handler, false); 27 | } 28 | }; 29 | } else { 30 | return function(element, event, handler) { 31 | if (element && event && handler) { 32 | element.attachEvent('on' + event, handler); 33 | } 34 | }; 35 | } 36 | })(); 37 | 38 | /* istanbul ignore next */ 39 | export const off = (function() { 40 | if (!isServer && document.removeEventListener) { 41 | return function(element, event, handler) { 42 | if (element && event) { 43 | element.removeEventListener(event, handler, false); 44 | } 45 | }; 46 | } else { 47 | return function(element, event, handler) { 48 | if (element && event) { 49 | element.detachEvent('on' + event, handler); 50 | } 51 | }; 52 | } 53 | })(); 54 | 55 | /* istanbul ignore next */ 56 | export const once = function(el, event, fn) { 57 | var listener = function() { 58 | if (fn) { 59 | fn.apply(this, arguments); 60 | } 61 | off(el, event, listener); 62 | }; 63 | on(el, event, listener); 64 | }; 65 | 66 | /* istanbul ignore next */ 67 | export function hasClass(el, cls) { 68 | if (!el || !cls) return false; 69 | if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.'); 70 | if (el.classList) { 71 | return el.classList.contains(cls); 72 | } else { 73 | return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1; 74 | } 75 | }; 76 | 77 | /* istanbul ignore next */ 78 | export function addClass(el, cls) { 79 | if (!el) return; 80 | var curClass = el.className; 81 | var classes = (cls || '').split(' '); 82 | 83 | for (var i = 0, j = classes.length; i < j; i++) { 84 | var clsName = classes[i]; 85 | if (!clsName) continue; 86 | 87 | if (el.classList) { 88 | el.classList.add(clsName); 89 | } else { 90 | if (!hasClass(el, clsName)) { 91 | curClass += ' ' + clsName; 92 | } 93 | } 94 | } 95 | if (!el.classList) { 96 | el.className = curClass; 97 | } 98 | }; 99 | 100 | /* istanbul ignore next */ 101 | export function removeClass(el, cls) { 102 | if (!el || !cls) return; 103 | var classes = cls.split(' '); 104 | var curClass = ' ' + el.className + ' '; 105 | 106 | for (var i = 0, j = classes.length; i < j; i++) { 107 | var clsName = classes[i]; 108 | if (!clsName) continue; 109 | 110 | if (el.classList) { 111 | el.classList.remove(clsName); 112 | } else { 113 | if (hasClass(el, clsName)) { 114 | curClass = curClass.replace(' ' + clsName + ' ', ' '); 115 | } 116 | } 117 | } 118 | if (!el.classList) { 119 | el.className = trim(curClass); 120 | } 121 | }; 122 | 123 | /* istanbul ignore next */ 124 | export const getStyle = ieVersion < 9 ? function(element, styleName) { 125 | if (isServer) return; 126 | if (!element || !styleName) return null; 127 | styleName = camelCase(styleName); 128 | if (styleName === 'float') { 129 | styleName = 'styleFloat'; 130 | } 131 | try { 132 | switch (styleName) { 133 | case 'opacity': 134 | try { 135 | return element.filters.item('alpha').opacity / 100; 136 | } catch (e) { 137 | return 1.0; 138 | } 139 | default: 140 | return (element.style[styleName] || element.currentStyle ? element.currentStyle[styleName] : null); 141 | } 142 | } catch (e) { 143 | return element.style[styleName]; 144 | } 145 | } : function(element, styleName) { 146 | if (isServer) return; 147 | if (!element || !styleName) return null; 148 | styleName = camelCase(styleName); 149 | if (styleName === 'float') { 150 | styleName = 'cssFloat'; 151 | } 152 | try { 153 | var computed = document.defaultView.getComputedStyle(element, ''); 154 | return element.style[styleName] || computed ? computed[styleName] : null; 155 | } catch (e) { 156 | return element.style[styleName]; 157 | } 158 | }; 159 | 160 | /* istanbul ignore next */ 161 | export function setStyle(element, styleName, value) { 162 | if (!element || !styleName) return; 163 | 164 | if (typeof styleName === 'object') { 165 | for (var prop in styleName) { 166 | if (styleName.hasOwnProperty(prop)) { 167 | setStyle(element, prop, styleName[prop]); 168 | } 169 | } 170 | } else { 171 | styleName = camelCase(styleName); 172 | if (styleName === 'opacity' && ieVersion < 9) { 173 | element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')'; 174 | } else { 175 | element.style[styleName] = value; 176 | } 177 | } 178 | }; 179 | -------------------------------------------------------------------------------- /package/src/js/emitter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [broadcast 向下传递] 3 | * @param {[type]} componentName [组件别名] 4 | * @param {[type]} eventName [事件别名] 5 | * @param {[type]} params [事件回调参数] 6 | */ 7 | function broadcast(componentName, eventName, params) { 8 | // 遍历当前实例的children节点 9 | this.$children.forEach(child => { 10 | var name = child.$options.componentName; 11 | // 如果子节点名称和组件别名相同,则当前子节点为目标节点 12 | if (name === componentName) { 13 | // 找到目标节点后,触发事件别名对应的回调,并将参数传入 14 | child.$emit.apply(child, [eventName].concat(params)); 15 | } 16 | // 如果子节点名称和组件别名不相同,继续遍历子节点的子节点,以此类推,直到找到目标节点 17 | else { 18 | broadcast.apply(child, [componentName, eventName].concat([params])); 19 | } 20 | }); 21 | } 22 | /** 23 | * [dispatch 向上传递] 24 | * @param {[type]} componentName [组件别名] 25 | * @param {[type]} eventName [事件别名] 26 | * @param {[type]} params [事件回调参数] 27 | */ 28 | function dispatch(componentName, eventName, params) { 29 | var parent = this.$parent || this.$root; 30 | var name = parent.$options.name; 31 | // 向上找目标父节点,如果上一级父节点不符合,则继续往上查询 32 | while (parent && (!name || name !== componentName)) { 33 | parent = parent.$parent; 34 | 35 | if (parent) { 36 | name = parent.$options.name; 37 | } 38 | } 39 | // 找到目标父节点后,触发事件别名对应的回调,并将参数传入 40 | if (parent) { 41 | parent.$emit.apply(parent, [eventName].concat(params)); 42 | } 43 | } 44 | export default { 45 | methods: { 46 | broadcast(componentName, eventName, params) { 47 | broadcast.apply(this, [componentName, eventName, params]); 48 | }, 49 | dispatch(componentName, eventName, params) { 50 | dispatch.apply(this, [componentName, eventName, params]); 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 46 | 47 | 75 | 76 | -------------------------------------------------------------------------------- /src/assets/beauty_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/beauty_1.png -------------------------------------------------------------------------------- /src/assets/beauty_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/beauty_2.png -------------------------------------------------------------------------------- /src/assets/beauty_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/beauty_3.png -------------------------------------------------------------------------------- /src/assets/beauty_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/beauty_4.png -------------------------------------------------------------------------------- /src/assets/beauty_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/beauty_5.png -------------------------------------------------------------------------------- /src/assets/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/close.png -------------------------------------------------------------------------------- /src/assets/dialog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/dialog.gif -------------------------------------------------------------------------------- /src/assets/dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/dropdown.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/picker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/picker.gif -------------------------------------------------------------------------------- /src/assets/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/qrcode.png -------------------------------------------------------------------------------- /src/assets/scroller.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/scroller.gif -------------------------------------------------------------------------------- /src/assets/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/search.gif -------------------------------------------------------------------------------- /src/assets/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/search.png -------------------------------------------------------------------------------- /src/assets/select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/select.gif -------------------------------------------------------------------------------- /src/assets/swiper.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/swiper.gif -------------------------------------------------------------------------------- /src/assets/switch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/switch.gif -------------------------------------------------------------------------------- /src/assets/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/src/assets/table.png -------------------------------------------------------------------------------- /src/components/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "Dropdown": "./dropdown/Dropdown", 3 | "DropdownMenu": "./dropdown/DropdownMenu", 4 | "DropdownList": "./dropdown/DropdownList", 5 | "Swipe": "./swiper/Swiper", 6 | "SwipeItem": "./swiper/SwiperItem", 7 | "Swiper": "./swiper/index", 8 | "Scroller": "./scroller/index", 9 | "Search": "./search/index", 10 | "SearchList": "./search/SearchList", 11 | "Table": "./table/Table", 12 | "TableColumn": "./table/TableColumn", 13 | "Picker": "./picker/Picker", 14 | "Select": "./select/Select", 15 | "Switch": "./switch/Switch" 16 | } 17 | -------------------------------------------------------------------------------- /src/components/dialog/index.js: -------------------------------------------------------------------------------- 1 | import Msg from './message'; 2 | import Modal from './modal'; 3 | 4 | export { 5 | Msg, 6 | Modal, 7 | }; 8 | -------------------------------------------------------------------------------- /src/components/dialog/message/Message.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 36 | 37 | 84 | -------------------------------------------------------------------------------- /src/components/dialog/message/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | const MsgConstructor = Vue.extend(require('./Message.vue')); 4 | 5 | const instance = new MsgConstructor().$mount(document.createElement('div')); 6 | 7 | // 这里可以销毁组件实例,也能通过在message组件内部通过this.$el进行销毁 8 | MsgConstructor.prototype.closeMsg = function () { 9 | const el = instance.$el; 10 | el.parentNode && el.parentNode.removeChild(el); 11 | document.getElementsByTagName('html')[0].style.overflow = 'initial' 12 | typeof this.callback === 'function' && this.callback(); 13 | }; 14 | 15 | const Message = (options = {}) => { 16 | instance.msg = options.msg; 17 | instance.timeout = options.timeout || 2000; 18 | instance.icon = options.icon; 19 | instance.callback = options.callback; 20 | document.body.appendChild(instance.$el); 21 | document.getElementsByTagName('html')[0].style.overflow = 'hidden' 22 | 23 | const timer = setTimeout(() => { 24 | clearTimeout(timer); 25 | instance.closeMsg(); 26 | }, instance.timeout); 27 | }; 28 | 29 | export default Message; 30 | -------------------------------------------------------------------------------- /src/components/dialog/modal/Modal.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 103 | 104 | 123 | 124 | 147 | -------------------------------------------------------------------------------- /src/components/dialog/modal/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | const ModalConstructor = Vue.extend(require('./Modal.vue')); 4 | 5 | const instance = new ModalConstructor({ 6 | el: document.createElement('div'), 7 | }); 8 | ModalConstructor.prototype.closeModal = function (stay, callback) { 9 | typeof callback === 'function' && callback(); 10 | if (stay) return; 11 | this.$emit('update:show', false); 12 | const el = instance.$el; 13 | el.parentNode && el.parentNode.removeChild(el); 14 | document.getElementsByTagName('html')[0].style.overflow = 'initial' 15 | }; 16 | 17 | const Modal = (options = {}) => { 18 | instance.title = options.title; 19 | instance.content = options.content; 20 | instance.cancelText = options.cancelText; 21 | instance.okText = options.okText; 22 | instance.onCancel = options.onCancel; 23 | instance.onOk = options.onOk; 24 | instance.showCloseIcon = options.showCloseIcon || true; 25 | instance.show = true; 26 | document.body.appendChild(instance.$el); 27 | document.getElementsByTagName('html')[0].style.overflow = 'hidden' 28 | }; 29 | 30 | export default Modal; 31 | -------------------------------------------------------------------------------- /src/components/dropdown/Dropdown.vue: -------------------------------------------------------------------------------- 1 | 10 | 20 | 21 | 102 | -------------------------------------------------------------------------------- /src/components/dropdown/DropdownList.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 26 | -------------------------------------------------------------------------------- /src/components/dropdown/DropdownMenu.vue: -------------------------------------------------------------------------------- 1 | 21 | 26 | 27 | 39 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | // // dropdown 2 | // exports.Dropdown = require('./dropdown/dropdown') 3 | // exports.DropdownMenu = require('./dropdown/dropdown-menu') 4 | // exports.DropdownList = require('./dropdown/dropdown-list') 5 | // // swiper 6 | // exports.Swipe = require('./swiper/swiper') 7 | // exports.SwipeItem = require('./swiper/swiper-item') 8 | // exports.Swiper = require('./swiper') 9 | // // scroller 10 | // exports.Scroller = require('./scroller') 11 | // // search 12 | // exports.Search = require('./search') 13 | // exports.SearchList = require('./search/search-list') 14 | // // table 15 | // exports.Table = require('./table/table') 16 | // exports.TableColumn = require('./table/table-column') 17 | // 18 | // const components = [ 19 | // require('./dropdown/dropdown'), 20 | // require('./dropdown/dropdown-menu'), 21 | // require('./dropdown/dropdown-list'), 22 | // require('./swiper/swiper'), 23 | // require('./swiper/swiper-item'), 24 | // require('./swiper'), 25 | // require('./scroller'), 26 | // require('./search'), 27 | // require('./search/search-list'), 28 | // require('./table/table'), 29 | // require('./table/table-column') 30 | // ] 31 | let components = [] 32 | const componentsFile = require('components/components.json') 33 | 34 | Object.keys(componentsFile).forEach((item, index) => { 35 | components[index] = require(`${componentsFile[item]}`) 36 | }) 37 | 38 | export default components 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/components/picker/Custom.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 167 | -------------------------------------------------------------------------------- /src/components/picker/animate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Scroller 3 | * http://github.com/zynga/scroller 4 | * 5 | * Copyright 2011, Zynga Inc. 6 | * Licensed under the MIT License. 7 | * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt 8 | * 9 | * Based on the work of: Unify Project (unify-project.org) 10 | * http://unify-project.org 11 | * Copyright 2011, Deutsche Telekom AG 12 | * License: MIT + Apache (V2) 13 | */ 14 | 15 | /** 16 | * Generic animation class with support for dropped frames both optional easing and duration. 17 | * 18 | * Optional duration is useful when the lifetime is defined by another condition than time 19 | * e.g. speed of an animating object, etc. 20 | * 21 | * Dropped frame logic allows to keep using the same updater logic independent from the actual 22 | * rendering. This eases a lot of cases where it might be pretty complex to break down a state 23 | * based on the pure time difference. 24 | */ 25 | const time = Date.now || function () { 26 | return +new Date(); 27 | }; 28 | 29 | let running = {}; 30 | let counter = 1; 31 | const desiredFrames = 60; 32 | const millisecondsPerSecond = 1000; 33 | 34 | module.exports = { 35 | /** 36 | * A requestAnimationFrame wrapper / polyfill. 37 | * 38 | * @param callback {Function} The callback to be invoked before the next repaint. 39 | * @param root {HTMLElement} The root element for the repaint 40 | */ 41 | requestAnimationFrame: (() => { 42 | // Check for request animation Frame support 43 | const requestFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame; 44 | let isNative = !!requestFrame; 45 | 46 | if (requestFrame && !/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString())) { 47 | isNative = false; 48 | } 49 | 50 | if (isNative) { 51 | return (callback, root) => { 52 | requestFrame(callback, root); 53 | }; 54 | } 55 | 56 | const TARGET_FPS = 60; 57 | const requests = {}; 58 | let requestCount = 0; 59 | let rafHandle = 1; 60 | let intervalHandle = null; 61 | let lastActive = +new Date(); 62 | 63 | return (callback, root) => { 64 | const callbackHandle = rafHandle++; 65 | 66 | // Store callback 67 | requests[callbackHandle] = callback; 68 | requestCount++; 69 | 70 | // Create timeout at first request 71 | if (intervalHandle === null) { 72 | intervalHandle = setInterval(() => { 73 | const time = +new Date(); 74 | const currentRequests = requests; 75 | // Reset data structure before executing callbacks 76 | requests; 77 | requestCount = 0; 78 | /* eslint-disable no-restricted-syntax */ 79 | /* eslint-disable guard-for-in */ 80 | for (const key in currentRequests) { 81 | /* eslint-disable no-prototype-builtins */ 82 | if (currentRequests.hasOwnProperty(key)) { 83 | currentRequests[key](time); 84 | lastActive = time; 85 | } 86 | } 87 | 88 | // Disable the timeout when nothing happens for a certain 89 | // period of time 90 | if (time - lastActive > 2500) { 91 | clearInterval(intervalHandle); 92 | intervalHandle = null; 93 | } 94 | }, 1000 / TARGET_FPS); 95 | } 96 | 97 | return callbackHandle; 98 | }; 99 | })(), 100 | 101 | /** 102 | * Stops the given animation. 103 | * 104 | * @param id {Integer} Unique animation ID 105 | * @return {Boolean} Whether the animation was stopped (aka, was running before) 106 | */ 107 | stop(id) { 108 | const cleared = running[id] != null; 109 | if (cleared) { 110 | running[id] = null; 111 | } 112 | return cleared; 113 | }, 114 | 115 | /** 116 | * Whether the given animation is still running. 117 | * 118 | * @param id {Integer} Unique animation ID 119 | * @return {Boolean} Whether the animation is still running 120 | */ 121 | isRunning(id) { 122 | return running[id] != null; 123 | }, 124 | 125 | 126 | /** 127 | * Start the animation. 128 | * 129 | * @param stepCallback {Function} Pointer to function which is executed on every step. 130 | * Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }` 131 | * @param verifyCallback {Function} Executed before every animation step. 132 | * Signature of the method should be `function() { return continueWithAnimation; }` 133 | * @param completedCallback {Function} 134 | * Signature of the method should be `function(droppedFrames, finishedAnimation) {}` 135 | * @param duration {Integer} Milliseconds to run the animation 136 | * @param easingMethod {Function} Pointer to easing function 137 | * Signature of the method should be `function(percent) { return modifiedValue; }` 138 | * @param root {Element ? document.body} Render root, when available. Used for internal 139 | * usage of requestAnimationFrame. 140 | * @return {Integer} Identifier of animation. Can be used to stop it any time. 141 | */ 142 | start(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) { 143 | const _this = this; 144 | const start = time(); 145 | let lastFrame = start; 146 | let percent = 0; 147 | let dropCounter = 0; 148 | const id = counter++; 149 | 150 | if (!root) { 151 | root = document.body; 152 | } 153 | 154 | // Compacting running db automatically every few new animations 155 | if (id % 20 === 0) { 156 | const newRunning = {}; 157 | /* eslint-disable no-restricted-syntax */ 158 | /* eslint-disable guard-for-in */ 159 | for (const usedId in running) { 160 | newRunning[usedId] = true; 161 | } 162 | running = newRunning; 163 | } 164 | 165 | // This is the internal step method which is called every few milliseconds 166 | const step = (virtual) => { 167 | // Normalize virtual value 168 | const render = virtual !== true; 169 | 170 | // Get current time 171 | const now = time(); 172 | 173 | // Verification is executed before next animation step 174 | if (!running[id] || (verifyCallback && !verifyCallback(id))) { 175 | running[id] = null; 176 | completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false); 177 | return; 178 | } 179 | 180 | // For the current rendering to apply let's update omitted steps in memory. 181 | // This is important to bring internal state variables up-to-date with progress in time. 182 | if (render) { 183 | const droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1; 184 | 185 | for (let j = 0; j < Math.min(droppedFrames, 4); j++) { 186 | step(true); 187 | 188 | dropCounter++; 189 | } 190 | } 191 | 192 | // Compute percent value 193 | if (duration) { 194 | percent = (now - start) / duration; 195 | if (percent > 1) { 196 | percent = 1; 197 | } 198 | } 199 | 200 | // Execute step callback, then... 201 | const value = easingMethod ? easingMethod(percent) : percent; 202 | if ((stepCallback(value, now, render) === false || percent === 1) && render) { 203 | running[id] = null; 204 | completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, percent === 1 || duration == null); 205 | } else if (render) { 206 | lastFrame = now; 207 | _this.requestAnimationFrame(step, root); 208 | } 209 | }; 210 | 211 | // Mark as running 212 | running[id] = true; 213 | 214 | // Init first step 215 | _this.requestAnimationFrame(step, root); 216 | 217 | // Return unique animation ID 218 | return id; 219 | }, 220 | }; 221 | -------------------------------------------------------------------------------- /src/components/picker/utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | 3 | export default { 4 | isDateTimeString(str) { 5 | return /^\d{4}((\.|-|\/)(0[1-9]|1[0-2]))((\.|-|\/)(0[1-9]|[12][0-9]|3[0-1]))( ([01][0-9]|2[0-3]):([012345][0-9]))?$/.test(str); 6 | }, 7 | isTimeString(str) { 8 | return /^([01][0-9]|2[0-3]):([012345][0-9])$/.test(str); 9 | }, 10 | mentStr(str) { 11 | return (`${100 + ~~str}`).substr(1, 2); 12 | }, 13 | getYearItems(config) { 14 | const years = []; 15 | const today = new Date(); 16 | 17 | let start = today.getFullYear(); 18 | let end = today.getFullYear() + 10; 19 | 20 | if (config.startYear !== 0) { 21 | start = ~~config.startYear; 22 | } 23 | 24 | if (config.endYear !== 0) { 25 | end = ~~config.endYear; 26 | } 27 | 28 | if (end < start) { 29 | end = start + 10; 30 | } 31 | 32 | if (config.startDate) { 33 | start = new Date(config.startDate.replace(/-/g, '/')).getFullYear(); 34 | } 35 | 36 | if (config.endDate) { 37 | end = new Date(config.endDate.replace(/-/g, '/')).getFullYear(); 38 | } 39 | 40 | while (start <= end) { 41 | years.push({ value: start, name: config.format.replace('{value}', start) }); 42 | start++; 43 | } 44 | 45 | return years; 46 | }, 47 | getMonthItems(config) { 48 | const months = []; 49 | let startMonth = 1; 50 | let endMonth = 12; 51 | 52 | if (config.startDate) { 53 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 54 | if (startDate.getFullYear() === config.currentYear) { 55 | startMonth = startDate.getMonth() + 1; 56 | endMonth = 12; 57 | } 58 | } 59 | 60 | if (config.endDate) { 61 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 62 | if (endDate.getFullYear() === config.currentYear) { 63 | startMonth = 1; 64 | endMonth = endDate.getMonth() + 1; 65 | } 66 | } 67 | 68 | while (startMonth <= endMonth) { 69 | const t = this.mentStr(startMonth); 70 | months.push({ value: t, name: config.format.replace('{value}', t) }); 71 | startMonth++; 72 | } 73 | 74 | return months; 75 | }, 76 | getDateItems(config) { 77 | const dates = []; 78 | const today = new Date(); 79 | let year = today.getFullYear(); 80 | let month = today.getMonth(); 81 | 82 | if (config.currentYear) year = ~~config.currentYear; 83 | 84 | if (config.currentMonth) month = ~~config.currentMonth - 1; 85 | 86 | let end = 30; 87 | 88 | if ([0, 2, 4, 6, 7, 9, 11].indexOf(month) > -1) { 89 | end = 31; 90 | } else if (month === 1) { 91 | end = year % 100 === 0 ? (year % 400 === 0 ? 29 : 28) : (year % 4 === 0 ? 29 : 28); 92 | } 93 | if (config.endDate) { 94 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 95 | if (endDate.getMonth() + 1 === config.currentMonth && endDate.getFullYear() === config.currentYear && endDate.getDate() < end) { 96 | end = endDate.getDate(); 97 | } 98 | } 99 | 100 | let d = 1; 101 | if (config.startDate) { 102 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 103 | if (startDate.getMonth() + 1 === config.currentMonth && startDate.getFullYear() === config.currentYear) { 104 | d = startDate.getDate(); 105 | } 106 | } 107 | 108 | while (d <= end) { 109 | const t = this.mentStr(d); 110 | dates.push({ value: t, name: config.format.replace('{value}', t) }); 111 | d++; 112 | } 113 | 114 | return dates; 115 | }, 116 | getHourItems(config) { 117 | const hours = []; 118 | let start = ~~config.startHour; 119 | let end = ~~config.endHour; 120 | 121 | if (end < start) { 122 | end = 23; 123 | } 124 | 125 | if (config.startDate) { 126 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 127 | if (startDate.getFullYear() === config.currentYear && startDate.getMonth() + 1 === config.currentMonth && startDate.getDate() === config.currentDay) { 128 | start = startDate.getHours(); 129 | } 130 | } 131 | 132 | if (config.endDate) { 133 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 134 | if (endDate.getFullYear() === config.currentYear && endDate.getMonth() + 1 === config.currentMonth && endDate.getDate() === config.currentDay) { 135 | end = endDate.getHours(); 136 | } 137 | } 138 | 139 | while (start <= end) { 140 | const t = this.mentStr(start); 141 | hours.push({ value: t, name: config.format.replace('{value}', t) }); 142 | start++; 143 | } 144 | 145 | return hours; 146 | }, 147 | getMinuteItems(config) { 148 | const minute = []; 149 | let start = ~~config.startMinute; 150 | let end = ~~config.endMinute; 151 | 152 | if (config.startDate) { 153 | const startDate = new Date(config.startDate.replace(/-/g, '/')); 154 | if (startDate.getFullYear() === config.currentYear && startDate.getMonth() + 1 === config.currentMonth && startDate.getDate() === config.currentDay && startDate.getHours() === config.currentHour) { 155 | start = startDate.getMinutes(); 156 | } 157 | } 158 | 159 | if (config.endDate) { 160 | const endDate = new Date(config.endDate.replace(/-/g, '/')); 161 | if (endDate.getFullYear() === config.currentYear && endDate.getMonth() + 1 === config.currentMonth && endDate.getDate() === config.currentDay && endDate.getHours() === config.currentHour) { 162 | end = endDate.getMinutes(); 163 | } 164 | } 165 | 166 | while (start <= end) { 167 | const t = this.mentStr(start); 168 | minute.push({ value: t, name: config.format.replace('{value}', t) }); 169 | start += config.timeStep; 170 | } 171 | return minute; 172 | }, 173 | }; 174 | -------------------------------------------------------------------------------- /src/components/scroller/Arrow.vue: -------------------------------------------------------------------------------- 1 | 79 | 89 | -------------------------------------------------------------------------------- /src/components/scroller/Spinner.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/scroller/render.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | function getContentRender(content) { 3 | var global = window; 4 | 5 | var docStyle = document.documentElement.style; 6 | 7 | var engine; 8 | if (global.opera && Object.prototype.toString.call(opera) === '[object Opera]') { 9 | engine = 'presto'; 10 | } else if ('MozAppearance' in docStyle) { 11 | engine = 'gecko'; 12 | } else if ('WebkitAppearance' in docStyle) { 13 | engine = 'webkit'; 14 | } else if (typeof navigator.cpuClass === 'string') { 15 | engine = 'trident'; 16 | } 17 | 18 | var vendorPrefix = { 19 | trident: 'ms', 20 | gecko: 'Moz', 21 | webkit: 'Webkit', 22 | presto: 'O' 23 | }[engine]; 24 | 25 | var helperElem = document.createElement("div"); 26 | var undef; 27 | 28 | var perspectiveProperty = vendorPrefix + "Perspective"; 29 | var transformProperty = vendorPrefix + "Transform"; 30 | 31 | if (helperElem.style[perspectiveProperty] !== undef) { 32 | 33 | return function (left, top, zoom) { 34 | content.style[transformProperty] = 'translate3d(' + (-left) + 'px,' + (-top) + 'px,0) scale(' + zoom + ')'; 35 | }; 36 | 37 | } else if (helperElem.style[transformProperty] !== undef) { 38 | 39 | return function (left, top, zoom) { 40 | content.style[transformProperty] = 'translate(' + (-left) + 'px,' + (-top) + 'px) scale(' + zoom + ')'; 41 | }; 42 | 43 | } else { 44 | 45 | return function (left, top, zoom) { 46 | content.style.marginLeft = left ? (-left / zoom) + 'px' : ''; 47 | content.style.marginTop = top ? (-top / zoom) + 'px' : ''; 48 | content.style.zoom = zoom || ''; 49 | }; 50 | 51 | } 52 | } 53 | 54 | module.exports = getContentRender; 55 | -------------------------------------------------------------------------------- /src/components/search/SearchList.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | -------------------------------------------------------------------------------- /src/components/search/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 42 | 65 | 155 | -------------------------------------------------------------------------------- /src/components/select/Select.vue: -------------------------------------------------------------------------------- 1 | 2 | 120 | 147 | 196 | -------------------------------------------------------------------------------- /src/components/swiper/SwiperItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /src/components/swiper/index.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 109 | 110 | 182 | -------------------------------------------------------------------------------- /src/components/switch/Switch.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 65 | 66 | 123 | -------------------------------------------------------------------------------- /src/components/table/Table.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 41 | 42 | 63 | 64 | -------------------------------------------------------------------------------- /src/components/table/TableColumn.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import App from './App' 4 | import router from './router'; 5 | 6 | import { Msg, Modal } from 'components/dialog' 7 | // import Modal from 'components/modal'; 8 | 9 | // Vue.prototype.$message = Message 10 | // or register global message component 11 | /* 12 | * import MessageComponent from 'components/message/message'; 13 | * Vue.component(MessageComponent.name, MessageComponent) 14 | */ 15 | 16 | // Vue.prototype.$modal = Modal 17 | // or register global modal component 18 | /* 19 | * import ModalComponent from 'components/modal/modal'; 20 | * Vue.component(ModalComponent.name, ModalComponent) 21 | * 22 | */ 23 | 24 | // const components = [ 25 | // require('./components').Dropdown, 26 | // require('./components').DropdownMenu, 27 | // require('./components').DropdownList, 28 | // require('./components').Swipe, 29 | // require('./components').SwipeItem, 30 | // require('./components').Swiper, 31 | // require('./components').Scroller, 32 | // require('./components').Search, 33 | // require('./components').SearchList, 34 | // require('./components').Table, 35 | // require('./components').TableColumn 36 | // ] 37 | import components from 'components' 38 | 39 | components.map(component => { 40 | Vue.component(component.name, component) 41 | }) 42 | 43 | 44 | Vue.prototype.$dialog = { 45 | msg: Msg, 46 | modal: Modal 47 | } 48 | 49 | // 动态设置页面title 设置全局指令 50 | Vue.directive('title', { 51 | inserted(el, binding) { 52 | document.title = el.dataset.title; 53 | }, 54 | }); 55 | 56 | /* eslint-disable no-new */ 57 | new Vue({ 58 | el: '#app', 59 | router, 60 | template: '', 61 | components: { App } 62 | }) 63 | -------------------------------------------------------------------------------- /src/mixins/emitter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [broadcast 向下传递] 3 | * @param {[type]} componentName [组件别名] 4 | * @param {[type]} eventName [事件别名] 5 | * @param {[type]} params [事件回调参数] 6 | */ 7 | function broadcast(componentName, eventName, params) { 8 | // 遍历当前实例的children节点 9 | this.$children.forEach(child => { 10 | var name = child.$options.componentName; 11 | // 如果子节点名称和组件别名相同,则当前子节点为目标节点 12 | if (name === componentName) { 13 | // 找到目标节点后,触发事件别名对应的回调,并将参数传入 14 | child.$emit.apply(child, [eventName].concat(params)); 15 | } 16 | // 如果子节点名称和组件别名不相同,继续遍历子节点的子节点,以此类推,直到找到目标节点 17 | else { 18 | broadcast.apply(child, [componentName, eventName].concat([params])); 19 | } 20 | }); 21 | } 22 | /** 23 | * [dispatch 向上传递] 24 | * @param {[type]} componentName [组件别名] 25 | * @param {[type]} eventName [事件别名] 26 | * @param {[type]} params [事件回调参数] 27 | */ 28 | function dispatch(componentName, eventName, params) { 29 | var parent = this.$parent || this.$root; 30 | var name = parent.$options.name; 31 | // 向上找目标父节点,如果上一级父节点不符合,则继续往上查询 32 | while (parent && (!name || name !== componentName)) { 33 | parent = parent.$parent; 34 | 35 | if (parent) { 36 | name = parent.$options.name; 37 | } 38 | } 39 | // 找到目标父节点后,触发事件别名对应的回调,并将参数传入 40 | if (parent) { 41 | parent.$emit.apply(parent, [eventName].concat(params)); 42 | } 43 | } 44 | export default { 45 | methods: { 46 | broadcast(componentName, eventName, params) { 47 | broadcast.apply(this, [componentName, eventName, params]); 48 | }, 49 | dispatch(componentName, eventName, params) { 50 | dispatch.apply(this, [componentName, eventName, params]); 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/pages/dialog.vue: -------------------------------------------------------------------------------- 1 | 25 | 31 | 32 | 55 | -------------------------------------------------------------------------------- /src/pages/dropdown.vue: -------------------------------------------------------------------------------- 1 | 7 | 19 | 20 | 30 | -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 22 | 23 | 24 | 49 | -------------------------------------------------------------------------------- /src/pages/picker.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 38 | 39 | 67 | -------------------------------------------------------------------------------- /src/pages/scroller.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | 31 | -------------------------------------------------------------------------------- /src/pages/search.vue: -------------------------------------------------------------------------------- 1 | 29 | 49 | 50 | -------------------------------------------------------------------------------- /src/pages/select.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 52 | -------------------------------------------------------------------------------- /src/pages/swiper.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | 31 | 40 | -------------------------------------------------------------------------------- /src/pages/switch.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 32 | 33 | 50 | -------------------------------------------------------------------------------- /src/pages/table.vue: -------------------------------------------------------------------------------- 1 | 10 | 27 | 28 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Router from 'vue-router'; 3 | import routers from './router.config'; 4 | 5 | Router.prototype.goBack = function () { 6 | this.isBack = true; 7 | window.history.go(-1); 8 | }; 9 | Router.prototype.setTitle = function (title) { 10 | document.title = title; 11 | }; 12 | 13 | Vue.use(Router); 14 | 15 | const RouterModel = new Router({ 16 | mode: 'history', 17 | routes: routers, 18 | }); 19 | 20 | export default RouterModel; 21 | -------------------------------------------------------------------------------- /src/router/router.config.js: -------------------------------------------------------------------------------- 1 | import App from '../App' 2 | 3 | const home = r => require.ensure([], () => r(require('pages/index')), 'home') 4 | const dropdown = r => require.ensure([], () => r(require('pages/dropdown')), 'dropdown') 5 | const scroller = r => require.ensure([], () => r(require('pages/scroller')), 'scroller') 6 | const search = r => require.ensure([], () => r(require('pages/search')), 'search') 7 | const swiper = r => require.ensure([], () => r(require('pages/swiper')), 'swiper') 8 | const table = r => require.ensure([], () => r(require('pages/table')), 'table') 9 | const picker = r => require.ensure([], () => r(require('pages/picker')), 'picker') 10 | const select = r => require.ensure([], () => r(require('pages/select')), 'select') 11 | const dialog = r => require.ensure([], () => r(require('pages/dialog')), 'dialog') 12 | const Switch = r => require.ensure([], () => r(require('pages/switch')), 'switch') 13 | 14 | let routers = []; 15 | routers = [ 16 | { 17 | path: '/', 18 | component: App, 19 | children: [ 20 | //地址为空时跳转home页面 21 | { 22 | path: '', 23 | redirect: '/home' 24 | }, 25 | { 26 | path: 'home', 27 | name: 'home', 28 | component: home 29 | }, 30 | { 31 | path: 'dropdown', 32 | name: 'dropdown', 33 | component: dropdown, 34 | }, 35 | { 36 | path: 'scroller', 37 | name: 'scroller', 38 | component: scroller, 39 | }, 40 | { 41 | path: 'search', 42 | name: 'search', 43 | component: search, 44 | }, 45 | { 46 | path: 'swiper', 47 | name: 'swiper', 48 | component: swiper, 49 | }, 50 | { 51 | path: 'table', 52 | name: 'table', 53 | component: table, 54 | }, 55 | { 56 | path: 'picker', 57 | name: 'picker', 58 | component: picker, 59 | }, 60 | { 61 | path: 'select', 62 | name: 'select', 63 | component: select, 64 | }, 65 | { 66 | path: 'dialog', 67 | name: 'dialog', 68 | component: dialog, 69 | }, 70 | { 71 | path: 'switch', 72 | name: 'switch', 73 | component: Switch, 74 | } 75 | ] 76 | } 77 | ] 78 | 79 | export default routers; 80 | -------------------------------------------------------------------------------- /src/stylus/_common.styl: -------------------------------------------------------------------------------- 1 | .l { 2 | float: left; 3 | } 4 | .r { 5 | float: right; 6 | } 7 | .gray { 8 | color: #999; 9 | } 10 | .clearfix { 11 | &::after { 12 | display: block; 13 | content: ' '; 14 | clear: both; 15 | } 16 | } 17 | .placeholder { 18 | font-size: 14px; 19 | color: #CCC 20 | } 21 | .x-list { 22 | padding: 0 0.32rem; 23 | background: #fff; 24 | color: #333; 25 | font-size: 14px; 26 | 27 | > li { 28 | min-height: 60px; 29 | padding-top: 21px; 30 | border-bottom: 1px solid #f2f2f2; 31 | overflow: hidden; 32 | 33 | > label { 34 | float: left; 35 | } 36 | 37 | > div { 38 | float: right; 39 | } 40 | 41 | .x-list-arrow { 42 | min-width: 100px; 43 | margin-right: 10px; 44 | position: relative; 45 | 46 | > div { 47 | float: right; 48 | text-align: right; 49 | margin-right: 10px; 50 | } 51 | 52 | &:after { 53 | content: ''; 54 | position: absolute; 55 | top: 4px; 56 | right: -5px; 57 | width: 10px; 58 | height: 10px; 59 | border-top: 1px solid #ccc; 60 | border-right: 1px solid #ccc; 61 | transform: rotate(45deg); 62 | -webkit-transform: rotate(45deg); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/stylus/_reset.styl: -------------------------------------------------------------------------------- 1 | *, 2 | *:before, 3 | *:after { 4 | box-sizing: border-box; 5 | outline: none; 6 | text-size-adjust: none; 7 | } 8 | html, body { 9 | height: 100%; 10 | font-size: 50px; 11 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 12 | } 13 | 14 | body { 15 | background-color: #f7f7f7; 16 | font-size: 16px; 17 | -webkit-font-smoothing: antialiased; 18 | font-family: arial, sans-serif; 19 | } 20 | 21 | body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td, iframe { 22 | margin: 0; 23 | padding: 0; 24 | } 25 | 26 | img, article, aside, details, figcaption, figure, footer, header, menu, nav, section, summary, time, mark, audio, video { 27 | display: block; 28 | margin: 0; 29 | padding: 0; 30 | } 31 | 32 | h1, h2, h3, h4, h5, h6 { 33 | font-size: 100%; 34 | } 35 | 36 | fieldset, img { 37 | border: 0; 38 | } 39 | 40 | address, caption, cite, dfn, em, th, var, i, em { 41 | font-style: normal; 42 | font-weight: normal; 43 | } 44 | 45 | ol, ul { 46 | list-style: none; 47 | } 48 | 49 | a { 50 | text-decoration: none; 51 | color: inherit; 52 | &:hover { 53 | text-decoration: none; 54 | } 55 | } 56 | 57 | a, label, button, input, select { 58 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 59 | } 60 | 61 | input, select, button { 62 | font:100% tahoma, "\5b8b\4f53", arial; 63 | vertical-align: baseline; 64 | border-radius: 0; 65 | background-color: transparent; 66 | } 67 | 68 | select { 69 | -webkit-appearance: none; 70 | -moz-appearance: none; 71 | } 72 | 73 | button::-moz-focus-inner, 74 | input[type="reset"]::-moz-focus-inner, 75 | input[type="button"]::-moz-focus-inner, 76 | input[type="submit"]::-moz-focus-inner, 77 | input[type="file"] > input[type="button"]::-moz-focus-inner { 78 | border: none; 79 | } 80 | 81 | input[type=checkbox], input[type=radio] { 82 | vertical-align: middle; 83 | } 84 | 85 | input[type="number"]::-webkit-outer-spin-button, 86 | input[type="number"]::-webkit-inner-spin-button { 87 | -webkit-appearance: none !important; 88 | -moz-appearance: none !important; 89 | margin: 0; 90 | } 91 | 92 | input:-webkit-autofill { 93 | -webkit-box-shadow: 0 0 0 1000px white inset; 94 | } 95 | 96 | input[type=search],input[type=tel],input[type=text],input { 97 | -webkit-appearance: none; /*去除系统默认的样式*/ 98 | -webkit-box-sizing: content-box; 99 | font-family: inherit; 100 | font-size: 100%; 101 | box-sizing: border-box; 102 | } 103 | 104 | input::-webkit-search-decoration, 105 | input::-webkit-search-cancel-button { 106 | display: none; 107 | } 108 | 109 | textarea { 110 | outline: none; 111 | border-radius: 0; 112 | -webkit-appearance: none; 113 | -moz-appearance: none; 114 | overflow: auto; 115 | resize: none; 116 | font: 100% tahoma, "\5b8b\4f53", arial; 117 | } 118 | 119 | 120 | textarea::-webkit-input-placeholder, input::-webkit-input-placeholder { /* WebKit browsers */ 121 | color: #CCC; 122 | } 123 | textarea:-moz-placeholder ,input:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ 124 | color: #CCC; 125 | opacity: 1; 126 | } 127 | textarea::-moz-placeholder, input::-moz-placeholder { /* Mozilla Firefox 19+ */ 128 | color: #CCC; 129 | opacity: 1; 130 | } 131 | textarea:-ms-input-placeholder, input:-ms-input-placeholder { /* Internet Explorer 10+ */ 132 | color: #CCC; 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/utils/clickoutside.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { on } from './dom.js'; 3 | 4 | const nodeList = []; 5 | const ctx = '@@clickoutsideContext'; 6 | 7 | !Vue.prototype.$isServer && on(document, 'click', e => { 8 | nodeList.forEach(node => node[ctx].documentHandler(e)); 9 | }); 10 | /** 11 | * v-clickoutside 12 | * @desc 点击元素外面才会触发的事件 13 | * @example 14 | * ```vue 15 | *
16 | * ``` 17 | */ 18 | export default { 19 | bind(el, binding, vnode) { 20 | const id = nodeList.push(el) - 1; 21 | const documentHandler = function(e) { 22 | if (!vnode.context || 23 | el.contains(e.target) || 24 | (vnode.context.popperElm && 25 | vnode.context.popperElm.contains(e.target))) return; 26 | 27 | if (binding.expression && 28 | el[ctx].methodName && 29 | vnode.context[el[ctx].methodName]) { 30 | vnode.context[el[ctx].methodName](); 31 | } else { 32 | el[ctx].bindingFn && el[ctx].bindingFn(); 33 | } 34 | }; 35 | el[ctx] = { 36 | id, 37 | documentHandler, 38 | methodName: binding.expression, 39 | bindingFn: binding.value 40 | }; 41 | }, 42 | 43 | update(el, binding) { 44 | el[ctx].methodName = binding.expression; 45 | el[ctx].bindingFn = binding.value; 46 | }, 47 | 48 | unbind(el) { 49 | let len = nodeList.length; 50 | 51 | for (let i = 0; i < len; i++) { 52 | if (nodeList[i][ctx].id === el[ctx].id) { 53 | nodeList.splice(i, 1); 54 | break; 55 | } 56 | } 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/utils/dom.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | 3 | import Vue from 'vue'; 4 | 5 | const isServer = Vue.prototype.$isServer; 6 | const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; 7 | const MOZ_HACK_REGEXP = /^moz([A-Z])/; 8 | const ieVersion = isServer ? 0 : Number(document.documentMode); 9 | 10 | /* istanbul ignore next */ 11 | const trim = function(string) { 12 | return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, ''); 13 | }; 14 | /* istanbul ignore next */ 15 | const camelCase = function(name) { 16 | return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { 17 | return offset ? letter.toUpperCase() : letter; 18 | }).replace(MOZ_HACK_REGEXP, 'Moz$1'); 19 | }; 20 | 21 | /* istanbul ignore next */ 22 | export const on = (function() { 23 | if (!isServer && document.addEventListener) { 24 | return function(element, event, handler) { 25 | if (element && event && handler) { 26 | element.addEventListener(event, handler, false); 27 | } 28 | }; 29 | } else { 30 | return function(element, event, handler) { 31 | if (element && event && handler) { 32 | element.attachEvent('on' + event, handler); 33 | } 34 | }; 35 | } 36 | })(); 37 | 38 | /* istanbul ignore next */ 39 | export const off = (function() { 40 | if (!isServer && document.removeEventListener) { 41 | return function(element, event, handler) { 42 | if (element && event) { 43 | element.removeEventListener(event, handler, false); 44 | } 45 | }; 46 | } else { 47 | return function(element, event, handler) { 48 | if (element && event) { 49 | element.detachEvent('on' + event, handler); 50 | } 51 | }; 52 | } 53 | })(); 54 | 55 | /* istanbul ignore next */ 56 | export const once = function(el, event, fn) { 57 | var listener = function() { 58 | if (fn) { 59 | fn.apply(this, arguments); 60 | } 61 | off(el, event, listener); 62 | }; 63 | on(el, event, listener); 64 | }; 65 | 66 | /* istanbul ignore next */ 67 | export function hasClass(el, cls) { 68 | if (!el || !cls) return false; 69 | if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.'); 70 | if (el.classList) { 71 | return el.classList.contains(cls); 72 | } else { 73 | return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1; 74 | } 75 | }; 76 | 77 | /* istanbul ignore next */ 78 | export function addClass(el, cls) { 79 | if (!el) return; 80 | var curClass = el.className; 81 | var classes = (cls || '').split(' '); 82 | 83 | for (var i = 0, j = classes.length; i < j; i++) { 84 | var clsName = classes[i]; 85 | if (!clsName) continue; 86 | 87 | if (el.classList) { 88 | el.classList.add(clsName); 89 | } else { 90 | if (!hasClass(el, clsName)) { 91 | curClass += ' ' + clsName; 92 | } 93 | } 94 | } 95 | if (!el.classList) { 96 | el.className = curClass; 97 | } 98 | }; 99 | 100 | /* istanbul ignore next */ 101 | export function removeClass(el, cls) { 102 | if (!el || !cls) return; 103 | var classes = cls.split(' '); 104 | var curClass = ' ' + el.className + ' '; 105 | 106 | for (var i = 0, j = classes.length; i < j; i++) { 107 | var clsName = classes[i]; 108 | if (!clsName) continue; 109 | 110 | if (el.classList) { 111 | el.classList.remove(clsName); 112 | } else { 113 | if (hasClass(el, clsName)) { 114 | curClass = curClass.replace(' ' + clsName + ' ', ' '); 115 | } 116 | } 117 | } 118 | if (!el.classList) { 119 | el.className = trim(curClass); 120 | } 121 | }; 122 | 123 | /* istanbul ignore next */ 124 | export const getStyle = ieVersion < 9 ? function(element, styleName) { 125 | if (isServer) return; 126 | if (!element || !styleName) return null; 127 | styleName = camelCase(styleName); 128 | if (styleName === 'float') { 129 | styleName = 'styleFloat'; 130 | } 131 | try { 132 | switch (styleName) { 133 | case 'opacity': 134 | try { 135 | return element.filters.item('alpha').opacity / 100; 136 | } catch (e) { 137 | return 1.0; 138 | } 139 | default: 140 | return (element.style[styleName] || element.currentStyle ? element.currentStyle[styleName] : null); 141 | } 142 | } catch (e) { 143 | return element.style[styleName]; 144 | } 145 | } : function(element, styleName) { 146 | if (isServer) return; 147 | if (!element || !styleName) return null; 148 | styleName = camelCase(styleName); 149 | if (styleName === 'float') { 150 | styleName = 'cssFloat'; 151 | } 152 | try { 153 | var computed = document.defaultView.getComputedStyle(element, ''); 154 | return element.style[styleName] || computed ? computed[styleName] : null; 155 | } catch (e) { 156 | return element.style[styleName]; 157 | } 158 | }; 159 | 160 | /* istanbul ignore next */ 161 | export function setStyle(element, styleName, value) { 162 | if (!element || !styleName) return; 163 | 164 | if (typeof styleName === 'object') { 165 | for (var prop in styleName) { 166 | if (styleName.hasOwnProperty(prop)) { 167 | setStyle(element, prop, styleName[prop]); 168 | } 169 | } 170 | } else { 171 | styleName = camelCase(styleName); 172 | if (styleName === 'opacity' && ieVersion < 9) { 173 | element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')'; 174 | } else { 175 | element.style[styleName] = value; 176 | } 177 | } 178 | }; 179 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuqiang521/vui/688a6b8b199915350ae90d29ddb1ddb8d9b5f76d/static/.gitkeep -------------------------------------------------------------------------------- /vue-part.js: -------------------------------------------------------------------------------- 1 | var hookRE = /^hook:/; 2 | /** 3 | * [$on 事件注册] 4 | * @param {[type]} event [注册事件别名] 5 | * @param {Function} fn [注册事件对应回调] 6 | */ 7 | Vue.prototype.$on = function (event, fn) { 8 | var this$1 = this; 9 | 10 | var vm = this; 11 | // 遍历需要发布的消息是否是数组,如果是,则循环注册 12 | if (Array.isArray(event)) { 13 | for (var i = 0, l = event.length; i < l; i++) { 14 | this$1.$on(event[i], fn); 15 | } 16 | // 如果不是则单次注册 17 | } else { 18 | // 默认值 vm._events = Object.create(null); 通过数组的push()将注册事件回调保存在vm._events[event]中 19 | (vm._events[event] || (vm._events[event] = [])).push(fn); 20 | if (hookRE.test(event)) { 21 | // 默认值vm._hasHookEvent = false 22 | vm._hasHookEvent = true; 23 | } 24 | } 25 | return vm 26 | }; 27 | /** 28 | * [$once 仅注册一次事件] 29 | * @param {[type]} event [注册事件别名] 30 | * @param {Function} fn [注册事件对应回调] 31 | */ 32 | Vue.prototype.$once = function (event, fn) { 33 | var vm = this; 34 | // 定义 on()函数进行事件监听并移除,同时作为$on() 函数的回调执行 35 | function on () { 36 | // 移除事件 37 | vm.$off(event, on); 38 | // 执行回调,进行事件监听 39 | fn.apply(vm, arguments); 40 | } 41 | on.fn = fn; 42 | vm.$on(event, on); 43 | return vm 44 | }; 45 | /** 46 | * [$off 事件移除] 47 | * @param {[type]} event [注册事件别名] 48 | * @param {Function} fn [注册事件对应回调] 49 | */ 50 | Vue.prototype.$off = function (event, fn) { 51 | var this$1 = this; 52 | 53 | var vm = this; 54 | // 移除所有的事件监听器 55 | if (!arguments.length) { 56 | vm._events = Object.create(null); 57 | return vm 58 | } 59 | // 如果事件别名是数组,则循环将数组中对应的所有事件别名对应的监听器移除 60 | if (Array.isArray(event)) { 61 | for (var i$1 = 0, l = event.length; i$1 < l; i$1++) { 62 | this$1.$off(event[i$1], fn); 63 | } 64 | return vm 65 | } 66 | // specific event 67 | var cbs = vm._events[event]; 68 | if (!cbs) { 69 | return vm 70 | } 71 | // 如果只传了事件别名一个参数,则移除该事件对应的所有监听器 72 | if (arguments.length === 1) { 73 | vm._events[event] = null; 74 | return vm 75 | } 76 | // 参数中既传了事件别名,还传了回调 77 | var cb; 78 | var i = cbs.length; 79 | // 遍历事件对应的所有监听器,即 cbs = vm._events[event] 80 | while (i--) { 81 | cb = cbs[i]; 82 | // 如果找到目标监听器,则通过splice移除数组中的监听器,并通过break终止循环 83 | if (cb === fn || cb.fn === fn) { 84 | cbs.splice(i, 1); 85 | break 86 | } 87 | } 88 | return vm 89 | }; 90 | /** 91 | * [$emit 触发事件] 92 | * @param {[type]} event [事件别名] 93 | */ 94 | Vue.prototype.$emit = function (event) { 95 | var vm = this; 96 | { 97 | var lowerCaseEvent = event.toLowerCase(); 98 | if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) { 99 | tip( 100 | "Event \"" + lowerCaseEvent + "\" is emitted in component " + 101 | (formatComponentName(vm)) + " but the handler is registered for \"" + event + "\". " + 102 | "Note that HTML attributes are case-insensitive and you cannot use " + 103 | "v-on to listen to camelCase events when using in-DOM templates. " + 104 | "You should probably use \"" + (hyphenate(event)) + "\" instead of \"" + event + "\"." 105 | ); 106 | } 107 | } 108 | // 定义cbs接收 vm._events[event] 109 | var cbs = vm._events[event]; 110 | if (cbs) { 111 | // 通过判断cbs缓存的监听器个数,确保cbs为数组,以便下面的循环执行 112 | cbs = cbs.length > 1 ? toArray(cbs) : cbs; 113 | var args = toArray(arguments, 1); 114 | // 遍历数组cbs,循环执行数组cbs中的方法 115 | for (var i = 0, l = cbs.length; i < l; i++) { 116 | cbs[i].apply(vm, args); 117 | } 118 | } 119 | return vm 120 | }; 121 | --------------------------------------------------------------------------------