├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── .travis.yml ├── LICENSE ├── README.md ├── build ├── build.js ├── check-versions.js ├── cooking.conf.js ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.components.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── components ├── LunarFullCalendar │ ├── LunarFullCalendar.vue │ ├── fullcalendar.css │ └── fullcalendar.js └── index.js ├── config ├── dev.env.js ├── index.js └── prod.env.js ├── docs ├── index.html └── static │ ├── img │ └── lunar.b0e3d9b.png │ └── js │ ├── 1.971963f2ecee05637c92.js │ ├── 1.971963f2ecee05637c92.js.map │ ├── 2.69462526e73edaa36ba8.js │ ├── 2.69462526e73edaa36ba8.js.map │ ├── 3.6d227b05e837433b3d45.js │ ├── 3.6d227b05e837433b3d45.js.map │ ├── 4.22a5aaaa784121c40a27.js │ ├── 4.22a5aaaa784121c40a27.js.map │ ├── app.c9dd13ceee3bb04b520a.js │ └── app.c9dd13ceee3bb04b520a.js.map ├── index.html ├── lib ├── LunarFullCalendar.min.js └── LunarFullCalendar.min.js.LICENSE ├── package-lock.json ├── package.json ├── src ├── App.vue ├── assets │ ├── css │ │ └── reset.scss │ └── img │ │ ├── lunar.png │ │ └── window.lunar.png ├── main.js ├── router │ └── index.js └── view │ ├── explain.vue │ └── lunarFullCalendar.vue ├── static └── .gitkeep └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2", 10 | "es2015" 11 | ], 12 | "plugins": ["transform-vue-jsx", "transform-runtime"], 13 | "compact": false // 文件超过500m提示 14 | } 15 | -------------------------------------------------------------------------------- /.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/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 28 | 'prefer-promise-reject-errors': 'off', 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | /docs/ 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | before_install: 5 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.27.5 6 | - export PATH="$HOME/.yarn/bin:$PATH" 7 | cache: yarn -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 hj 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## [vue-lunar-full-calendar](https://hjdev.github.io/vue-lunar-fullcalendar) 2 | 3 | [![NPM version](https://img.shields.io/badge/npm-v1.3.2-green.svg)](https://www.npmjs.com/package/vue-lunar-full-calendar) 4 | [![NPM downloads](https://img.shields.io/badge/downloads-vue--lunar--full--calendar-green.svg)](https://www.npmjs.com/package/vue-lunar-full-calendar) 5 | 6 | ## Image and demo 7 | 8 | ``` 9 | a vue component for lunar fullcalendar. Uses Moment.js for date operations. 10 | Increase the functions of Chinese lunar calendar, 24 solar terms and holidays 11 | ``` 12 | 13 | [Simple Live Demo](https://hjdev.github.io/vue-lunar-fullcalendar) 14 | ![Image text](https://raw.githubusercontent.com/hjdev/vue-lunar-fullcalendar/master/src/assets/img/lunar.png) 15 | 16 | ## Installation 17 | 18 | ``` 19 | npm install --save vue-lunar-full-calendar 20 | ``` 21 | 22 | ```js 23 | //main.js 24 | import LunarFullCalendar from "vue-lunar-full-calendar"; 25 | Vue.use(LunarFullCalendar); 26 | ``` 27 | 28 | But you can also import the standalone component to add locally or for more complex installations. 29 | 30 | ```js 31 | // index.vue 32 | import { LunarFullCalendar } from "vue-lunar-full-calendar"; 33 | export default { 34 | components: { 35 | LunarFullCalendar 36 | } 37 | }; 38 | ``` 39 | 40 | ## Important function 41 | 42 | 1、window.lunar(date) 43 | 44 | Use vue-lunar-full-calendar , You can use one function to get the date of a certain day. 45 | 46 | 2、lunarCalendar (default: true) 47 | 48 | You can pass any custom options through to fullcalendar by using the `config` prop. 49 | Control whether the Chinese calendar shows true. 50 | 51 | ```html 52 | 53 | ... 54 | 68 | ``` 69 | 70 | ![Image text](https://raw.githubusercontent.com/hjdev/vue-lunar-fullcalendar/master/src/assets/img/window.lunar.png) 71 | 72 | ## API document 73 | 74 | [Fullcalendar 文档(Fullcalendar docs)](https://fullcalendar.io/docs/) 75 | 76 | ## Example App 77 | 78 | I have created a simple Vue 2 webpack application as an example/playground 79 | https://github.com/hjdev/vue-lunar-fullcalendar 80 | 81 | ## Basic Usage 82 | 83 | You can pass an array of fullclendar objects through the props 84 | 85 | ```html 86 | ... 87 | 111 | ``` 112 | 113 | More event options can be found at http://fullcalendar.io/docs/event_data/Event_Object/ 114 | 115 | ## Using a JSON Feed 116 | 117 | ```html 118 | ... 119 | 146 | ``` 147 | 148 | ## Custom Config 149 | 150 | You can pass any custom [options](https://fullcalendar.io/docs/) through to fullcalendar by using the `config` prop, this includes extra event handlers. 151 | 152 | ```html 153 | ... 154 | 169 | ``` 170 | 171 | ## Further Props 172 | 173 | You can edit the look and feel of fullcalendar by passing through extra props. These all have sensible defaults 174 | 175 | - **header** - [obj] - [docs](http://fullcalendar.io/docs/display/header/) 176 | - **defaultView** - ['agendaWeek'] - [docs](http://fullcalendar.io/docs/views/defaultView/) 177 | - **editable** - [true] - [docs](http://fullcalendar.io/docs/event_ui/editable/) 178 | - **selectable** - [true] - [docs](http://fullcalendar.io/docs/selection/selectable/) 179 | - **selectHelper** - [true] - [docs](http://fullcalendar.io/docs/selection/selectHelper/) 180 | - **config** - [true] - Pass your own custom config straight through to fullcalendar 181 | 182 | ## Methods 183 | 184 | Sometimes you may need to manipulate the Calendar from your parent component, you can use `fireMethod` for this. This works with anything in the [Fullcalendar docs](https://fullcalendar.io/docs/) suffixed with `(method)` and it will dynamically handle as many arguments as needed. 185 | 186 | ```html 187 | ... 188 | 206 | ``` 207 | 208 | ## Events and Hooks 209 | 210 | ### Emitted 211 | 212 | - **event-selected(event, jsEvent, view)** - Triggered on eventClick() 213 | - **event-drop(event)** - Triggered on eventDrop() 214 | - **event-resize(event)** - Triggered on eventResize() 215 | - **event-created(event)** - Triggered on select() 216 | - **event-receive(event)** - Triggered on eventReceive() 217 | - **event-render(event)** - Triggered on eventRender() 218 | - **day-click(date, jsEvent, view)** - Triggered on dayClick() 219 | 220 | You can listen for these events using the following markup 221 | 222 | ```html 223 | 227 | ``` 228 | 229 | ### Listens on 230 | 231 | - **render-event(event)** - Adds a new event to calendar 232 | - **remove-event(event)** - Removes event from calendar 233 | - **rerender-events()** - Rerenders events to reflect local changes 234 | - **refetch-events()** - Makes another JSON call to event sources 235 | - **reload-events()** - Removes all events and adds all events in this.events 236 | 237 | You can trigger these events in the parent component like so... 238 | 239 | ```html 240 | 244 | ... 245 | 254 | ``` 255 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = process.env.NODE_ENV || 'production' 5 | console.log('NODE_ENV', process.env.NODE_ENV) 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /build/cooking.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var cooking = require('cooking') 3 | var config = require('./webpack.base.conf') 4 | 5 | cooking.set({ 6 | // use: 'vue', 7 | entry: { 8 | 'LunarFullCalendar.min': './components/index.js' 9 | }, 10 | dist: './lib', 11 | 12 | minimize: true, 13 | clean: true, 14 | // sourceMap: true, 15 | // extractCSS: true, 16 | publicPath: '/lib/', 17 | // assetsPath: 'static', 18 | format: 'cjs', 19 | extends: ['vue'], 20 | alias: config.resolve.alias, 21 | externals: { 22 | 'vue': 'vue' 23 | } 24 | }) 25 | // cooking.add('loader.js.exclude', '/node_modules/'); 26 | // cooking.add('loader.scss', { 27 | // test: /\.scss$/, 28 | // loaders: ['style-loader', 'css-loader', 'sass-loader'] 29 | // }); 30 | // cooking.add('vue.preserveWhitespace', false); 31 | 32 | module.exports = cooking.resolve() 33 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | // const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 6 | const packageConfig = require('../package.json') 7 | 8 | exports.assetsPath = function (_path) { 9 | const assetsSubDirectory = process.env.NODE_ENV === 'production' ? 10 | config.build.assetsSubDirectory : 11 | config.dev.assetsSubDirectory 12 | 13 | return path.posix.join(assetsSubDirectory, _path) 14 | } 15 | 16 | exports.cssLoaders = function (options) { 17 | options = options || {} 18 | 19 | const cssLoader = { 20 | loader: 'css-loader', 21 | options: { 22 | sourceMap: options.sourceMap 23 | } 24 | } 25 | 26 | const postcssLoader = { 27 | loader: 'postcss-loader', 28 | options: { 29 | sourceMap: options.sourceMap 30 | } 31 | } 32 | 33 | // generate loader string to be used with extract text plugin 34 | function generateLoaders(loader, loaderOptions) { 35 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 36 | 37 | if (loader) { 38 | loaders.push({ 39 | loader: loader + '-loader', 40 | options: Object.assign({}, loaderOptions, { 41 | sourceMap: options.sourceMap 42 | }) 43 | }) 44 | } 45 | 46 | // Extract CSS when that option is specified 47 | // (which is the case during production build) 48 | if (options.extract) { 49 | loaders.push(MiniCssExtractPlugin.loader) 50 | // return ExtractTextPlugin.extract({ 51 | // use: loaders, 52 | // fallback: 'vue-style-loader' 53 | // }) 54 | } else { 55 | loaders.push('vue-style-loader') 56 | // return ['vue-style-loader'].concat(loaders) 57 | } 58 | } 59 | 60 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 61 | return { 62 | css: generateLoaders(), 63 | postcss: generateLoaders(), 64 | less: generateLoaders('less'), 65 | sass: generateLoaders('sass', { 66 | indentedSyntax: true 67 | }), 68 | scss: generateLoaders('sass'), 69 | stylus: generateLoaders('stylus'), 70 | styl: generateLoaders('stylus') 71 | } 72 | } 73 | 74 | // Generate loaders for standalone style files (outside of .vue) 75 | exports.styleLoaders = function (options) { 76 | const output = [] 77 | const loaders = exports.cssLoaders(options) 78 | 79 | for (const extension in loaders) { 80 | const loader = loaders[extension] 81 | output.push({ 82 | test: new RegExp('\\.' + extension + '$'), 83 | use: loader 84 | }) 85 | } 86 | 87 | return output 88 | } 89 | 90 | exports.createNotifierCallback = () => { 91 | const notifier = require('node-notifier') 92 | 93 | return (severity, errors) => { 94 | if (severity !== 'error') return 95 | 96 | const error = errors[0] 97 | const filename = error.file && error.file.split('!').pop() 98 | 99 | notifier.notify({ 100 | title: packageConfig.name, 101 | message: severity + ': ' + error.name, 102 | subtitle: filename || '', 103 | icon: path.join(__dirname, 'logo.png') 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | const VueLoaderPlugin = require('vue-loader/lib/plugin') 7 | 8 | function resolve(dir) { 9 | return path.join(__dirname, '..', dir) 10 | } 11 | 12 | const createLintingRule = () => ({ 13 | test: /\.(js|vue)$/, 14 | loader: 'eslint-loader', 15 | enforce: 'pre', 16 | include: [resolve('src'), resolve('test')], 17 | options: { 18 | formatter: require('eslint-friendly-formatter'), 19 | emitWarning: !config.dev.showEslintErrorsInOverlay 20 | } 21 | }) 22 | 23 | module.exports = { 24 | mode: 'development', 25 | context: path.resolve(__dirname, '../'), 26 | entry: { 27 | app: ['babel-polyfill', './src/main.js'] 28 | }, 29 | output: { 30 | path: config.build.assetsRoot, 31 | filename: '[name].[hash].js', 32 | publicPath: process.env.NODE_ENV === 'production' ? 33 | config.build.assetsPublicPath : config.dev.assetsPublicPath 34 | }, 35 | performance: { 36 | hints: false 37 | }, 38 | // performance: { 39 | // hints: 'warning', 40 | // //入口起点的最大体积 41 | // maxEntrypointSize: 50000000, 42 | // //生成文件的最大体积 43 | // maxAssetSize: 30000000, 44 | // //只给出 js 文件的性能提示 45 | // assetFilter: function (assetFilename) { 46 | // return assetFilename.endsWith('.js'); 47 | // } 48 | // }, 49 | resolve: { 50 | extensions: ['.js', '.vue', '.json'], 51 | alias: { 52 | 'vue$': 'vue/dist/vue.esm.js', 53 | '@': resolve('src'), 54 | } 55 | }, 56 | module: { 57 | rules: [ 58 | ...(config.dev.useEslint ? [createLintingRule()] : []), 59 | { 60 | test: /\.vue$/, 61 | loader: 'vue-loader', 62 | options: vueLoaderConfig 63 | }, 64 | { 65 | test: /\.(css|scss)$/, 66 | use: ['style-loader', 'css-loader', 'sass-loader'] 67 | }, 68 | { 69 | test: /\.js$/, 70 | loader: 'babel-loader', 71 | exclude: [resolve('lib')], 72 | include: [resolve('src'), resolve('components'), resolve('node_modules/webpack-dev-server/client')] 73 | }, 74 | { 75 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 76 | loader: 'url-loader', 77 | options: { 78 | limit: 10000, 79 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 80 | } 81 | }, 82 | { 83 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 84 | loader: 'url-loader', 85 | options: { 86 | limit: 10000, 87 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 88 | } 89 | }, 90 | { 91 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 92 | loader: 'url-loader', 93 | options: { 94 | limit: 10000, 95 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 96 | } 97 | } 98 | ] 99 | }, 100 | node: { 101 | // prevent webpack from injecting useless setImmediate polyfill because Vue 102 | // source contains it (although only uses it if it's native). 103 | setImmediate: false, 104 | // prevent webpack from injecting mocks to Node native modules 105 | // that does not make sense for the client 106 | dgram: 'empty', 107 | fs: 'empty', 108 | net: 'empty', 109 | tls: 'empty', 110 | child_process: 'empty' 111 | }, 112 | plugins: [ 113 | new VueLoaderPlugin() 114 | ] 115 | } 116 | -------------------------------------------------------------------------------- /build/webpack.components.conf.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const path = require('path'); 3 | const ProgressBarPlugin = require('progress-bar-webpack-plugin'); 4 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); 5 | const nodeExternals = require('webpack-node-externals'); 6 | const TerserPlugin = require('terser-webpack-plugin'); 7 | const { 8 | CleanWebpackPlugin 9 | } = require('clean-webpack-plugin') 10 | const version = process.env.VERSION || require('../package.json').version 11 | 12 | let banner = 13 | 'vue-lunar-fullcalendar v' + version + '\n' + 14 | '(c) ' + new Date().getFullYear() + ' h j \n' + 15 | '@license MIT' 16 | 17 | module.exports = { 18 | mode: 'production', 19 | entry: { 20 | app: ['./components/index.js'] 21 | }, 22 | output: { 23 | path: path.resolve(__dirname, '../lib'), 24 | publicPath: '/', 25 | filename: 'LunarFullCalendar.min.js', 26 | chunkFilename: '[id].js', 27 | libraryExport: 'default', 28 | library: 'LunarFullCalendar', 29 | libraryTarget: 'umd', 30 | umdNamedDefine: true, 31 | globalObject: 'typeof self !== \'undefined\' ? self : this' 32 | }, 33 | resolve: { 34 | extensions: ['.js', '.vue', '.json'], 35 | alias: { 36 | 'vue$': 'vue/dist/vue.esm.js' 37 | }, 38 | modules: ['node_modules'] 39 | }, 40 | externals: [{ 41 | vue: { 42 | root: 'Vue', 43 | commonjs: 'vue', 44 | commonjs2: 'vue', 45 | amd: 'vue' 46 | } 47 | }, nodeExternals()], 48 | performance: { 49 | hints: false 50 | }, 51 | // stats: { 52 | // children: false 53 | // }, 54 | optimization: { 55 | minimizer: [ 56 | new TerserPlugin({ 57 | terserOptions: { 58 | output: { 59 | comments: false 60 | } 61 | } 62 | }) 63 | ] 64 | }, 65 | module: { 66 | rules: [{ 67 | test: /\.(jsx?|babel|es6)$/, 68 | include: process.cwd(), 69 | exclude: /node_modules|utils\/popper\.js|utils\/date\.js/, 70 | loader: 'babel-loader' 71 | }, 72 | { 73 | test: /\.vue$/, 74 | loader: 'vue-loader', 75 | options: { 76 | compilerOptions: { 77 | preserveWhitespace: false 78 | } 79 | } 80 | }, 81 | { 82 | test: /\.css$/, 83 | loaders: ['style-loader', 'css-loader'] 84 | }, 85 | { 86 | test: /\.(svg|otf|ttf|woff2?|eot|gif|png|jpe?g)(\?\S*)?$/, 87 | loader: 'url-loader', 88 | query: { 89 | limit: 10000, 90 | name: path.posix.join('static', '[name].[hash:7].[ext]') 91 | } 92 | } 93 | ] 94 | }, 95 | plugins: [ 96 | new ProgressBarPlugin(), 97 | new VueLoaderPlugin(), 98 | new CleanWebpackPlugin(), 99 | new webpack.BannerPlugin({ 100 | banner 101 | }) 102 | ] 103 | } 104 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.dev.cssSourceMap, 20 | usePostCSS: true 21 | }) 22 | }, 23 | // cheap-module-eval-source-map is faster for development 24 | devtool: config.dev.devtool, 25 | 26 | // these devServer options should be customized in /config/index.js 27 | devServer: { 28 | clientLogLevel: 'warning', 29 | historyApiFallback: { 30 | rewrites: [{ 31 | from: /.*/, 32 | to: path.posix.join(config.dev.assetsPublicPath, 'index.html') 33 | }, ], 34 | }, 35 | hot: true, 36 | contentBase: false, // since we use CopyWebpackPlugin. 37 | compress: true, 38 | host: HOST || config.dev.host, 39 | port: PORT || config.dev.port, 40 | open: config.dev.autoOpenBrowser, 41 | overlay: config.dev.errorOverlay ? 42 | { 43 | warnings: false, 44 | errors: true 45 | } : 46 | false, 47 | publicPath: config.dev.assetsPublicPath, 48 | proxy: config.dev.proxyTable, 49 | quiet: true, // necessary for FriendlyErrorsPlugin 50 | watchOptions: { 51 | poll: config.dev.poll, 52 | } 53 | }, 54 | plugins: [ 55 | new webpack.DefinePlugin({ 56 | 'process.env': require('../config/dev.env') 57 | }), 58 | new webpack.HotModuleReplacementPlugin(), 59 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 60 | new webpack.NoEmitOnErrorsPlugin(), 61 | // https://github.com/ampedandwired/html-webpack-plugin 62 | new HtmlWebpackPlugin({ 63 | filename: 'index.html', 64 | template: 'index.html', 65 | inject: true 66 | }), 67 | // copy custom static assets 68 | new CopyWebpackPlugin([{ 69 | from: path.resolve(__dirname, '../static'), 70 | to: config.dev.assetsSubDirectory, 71 | ignore: ['.*'] 72 | }]) 73 | ] 74 | }) 75 | 76 | module.exports = new Promise((resolve, reject) => { 77 | portfinder.basePort = process.env.PORT || config.dev.port 78 | portfinder.getPort((err, port) => { 79 | if (err) { 80 | reject(err) 81 | } else { 82 | // publish the new Port, necessary for e2e tests 83 | process.env.PORT = port 84 | // add port to devServer config 85 | devWebpackConfig.devServer.port = port 86 | 87 | // Add FriendlyErrorsPlugin 88 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 89 | compilationSuccessInfo: { 90 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 91 | }, 92 | onErrors: config.dev.notifyOnErrors ? 93 | utils.createNotifierCallback() : 94 | undefined 95 | })) 96 | 97 | resolve(devWebpackConfig) 98 | } 99 | }) 100 | }) 101 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | // const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | // const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | const TerserPlugin = require('terser-webpack-plugin') // 代替 uglifyjs-webpack-plugin 14 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') //代替 extract-text-webpack-plugin 15 | 16 | const env = require('../config/prod.env') 17 | 18 | const webpackConfig = merge(baseWebpackConfig, { 19 | mode: 'production', 20 | module: { 21 | rules: utils.styleLoaders({ 22 | sourceMap: config.build.productionSourceMap, 23 | extract: true, 24 | usePostCSS: true 25 | }) 26 | }, 27 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 28 | output: { 29 | path: config.build.assetsRoot, 30 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 31 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 32 | }, 33 | optimization: { 34 | splitChunks: { 35 | chunks: 'all', 36 | minSize: 30000, 37 | maxAsyncRequests: 5, 38 | maxInitialRequests: 3, 39 | automaticNameDelimiter: '~', 40 | name: true 41 | // cacheGroups: { 42 | // styles: { 43 | // name: 'styles', 44 | // test: /\.css$/, 45 | // chunks: 'all', 46 | // enforce: true, 47 | // } 48 | // } 49 | }, 50 | minimizer: [ 51 | new TerserPlugin({ 52 | cache: true, 53 | parallel: true, 54 | // warnings: false, 55 | sourceMap: true, // Must be set to true if using source-maps in production 56 | terserOptions: { 57 | // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions 58 | } 59 | }), 60 | new MiniCssExtractPlugin({ 61 | filename: utils.assetsPath('css/[name].[contenthash].css'), 62 | allChunks: true 63 | }), 64 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 65 | new webpack.DefinePlugin({ 66 | 'process.env': env 67 | }), 68 | // new UglifyJsPlugin({ 69 | // uglifyOptions: { 70 | // compress: { 71 | // warnings: false 72 | // } 73 | // }, 74 | // sourceMap: config.build.productionSourceMap, 75 | // parallel: true 76 | // }), 77 | // extract css into its own file 78 | // new ExtractTextPlugin({ 79 | // filename: utils.assetsPath('css/[name].[contenthash].css'), 80 | // // Setting the following option to `false` will not extract CSS from codesplit chunks. 81 | // // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 82 | // // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 83 | // // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 84 | // allChunks: true, 85 | // }), 86 | // Compress extracted CSS. We are using this plugin so that possible 87 | // duplicated CSS from different components can be deduped. 88 | new OptimizeCSSPlugin({ 89 | cssProcessorOptions: config.build.productionSourceMap ? { 90 | safe: true, 91 | map: { 92 | inline: false 93 | } 94 | } : { 95 | safe: true 96 | } 97 | }), 98 | // generate dist index.html with correct asset hash for caching. 99 | // you can customize output by editing /index.html 100 | // see https://github.com/ampedandwired/html-webpack-plugin 101 | new HtmlWebpackPlugin({ 102 | filename: config.build.index, 103 | template: 'index.html', 104 | inject: true, 105 | minify: { 106 | removeComments: true, 107 | collapseWhitespace: true, 108 | removeAttributeQuotes: true 109 | // more options: 110 | // https://github.com/kangax/html-minifier#options-quick-reference 111 | }, 112 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 113 | chunksSortMode: 'dependency' 114 | }), 115 | // keep module.id stable when vendor modules does not change 116 | new webpack.HashedModuleIdsPlugin(), 117 | // enable scope hoisting 118 | new webpack.optimize.ModuleConcatenationPlugin(), 119 | // split vendor js into its own file 120 | // new webpack.optimize.CommonsChunkPlugin({ 121 | // name: 'vendor', 122 | // minChunks(module) { 123 | // // any required modules inside node_modules are extracted to vendor 124 | // return ( 125 | // module.resource && 126 | // /\.js$/.test(module.resource) && 127 | // module.resource.indexOf( 128 | // path.join(__dirname, '../node_modules') 129 | // ) === 0 130 | // ) 131 | // } 132 | // }), 133 | // // extract webpack runtime and module manifest to its own file in order to 134 | // // prevent vendor hash from being updated whenever app bundle is updated 135 | // new webpack.optimize.CommonsChunkPlugin({ 136 | // name: 'manifest', 137 | // minChunks: Infinity 138 | // }), 139 | // // This instance extracts shared chunks from code splitted chunks and bundles them 140 | // // in a separate chunk, similar to the vendor chunk 141 | // // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 142 | // new webpack.optimize.CommonsChunkPlugin({ 143 | // name: 'app', 144 | // async: 'vendor-async', 145 | // children: true, 146 | // minChunks: 3 147 | // }), 148 | 149 | // copy custom static assets 150 | new CopyWebpackPlugin([{ 151 | from: path.resolve(__dirname, '../static'), 152 | to: config.build.assetsSubDirectory, 153 | ignore: ['.*'] 154 | }]) 155 | ] 156 | } 157 | }) 158 | 159 | if (config.build.productionGzip) { 160 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 161 | 162 | webpackConfig.plugins.push( 163 | new CompressionWebpackPlugin({ 164 | asset: '[path].gz[query]', 165 | algorithm: 'gzip', 166 | test: new RegExp( 167 | '\\.(' + 168 | config.build.productionGzipExtensions.join('|') + 169 | ')$' 170 | ), 171 | threshold: 10240, 172 | minRatio: 0.8 173 | }) 174 | ) 175 | } 176 | 177 | if (config.build.bundleAnalyzerReport) { 178 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 179 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 180 | } 181 | 182 | module.exports = webpackConfig 183 | -------------------------------------------------------------------------------- /components/LunarFullCalendar/LunarFullCalendar.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 207 | 210 | -------------------------------------------------------------------------------- /components/LunarFullCalendar/fullcalendar.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * FullCalendar v3.9.0 3 | */ 4 | .fc { 5 | direction: ltr; 6 | text-align: left; 7 | } 8 | 9 | .fc-rtl { 10 | text-align: right; 11 | } 12 | 13 | body .fc { 14 | /* extra precedence to overcome jqui */ 15 | font-size: 1em; 16 | } 17 | 18 | /* Colors 19 | --------------------------------------------------------------------------------------------------*/ 20 | .fc-highlight { 21 | /* when user is selecting cells */ 22 | background: #bce8f1; 23 | opacity: .3; 24 | } 25 | 26 | .fc-bgevent { 27 | /* default look for background events */ 28 | background: #8fdf82; 29 | opacity: .3; 30 | } 31 | 32 | .fc-nonbusiness { 33 | /* default look for non-business-hours areas */ 34 | /* will inherit .fc-bgevent's styles */ 35 | background: #d7d7d7; 36 | } 37 | 38 | /* Buttons (styled