├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .stylelintrc
├── .travis.yml
├── README.md
├── build
├── bin
│ ├── build-entry.js
│ ├── build-locale.js
│ ├── build-mixins.js
│ ├── build-theme.js
│ └── build-utils.js
├── config.js
├── dev-server.js
├── happypack.js
├── webpack.base.config.js
├── webpack.components.config.js
├── webpack.dev.config.js
├── webpack.package.config.js
├── webpack.prod.config.js
└── webpack.test.config.js
├── commitlint.config.js
├── components.json
├── components
├── button
│ ├── index.js
│ └── src
│ │ └── index.vue
├── carousel
│ ├── index.js
│ └── src
│ │ └── index.vue
├── checkbox
│ ├── index.js
│ └── src
│ │ └── checkbox.vue
├── collapse-item
│ ├── index.js
│ └── src
│ │ ├── collapse-transition.js
│ │ └── index.vue
├── collapse
│ ├── index.js
│ └── src
│ │ └── index.vue
├── dialog
│ ├── index.js
│ └── src
│ │ ├── index.vue
│ │ └── main.js
├── input
│ ├── index.js
│ └── src
│ │ └── index.vue
├── loading
│ ├── index.js
│ └── src
│ │ └── index.vue
├── notification
│ ├── index.js
│ └── src
│ │ ├── index.vue
│ │ └── main.js
├── option
│ └── index.js
├── radio-button-group
│ ├── index.js
│ └── src
│ │ └── index.vue
├── radio
│ ├── index.js
│ └── src
│ │ └── radio.vue
├── rate
│ ├── index.js
│ └── src
│ │ └── index.vue
├── select
│ ├── index.js
│ └── src
│ │ ├── index.vue
│ │ └── option.vue
├── switch
│ ├── index.js
│ └── src
│ │ └── index.vue
├── tab-panel
│ ├── index.js
│ └── src
│ │ └── index.vue
├── tabs
│ ├── index.js
│ └── src
│ │ └── index.vue
└── toast
│ ├── index.js
│ └── src
│ ├── index.vue
│ └── main.js
├── config
└── index.js
├── docs
├── common.less
├── components
│ ├── demo-block.vue
│ ├── header.vue
│ └── side-nav.vue
├── dist
│ ├── 0.da699a70.js
│ ├── 0.da699a70.js.map
│ ├── 1.37c351b9.js
│ ├── 1.37c351b9.js.gz
│ ├── 1.37c351b9.js.map
│ ├── 10.4be66b11.js
│ ├── 10.4be66b11.js.gz
│ ├── 10.4be66b11.js.map
│ ├── 11.cec9d3a6.js
│ ├── 11.cec9d3a6.js.map
│ ├── 12.89eeb768.js
│ ├── 12.89eeb768.js.gz
│ ├── 12.89eeb768.js.map
│ ├── 13.154fba12.js
│ ├── 13.154fba12.js.gz
│ ├── 13.154fba12.js.map
│ ├── 14.8242b6a4.js
│ ├── 14.8242b6a4.js.gz
│ ├── 14.8242b6a4.js.map
│ ├── 15.2d69444c.js
│ ├── 15.2d69444c.js.gz
│ ├── 15.2d69444c.js.map
│ ├── 16.6b4129ec.js
│ ├── 16.6b4129ec.js.gz
│ ├── 16.6b4129ec.js.map
│ ├── 17.08c2c7cb.js
│ ├── 17.08c2c7cb.js.map
│ ├── 2.d39ad9e2.js
│ ├── 2.d39ad9e2.js.gz
│ ├── 2.d39ad9e2.js.map
│ ├── 3.2d5ecdc4.js
│ ├── 3.2d5ecdc4.js.gz
│ ├── 3.2d5ecdc4.js.map
│ ├── 4.b1fc655a.js
│ ├── 4.b1fc655a.js.gz
│ ├── 4.b1fc655a.js.map
│ ├── 5.7b5d6514.js
│ ├── 5.7b5d6514.js.map
│ ├── 6.8633996a.js
│ ├── 6.8633996a.js.map
│ ├── 7.70d5d555.js
│ ├── 7.70d5d555.js.gz
│ ├── 7.70d5d555.js.map
│ ├── 8.314732f0.js
│ ├── 8.314732f0.js.map
│ ├── 9.7b7f3815.js
│ ├── 9.7b7f3815.js.map
│ ├── app.1be2bc04.js
│ ├── app.1be2bc04.js.gz
│ ├── app.1be2bc04.js.map
│ ├── app.9b85dbc2.css
│ ├── app.dae792d1.css
│ ├── vendor.01d16740.js
│ ├── vendor.01d16740.js.gz
│ └── vendor.01d16740.js.map
├── entry.js
├── entry.vue
├── examples
│ ├── button.md
│ ├── carousel.md
│ ├── checkbox.md
│ ├── collapse.md
│ ├── dialog.md
│ ├── input.md
│ ├── loading.md
│ ├── notification.md
│ ├── radio.md
│ ├── rate.md
│ ├── rbg.md
│ ├── select.md
│ ├── switch.md
│ ├── tabs.md
│ ├── toast.md
│ └── util.md
├── i18n
│ ├── en-US.json
│ ├── index.js
│ └── zh-CN.json
├── index.html
├── logo.png
├── nav.config.json
├── pages
│ ├── change-log.vue
│ ├── guide.md
│ └── i18n.md
├── routes.config.js
└── tpl.html
├── package.json
├── postcss.config.js
├── src
├── index.js
├── locale
│ ├── format.js
│ ├── index.js
│ └── lang
│ │ ├── en-US.json
│ │ ├── zh-CN.json
│ │ ├── zh-HK.json
│ │ └── zh-TW.json
├── mixins
│ └── i18n.js
└── utils
│ ├── helpers.js
│ ├── index.js
│ └── storage.js
├── test
├── index.js
├── karma.conf.js
├── specs
│ └── button.spec.js
└── utils.js
├── theme-default
├── gulpfile.js
└── src
│ ├── base.less
│ ├── button.less
│ ├── carousel.less
│ ├── checkbox.less
│ ├── collapse-item.less
│ ├── collapse.less
│ ├── common
│ ├── transition.less
│ └── var.less
│ ├── dialog.less
│ ├── index.less
│ ├── input.less
│ ├── loading.less
│ ├── notification.less
│ ├── option.less
│ ├── radio-button-group.less
│ ├── radio.less
│ ├── rate.less
│ ├── select.less
│ ├── switch.less
│ ├── tab-panel.less
│ ├── tabs.less
│ └── toast.less
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env", {
5 | "targets": {
6 | "browsers": ["last 2 versions", "safari > 7"]
7 | },
8 | "modules": false,
9 | "useBuiltIns": "entry",
10 | "loose": true
11 | }
12 | ],
13 | "stage-2"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
11 | # Matches multiple files with brace expansion notation
12 | # Set default
13 | [*.{js,vue,less}]
14 | charset = utf-8
15 | indent_style = space
16 | indent_size = 4
17 |
18 | # Matches the exact files either package.json or .travis.yml
19 | [{package.json,.travis.yml}]
20 | indent_style = space
21 | indent_size = 2
22 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | root: true,
3 | parser: "babel-eslint",
4 | parserOptions: {
5 | ecmaVersion: 7,
6 | sourceType: "module",
7 | allowImportExportEverywhere: false,
8 | ecmaFeatures: {
9 | jsx: true,
10 | modules: true
11 | }
12 | },
13 | env: {
14 | es6: true,
15 | node: true,
16 | browser: true
17 | },
18 | extends: "vue",
19 | rules: {
20 | indent: [2, 4],
21 | quotes: [2, "single", { "allowTemplateLiterals": true }],
22 | linebreak-style: [2, "unix"],
23 | semi: [2, "always"],
24 | eqeqeq: [2, "always"],
25 | strict: [2, "global"],
26 | key-spacing: [2, { "afterColon": true }],
27 | no-console: 0,
28 | no-debugger: 0,
29 | no-empty: 0,
30 | no-unused-vars: 0,
31 | no-constant-condition: 0,
32 | no-undef: 0,
33 | handle-callback-err: 0,
34 | no-trailing-spaces: 0,
35 | no-unneeded-ternary: 0,
36 | no-cond-assign: 0,
37 | no-return-assign: 0
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like istanbul
17 | coverage
18 |
19 | # nyc test coverage
20 | .nyc_output
21 |
22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
23 | .grunt
24 |
25 | # node-waf configuration
26 | .lock-wscript
27 |
28 | # Compiled binary addons (http://nodejs.org/api/addons.html)
29 | build/Release
30 |
31 | # Dependency directories
32 | node_modules
33 | jspm_packages
34 |
35 | # Optional npm cache directory
36 | .npm
37 |
38 | # Optional REPL history
39 | .node_repl_history
40 |
41 | .idea
42 | public/dist
43 | .DS_Store
44 | .cache
45 | converage
46 | .vscode
47 | reports
48 | .cache
49 | .happypack
50 | lib
51 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-standard",
3 | "rules": {
4 | "comment-empty-line-before": null,
5 | "declaration-empty-line-before": null,
6 | "function-comma-newline-after": null,
7 | "function-name-case": null,
8 | "function-parentheses-newline-inside": null,
9 | "function-max-empty-lines": null,
10 | "function-whitespace-after": null,
11 | "indentation": null,
12 | "number-leading-zero": null,
13 | "number-no-trailing-zeros": null,
14 | "rule-empty-line-before": null,
15 | "selector-combinator-space-after": null,
16 | "selector-list-comma-newline-after": null,
17 | "selector-pseudo-element-colon-notation": null,
18 | "unit-no-unknown": null,
19 | "value-list-max-empty-lines": null
20 | }
21 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 |
2 | language: node_js
3 | sudo: false
4 | dist: trusty
5 | cache:
6 | directories:
7 | - node_modules
8 | addons:
9 | chrome: stable
10 | before_install:
11 | - export CHROME_BIN=chromium-browser
12 | - export DISPLAY=:99.0
13 | - sh -e /etc/init.d/xvfb start
14 | branches:
15 | only:
16 | - master
17 | node_js:
18 | - '8'
19 | script:
20 | - npm test
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/fmfe/fm-vue-ui)  
2 | ## fm-vue-ui
3 | Basic vue components.
4 |
5 | ## Development
6 |
7 | ```
8 | git clone -b dev git@github.com:fmfe/fm-vue-ui.git
9 |
10 | cd fm-vue-ui
11 |
12 | npm i
13 |
14 | npm run dev:docs
15 | # http://localhost:3000/fm-vue-ui/index
16 | ```
17 |
18 | 文档地址:https://fmfe.github.io/fm-vue-ui/index
19 |
20 | ## 贡献指南
21 | 1. 可以在 [fm-vue-ui](https://github.com/fmfe/fm-vue-ui/issues) 以 issue 的形式说明你的需求
22 | 2. fork [fm-vue-ui](https://github.com/fmfe/fm-vue-ui), 然后开发自己的组件,写好对应的文档说明和单测,提交 PR
23 | 3. 代码规范请参考 [coding style](https://github.com/fmfe/fe-coding-style-guide/)
24 | 4. 对于组件中涉及的图标,请优先使用 CSS 来实现; 如果实现不了, 请将对应的图片资源放在 CDN 上
25 | ## 开发步骤
26 | 1. 在 `components.json` 文件中添加对应组件的映射(组件名和组件路径)
27 | 2. 在 `components` 目录下按格式建立自己的组件目录,对应的样式在 `theme-default/src` 目录下,文件名和映射的组件名保持一致
28 | 3. 执行 `npm run com`
--------------------------------------------------------------------------------
/build/bin/build-entry.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const endOfLine = require('os').EOL;
4 | const render = require('json-templater/string');
5 | const uppercamelcase = require('uppercamelcase');
6 |
7 | const Components = require('../../components.json');
8 | const OUTPUT_PATH = path.join(__dirname, '../../src/index.js');
9 | const IMPORT_TEMPLATE = 'import {{name}} from \'../components/{{package}}/index.js\';';
10 | const INSTALL_COMPONENT_TEMPLATE = ' {{name}}';
11 | const MAIN_TEMPLATE = `/* Automatic generated by './build/build-entry.js' */
12 |
13 | import utils from 'fm-vue-ui/src/utils';
14 | import locale from 'fm-vue-ui/src/locale';
15 |
16 | {{include}}
17 |
18 | const components = [
19 | {{install}}
20 | ];
21 |
22 | const install = function (Vue, opts = {}) {
23 | if (install.installed) return;
24 |
25 | let lang = 'zh-CN';
26 | try {
27 | lang = opts.lang || (window.FMlocale ? window.FMlocale() : 'zh-CN');
28 | } catch (e) {}
29 | locale.use(lang);
30 |
31 | components.map(component => {
32 | Vue.component(component.name, component);
33 | });
34 |
35 | Vue.prototype.$fmdialog = Dialog;
36 | Vue.prototype.$fmtoast = Toast;
37 | Vue.prototype.$fmutils = utils;
38 | };
39 |
40 | if (typeof window !== 'undefined' && window.Vue) {
41 | install(window.Vue);
42 | };
43 |
44 | export {
45 | {{list}}
46 | };
47 |
48 | export default {
49 | version: '{{version}}',
50 | install,
51 | {{list}}
52 | };
53 |
54 | `;
55 |
56 | const ComponentNames = Object.keys(Components);
57 |
58 | const includeComponentTemplate = [];
59 | const installTemplate = [];
60 | const listTemplate = [];
61 |
62 | ComponentNames.forEach(name => {
63 | const componentName = uppercamelcase(name);
64 |
65 | includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
66 | name: componentName,
67 | package: name
68 | }));
69 |
70 | if (['Dialog'].indexOf(componentName) === -1) {
71 | installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
72 | name: componentName,
73 | component: name
74 | }));
75 | }
76 | listTemplate.push(` ${componentName}`);
77 | });
78 |
79 | const template = render(MAIN_TEMPLATE, {
80 | include: includeComponentTemplate.join(endOfLine),
81 | install: installTemplate.join(',' + endOfLine),
82 | version: process.env.VERSION || require('../../package.json').version,
83 | list: listTemplate.join(',' + endOfLine)
84 | });
85 |
86 | fs.writeFileSync(OUTPUT_PATH, template);
87 | console.log('[build entry] DONE:', OUTPUT_PATH);
88 |
89 |
--------------------------------------------------------------------------------
/build/bin/build-locale.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const exec = require('child_process').execSync;
4 | const babel = require('babel-core');
5 |
6 | const localePath = path.resolve(__dirname, '../../src/locale');
7 | const outputPath = path.resolve(__dirname, '../../lib/locale');
8 | const filesList = fs.readdirSync(localePath);
9 |
10 | function dirExist (path) {
11 | try {
12 | fs.accessSync(path, fs.F_OK);
13 | } catch (e) {
14 | return false;
15 | }
16 | return true;
17 | }
18 |
19 | function copyFiles () {
20 | if (!dirExist(path.resolve(__dirname, '../../lib'))) {
21 | exec('mkdir lib');
22 | }
23 |
24 | if (!dirExist(path.resolve(__dirname, '../../lib/locale'))) {
25 | exec('mkdir lib/locale');
26 | }
27 |
28 | exec('cp -rf ./src/locale/lang ./lib/locale/lang');
29 | }
30 |
31 | copyFiles();
32 |
33 | function transformFile (filename, name, cb) {
34 | babel.transformFile(path.resolve(localePath, filename), {
35 | plugins: ['add-module-exports', 'transform-es2015-modules-commonjs'],
36 | moduleId: name
37 | }, cb);
38 | }
39 |
40 | filesList.forEach(file => {
41 | const name = path.basename(file, '.js');
42 | if (fs.lstatSync(path.resolve(localePath, file)).isDirectory()) {
43 | return;
44 | }
45 | transformFile(file, name, (err, result) => {
46 | if (err) {
47 | console.log('write failed', err);
48 | } else {
49 | const code = result.code;
50 | fs.writeFileSync(path.resolve(outputPath, file), code);
51 | }
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/build/bin/build-mixins.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const exec = require('child_process').execSync;
4 | const babel = require('babel-core');
5 |
6 | const mixinsPath = path.resolve(__dirname, '../../src/mixins/');
7 | const outputPath = path.resolve(__dirname, '../../lib/mixins/');
8 |
9 | const filesList = fs.readdirSync(mixinsPath);
10 |
11 | function dirExist (path) {
12 | try {
13 | fs.accessSync(path, fs.F_OK);
14 | } catch (e) {
15 | return false;
16 | }
17 | return true;
18 | }
19 |
20 | function transformFile (filename, name, cb) {
21 | babel.transformFile(path.resolve(mixinsPath, filename), {
22 | plugins: [['replace-import-path', {
23 | src: 'fm-vue-ui/src/locale/index.js',
24 | dest: '@fmfe/fm-vue-ui/lib/locale/index.js'
25 | }], 'add-module-exports', 'transform-es2015-modules-commonjs'],
26 | moduleId: name
27 | }, cb);
28 | }
29 |
30 | filesList.forEach(file => {
31 | const name = path.basename(file, '.js');
32 | transformFile(file, name, (err, result) => {
33 | if (err) {
34 | console.log('write failed', err);
35 | } else {
36 | if (!dirExist(path.resolve(__dirname, '../../lib'))) {
37 | exec('mkdir lib');
38 | }
39 | if (!dirExist(path.resolve(__dirname, '../../lib/mixins'))) {
40 | exec('mkdir lib/mixins');
41 | }
42 |
43 | const code = result.code;
44 | fs.writeFileSync(path.resolve(outputPath, file), code);
45 | }
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/build/bin/build-theme.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const Components = require('../../components.json');
4 |
5 | const allKeys = Object.keys(Components);
6 | const basepath = path.resolve(__dirname, '../../theme-default/');
7 |
8 | function fileExists (filePath) {
9 | try {
10 | return fs.statSync(filePath).isFile();
11 | } catch (err) {
12 | return false;
13 | }
14 | }
15 |
16 | let indexContent = '@import "./base.less";\n';
17 |
18 | allKeys.forEach(key => {
19 | const fileName = `${key}.less`;
20 | indexContent += '@import "./' + fileName + '";\n';
21 |
22 | const filePath = path.resolve(basepath, 'src', fileName);
23 | if (!fileExists(filePath)) {
24 | fs.writeFileSync(filePath, '', 'utf8');
25 | console.log('创建遗漏的 ', fileName, ' 文件');
26 | }
27 | });
28 |
29 | fs.writeFileSync(path.resolve(basepath, 'src', 'index.less'), indexContent);
30 |
--------------------------------------------------------------------------------
/build/bin/build-utils.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const exec = require('child_process').execSync;
4 | const babel = require('babel-core');
5 |
6 | const utilsPath = path.resolve(__dirname, '../../src/utils/');
7 | const outputPath = path.resolve(__dirname, '../../lib/utils/');
8 |
9 | const filesList = fs.readdirSync(utilsPath);
10 |
11 | function dirExist (path) {
12 | try {
13 | fs.accessSync(path, fs.F_OK);
14 | } catch (e) {
15 | return false;
16 | }
17 | return true;
18 | }
19 |
20 | function transformFile (filename, name, cb) {
21 | babel.transformFile(path.resolve(utilsPath, filename), {
22 | plugins: ['add-module-exports', 'transform-es2015-modules-commonjs'],
23 | moduleId: name
24 | }, cb);
25 | }
26 |
27 | filesList.forEach(file => {
28 | const name = path.basename(file, '.js');
29 | transformFile(file, name, (err, result) => {
30 | if (err) {
31 | console.log('write failed', err);
32 | } else {
33 | if (!dirExist(path.resolve(__dirname, '../../lib'))) {
34 | exec('mkdir lib');
35 | }
36 | if (!dirExist(path.resolve(__dirname, '../../lib/utils'))) {
37 | exec('mkdir lib/utils');
38 | }
39 |
40 | const code = result.code;
41 | fs.writeFileSync(path.resolve(outputPath, file), code);
42 | }
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/build/config.js:
--------------------------------------------------------------------------------
1 | const nodeExternals = require('webpack-node-externals');
2 | const path = require('path');
3 | const fs = require('fs');
4 |
5 | const Components = require('../components.json');
6 | // const utilsList = fs.readdirSync(path.resolve(__dirname, '../src/utils'));
7 | const mixinsList = fs.readdirSync(path.resolve(__dirname, '../src/mixins'));
8 |
9 | const vueExternal = {
10 | 'vue': {
11 | root: 'Vue',
12 | commonjs: 'vue',
13 | commonjs2: 'vue',
14 | amd: 'vue'
15 | }
16 | };
17 |
18 | let externals = {};
19 |
20 | Object.keys(Components).forEach(function (key) {
21 | externals[`fm-vue-ui/components/${key}`] = `@fmfe/fm-vue-ui/lib/${key}`;
22 | });
23 |
24 | externals['fm-vue-ui/src/locale'] = '@fmfe/fm-vue-ui/lib/locale';
25 | externals[`fm-vue-ui/src/utils`] = `@fmfe/fm-vue-ui/lib/utils`;
26 |
27 | // utilsList.forEach(file => {
28 | // file = path.basename(file, '.js');
29 | // externals[`fm-vue-ui/src/utils/${file}`] = `fm-vue-ui/lib/utils/${file}`;
30 | // });
31 |
32 | mixinsList.forEach(file => {
33 | file = path.basename(file, '.js');
34 | externals[`fm-vue-ui/src/mixins/${file}`] = `@fmfe/fm-vue-ui/lib/mixins/${file}`;
35 | });
36 |
37 | externals = [
38 | Object.assign({}, externals, vueExternal),
39 | nodeExternals()
40 | ];
41 |
42 | const alias = {
43 | main: path.resolve(__dirname, '../src'),
44 | components: path.resolve(__dirname, '../components'),
45 | 'fm-vue-ui': path.resolve(__dirname, '../')
46 | };
47 |
48 | module.exports = {
49 | alias,
50 | externals
51 | };
52 |
--------------------------------------------------------------------------------
/build/dev-server.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const WebpackDevServer = require('webpack-dev-server');
3 | const ora = require('ora');
4 | const gutil = require('gulp-util');
5 |
6 | const webpackDevConfig = require('./webpack.dev.config.js');
7 | const config = require('../config/index');
8 |
9 | const compiler = webpack(webpackDevConfig);
10 | const server = new WebpackDevServer(compiler, webpackDevConfig.devServer);
11 |
12 | const env = process.env.NODE_ENV || 'development';
13 | const url = `localhost:${config.dev.port}/fm-vue-ui/index`;
14 |
15 | let spinner = ora({
16 | text: 'Webpack 正在编译...\n',
17 | color: 'green'
18 | }).start();
19 |
20 | function compiledFail () {
21 | if (spinner) {
22 | spinner.color = 'red';
23 | spinner.text = gutil.colors.white('Webpack 编译失败: \n');
24 | spinner.fail();
25 | spinner = null;
26 | }
27 | }
28 |
29 | server.listen(config.dev.port, 'localhost', (err) => {
30 | if (err) {
31 | compiledFail();
32 | throw new gutil.PluginError('[webpack-dev-server err]', err);
33 | }
34 | });
35 |
36 | // 编译完成
37 | compiler.plugin('done', (stats) => {
38 | if (spinner) {
39 | spinner.text = gutil.colors.green(`Webpack 编译成功, open browser to visit ${url}\n`);
40 | spinner.succeed();
41 | spinner = null;
42 | }
43 | });
44 |
45 | // 编译失败
46 | compiler.plugin('failed', (err) => {
47 | compiledFail();
48 | throw new gutil.PluginError('[webpack build err]', err);
49 | });
50 |
51 | // 监听文件修改
52 | compiler.plugin('compilation', compilation => {});
53 |
--------------------------------------------------------------------------------
/build/happypack.js:
--------------------------------------------------------------------------------
1 | const HappyPack = require('happypack');
2 | const os = require('os');
3 | const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
4 |
5 | module.exports = function (opts) {
6 | return {
7 | id: opts.id,
8 | threadPool: happyThreadPool,
9 | verbose: true,
10 | loaders: opts.loaders
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/build/webpack.components.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
4 | const os = require('os');
5 |
6 | const Components = require('../components.json');
7 | const config = require('./config');
8 |
9 | module.exports = {
10 | entry: Components,
11 | output: {
12 | path: path.join(__dirname, '../lib'),
13 | filename: '[name].js',
14 | libraryTarget: 'commonjs2'
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.vue$/,
20 | loader: 'vue-loader'
21 | },
22 | {
23 | test: /\.js$/,
24 | exclude: /node_modules/,
25 | loader: 'babel-loader'
26 | },
27 | {
28 | test: /\.less$/,
29 | use: ['vue-style-loader', 'css-loader', 'less-loader']
30 | },
31 | {
32 | test: /\.css$/,
33 | use: ['vue-style-loader', 'css-loader']
34 | }
35 | ]
36 | },
37 | externals: config.externals,
38 | resolve: {
39 | alias: config.alias
40 | },
41 | plugins: [
42 | // new ParallelUglifyPlugin({
43 | // workerCount: os.cpus().length,
44 | // cacheDir: '.cache/',
45 | // uglifyJS: {
46 | // compress: {
47 | // warnings: false,
48 | // /* eslint-disable */
49 | // drop_debugger: true,
50 | // drop_console: true
51 | // },
52 | // comments: false,
53 | // sourceMap: true,
54 | // mangle: true
55 | // }
56 | // }),
57 | new webpack.optimize.ModuleConcatenationPlugin()
58 | ]
59 | };
60 |
--------------------------------------------------------------------------------
/build/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const OpenBrowserPlugin = require('open-browser-webpack-plugin');
4 | const HappyPack = require('happypack');
5 |
6 | const getHappyPackConfig = require('./happypack');
7 |
8 | const devConfig = require('./webpack.base.config');
9 | const config = require('../config');
10 | const url = `http://localhost:${config.dev.port}/fm-vue-ui/index`;
11 |
12 | devConfig.module.rules.unshift({
13 | test: /\.less$/,
14 | use: ['happypack/loader?id=less-dev']
15 | }, {
16 | test: /\.css$/,
17 | use: ['happypack/loader?id=css-dev']
18 | });
19 |
20 | devConfig.plugins = (devConfig.plugins || []).concat([
21 | new webpack.HotModuleReplacementPlugin(),
22 |
23 | new webpack.DefinePlugin({
24 | 'process.env': {
25 | 'NODE_ENV': JSON.stringify(config.dev.env)
26 | }
27 | }),
28 |
29 | new HappyPack(getHappyPackConfig({
30 | id: 'less-dev',
31 | loaders: ['vue-style-loader', 'css-loader', 'postcss-loader', 'less-loader']
32 | })),
33 |
34 | new HappyPack(getHappyPackConfig({
35 | id: 'css-dev',
36 | loaders: ['vue-style-loader', 'css-loader', 'postcss-loader']
37 | })),
38 |
39 | new webpack.NoEmitOnErrorsPlugin(),
40 | new OpenBrowserPlugin({ url: url })
41 | ]);
42 |
43 | // see https://webpack.github.io/docs/webpack-dev-server.html
44 | devConfig.devServer = {
45 | hot: true,
46 | noInfo: false,
47 | quiet: false,
48 | port: config.dev.port,
49 | // #https://github.com/webpack/webpack-dev-server/issues/882
50 | disableHostCheck: true,
51 | headers: {
52 | 'Access-Control-Allow-Origin': '*',
53 | 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
54 | },
55 | inline: true,
56 | // 解决开发模式下 在子路由刷新返回 404 的情景
57 | historyApiFallback: {
58 | index: config.dev.assetsPublicPath
59 | },
60 | stats: {
61 | colors: true,
62 | modules: false
63 | },
64 | contentBase: config.dev.contentBase,
65 | publicPath: config.dev.assetsPublicPath
66 | };
67 |
68 | module.exports = Object.assign({}, devConfig, {
69 | entry: {
70 | app: [
71 | 'webpack/hot/dev-server',
72 | `webpack-dev-server/client?http://localhost:${config.dev.port}/`,
73 | path.resolve(__dirname, '../docs/entry.js')
74 | ]
75 | },
76 | output: {
77 | filename: '[name].js',
78 | path: config.dev.assetsRoot,
79 | publicPath: config.dev.assetsPublicPath,
80 | sourceMapFilename: '[file].map',
81 | chunkFilename: '[name].js'
82 | },
83 | devtool: 'source-map'
84 | });
85 |
--------------------------------------------------------------------------------
/build/webpack.package.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
4 | const os = require('os');
5 |
6 | const config = require('./config');
7 |
8 | module.exports = {
9 | entry: path.join(__dirname, '../src/index'),
10 | output: {
11 | path: path.join(__dirname, '../lib'),
12 | filename: 'fm-vue-ui.common.js',
13 | library: 'FMUI',
14 | libraryTarget: 'commonjs2'
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.vue$/,
20 | loader: 'vue-loader'
21 | },
22 | {
23 | test: /\.js$/,
24 | exclude: /node_modules/,
25 | loader: 'babel-loader'
26 | },
27 | {
28 | test: /\.less$/,
29 | use: ['vue-style-loader', 'css-loader', 'less-loader']
30 | },
31 | {
32 | test: /\.css$/,
33 | use: ['vue-style-loader', 'css-loader']
34 | }
35 | ]
36 | },
37 | externals: config.externals,
38 | resolve: {
39 | alias: config.alias
40 | },
41 | plugins: [
42 | // new ParallelUglifyPlugin({
43 | // workerCount: os.cpus().length,
44 | // cacheDir: '.cache/',
45 | // uglifyJS: {
46 | // compress: {
47 | // warnings: false,
48 | // /* eslint-disable */
49 | // drop_debugger: true,
50 | // drop_console: true
51 | // },
52 | // comments: false,
53 | // sourceMap: true,
54 | // mangle: true
55 | // }
56 | // }),
57 | new webpack.optimize.ModuleConcatenationPlugin()
58 | ]
59 | };
60 |
--------------------------------------------------------------------------------
/build/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
4 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
5 | const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
6 | const CleanWebpackPlugin = require('clean-webpack-plugin');
7 | const WebpackMd5Hash = require('webpack-md5-hash');
8 | const os = require('os');
9 | const CompressionPlugin = require('compression-webpack-plugin');
10 | const HappyPack = require('happypack');
11 |
12 | const getHappyPackConfig = require('./happypack');
13 |
14 | const prodConfig = require('./webpack.base.config');
15 | const config = require('../config');
16 |
17 | prodConfig.module.rules.unshift({
18 | test: /\.less$/,
19 | use: ExtractTextPlugin.extract({
20 | fallback: 'vue-style-loader',
21 | use: ['happypack/loader?id=less-prod']
22 | })
23 | }, {
24 | test: /\.css$/,
25 | use: ExtractTextPlugin.extract({
26 | fallback: 'vue-style-loader',
27 | use: ['happypack/loader?id=css-prod']
28 | })
29 | });
30 |
31 | prodConfig.plugins = (prodConfig.plugins || []).concat([
32 | new CleanWebpackPlugin(['dist'], {
33 | root: path.join(__dirname, '../docs'),
34 | verbose: true,
35 | dry: false
36 | }),
37 |
38 | new webpack.DefinePlugin({
39 | 'process.env': {
40 | 'NODE_ENV': JSON.stringify(config.build.env)
41 | }
42 | }),
43 |
44 | new ExtractTextPlugin({
45 | filename: '[name].[contenthash:8].css'
46 | }),
47 |
48 | new HappyPack(getHappyPackConfig({
49 | id: 'less-prod',
50 | loaders: ['css-loader', {
51 | path: 'postcss-loader',
52 | query: {
53 | sourceMap: 'inline'
54 | }
55 | }, 'less-loader']
56 | })),
57 |
58 | new HappyPack(getHappyPackConfig({
59 | id: 'css-prod',
60 | loaders: ['css-loader', {
61 | path: 'postcss-loader',
62 | query: {
63 | sourceMap: 'inline'
64 | }
65 | }]
66 | })),
67 |
68 | // Compress extracted CSS. We are using this plugin so that possible
69 | // duplicated CSS from different components can be deduped.
70 | new OptimizeCSSPlugin({
71 | cssProcessorOptions: {
72 | safe: true
73 | },
74 | cssProcessor: require('cssnano'),
75 | assetNameRegExp: /\.less|\.css$/g
76 | }),
77 |
78 | new webpack.optimize.CommonsChunkPlugin({
79 | name: 'vendor',
80 | minChunks: ({ resource }) => (
81 | resource &&
82 | resource.indexOf('node_modules') >= 0 &&
83 | resource.match(/\.js$/)
84 | )
85 | }),
86 |
87 | // gzip
88 | new CompressionPlugin({
89 | asset: '[path].gz[query]',
90 | algorithm: 'gzip',
91 | test: /\.(js|html|less)$/,
92 | threshold: 10240,
93 | minRatio: 0.8
94 | }),
95 |
96 | new ParallelUglifyPlugin({
97 | workerCount: os.cpus().length,
98 | cacheDir: '.cache/',
99 | sourceMap: true,
100 | uglifyJS: {
101 | compress: {
102 | // warnings: false,
103 | /* eslint-disable camelcase */
104 | drop_debugger: true,
105 | drop_console: true
106 | },
107 | mangle: true
108 | }
109 | }),
110 |
111 | new webpack.optimize.ModuleConcatenationPlugin(),
112 | new WebpackMd5Hash()
113 | ]);
114 |
115 | module.exports = Object.assign({}, prodConfig, {
116 | entry: {
117 | app: path.resolve(__dirname, '../docs/entry.js')
118 | },
119 | output: {
120 | filename: '[name].[chunkhash:8].js',
121 | path: config.build.assetsRoot,
122 | publicPath: config.build.assetsPublicPath,
123 | sourceMapFilename: '[file].map',
124 | chunkFilename: '[name].[chunkhash:8].js'
125 | },
126 | devtool: 'source-map'
127 | });
128 |
--------------------------------------------------------------------------------
/build/webpack.test.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: path.join(__dirname, '../src/index'),
6 | module: {
7 | rules: [
8 | {
9 | test: /\.vue$/,
10 | loader: 'vue-loader'
11 | },
12 | {
13 | test: /\.js$/,
14 | exclude: /node_modules/,
15 | loader: 'babel-loader'
16 | },
17 | {
18 | test: /\.less$/,
19 | use: ['vue-style-loader', 'css-loader', 'less-loader']
20 | },
21 | {
22 | test: /\.css$/,
23 | use: ['vue-style-loader', 'css-loader']
24 | }
25 | ]
26 | },
27 | // externals: config.externals,
28 | resolve: {
29 | extensions: ['.js', '.vue'],
30 | alias: {
31 | components: path.resolve(__dirname, '../components'),
32 | 'main': path.resolve(__dirname, '../src'),
33 | 'vue$': 'vue/dist/vue.esm.js',
34 | 'fm-vue-ui': path.resolve(__dirname, '../')
35 | },
36 | modules: [path.join(__dirname, '../node_modules')]
37 | },
38 | devtool: 'inline-source-map'
39 | };
40 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-angular'],
3 | rules: {
4 | 'subject-case': [0]
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "button": "./components/button/index.js",
3 | "dialog": "./components/dialog/index.js",
4 | "toast": "./components/toast/index.js",
5 | "notification": "./components/notification/index.js",
6 | "loading": "./components/loading/index.js",
7 | "collapse": "./components/collapse/index.js",
8 | "collapse-item": "./components/collapse-item/index.js",
9 | "tabs": "./components/tabs/index.js",
10 | "tab-panel": "./components/tab-panel/index.js",
11 | "rate": "./components/rate/index.js",
12 | "carousel": "./components/carousel/index.js",
13 | "input": "./components/input/index.js",
14 | "checkbox": "./components/checkbox/index.js",
15 | "radio": "./components/radio/index.js",
16 | "switch": "./components/switch/index.js",
17 | "option": "./components/option/index.js",
18 | "select": "./components/select/index.js",
19 | "radio-button-group": "./components/radio-button-group/index.js"
20 | }
21 |
--------------------------------------------------------------------------------
/components/button/index.js:
--------------------------------------------------------------------------------
1 | import FMButton from './src/index.vue';
2 |
3 | FMButton.install = function (Vue) {
4 | Vue.component(FMButton.name, FMButton);
5 | };
6 |
7 | export default FMButton;
8 |
--------------------------------------------------------------------------------
/components/button/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
59 |
--------------------------------------------------------------------------------
/components/carousel/index.js:
--------------------------------------------------------------------------------
1 | import FMCarousel from './src/index.vue';
2 |
3 | FMCarousel.install = function (Vue) {
4 | Vue.component(FMCarousel.name, FMCarousel);
5 | };
6 |
7 | export default FMCarousel;
8 |
--------------------------------------------------------------------------------
/components/carousel/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
58 |
--------------------------------------------------------------------------------
/components/checkbox/index.js:
--------------------------------------------------------------------------------
1 | import FMCheckbox from './src/checkbox.vue';
2 |
3 | FMCheckbox.install = function (Vue) {
4 | Vue.component(FMCheckbox.name, FMCheckbox);
5 | };
6 |
7 | export default FMCheckbox;
8 |
--------------------------------------------------------------------------------
/components/checkbox/src/checkbox.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
63 |
--------------------------------------------------------------------------------
/components/collapse-item/index.js:
--------------------------------------------------------------------------------
1 | import FMCollapseItem from './src/index.vue';
2 |
3 | FMCollapseItem.install = function (Vue) {
4 | Vue.component(FMCollapseItem.name, FMCollapseItem);
5 | };
6 |
7 | export default FMCollapseItem;
8 |
--------------------------------------------------------------------------------
/components/collapse-item/src/collapse-transition.js:
--------------------------------------------------------------------------------
1 | import { createTinyDOM, css } from 'tiny-dom-helpers';
2 |
3 | // https://cn.vuejs.org/v2/guide/transitions.html#可复用的过渡
4 | const hooks = {
5 | // 进入时
6 | beforeEnter (el) {
7 | const dom = createTinyDOM(el);
8 | dom.addClass('fm-collapse-enter-transition');
9 | if (!el.dataset) {
10 | el.dataset = {};
11 | }
12 | el.dataset.oldPaddingTop = css(el, 'padding-top', null);
13 | el.dataset.oldPaddingBottom = css(el, 'padding-bottom', null);
14 | dom.attr('style', 'height: 0; padding-top: 0; padding-bottom: 0');
15 | },
16 |
17 | enter (el, done) {
18 | el.dataset.oldOverflow = css(el, 'overflow', null);
19 | const dom = createTinyDOM(el);
20 | if (el.scrollHeight !== 0) {
21 | dom.attr('style', `overflow: hidden; height: ${el.scrollHeight}px; padding-top: ${el.dataset.oldPaddingTop}; padding-bottom: ${el.dataset.oldPaddingBottom}`);
22 | } else {
23 | dom.attr('style', `overflow: hidden; height: ''; padding-top: ${el.dataset.oldPaddingTop}; padding-bottom: ${el.dataset.oldPaddingBottom}`);
24 | }
25 | },
26 |
27 | afterEnter (el) {
28 | const dom = createTinyDOM(el);
29 | dom.removeClass('fm-collapse-enter-transition');
30 | el.style.height = 0;
31 | el.style.overflow = el.dataset.oldOverflow;
32 | },
33 |
34 | // 离开时
35 | beforeLeave (el) {
36 | if (!el.dataset) {
37 | el.dataset = {};
38 | };
39 | const dom = createTinyDOM(el);
40 | dom.addClass('fm-collapse-leave-transition');
41 | el.dataset.oldPaddingTop = css(el, 'padding-top', null);
42 | el.dataset.oldPaddingBottom = css(el, 'padding-bottom', null);
43 | el.dataset.oldOverflow = css(el, 'overflow', null);
44 |
45 | dom.attr('style', `overflow: hidden; height: ${el.scrollHeight}px`);
46 | },
47 |
48 | leave (el) {
49 | const dom = createTinyDOM(el);
50 | if (el.scrollHeight !== 0) {
51 | dom.addClass('fm-collapse-leave-transition');
52 | dom.attr('style', `overflow: hidden; height: 0; padding-top: 0; padding-bottom: 0`);
53 | }
54 | },
55 |
56 | afterLeave (el) {
57 | const dom = createTinyDOM(el);
58 | dom.removeClass('fm-collapse-leave-transition');
59 | dom.attr('style', `overflow: ${el.dataset.oldOverflow}; height: 0; padding-top: ${el.dataset.oldPaddingTop}; padding-bottom: ${el.dataset.oldPaddingBottom}`);
60 | }
61 | };
62 |
63 | export default {
64 | name: 'fm-collapse-transition',
65 | functional: true,
66 | render (h, { children }) {
67 | const data = {
68 | on: hooks,
69 | props: {
70 | mode: 'out-in'
71 | }
72 | };
73 | return h('transition', data, children);
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/components/collapse/index.js:
--------------------------------------------------------------------------------
1 | import FMCollapse from './src/index.vue';
2 |
3 | FMCollapse.install = function (Vue) {
4 | Vue.component(FMCollapse.name, FMCollapse);
5 | };
6 |
7 | export default FMCollapse;
8 |
--------------------------------------------------------------------------------
/components/collapse/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/components/dialog/index.js:
--------------------------------------------------------------------------------
1 | import FMDialog from './src/main.js';
2 | export default FMDialog;
3 |
--------------------------------------------------------------------------------
/components/dialog/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import FMDialog from './index.vue';
3 |
4 | const DialogConstructor = Vue.extend(FMDialog);
5 |
6 | let instance = null;
7 |
8 | DialogConstructor.prototype.remove = function () {
9 | this.shown = false;
10 | if (this.$el.parentNode) {
11 | this.$el.parentNode.removeChild(this.$el);
12 | }
13 | instance = null;
14 | };
15 |
16 | const getAnInstance = (props) => {
17 | if (instance) {
18 | return instance;
19 | }
20 |
21 | return new DialogConstructor({
22 | propsData: props,
23 | el: document.createElement('div')
24 | });
25 | };
26 |
27 | const Dialog = function (opts) {
28 | if (Vue.prototype.$isServer) {
29 | return;
30 | }
31 |
32 | if (typeof opts === 'string') {
33 | opts = {
34 | message: opts
35 | };
36 | }
37 |
38 | if (typeof opts.onCancel === 'function') {
39 | const cancel = opts.onCancel;
40 | opts.onCancel = () => {
41 | instance.remove();
42 | cancel();
43 | };
44 | } else {
45 | opts.onCancel = () => {
46 | instance.remove();
47 | };
48 | }
49 |
50 | if (typeof opts.onConfirm === 'function') {
51 | const confirm = opts.onConfirm;
52 | opts.onConfirm = () => {
53 | instance.remove();
54 | confirm();
55 | };
56 | } else {
57 | opts.onConfirm = () => {
58 | instance.remove();
59 | };
60 | }
61 |
62 | if (typeof opts.onClose === 'function') {
63 | const close = opts.onClose;
64 | opts.onClose = () => {
65 | instance.remove();
66 | close();
67 | };
68 | } else {
69 | opts.onClose = () => {};
70 | }
71 |
72 | instance = getAnInstance(opts);
73 | document.body.appendChild(instance.$el);
74 | instance.shown = true;
75 |
76 | // 自动销毁
77 | if (instance.validType) {
78 | setTimeout(() => {
79 | instance && instance.remove();
80 | }, instance.duration);
81 | }
82 | };
83 |
84 | export default Dialog;
85 |
--------------------------------------------------------------------------------
/components/input/index.js:
--------------------------------------------------------------------------------
1 | import FMInput from './src/index.vue';
2 |
3 | FMInput.install = function (Vue) {
4 | Vue.component(FMInput.name, FMInput);
5 | };
6 |
7 | export default FMInput;
8 |
--------------------------------------------------------------------------------
/components/input/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
18 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
149 |
--------------------------------------------------------------------------------
/components/loading/index.js:
--------------------------------------------------------------------------------
1 | import FMLoading from './src/index.vue';
2 |
3 | FMLoading.install = function (Vue) {
4 | Vue.component(FMLoading.name, FMLoading);
5 | };
6 |
7 | export default FMLoading;
8 |
--------------------------------------------------------------------------------
/components/loading/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
23 |
--------------------------------------------------------------------------------
/components/notification/index.js:
--------------------------------------------------------------------------------
1 | import FMNotification from './src/main.js';
2 | export default FMNotification;
3 |
--------------------------------------------------------------------------------
/components/notification/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 | {{message}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
71 |
--------------------------------------------------------------------------------
/components/notification/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import FMToast from './index.vue';
3 |
4 | const ToastConstructor = Vue.extend(FMToast);
5 |
6 | let instance = null;
7 | let closeTimeoutEvent = null;
8 |
9 | ToastConstructor.prototype.remove = function () {
10 | this.shown = false;
11 | if (this.$el.parentNode) {
12 | this.$el.parentNode.removeChild(this.$el);
13 | }
14 | instance = null;
15 | };
16 |
17 | const getAnInstance = (props) => {
18 | if (instance) {
19 | instance.remove();
20 | instance = null;
21 | // return instance;
22 | }
23 |
24 | return new ToastConstructor({
25 | propsData: props,
26 | el: document.createElement('div')
27 | });
28 | };
29 |
30 | const Toast = function (opts) {
31 | if (Vue.prototype.$isServer) {
32 | return;
33 | }
34 |
35 | if (typeof opts === 'string') {
36 | opts = {
37 | message: opts
38 | };
39 | }
40 |
41 | if (typeof opts.onClose === 'function') {
42 | const close = opts.onClose;
43 | opts.onClose = () => {
44 | instance.remove();
45 | close();
46 | };
47 | } else {
48 | opts.onClose = () => {
49 | instance.remove();
50 | clearTimeout(closeTimeoutEvent);
51 | };
52 | }
53 |
54 | instance = getAnInstance(opts);
55 | document.body.appendChild(instance.$el);
56 | instance.shown = true;
57 |
58 | // 自动销毁
59 | if (instance.duration > 0) {
60 | clearTimeout(closeTimeoutEvent);
61 | closeTimeoutEvent = setTimeout(() => {
62 | instance && instance.remove();
63 | }, instance.duration);
64 | }
65 | };
66 |
67 | export default Toast;
68 |
--------------------------------------------------------------------------------
/components/option/index.js:
--------------------------------------------------------------------------------
1 | import FMOption from '../select/src/option.vue';
2 |
3 | FMOption.install = function (Vue) {
4 | Vue.component(FMOption.name, FMOption);
5 | };
6 |
7 | export default FMOption;
8 |
--------------------------------------------------------------------------------
/components/radio-button-group/index.js:
--------------------------------------------------------------------------------
1 | import RadioButtonGroup from './src/index.vue';
2 |
3 | RadioButtonGroup.install = function (Vue) {
4 | Vue.component(RadioButtonGroup.name, RadioButtonGroup);
5 | };
6 |
7 | export default RadioButtonGroup;
8 |
--------------------------------------------------------------------------------
/components/radio-button-group/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
58 |
--------------------------------------------------------------------------------
/components/radio/index.js:
--------------------------------------------------------------------------------
1 | import FMRadio from './src/radio.vue';
2 |
3 | FMRadio.install = function (Vue) {
4 | Vue.component(FMRadio.name, FMRadio);
5 | };
6 |
7 | export default FMRadio;
8 |
--------------------------------------------------------------------------------
/components/radio/src/radio.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
79 |
--------------------------------------------------------------------------------
/components/rate/index.js:
--------------------------------------------------------------------------------
1 | import FMRate from './src/index.vue';
2 |
3 | FMRate.install = function (Vue) {
4 | Vue.component(FMRate.name, FMRate);
5 | };
6 |
7 | export default FMRate;
8 |
--------------------------------------------------------------------------------
/components/rate/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 | {{score}}
13 |
14 |
15 |
99 |
--------------------------------------------------------------------------------
/components/select/index.js:
--------------------------------------------------------------------------------
1 | import FMSelect from './src/index.vue';
2 |
3 | FMSelect.install = function (Vue) {
4 | Vue.component(FMSelect.name, FMSelect);
5 | };
6 |
7 | export default FMSelect;
8 |
--------------------------------------------------------------------------------
/components/select/src/option.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 | {{label || value}}
10 |
11 |
12 |
13 |
14 |
57 |
--------------------------------------------------------------------------------
/components/switch/index.js:
--------------------------------------------------------------------------------
1 | import FMSwitch from './src/index.vue';
2 |
3 | FMSwitch.install = function (Vue) {
4 | Vue.component(FMSwitch.name, FMSwitch);
5 | };
6 |
7 | export default FMSwitch;
8 |
--------------------------------------------------------------------------------
/components/switch/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
82 |
--------------------------------------------------------------------------------
/components/tab-panel/index.js:
--------------------------------------------------------------------------------
1 | import FMTabPanel from './src/index.vue';
2 |
3 | FMTabPanel.install = function (Vue) {
4 | Vue.component(FMTabPanel.name, FMTabPanel);
5 | };
6 |
7 | export default FMTabPanel;
8 |
--------------------------------------------------------------------------------
/components/tab-panel/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/components/tabs/index.js:
--------------------------------------------------------------------------------
1 | import FMTabs from './src/index.vue';
2 |
3 | FMTabs.install = function (Vue) {
4 | Vue.component(FMTabs.name, FMTabs);
5 | };
6 |
7 | export default FMTabs;
8 |
--------------------------------------------------------------------------------
/components/tabs/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 | {{panel.label}}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
121 |
--------------------------------------------------------------------------------
/components/toast/index.js:
--------------------------------------------------------------------------------
1 | import FMToast from './src/main.js';
2 | export default FMToast;
3 |
--------------------------------------------------------------------------------
/components/toast/src/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{message}}
7 |
8 |
9 |
10 |
11 |
12 |
61 |
--------------------------------------------------------------------------------
/components/toast/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import FMToast from './index.vue';
3 |
4 | const ToastConstructor = Vue.extend(FMToast);
5 |
6 | let instance = null;
7 | let closeTimeoutEvent = null;
8 |
9 | ToastConstructor.prototype.remove = function () {
10 | this.shown = false;
11 | if (this.$el.parentNode) {
12 | this.$el.parentNode.removeChild(this.$el);
13 | }
14 | instance = null;
15 | };
16 |
17 | const getAnInstance = (props) => {
18 | if (instance) {
19 | instance.remove();
20 | instance = null;
21 | // return instance;
22 | }
23 |
24 | return new ToastConstructor({
25 | propsData: props,
26 | el: document.createElement('div')
27 | });
28 | };
29 |
30 | const Toast = function (opts) {
31 | if (Vue.prototype.$isServer) {
32 | return;
33 | }
34 |
35 | if (typeof opts === 'string') {
36 | opts = {
37 | message: opts
38 | };
39 | }
40 |
41 | if (typeof opts.onClose === 'function') {
42 | const close = opts.onClose;
43 | opts.onClose = () => {
44 | instance.remove();
45 | close();
46 | };
47 | } else {
48 | opts.onClose = () => {
49 | instance.remove();
50 | clearTimeout(closeTimeoutEvent);
51 | };
52 | }
53 |
54 | instance = getAnInstance(opts);
55 | document.body.appendChild(instance.$el);
56 | instance.shown = true;
57 |
58 | // 自动销毁
59 | if (instance.duration > 0) {
60 | clearTimeout(closeTimeoutEvent);
61 | closeTimeoutEvent = setTimeout(() => {
62 | instance && instance.remove();
63 | }, instance.duration);
64 | }
65 | };
66 |
67 | export default Toast;
68 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | dev: {
5 | env: 'development',
6 | assetsRoot: path.resolve(__dirname, '../docs/dist'),
7 | assetsPublicPath: '/fm-vue-ui/dist/',
8 | contentBase: path.resolve(__dirname, '../docs/dist'),
9 | port: 3000
10 | },
11 | build: {
12 | env: 'production',
13 | assetsRoot: path.resolve(__dirname, '../docs/dist'),
14 | assetsPublicPath: '/fm-vue-ui/dist/'
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/docs/common.less:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | body, html {
6 | margin: 0 auto;
7 | width: 1024px;
8 | font: 14px/1.4 Microsoft Yahei, Tahoma, "PingFang SC", Helvetica, Arial, sans-serif;
9 | background-color: #f4f4f4;
10 | }
11 |
12 | ul, ol {
13 | list-style: none;
14 | padding: 0;
15 | margin: 0
16 | }
17 |
18 | a {
19 | text-decoration: none
20 | }
21 |
22 | h1, h2, h3, h4, h5, h6 {
23 | cursor: pointer;
24 | a {
25 | visibility: hidden;
26 | color: #ff6200
27 | }
28 | &:hover {
29 | a {
30 | visibility: visible;
31 | }
32 | }
33 | }
34 |
35 | .docs-content-wrap {
36 | position: relative;
37 | padding-top: 80px;
38 | }
39 |
40 | .docs-content {
41 | display: inline-block;
42 | height: 100%;
43 | width: 788px;
44 | margin-left: 185px;
45 | padding: 15px;
46 | background: #fff;
47 | & > p {
48 | margin-left: 15px;
49 | }
50 | ol {
51 | list-style: decimal;
52 | margin-left: 30px;
53 | }
54 | }
55 |
56 | .table {
57 | border-collapse: collapse;
58 | width: 100%;
59 | background-color: #fff;
60 | font-size: 14px;
61 | margin-bottom: 25px;
62 | line-height: 1.5em;
63 | margin-left: 15px;
64 | tr {
65 | border-bottom: 1px solid #e1e2e6;
66 | height: 45px;
67 | }
68 | }
69 |
70 | code {
71 | background-color: #f9fafc;
72 | padding: 0 4px;
73 | border: 1px solid #eaeefb;
74 | border-radius: 4px;
75 | }
76 |
77 | .hljs {
78 | line-height: 1.8;
79 | font-family: Menlo, Monaco, Consolas, Courier, monospace;
80 | font-size: 12px;
81 | padding: 18px 24px;
82 | background-color: #fafafa;
83 | border: solid 1px #eaeefb;
84 | margin-bottom: 25px;
85 | border-radius: 4px;
86 | -webkit-font-smoothing: auto;
87 | }
88 |
89 | blockquote {
90 | background: #fafafa;
91 | margin-left: 15px;
92 | p {
93 | padding: 5px 10px;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/docs/components/demo-block.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | {{ isExpanded ? '隐藏代码' : '显示代码' }}
11 |
12 |
13 |
14 |
15 |
25 |
26 |
61 |
--------------------------------------------------------------------------------
/docs/components/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
15 |
16 |
39 |
--------------------------------------------------------------------------------
/docs/components/side-nav.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
12 |
13 |
14 |
15 | -
16 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/dist/1.37c351b9.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/1.37c351b9.js.gz
--------------------------------------------------------------------------------
/docs/dist/10.4be66b11.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/10.4be66b11.js.gz
--------------------------------------------------------------------------------
/docs/dist/12.89eeb768.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/12.89eeb768.js.gz
--------------------------------------------------------------------------------
/docs/dist/13.154fba12.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/13.154fba12.js.gz
--------------------------------------------------------------------------------
/docs/dist/14.8242b6a4.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/14.8242b6a4.js.gz
--------------------------------------------------------------------------------
/docs/dist/15.2d69444c.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/15.2d69444c.js.gz
--------------------------------------------------------------------------------
/docs/dist/16.6b4129ec.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/16.6b4129ec.js.gz
--------------------------------------------------------------------------------
/docs/dist/17.08c2c7cb.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([17],{248:function(t,a,e){t.exports=e(268)},268:function(t,a,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});var l={render:function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("section",[t._m(0),t._v(" "),e("p",[t._v("在有限空间内,循环播放同一类型的图片、文字等内容")]),t._v(" "),t._m(1),t._v(" "),e("p",[t._v("适用广泛的基础用法。")]),t._v(" "),e("docs-demo-block",[e("div",{staticClass:"source",attrs:{slot:"source"},slot:"source"},[e("fm-carousel",{attrs:{list:t.list}})],1),t._v(" "),e("p",[t._v("Click 指示器触发")]),t._v(" "),e("pre",{pre:!0},[e("code",{pre:!0,attrs:{class:"hljs language-html"}},[e("span",{pre:!0,attrs:{class:"hljs-tag"}},[t._v("<"),e("span",{pre:!0,attrs:{class:"hljs-name"}},[t._v("fm-carousel")]),t._v(" "),e("span",{pre:!0,attrs:{class:"hljs-attr"}},[t._v(":list")]),t._v("="),e("span",{pre:!0,attrs:{class:"hljs-string"}},[t._v('"list"')]),t._v(">")]),e("span",{pre:!0,attrs:{class:"hljs-tag"}},[t._v(""),e("span",{pre:!0,attrs:{class:"hljs-name"}},[t._v("fm-carousel")]),t._v(">")]),t._v("\n")])])]),t._v(" "),t._m(2),t._v(" "),t._m(3)],1)},staticRenderFns:[function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("h2",{attrs:{id:"carousel-zou-ma-deng"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#carousel-zou-ma-deng","aria-hidden":"true"}},[t._v("¶")]),t._v(" Carousel 走马灯")])},function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("h3",{attrs:{id:"ji-chu-yong-fa"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#ji-chu-yong-fa","aria-hidden":"true"}},[t._v("¶")]),t._v(" 基础用法")])},function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("h3",{attrs:{id:"shu-xing"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#shu-xing","aria-hidden":"true"}},[t._v("¶")]),t._v(" 属性")])},function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("table",{staticClass:"table"},[e("thead",[e("tr",[e("th",{staticStyle:{"text-align":"left"}},[t._v("参数")]),t._v(" "),e("th",{staticStyle:{"text-align":"left"}},[t._v("说明")]),t._v(" "),e("th",{staticStyle:{"text-align":"left"}},[t._v("类型")]),t._v(" "),e("th",{staticStyle:{"text-align":"left"}},[t._v("可选值")]),t._v(" "),e("th",{staticStyle:{"text-align":"left"}},[t._v("默认值")])])]),t._v(" "),e("tbody",[e("tr",[e("td",{staticStyle:{"text-align":"left"}},[t._v("delay")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("轮播切换时间")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("number")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("-")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("3000")])]),t._v(" "),e("tr",[e("td",{staticStyle:{"text-align":"left"}},[t._v("list")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("轮播图片信息,默认值为,image地址,url地址,title名组成的数组对象")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("array")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("-")]),t._v(" "),e("td",{staticStyle:{"text-align":"left"}},[t._v("[{image: '', url: '', title: ''}]")])])])])}]},s=e(4)({data:function(){return{list:[{image:"http://ww2.sinaimg.cn/large/0060lm7Tly1fm9dc8l3pqj30dw09aq4g.jpg",url:"http://ww2.sinaimg.cn/large/0060lm7Tly1fm9dc8l3pqj30dw09aq4g.jpg",title:"家有萌宠"},{image:"http://ww4.sinaimg.cn/large/0060lm7Tly1fm9dca7vthj30dw09a0tw.jpg",url:"http://ww4.sinaimg.cn/large/0060lm7Tly1fm9dca7vthj30dw09a0tw.jpg",title:"家有萌宠"},{image:"http://ww2.sinaimg.cn/large/0060lm7Tly1fm9dcac9a5j30dw09adhc.jpg",url:"http://ww2.sinaimg.cn/large/0060lm7Tly1fm9dcac9a5j30dw09adhc.jpg",title:"家有萌宠"},{image:"http://ww3.sinaimg.cn/large/0060lm7Tly1fm9dca63euj30dw09a76n.jpg",url:"http://ww3.sinaimg.cn/large/0060lm7Tly1fm9dca63euj30dw09a76n.jpg",title:"家有萌宠"}]}}},l,!1,null,null,null);a.default=s.exports}});
2 | //# sourceMappingURL=17.08c2c7cb.js.map
--------------------------------------------------------------------------------
/docs/dist/17.08c2c7cb.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack:///./examples/carousel.md","webpack:///examples/carousel.vue","webpack:///./examples/carousel.md?a05a","webpack:///./examples/carousel.md?7087"],"names":["module","exports","__webpack_require__","markdown_compilerraw_examples_carousel","render","_vm","this","_h","$createElement","_c","_self","_m","_v","staticClass","attrs","slot","list","pre","class","staticRenderFns","id","href","aria-hidden","staticStyle","text-align","Component","normalizeComponent","data","image","url","title","__webpack_exports__"],"mappings":"uCAAAA,EAAAC,QAAiBC,EAAQ,wFC0CzB,ICvCeC,EADf,CAAiBC,OAFjB,WAA0B,IAAAC,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,WAAAJ,EAAAM,GAAA,GAAAN,EAAAO,GAAA,KAAAH,EAAA,KAAAJ,EAAAO,GAAA,8BAAAP,EAAAO,GAAA,KAAAP,EAAAM,GAAA,GAAAN,EAAAO,GAAA,KAAAH,EAAA,KAAAJ,EAAAO,GAAA,gBAAAP,EAAAO,GAAA,KAAAH,EAAA,mBAAAA,EAAA,OAAqMI,YAAA,SAAAC,MAAA,CAA4BC,KAAA,UAAgBA,KAAA,UAAe,CAAAN,EAAA,eAAoBK,MAAA,CAAOE,KAAAX,EAAAW,SAAiB,GAAAX,EAAAO,GAAA,KAAAH,EAAA,KAAAJ,EAAAO,GAAA,iBAAAP,EAAAO,GAAA,KAAAH,EAAA,OAAwEQ,KAAA,GAAS,CAAAR,EAAA,QAAaQ,KAAA,EAAAH,MAAA,CAAgBI,MAAA,uBAA8B,CAAAT,EAAA,QAAaQ,KAAA,EAAAH,MAAA,CAAgBI,MAAA,aAAoB,CAAAb,EAAAO,GAAA,KAAAH,EAAA,QAAyBQ,KAAA,EAAAH,MAAA,CAAgBI,MAAA,cAAqB,CAAAb,EAAAO,GAAA,iBAAAP,EAAAO,GAAA,KAAAH,EAAA,QAAiDQ,KAAA,EAAAH,MAAA,CAAgBI,MAAA,cAAqB,CAAAb,EAAAO,GAAA,WAAAP,EAAAO,GAAA,KAAAH,EAAA,QAA2CQ,KAAA,EAAAH,MAAA,CAAgBI,MAAA,gBAAuB,CAAAb,EAAAO,GAAA,YAAAP,EAAAO,GAAA,OAAAH,EAAA,QAAgDQ,KAAA,EAAAH,MAAA,CAAgBI,MAAA,aAAoB,CAAAb,EAAAO,GAAA,MAAAH,EAAA,QAA0BQ,KAAA,EAAAH,MAAA,CAAgBI,MAAA,cAAqB,CAAAb,EAAAO,GAAA,iBAAAP,EAAAO,GAAA,OAAAP,EAAAO,GAAA,YAAAP,EAAAO,GAAA,KAAAP,EAAAM,GAAA,GAAAN,EAAAO,GAAA,KAAAP,EAAAM,GAAA,QAE16BQ,gBADjB,YAAoC,IAAAd,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,MAAgBK,MAAA,CAAOM,GAAA,yBAA6B,CAAAX,EAAA,KAAUI,YAAA,gBAAAC,MAAA,CAAmCO,KAAA,wBAAAC,cAAA,SAAqD,CAAAjB,EAAAO,GAAA,OAAAP,EAAAO,GAAA,oBAA0C,WAAc,IAAAP,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,MAAgBK,MAAA,CAAOM,GAAA,mBAAuB,CAAAX,EAAA,KAAUI,YAAA,gBAAAC,MAAA,CAAmCO,KAAA,kBAAAC,cAAA,SAA+C,CAAAjB,EAAAO,GAAA,OAAAP,EAAAO,GAAA,YAAkC,WAAc,IAAAP,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,MAAgBK,MAAA,CAAOM,GAAA,aAAiB,CAAAX,EAAA,KAAUI,YAAA,gBAAAC,MAAA,CAAmCO,KAAA,YAAAC,cAAA,SAAyC,CAAAjB,EAAAO,GAAA,OAAAP,EAAAO,GAAA,UAAgC,WAAc,IAAAP,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,SAAmBI,YAAA,SAAoB,CAAAJ,EAAA,SAAAA,EAAA,MAAAA,EAAA,MAAgCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,QAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAsCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,QAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAsCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,QAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAsCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,SAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAuCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,aAAAP,EAAAO,GAAA,KAAAH,EAAA,SAAAA,EAAA,MAAAA,EAAA,MAAgEc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,WAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAyCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,YAAAP,EAAAO,GAAA,KAAAH,EAAA,MAA0Cc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,YAAAP,EAAAO,GAAA,KAAAH,EAAA,MAA0Cc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,OAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAqCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,YAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAAA,EAAA,MAAmDc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,UAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAwCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,6CAAAP,EAAAO,GAAA,KAAAH,EAAA,MAA2Ec,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,WAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAyCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,OAAAP,EAAAO,GAAA,KAAAH,EAAA,MAAqCc,YAAA,CAAaC,aAAA,SAAqB,CAAAnB,EAAAO,GAAA,gDCaxgEa,EAdyBvB,EAAQ,EAcjCwB,CF4BA,CACAC,KADA,WAEA,OACAX,KAAA,CACA,CACAY,MAAA,mEACAC,IAAA,mEACAC,MAAA,QAEA,CACAF,MAAA,mEACAC,IAAA,mEACAC,MAAA,QAEA,CACAF,MAAA,mEACAC,IAAA,mEACAC,MAAA,QAEA,CACAF,MAAA,mEACAC,IAAA,mEACAC,MAAA,YEhDE3B,GATF,EAEA,KAEA,KAEA,MAUe4B,EAAA,QAAAN,EAAiB","file":"17.08c2c7cb.js","sourceRoot":""}
--------------------------------------------------------------------------------
/docs/dist/2.d39ad9e2.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/2.d39ad9e2.js.gz
--------------------------------------------------------------------------------
/docs/dist/3.2d5ecdc4.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/3.2d5ecdc4.js.gz
--------------------------------------------------------------------------------
/docs/dist/4.b1fc655a.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/4.b1fc655a.js.gz
--------------------------------------------------------------------------------
/docs/dist/5.7b5d6514.js:
--------------------------------------------------------------------------------
1 | webpackJsonp([5],{264:function(e,n,r){e.exports=r(292)},292:function(e,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var o={render:function(){var e=this,n=e.$createElement;e._self._c;return e._m(0)},staticRenderFns:[function(){var e=this,n=e.$createElement,r=e._self._c||n;return r("section",[r("h2",{attrs:{id:"guo-ji-hua"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#guo-ji-hua","aria-hidden":"true"}},[e._v("¶")]),e._v(" 国际化")]),e._v(" "),r("p",[r("code",{pre:!0},[e._v("fm-vue-ui")]),e._v(" 目前仅支持两种语言: "),r("code",{pre:!0},[e._v("zh-CN")]),e._v("、"),r("code",{pre:!0},[e._v("en-US")]),e._v(" 和 "),r("code",{pre:!0},[e._v("zh-HK")]),e._v(",默认使用 "),r("code",{pre:!0},[e._v("zh-CN")]),e._v("。")]),e._v(" "),r("blockquote",[r("p",[e._v("我们所有的项目都是需要支持国际化的")])]),e._v(" "),r("p",[e._v("如果需要设置其它语言,配置示例如下:")]),e._v(" "),r("pre",{pre:!0},[r("code",{pre:!0,attrs:{class:"hljs language-html"}},[e._v("// 项目的 i18n 配置 src/i18n/index.js\nimport Vue from 'vue';\nimport VueI18n from 'vue-i18n'; // 7.x\nimport en from './en-US';\nimport cn from './zh-CN';\n\nVue.use(VueI18n);\n\nexport default new VueI18n({\n locale: 'zh-CN',\n messages: {\n 'en-US': en,\n 'zh-CN': cn\n }\n});\n\n// page/index.js\nimport Vue from 'vue';\nimport FMUI from 'fm-vue-ui';\n\nimport i18n from './i18n/index';\n\nVue.use(FMUI, {\n lang: i18n.locale\n});\n")])]),e._v(" "),r("p",[e._v("如果是按需引入:")]),e._v(" "),r("pre",{pre:!0},[r("code",{pre:!0,attrs:{class:"hljs language-html"}},[e._v("// 按需引入\nimport Vue from 'vue'\nimport { Button } from 'fm-vue-ui'\nimport locale from 'fm-vue-ui/lib/locale/index';\n\nlocale.use(i18n.locale);\nVue.use(Button); // 然后安装组件\n")])]),e._v(" "),r("p",[e._v("组件内的开发需要考虑国际化文案时,使用案例请参考 "),r("code",{pre:!0},[e._v("Dialog")]),e._v("。")])])}]},t=r(4)(null,o,!1,null,null,null);n.default=t.exports}});
2 | //# sourceMappingURL=5.7b5d6514.js.map
--------------------------------------------------------------------------------
/docs/dist/5.7b5d6514.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack:///./pages/i18n.md","webpack:///./pages/i18n.md?d9bc","webpack:///./pages/i18n.md?58e6"],"names":["module","exports","__webpack_require__","i18n","render","_vm","this","_h","$createElement","_self","_c","_m","staticRenderFns","attrs","id","staticClass","href","aria-hidden","_v","pre","class","Component","normalizeComponent","__webpack_exports__"],"mappings":"sCAAAA,EAAAC,QAAiBC,EAAQ,wFCAzB,IAGeC,EADf,CAAiBC,OAFjB,WAA0B,IAAAC,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BH,EAAAI,MAAAC,GAAwB,OAAAL,EAAAM,GAAA,IAExEC,gBADjB,YAAoC,IAAAP,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BE,EAAAL,EAAAI,MAAAC,IAAAH,EAAwB,OAAAG,EAAA,WAAAA,EAAA,MAA8BG,MAAA,CAAOC,GAAA,eAAmB,CAAAJ,EAAA,KAAUK,YAAA,gBAAAF,MAAA,CAAmCG,KAAA,cAAAC,cAAA,SAA2C,CAAAZ,EAAAa,GAAA,OAAAb,EAAAa,GAAA,UAAAb,EAAAa,GAAA,KAAAR,EAAA,KAAAA,EAAA,QAAgES,KAAA,GAAS,CAAAd,EAAAa,GAAA,eAAAb,EAAAa,GAAA,gBAAAR,EAAA,QAA0DS,KAAA,GAAS,CAAAd,EAAAa,GAAA,WAAAb,EAAAa,GAAA,KAAAR,EAAA,QAA2CS,KAAA,GAAS,CAAAd,EAAAa,GAAA,WAAAb,EAAAa,GAAA,OAAAR,EAAA,QAA6CS,KAAA,GAAS,CAAAd,EAAAa,GAAA,WAAAb,EAAAa,GAAA,UAAAR,EAAA,QAAgDS,KAAA,GAAS,CAAAd,EAAAa,GAAA,WAAAb,EAAAa,GAAA,OAAAb,EAAAa,GAAA,KAAAR,EAAA,cAAAA,EAAA,KAAAL,EAAAa,GAAA,yBAAAb,EAAAa,GAAA,KAAAR,EAAA,KAAAL,EAAAa,GAAA,wBAAAb,EAAAa,GAAA,KAAAR,EAAA,OAAgLS,KAAA,GAAS,CAAAT,EAAA,QAAaS,KAAA,EAAAN,MAAA,CAAgBO,MAAA,uBAA8B,CAAAf,EAAAa,GAAA,idAAidb,EAAAa,GAAA,KAAAR,EAAA,KAAAL,EAAAa,GAAA,cAAAb,EAAAa,GAAA,KAAAR,EAAA,OAAwES,KAAA,GAAS,CAAAT,EAAA,QAAaS,KAAA,EAAAN,MAAA,CAAgBO,MAAA,uBAA8B,CAAAf,EAAAa,GAAA,sLAA4Kb,EAAAa,GAAA,KAAAR,EAAA,KAAAL,EAAAa,GAAA,6BAAAR,EAAA,QAAsFS,KAAA,GAAS,CAAAd,EAAAa,GAAA,YAAAb,EAAAa,GAAA,YCY9nDG,EAbyBnB,EAAQ,EAajCoB,CAXA,KAaEnB,GATF,EAEA,KAEA,KAEA,MAUeoB,EAAA,QAAAF,EAAiB","file":"5.7b5d6514.js","sourceRoot":""}
--------------------------------------------------------------------------------
/docs/dist/7.70d5d555.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/7.70d5d555.js.gz
--------------------------------------------------------------------------------
/docs/dist/app.1be2bc04.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/app.1be2bc04.js.gz
--------------------------------------------------------------------------------
/docs/dist/vendor.01d16740.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/dist/vendor.01d16740.js.gz
--------------------------------------------------------------------------------
/docs/entry.js:
--------------------------------------------------------------------------------
1 | import 'normalize.css';
2 | import 'highlight.js/styles/color-brewer.css';
3 | import 'github-markdown-css';
4 |
5 | import './common.less';
6 |
7 | import 'babel-polyfill';
8 |
9 | import Vue from 'vue';
10 | import VueRouter from 'vue-router';
11 |
12 | import FMUI from 'main/index';
13 | // import fmutils from 'main/utils/index';
14 | import 'fm-vue-ui/theme-default/src/index.less';
15 | // import locale from 'main/locale/index';
16 |
17 | import routes from './routes.config';
18 | import entry from './entry.vue';
19 | import DocsHeader from './components/header.vue';
20 | import DocsSideNav from './components/side-nav.vue';
21 | import DocsDemoBlock from './components/demo-block.vue';
22 |
23 | import i18n from './i18n/index';
24 | // Vue.prototype.$utils = fmutils;
25 | // locale.use(i18n.locale);
26 | Vue.use(FMUI, {
27 | lang: i18n.locale
28 | });
29 | // Vue.use(Button);
30 | // Vue.use(Input);
31 |
32 | Vue.use(VueRouter);
33 | Vue.component('docs-demo-block', DocsDemoBlock);
34 | Vue.component('docs-header', DocsHeader);
35 | Vue.component('docs-side-nav', DocsSideNav);
36 |
37 | const router = new VueRouter({
38 | mode: 'history',
39 | base: '/fm-vue-ui/',
40 | fallback: true,
41 | routes
42 | });
43 |
44 | new Vue({
45 | render: h => h(entry),
46 | router,
47 | i18n
48 | }).$mount('#app');
49 |
--------------------------------------------------------------------------------
/docs/entry.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/docs/examples/button.md:
--------------------------------------------------------------------------------
1 |
6 |
15 | ## Button 按钮
16 | 常用的操作按钮。
17 |
18 | ### 基础用法
19 |
20 | 基础的按钮用法。
21 |
22 | :::demo 使用 `type` `radius` 和 `size` 属性来定义 Button 的样式。
23 |
24 | ```html
25 |
26 | 默认按钮
27 | 主要按钮
28 | 幽灵按钮
29 | 小圆角按钮
30 |
31 |
32 |
33 | small(默认)
34 | medium
35 | large
36 | xlarge
37 |
38 | ```
39 | :::
40 |
41 | ### 深色模式
42 |
43 | 按钮可以切换成深色模式。
44 |
45 | :::demo 可以使用 `invert` 属性来决定是否使用深色模式,它接受一个Boolean值。
46 |
47 | ```html
48 |
49 | 默认按钮
50 | 主要按钮
51 | 幽灵按钮
52 |
53 | ```
54 | :::
55 | ### loading状态
56 |
57 | 加载中状态。
58 |
59 | :::demo 可以使用 `loading` 属性来定义按钮是否是正在加载状态,它接受一个`Boolean`值。可以通过传递`loadingText`来直接显示 也可以自定义slot(不带loading图标)
60 |
61 | ```html
62 | Tap to loading
63 | Tap to loading
64 | Tap to loading
65 | Tap to loading
66 |
67 |
68 | {{scope.loading ? 'loading...' : 'Tap to loading'}}
69 |
70 |
71 | ```
72 | :::
73 |
74 | ### 禁用状态
75 |
76 | 按钮不可用状态。
77 |
78 | :::demo 可以使用 `disabled` 属性来定义按钮是否可用,它接受一个Boolean值。
79 |
80 | ```html
81 | 默认按钮
82 | 主要按钮
83 | 幽灵按钮
84 | ```
85 | :::
86 |
87 | ### 属性
88 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
89 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
90 | | type | 类型 | string | success/ghost/default | default |
91 | | size | 大小 | string | small/large | small |
92 | | invert | 深色模式 | boolean | — | false |
93 | | disabled | 是否禁用 | boolean | — | false |
94 | | radius | 是否启用大圆角模式 | boolean | — | true |
95 | | loading | 是否loading | boolean | — | false |
96 | | loadingText | 加载中显示的文案 | string | — | |
97 |
--------------------------------------------------------------------------------
/docs/examples/carousel.md:
--------------------------------------------------------------------------------
1 |
31 |
32 | ## Carousel 走马灯
33 |
34 | 在有限空间内,循环播放同一类型的图片、文字等内容
35 |
36 | ### 基础用法
37 |
38 | 适用广泛的基础用法。
39 |
40 | :::demo Click 指示器触发
41 |
42 | ```html
43 |
44 | ```
45 | :::
46 |
47 | ### 属性
48 |
49 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
50 | | :-------------- | :-------------- | :---------- | :--------------------------- | :-------- |
51 | | delay | 轮播切换时间 | number | - | 3000 |
52 | | list | 轮播图片信息,默认值为,image地址,url地址,title名组成的数组对象 | array | - | [{image: '', url: '', title: ''}] |
53 |
--------------------------------------------------------------------------------
/docs/examples/checkbox.md:
--------------------------------------------------------------------------------
1 |
13 | ## Checkbox 多选框
14 | 一组备选项中进行多选。
15 |
16 | ### 基础用法
17 |
18 |
19 | :::demo 基础的多选框用法。
20 |
21 | ```html
22 | 复选框
23 | 复选框
24 | 复选框
25 |
26 | 复选框
27 | 复选框
28 | 复选框
29 |
30 |
39 | ```
40 | :::
41 |
42 | ### 禁用状态
43 | 多选框的不可用状态
44 |
45 | :::demo 设置 `disabled` 属性即可。
46 |
47 | ```html
48 | 不可用
49 | 选中不可用
50 |
51 |
62 | ```
63 | :::
64 |
65 | ### 尺寸
66 | 指定多选框的大小
67 |
68 | ::: demo 可通过 `size` 属性指定输入框的尺寸,默认大小是 `small`
69 | ```html
70 |
71 |
72 |
73 | ```
74 | :::
75 |
76 | ### 属性
77 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
78 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
79 | | value | 绑定值 | string/number | - | - |
80 | | size | 输入框尺寸 | string | large/medium/small | small |
81 | | disabled | 是否禁用 | boolean | — | false |
82 | | name | 原生 name 属性 | string | — | - |
83 | | label | 选中状态的值 | string/boolean | — | - |
84 |
85 | ### 事件
86 | | 事件名称 | 说明 | 回调参数 |
87 | | :---------- | :-------------- | :---------- |
88 | | change | 当绑定值变化时触发的事件 | (value: string \| number, event: Event) |
89 |
--------------------------------------------------------------------------------
/docs/examples/collapse.md:
--------------------------------------------------------------------------------
1 |
36 |
45 | ## Collapse 折叠面板
46 | 通过折叠面板收纳内容区域。
47 |
48 | ### 基础用法
49 | 同时只能展开一个面板。具体见个人中心-个人设置页面。
50 |
51 | :::demo 同时只能展开一个面板。
52 |
53 | ```html
54 |
55 |
56 | 个人设置-上传头像
57 |
58 |
59 | 个人设置-基本资料
60 |
61 |
62 | ```
63 | :::
64 |
65 | 也可以按照个人中心-交易设置页面处理:
66 |
67 | :::demo 可以设置简要说明、右侧区域的展开/收缩文本。
68 |
69 | ```html
70 |
71 |
72 |
73 |
交易账号
74 |
展开/收缩文本需要以空格分开
75 |
76 |
77 |
78 |
79 |
快速下单
80 |
info 用于设置简要说明
81 |
82 |
83 |
84 |
85 |
快速平仓
86 |
设置右侧 icon:icon 的类以空格分开
87 |
类名包含 hover 的是鼠标 hover 上去添加的类,包含 active 的是面板展开时添加的类,带 hover & active 的是展开时鼠标 hover 时添加的类
88 |
89 |
90 |
91 | ```
92 | :::
93 |
94 | ### 手风琴效果
95 | 展开多个面板。
96 |
97 | :::demo 设置 `accordion` 为 true,可实现手风琴效果。
98 |
99 | ```html
100 |
101 |
102 | 个人设置-上传头像
103 |
104 |
105 | 个人设置-基本资料
106 |
107 |
108 | ```
109 | :::
110 |
111 | ### 默认展开面板
112 | :::demo 方法调用: `this.$refs.list.showCollapseItem()`。
113 |
114 | ```html
115 |
116 |
117 | 默认展开的面板
118 |
119 |
120 | ```
121 | :::
122 |
123 | ### Collapse 属性
124 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
125 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
126 | | accordion | 是否启用手风琴效果 | boolean | - | false |
127 |
128 | ### Collapse Item 属性
129 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
130 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
131 | | title | 面板标题 | string | - | '' |
132 | | info | 标题辅助说明 | string | - | '' |
133 | | act-text | 空格分开的文本 | string | - | '' |
134 | | icon-classes | icon 的类名,空格分开 | string | - | '' |
--------------------------------------------------------------------------------
/docs/examples/dialog.md:
--------------------------------------------------------------------------------
1 |
30 | ## Dialog 对话框
31 | 在保留当前页面状态的情况下,告知用户并承载相关操作。
32 |
33 | ### 基本用法
34 |
35 | Dialog 弹出一个对话框,适合需要定制性更大的场景。
36 |
37 | :::demo Dialog 以 `$fmdialog` 属性挂载在 Vue Components 上, 可以在组件内通过 `this.$fmdialog` 的方式直接调用。
38 |
39 | ```html
40 | 不带Icon的Dialog
41 | 成功
42 | 信息
43 | 警告
44 | 错误
45 |
46 |
58 | ```
59 | :::
60 |
61 | 对话框弹出默认是没有后层遮罩的, 如果需要遮罩, 可以设置 `mask` 属性.
62 |
63 | :::demo `mask` 属性是一个 `Boolean` 值, 默认是 `false`.
64 |
65 | ```html
66 | 显示Mask
67 |
68 |
81 | ```
82 | :::
83 |
84 | 对于对话框需要和用户进行交互的, 不需要指定 `type` 值.
85 |
86 | :::demo `isSingle` 属性是一个 `Boolean` 值, 默认是 `false`, 表示是否只显示确认按钮. 显示带交互行为的对话框时, 需要传入 `onConfirm` 回调.
87 |
88 | ```html
89 | 两个按钮
90 | 一个按钮
91 |
92 |
105 | ```
106 | :::
107 |
108 | ### 属性
109 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
110 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
111 | | mask | 是否显示遮罩 | boolean | - | false |
112 | | classes | 用于 Dialog 的类名 | string | - | '' |
113 | | title | Dialog 的标题 | string | — | '请确认' |
114 | | type | 类型 | string | success/failure | '' |
115 | | duration | 自动关闭的时间 | number | - | 1500 |
116 | | message | 消息体 | string | - | '' |
117 | | cancelBtnText | 取消按钮的文本 | string | - | '取消' |
118 | | confirmBtnText | 确认按钮的文本 | string | - | '确认' |
119 | | isSingle | 是否显示取消按钮 | boolean | - | false |
120 | | onCancel | 点击取消按钮时的回调 | function | - | - |
121 | | onConfirm | 点击确认按钮时的回调 | function | - | - |
122 |
--------------------------------------------------------------------------------
/docs/examples/input.md:
--------------------------------------------------------------------------------
1 |
17 |
22 | ## Input 输入框
23 | 通过鼠标或键盘输入字符
24 |
25 | ### 基础用法
26 |
27 | ::: demo 基础的输入框用法。
28 | ```html
29 |
30 |
39 | ```
40 | :::
41 |
42 | ### 禁用状态
43 | ::: demo 通过 `disabled` 属性指定是否禁用 input 组件
44 | ```html
45 |
46 | ```
47 | :::
48 |
49 | ### 尺寸
50 | ::: demo 可通过 `size` 属性指定输入框的尺寸,默认大小是 `medium`
51 | ```html
52 |
53 |
54 |
55 |
56 | ```
57 | :::
58 |
59 | ### 可清空
60 |
61 | ::: demo 可通过 `clearable` 属性得到一个可清空的输入框
62 | ```html
63 |
64 |
73 | ```
74 | :::
75 |
76 | ### 复合型输入框
77 | 可前置或后置元素,一般为标签或按钮
78 |
79 | :::demo 可通过 slot 来指定在 input 中前置或者后置内容
80 | ```html
81 |
82 |
83 | Http://
84 |
85 |
86 |
87 |
88 | .com
89 |
90 |
91 |
92 |
93 | Http://
94 | .com
95 |
96 |
97 | ```
98 | :::
99 |
100 | ### 属性
101 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
102 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
103 | | value | 绑定值 | string/number | - | - |
104 | | size | 输入框尺寸 | string | large/medium/small/mini | medium |
105 | | disabled | 是否禁用 | boolean | — | false |
106 | | placeholder | 输入框占位文本 | string | — | 请输入/Please input |
107 | | clearable | 是否可清空 | boolean | — | false |
108 |
109 | ### Slots
110 | | name | 说明 |
111 | | :---------- | :-------------- |
112 | | prepend | 输入框前置内容 |
113 | | append | 输入框后置内容 |
114 |
115 | ### 事件
116 | | 事件名称 | 说明 | 回调参数 |
117 | | :---------- | :-------------- | :---------- |
118 | | blur | 在 Input 失去焦点时触发 | (event: Event) |
119 | | focus | 在 Input 获得焦点时触发 | (event: Event) |
120 | | change | 在 Input 值改变时触发 | (value: string \| number) |
121 |
122 |
--------------------------------------------------------------------------------
/docs/examples/loading.md:
--------------------------------------------------------------------------------
1 |
8 | ## Loading 加载
9 | 加载数据时显示动效。
10 |
11 | ### 区域加载
12 | 在容器元素中加载数据时显示。
13 |
14 | :::demo 容器元素需要的 `postion` 属性为非 `static`。
15 |
16 | ```html
17 |
18 |
19 |
20 | ```
21 | :::
22 |
23 | ### 自定义
24 | 可自定义加载文案。
25 |
26 | :::demo 自定义加载文案。
27 |
28 | ```html
29 |
30 |
31 | 加载中加载中...
32 |
33 |
34 | ```
35 | :::
36 |
37 | ### 属性
38 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
39 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
40 | | shown | 是否显示 loading | boolean | - | false |
41 |
--------------------------------------------------------------------------------
/docs/examples/notification.md:
--------------------------------------------------------------------------------
1 |
26 | ## Notification 对话框
27 | 从网页顶部弹出一个提示框。
28 |
29 | ### 基本用法
30 |
31 | Notification 弹出一个提示框,适合提醒用户任务操作结果等。
32 |
33 | :::demo Notification 以 `$fmtoast` 属性挂载在 Vue Components 上, 可以在组件内通过 `this.$fmtoast` 的方式直接调用。
34 |
35 | ```html
36 | 成功
37 | 信息
38 | 警告
39 | 错误
40 |
41 |
54 | ```
55 | :::
56 |
57 | 可以指定关闭事件 也可以通过传递 `duration: 0` 使得只能用户主动点击才关闭
58 |
59 | :::demo
60 |
61 | ```html
62 | 不自动关闭
63 |
64 |
77 | ```
78 | :::
79 |
80 | ### 属性
81 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
82 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
83 | | classes | 用于 Notification 的类名 | string | - | '' |
84 | | type | 类型 | string | success/info/warning/error | '' |
85 | | title | 标题 | string | string | '' |
86 | | duration | 自动关闭的时间 设置为 0 则点击关闭 | number | - | `3000` |
87 | | message | 消息体 | string | - | '' |
88 | | onClose | 点击关闭的事件 | function | - | - |
89 |
--------------------------------------------------------------------------------
/docs/examples/radio.md:
--------------------------------------------------------------------------------
1 |
12 | ## Radio 单选框
13 | 在一组备选项中进行单选。
14 |
15 | ### 基础用法
16 |
17 | :::demo 基础的单选框用法,`v-model` 绑定的是 `label` 属性对应的值。
18 |
19 | ```html
20 | 单选框
21 | 单选框
22 |
23 |
32 | ```
33 | :::
34 |
35 | ### 禁用状态
36 | 单选框的不可用状态
37 |
38 | :::demo 设置 `disabled` 属性即可。
39 |
40 | ```html
41 | 不可用
42 | 选中不可用
43 |
44 |
53 | ```
54 | :::
55 |
56 | ### 尺寸
57 | 指定单选框的大小
58 |
59 | ::: demo 可通过 `size` 属性指定输入框的尺寸,默认大小是 `small`
60 | ```html
61 |
62 |
63 |
64 | ```
65 | :::
66 |
67 | ### 属性
68 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
69 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
70 | | value | 绑定值 | string/number | - | - |
71 | | size | 输入框尺寸 | string | large/medium/small | small |
72 | | disabled | 是否禁用 | boolean | — | false |
73 | | name | 原生 name 属性 | string | — | - |
74 | | label | 选中状态的值 | string/boolean | — | - |
75 |
76 | ### 事件
77 | | 事件名称 | 说明 | 回调参数 |
78 | | :---------- | :-------------- | :---------- |
79 | | change | 当绑定值变化时触发的事件 | (value: string \| number, event: Event) |
--------------------------------------------------------------------------------
/docs/examples/rate.md:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 | ## Rate 评分
20 |
21 | 常用评分组件
22 |
23 | ### 基本用法
24 |
25 | 可点击,进行选择评分。通过传入`colors`可改变选择不同分值时的颜色效果,不传入,则默认不改颜色。
26 |
27 | :::demo
28 |
29 | ```html
30 |
31 |
32 |
33 |
47 | ```
48 | :::
49 |
50 | ### 显示分值
51 |
52 | 可选择显示评分。通过传入`score-text`可改变选择不同类型分值,不传入,则默认为`2, 4, 6, 8, 10`。
53 |
54 | :::demo
55 |
56 | ```html
57 |
58 |
59 |
60 |
74 | ```
75 | :::
76 |
77 | ### 只读
78 |
79 | 显示评分,不可点击,可选择是否显示分值。
80 |
81 | :::demo `disabled`设置不可点击,`show-text`设置显示分值。
82 |
83 | ```html
84 |
85 |
86 |
87 |
97 | ```
98 | :::
99 |
100 | ### 属性
101 |
102 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
103 | | :-------------- | :-------------- | :---------- | :--------------------------- | :-------- |
104 | | max | 最大分值 | number | - | 5 |
105 | | show-text | 是否显示分值 | boolean | - | false |
106 | | score-text | 分值显示类型数组 | array | - | ['2 分', '4 分', '6 分', '8 分', '10 分'] |
107 | | text-color | 分值文字颜色 | string | - | '#ffbe58' |
108 | | colors | icon颜色数组,共有 3 个元素,为 3 个分段所对应的颜色 | array | - | ['ffbe58', 'ffbe58', 'ffbe58'] |
109 | | disabled | 是否为只读 | boolean | - | false |
110 |
--------------------------------------------------------------------------------
/docs/examples/rbg.md:
--------------------------------------------------------------------------------
1 |
17 |
18 | ## RadioButtonGroup 按钮组
19 |
20 | ### 基础用法
21 |
22 | ::: demo 基本用法
23 | ```html
24 |
25 |
36 | ```
37 | :::
38 |
39 | ### 属性
40 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
41 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
42 | | value | 绑定值 | - | - | - |
43 | | data-source | 用于渲染按钮组 | array | - | [] |
44 | | button-width | 按钮最小宽度 | number | — | 50 |
45 |
46 |
47 | ### 事件
48 | | 事件名称 | 说明 | 回调参数 |
49 | | :---------- | :-------------- | :---------- |
50 | | change | 选项改变时触发 | 选择的 value |
51 |
--------------------------------------------------------------------------------
/docs/examples/select.md:
--------------------------------------------------------------------------------
1 |
22 | ## Select 选择器
23 | 当选项过多时,使用下拉菜单展示并选择内容。
24 |
25 | ### 基础用法
26 | 适用广泛的基础单选
27 |
28 | ::: demo `v-model` 的值为当前被选中的 `fm-option` 的 `value` 属性值
29 | ```html
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
53 | ```
54 | :::
55 |
56 | ### 禁用状态
57 | ::: demo 通过 `disabled` 属性指定是否禁用 select 组件
58 | ```html
59 |
60 |
61 |
62 | ```
63 | :::
64 |
65 | ### Select 属性
66 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
67 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
68 | | value | 绑定值 | string/number/boolean | - | - |
69 | | disabled | 是否禁用 | boolean | — | false |
70 | | placeholder | 输入框占位文本 | string | — | 请选择/Please select |
71 | | emptyText | 无数据时显示的文本 | string | — | 暂无数据/No Data |
72 |
73 | ### Option 属性
74 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
75 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
76 | | value | 选项的值, 若该值为假则与 `label` 相同 | string/number/boolean | - | - |
77 | | label | 选项的标签,若不设置则默认与 `value` 相同 | string/number | — | - |
78 |
79 | ### 事件
80 | | 事件名称 | 说明 | 回调参数 |
81 | | :---------- | :-------------- | :---------- |
82 | | change | 选值改变时触发 | (value: string \| number \| boolean) |
--------------------------------------------------------------------------------
/docs/examples/switch.md:
--------------------------------------------------------------------------------
1 |
2 |
14 | ## Switch 开关
15 | 表示两种相互对立的状态间的切换,多用于触发「开/关」。
16 |
17 | ### 基础用法
18 | 基础的开关用法
19 |
20 | :::demo `v-model` 绑定的值是一个 `Boolean` 值。
21 |
22 | ```html
23 |
24 |
25 |
26 |
36 | ```
37 | :::
38 |
39 | ### 禁用状态
40 | 开关的不可用状态
41 |
42 | :::demo 设置 `disabled` 属性即可。
43 |
44 | ```html
45 |
46 |
47 |
48 |
58 | ```
59 | :::
60 |
61 | ### 属性
62 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
63 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
64 | | value | 绑定值 | boolean | - | false |
65 | | disabled | 是否禁用 | boolean | — | false |
66 |
67 | ### 事件
68 | | 事件名称 | 说明 | 回调参数 |
69 | | :---------- | :-------------- | :---------- |
70 | | change | 当绑定值变化时触发的事件 | (event: Event) |
--------------------------------------------------------------------------------
/docs/examples/toast.md:
--------------------------------------------------------------------------------
1 |
23 | ## Toast 对话框
24 | 从网页顶部弹出一个提示框。
25 |
26 | ### 基本用法
27 |
28 | Toast 弹出一个提示框,适合提醒用户任务操作结果等。
29 |
30 | :::demo Toast 以 `$fmtoast` 属性挂载在 Vue Components 上, 可以在组件内通过 `this.$fmtoast` 的方式直接调用。
31 |
32 | ```html
33 | 成功
34 | 信息
35 | 警告
36 | 错误
37 |
38 |
51 | ```
52 | :::
53 |
54 | 可以指定关闭事件 也可以通过传递 `duration: 0` 使得只能用户主动点击才关闭
55 |
56 | :::demo
57 |
58 | ```html
59 | 不自动关闭
60 |
61 |
74 | ```
75 | :::
76 |
77 | ### 属性
78 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
79 | | :---------- | :-------------- | :---------- | :-------------------------------- | :-------- |
80 | | classes | 用于 Toast 的类名 | string | - | '' |
81 | | type | 类型 | string | success/info/warning/error | '' |
82 | | duration | 自动关闭的时间 设置为 0 则点击关闭 | number | - | `3000` |
83 | | message | 消息体 | string | - | '' |
84 | | onClose | 点击关闭的事件 | function | - | - |
85 |
--------------------------------------------------------------------------------
/docs/examples/util.md:
--------------------------------------------------------------------------------
1 |
22 |
23 | ## Utils 工具库
24 | 常用的工具函数。
25 |
26 | ### 基本使用
27 | 在 `main.js` 中写入以下内容:
28 |
29 | ```html
30 | import Vue from 'vue'
31 | import App from './App.vue'
32 |
33 | // 按需引入时需要手动引入 utils,全量引入会挂载在 $fmutils
34 | import fmutils from fm-vue-ui/lib/utils/index';
35 | Vue.prototype.$fmutils = fmutils;
36 |
37 | new Vue({
38 | el: '#app',
39 | render: h => h(App)
40 | })
41 | ```
42 |
43 | `fmutils` 目前提供的 API 列表如下:
44 |
45 | ```js
46 | export default {
47 | formatDateToStr, // 时间格式化
48 | avatarError, // 设置图像加载失败时的默认图像
49 | // 下面三个是对 Storage 的封装, 均提供 set/get/remove/clear/getAll 五个 api
50 | localStorage, // ==> window.localStorage
51 | sessionStorage, // ==> window.sessionStorage
52 | memoryStorage // ==> 存在内存中的一个 map 对象
53 | };
54 | ```
55 |
56 | ### 默认图像
57 | :::demo 用于图像加载失败时显示统一的默认图像。默认图像是 Base64 格式的。也可以指定一个 url。
58 |
59 | ```html
60 |
61 |
62 |
63 |
76 | ```
77 | :::
78 |
79 | ### 时间格式化输出
80 | :::demo 接受一个 Date 实例和格式输出。默认的输出格式是 `yyyy-MM-dd`。
81 |
82 | ```html
83 | 默认的当前时间格式:
84 | 默认输出的格式化时间:{{formatDate()}}
85 | 指定格式输出:{{formatDate('yyyy/MM/dd hh:mm:ss')}}
86 | 只输出时间:{{formatDate('hh:mm:ss')}}
87 | (只有一个 M/m/h/m/s等时)不加0:{{formatDate('hh:mm:s')}}
88 |
89 |
102 | ```
103 | :::
104 |
105 | ### Storage 操作
106 |
107 | `localStorage` 和 `sessionStorage` 仅是对 `window.localStorage` 和 `window.sessionStorage` 的一个封装:
108 |
109 | ```js
110 | // 写入的 key 均会有私有前缀 _fm_
111 | this.$fmutils.localStorage.set('name', 'test'); // 写
112 | this.$fmutils.localStorage.get('name'); // 读 => 'test'
113 | this.$fmutils.localStorage.set('name2', 'test2');
114 | this.$fmutils.localStorage.getAll(); // 读取所有 => {name: 'test', name2: 'test2'}
115 | this.$fmutils.localStorage.remove('name2'); // 清除指定的 key
116 | this.$fmutils.localStorage.clear(); // 清除所有 key
117 | ```
118 |
119 | **需要注意的是 `memoryStorage` 只是内存中的一个对象,可用于临时存储应用数据,但不可用于持久化数据。**
120 |
--------------------------------------------------------------------------------
/docs/i18n/en-US.json:
--------------------------------------------------------------------------------
1 | {
2 | "test": "I18n Test"
3 | }
--------------------------------------------------------------------------------
/docs/i18n/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueI18n from 'vue-i18n';
3 | import en from './en-US.json';
4 | import cn from './zh-CN.json';
5 |
6 | Vue.use(VueI18n);
7 |
8 | export default new VueI18n({
9 | locale: 'zh-CN',
10 | fallbackLocale: 'zh-CN',
11 | messages: {
12 | 'en-US': en,
13 | 'zh-CN': cn
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/docs/i18n/zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "test": "国际化测试"
3 | }
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | FM Basic Vue Components
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/docs/logo.png
--------------------------------------------------------------------------------
/docs/nav.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "zh-CN": [
3 | {
4 | "name": "guide",
5 | "title": "开发指南",
6 | "path": "/index"
7 | },
8 | {
9 | "name": "log",
10 | "title": "更新日志",
11 | "path": "/changelog"
12 | },
13 | {
14 | "name": "i18n",
15 | "title": "国际化",
16 | "path": "/i18n"
17 | },
18 | {
19 | "title": "代码规范",
20 | "href": "https://github.com/fmfe/fe-coding-style-guide/"
21 | },
22 | {
23 | "title": "Table 组件",
24 | "href": "https://github.com/dwqs/v2-table"
25 | },
26 | {
27 | "title": "Datepicker 组件",
28 | "href": "https://github.com/dwqs/v2-datepicker"
29 | },
30 | {
31 | "title": "LazyList 组件",
32 | "href": "https://github.com/dwqs/v2-lazy-list"
33 | },
34 | {
35 | "title": "Scrollbar 组件",
36 | "href": "https://github.com/dwqs/beautify-scrollbar"
37 | },
38 | {
39 | "title": "基础组件",
40 | "children": [
41 | {
42 | "title": "Utils 工具库",
43 | "name": "util",
44 | "path": "/utils"
45 | },
46 | {
47 | "title": "Button 按钮",
48 | "name": "button",
49 | "path": "/button"
50 | },
51 | {
52 | "title": "Toast 提醒",
53 | "name": "toast",
54 | "path": "/toast"
55 | },
56 | {
57 | "title": "Notification 通知",
58 | "name": "notification",
59 | "path": "/notification"
60 | },
61 | {
62 | "title": "Dialog 对话框",
63 | "name": "dialog",
64 | "path": "/dialog"
65 | },
66 | {
67 | "title": "Loading 加载",
68 | "name": "loading",
69 | "path": "/loading"
70 | },
71 | {
72 | "title": "Collapse 折叠面板",
73 | "name": "collapse",
74 | "path": "/collapse"
75 | },
76 | {
77 | "title": "Tabs 标签页",
78 | "name": "tabs",
79 | "path": "/tabs"
80 | },
81 | {
82 | "title": "Rate 评分",
83 | "name": "rate",
84 | "path": "/rate"
85 | },
86 | {
87 | "title": "Carousel 走马灯",
88 | "name": "carousel",
89 | "path": "/carousel"
90 | },
91 | {
92 | "title": "Input 输入框",
93 | "name": "input",
94 | "path": "/input"
95 | },
96 | {
97 | "title": "Checkbox 多选框",
98 | "name": "checkbox",
99 | "path": "/checkbox"
100 | },
101 | {
102 | "title": "Radio 单选框",
103 | "name": "radio",
104 | "path": "/radio"
105 | },
106 | {
107 | "title": "Switch 开关",
108 | "name": "switch",
109 | "path": "/switch"
110 | },
111 | {
112 | "title": "Select 选择器",
113 | "name": "select",
114 | "path": "/select"
115 | },
116 | {
117 | "title": "RBG 按钮组",
118 | "name": "rbg",
119 | "path": "/rbg"
120 | }
121 | ]
122 | }
123 | ]
124 | }
125 |
--------------------------------------------------------------------------------
/docs/pages/change-log.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
更新日志
4 |
5 | -
6 |
2.0.1
7 | 2018/04/11
8 |
9 | - select组件支持 emptyText 属性
10 | - fix select组件的 offsetTop 计算
11 |
12 |
13 | -
14 |
2.0.0
15 | 2018/04/11
16 |
17 | - 添加 RadioButtonGroup 组件
18 | - 优化 select/tabs 等组件
19 | - 优化国际化处理
20 | - 一些 BugFix
21 | - 构建优化
22 |
23 |
24 | -
25 |
1.0.0
26 | 2017/12/27
27 |
28 | - 添加 Select/Switch/Input/Checkbox/Radio 等基本表单组件
29 | - 添加 Rate/Carousel 等组件
30 |
31 |
32 | -
33 |
0.2.0
34 | 2017/12/06
35 |
36 | - 添加公共库工具
37 | - 添加 Loading/Collapse/Tabs 组件
38 |
39 |
40 | -
41 |
0.1.0
42 | 2017/12/02
43 |
44 | - 样式集中管理,便于主题式开发
45 | - 更换包名为 fm-vue-ui,以支持样式的按需加载
46 | - 更改国际化支持方式
47 |
48 |
49 | -
50 |
0.0.5
51 | 2017/12/02
52 |
53 | - 支持国际化配置
54 | - 添加组件的单元测试示例
55 |
56 |
57 | -
58 |
0.0.2
59 | 2017/11/27
60 |
61 | - 搭建组件库的开发框架和流程规范
62 | - 搭建组件库的文档以及示例说明
63 | - 提供 Button/Dialog 两个基础组件
64 |
65 |
66 |
67 |
68 |
69 |
70 |
75 |
76 |
--------------------------------------------------------------------------------
/docs/pages/guide.md:
--------------------------------------------------------------------------------
1 | ## 安装
2 | ### npm 安装
3 | ```
4 | npm i fm-vue-ui -S
5 | ```
6 | ### 引入 FM-ICON (Toast Dialog Alert 会用到字体图标 需要用到以上组件请务必引入)
7 | ```html
8 |
9 | ```
10 |
11 | ### 引入 FMUI
12 | 可以引入整个 FMUI,或是根据需要仅引入部分组件。
13 |
14 | #### 完整引入
15 |
16 | 在 `main.js` 中写入以下内容:
17 |
18 | ```html
19 | import Vue from 'vue'
20 | import FMUI from 'fm-vue-ui'
21 | import 'fm-vue-ui/lib/theme-default/index.css'
22 | import App from './App.vue'
23 |
24 | Vue.use(FMUI)
25 |
26 | new Vue({
27 | el: '#app',
28 | render: h => h(App)
29 | })
30 | ```
31 |
32 | #### 按需引入
33 | 借助 [babel-plugin-component](https://github.com/QingWei-Li/babel-plugin-component) 或者 [babel-plugin-on-demand-import](https://github.com/dwqs/babel-plugin-on-demand-import), 我们可以只引入需要的组件,以达到减小项目体积的目的。
34 |
35 |
36 |
37 | babel-plugin-component配置:
38 |
39 | ```js
40 | // .babelrc
41 | // ....
42 | "plugins": [["component", [
43 | {
44 | "libraryName": "fm-vue-ui",
45 | "styleLibraryName": "theme-default"
46 | }
47 | ]]]
48 | // ...
49 | ```
50 |
51 | babel-plugin-on-demand-import配置:
52 |
53 | ```js
54 | // .babelrc
55 | // ....
56 | "plugins": [[
57 | "on-demand-import", {
58 | "libraryName": "fm-vue-ui",
59 | "libraryPath": "lib",
60 | "stylePath": "lib/theme-default",
61 | "needImportStyle": true
62 | }
63 | ]]
64 | // ...
65 | ```
66 |
67 | 接下来,如果你只希望引入部分组件,比如 `Button`,那么需要在 `main.js` 中写入以下内容:
68 |
69 | ```html
70 | import Vue from 'vue'
71 | import { Button } from 'fm-vue-ui'
72 | import App from './App.vue'
73 |
74 | Vue.component(Button.name, Button)
75 | /* 或写为
76 | * Vue.use(Button)
77 | */
78 |
79 | new Vue({
80 | el: '#app',
81 | render: h => h(App)
82 | })
83 | ```
84 |
85 | 组件列表以 [components.json](https://github.com/fmfe/fm-vue-ui/blob/master/components.json) 中列出的为准。
86 |
87 | ### UI 层级规范
88 | 1. 常规元素的 `z-index` 的范围是 [0, 100]
89 | 2. 顶部导航、侧边导航等元素的 `z-index` 的范围是 (100, 1000]
90 | 3. 遮罩层的 `z-index` 的范围是 (1000, 10000]
91 | 4. 弹框、弹层以及toast等元素的 `z-index` 的范围是 (10000, 100000]
92 |
93 | ## 贡献指南
94 |
95 | 1. 可以在 [fm-vue-ui](https://github.com/fmfe/fm-vue-ui/issues) 以 issue 的形式说明你的需求
96 | 2. fork [fm-vue-ui](https://github.com/fmfe/fm-vue-ui), 然后开发自己的组件,写好对应的文档说明和单测,提交 PR
97 | 3. 代码规范请参考 [coding style](https://github.com/fmfe/fe-coding-style-guide/)
98 | 4. 对于组件中涉及的图标,请优先使用 CSS 来实现; 如果实现不了, 请将对应的图片资源放在 CDN 上
99 | ## 开发步骤
100 |
101 | 1. 在 `components.json` 文件中添加对应组件的映射(组件名和组件路径)
102 | 2. 在 `components` 目录下按格式建立自己的组件目录,对应的样式在 `theme-default/src` 目录下,文件名和映射的组件名保持一致
103 | 3. 执行 `npm run com`
104 |
--------------------------------------------------------------------------------
/docs/pages/i18n.md:
--------------------------------------------------------------------------------
1 | ## 国际化
2 | `fm-vue-ui` 目前仅支持两种语言: `zh-CN`、`en-US` 和 `zh-HK`,默认使用 `zh-CN`。
3 |
4 | >我们所有的项目都是需要支持国际化的
5 |
6 | 如果需要设置其它语言,配置示例如下:
7 |
8 | ```html
9 | // 项目的 i18n 配置 src/i18n/index.js
10 | import Vue from 'vue';
11 | import VueI18n from 'vue-i18n'; // 7.x
12 | import en from './en-US';
13 | import cn from './zh-CN';
14 |
15 | Vue.use(VueI18n);
16 |
17 | export default new VueI18n({
18 | locale: 'zh-CN',
19 | messages: {
20 | 'en-US': en,
21 | 'zh-CN': cn
22 | }
23 | });
24 |
25 | // page/index.js
26 | import Vue from 'vue';
27 | import FMUI from 'fm-vue-ui';
28 |
29 | import i18n from './i18n/index';
30 |
31 | Vue.use(FMUI, {
32 | lang: i18n.locale
33 | });
34 | ```
35 |
36 | 如果是按需引入:
37 |
38 | ```html
39 | // 按需引入
40 | import Vue from 'vue'
41 | import { Button } from 'fm-vue-ui'
42 | import locale from 'fm-vue-ui/lib/locale/index';
43 |
44 | locale.use(i18n.locale);
45 | Vue.use(Button); // 然后安装组件
46 | ```
47 |
48 | 组件内的开发需要考虑国际化文案时,使用案例请参考 `Dialog`。
--------------------------------------------------------------------------------
/docs/routes.config.js:
--------------------------------------------------------------------------------
1 | import navConfig from './nav.config.json';
2 |
3 | const LANG = 'zh-CN';
4 | const routes = [];
5 |
6 | function loadMD (name) {
7 | return resolve => import(`./examples/${name}.md`).then(component => resolve(component || component.default));
8 | // return r => require.ensure([], () => r(require(`./examples/${name}.md`)), 'examples');
9 | };
10 |
11 | function loadPages (name) {
12 | return resolve => import(`./pages/${name}.md`).then(component => resolve(component || component.default));
13 | };
14 |
15 | import ChangeLog from './pages/change-log.vue';
16 |
17 | function regiterRoute (navConfig) {
18 | const navs = navConfig[LANG];
19 |
20 | navs.forEach(nav => {
21 | if (!nav.href) {
22 | if (nav.children) {
23 | nav.children.forEach(child => {
24 | routes.push({
25 | path: child.path,
26 | name: child.name,
27 | component: loadMD(child.name)
28 | });
29 | });
30 | } else if (nav.path === '/changelog') {
31 | routes.push({
32 | path: nav.path,
33 | name: nav.name,
34 | component: ChangeLog
35 | });
36 | } else {
37 | routes.push({
38 | path: nav.path,
39 | name: nav.name,
40 | component: loadPages(nav.name)
41 | });
42 | }
43 | }
44 | });
45 | };
46 |
47 | regiterRoute(navConfig);
48 |
49 | export default routes;
50 |
--------------------------------------------------------------------------------
/docs/tpl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | FM Basic Vue Components
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | // fix: https://github.com/akveo/ng2-admin/issues/604
2 | // 使用 happypack 之后 需单独提供 postcss 配置文件
3 | module.exports = {
4 | plugins: [
5 | require('autoprefixer')({ browsers: ['last 5 versions', 'Android >= 4.0', 'iOS >= 7'] })
6 | ]
7 | };
8 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* Automatic generated by './build/build-entry.js' */
2 |
3 | import utils from 'fm-vue-ui/src/utils';
4 | import locale from 'fm-vue-ui/src/locale';
5 |
6 | import Button from '../components/button/index.js';
7 | import Dialog from '../components/dialog/index.js';
8 | import Toast from '../components/toast/index.js';
9 | import Notification from '../components/notification/index.js';
10 | import Loading from '../components/loading/index.js';
11 | import Collapse from '../components/collapse/index.js';
12 | import CollapseItem from '../components/collapse-item/index.js';
13 | import Tabs from '../components/tabs/index.js';
14 | import TabPanel from '../components/tab-panel/index.js';
15 | import Rate from '../components/rate/index.js';
16 | import Carousel from '../components/carousel/index.js';
17 | import Input from '../components/input/index.js';
18 | import Checkbox from '../components/checkbox/index.js';
19 | import Radio from '../components/radio/index.js';
20 | import Switch from '../components/switch/index.js';
21 | import Option from '../components/option/index.js';
22 | import Select from '../components/select/index.js';
23 | import RadioButtonGroup from '../components/radio-button-group/index.js';
24 |
25 | const components = [
26 | Button,
27 | Toast,
28 | Notification,
29 | Loading,
30 | Collapse,
31 | CollapseItem,
32 | Tabs,
33 | TabPanel,
34 | Rate,
35 | Carousel,
36 | Input,
37 | Checkbox,
38 | Radio,
39 | Switch,
40 | Option,
41 | Select,
42 | RadioButtonGroup
43 | ];
44 |
45 | const install = function (Vue, opts = {}) {
46 | if (install.installed) return;
47 |
48 | let lang = 'zh-CN';
49 | try {
50 | lang = opts.lang || (window.FMlocale ? window.FMlocale() : 'zh-CN');
51 | } catch (e) {}
52 | locale.use(lang);
53 |
54 | components.map(component => {
55 | Vue.component(component.name, component);
56 | });
57 |
58 | Vue.prototype.$fmdialog = Dialog;
59 | Vue.prototype.$fmtoast = Toast;
60 | Vue.prototype.$fmutils = utils;
61 | };
62 |
63 | if (typeof window !== 'undefined' && window.Vue) {
64 | install(window.Vue);
65 | };
66 |
67 | export {
68 | Button,
69 | Dialog,
70 | Toast,
71 | Notification,
72 | Loading,
73 | Collapse,
74 | CollapseItem,
75 | Tabs,
76 | TabPanel,
77 | Rate,
78 | Carousel,
79 | Input,
80 | Checkbox,
81 | Radio,
82 | Switch,
83 | Option,
84 | Select,
85 | RadioButtonGroup
86 | };
87 |
88 | export default {
89 | version: '2.1.6',
90 | install,
91 | Button,
92 | Dialog,
93 | Toast,
94 | Notification,
95 | Loading,
96 | Collapse,
97 | CollapseItem,
98 | Tabs,
99 | TabPanel,
100 | Rate,
101 | Carousel,
102 | Input,
103 | Checkbox,
104 | Radio,
105 | Switch,
106 | Option,
107 | Select,
108 | RadioButtonGroup
109 | };
110 |
111 |
--------------------------------------------------------------------------------
/src/locale/format.js:
--------------------------------------------------------------------------------
1 | function hasOwn (obj, key) {
2 | return Object.prototype.hasOwnProperty.call(obj, key);
3 | }
4 |
5 | const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
6 | /**
7 | * String format template
8 | * - Inspired:
9 | * https://github.com/Matt-Esch/string-template/index.js
10 | */
11 | export default function () {
12 | /**
13 | * template
14 | *
15 | * @param {String} string
16 | * @param {Array} ...args
17 | * @return {String}
18 | */
19 |
20 | function template (string, ...args) {
21 | if (args.length === 1 && typeof args[0] === 'object') {
22 | args = args[0];
23 | }
24 |
25 | if (!args || !args.hasOwnProperty) {
26 | args = {};
27 | }
28 |
29 | return string.replace(RE_NARGS, (match, prefix, i, index) => {
30 | let result;
31 |
32 | if (
33 | string[index - 1] === '{' &&
34 | string[index + match.length] === '}'
35 | ) {
36 | return i;
37 | } else {
38 | result = hasOwn(args, i) ? args[i] : null;
39 | if (result === null || result === undefined) {
40 | return '';
41 | }
42 |
43 | return result;
44 | }
45 | });
46 | }
47 |
48 | return template;
49 | }
50 |
--------------------------------------------------------------------------------
/src/locale/index.js:
--------------------------------------------------------------------------------
1 | import cn from './lang/zh-CN.json';
2 | import en from './lang/en-US.json';
3 | import hk from './lang/zh-HK.json';
4 |
5 | import Format from './format';
6 |
7 | const language = {
8 | 'zh-CN': cn,
9 | 'en-US': en,
10 | 'zh-HK': hk
11 | };
12 |
13 | const defaultLang = 'zh-CN';
14 | const format = Format();
15 | let locale = defaultLang;
16 |
17 | // let i18nHandler = function i18nHandler () {
18 | // const vuei18n = Object.getPrototypeOf(this).$t;
19 | // if (typeof vuei18n === 'function') {
20 | // return vuei18n.apply(this, arguments);
21 | // }
22 | // };
23 |
24 | // function i18n (fn) {
25 | // i18nHandler = fn || i18nHandler;
26 | // }
27 |
28 | function t (path, options) {
29 | let value = false;
30 | const array = path.split('.');
31 | let current = language[locale] || language[defaultLang];
32 | for (var i = 0, j = array.length; i < j; i++) {
33 | const property = array[i];
34 | value = current[property];
35 | if (i === j - 1) return format(value, options);
36 | if (!value) return '';
37 | current = value;
38 | }
39 | return '';
40 | }
41 |
42 | function use (l) {
43 | locale = l || defaultLang;
44 | }
45 |
46 | function getLocale () {
47 | return locale;
48 | }
49 |
50 | export default { use, t, getLocale };
51 |
--------------------------------------------------------------------------------
/src/locale/lang/en-US.json:
--------------------------------------------------------------------------------
1 | {
2 | "fmdialog": {
3 | "title": "Confirm",
4 | "confirmText": "Confirm",
5 | "cancelText": "Cancel"
6 | },
7 | "fmselect": {
8 | "placeholder": "Please select",
9 | "empty": "No Data"
10 | },
11 | "fminput": {
12 | "placeholder": "Please input"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/locale/lang/zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "fmdialog": {
3 | "title": "请确认",
4 | "confirmText": "确认",
5 | "cancelText": "取消"
6 | },
7 | "fmselect": {
8 | "placeholder": "请选择",
9 | "empty": "暂无数据"
10 | },
11 | "fminput": {
12 | "placeholder": "请输入"
13 | }
14 | }
--------------------------------------------------------------------------------
/src/locale/lang/zh-HK.json:
--------------------------------------------------------------------------------
1 | {
2 | "fmdialog": {
3 | "title": "請確認",
4 | "confirmText": "確認",
5 | "cancelText": "取消"
6 | },
7 | "fmselect": {
8 | "placeholder": "請選擇",
9 | "empty": "暫無數據"
10 | },
11 | "fminput": {
12 | "placeholder": "請輸入"
13 | }
14 | }
--------------------------------------------------------------------------------
/src/locale/lang/zh-TW.json:
--------------------------------------------------------------------------------
1 | {
2 | "fmdialog": {
3 | "title": "請確認",
4 | "confirmText": "確認",
5 | "cancelText": "取消"
6 | },
7 | "fmselect": {
8 | "placeholder": "請選擇",
9 | "empty": "暫無數據"
10 | },
11 | "fminput": {
12 | "placeholder": "請輸入"
13 | }
14 | }
--------------------------------------------------------------------------------
/src/mixins/i18n.js:
--------------------------------------------------------------------------------
1 | import locale from 'fm-vue-ui/src/locale/index.js';
2 |
3 | export default {
4 | computed: {
5 | $fl () {
6 | return locale.getLocale();
7 | }
8 | },
9 |
10 | methods: {
11 | $ft (...args) {
12 | return locale.t.apply(this, args);
13 | }
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/utils/helpers.js:
--------------------------------------------------------------------------------
1 | export const addZero = (val) => {
2 | return val > 9 ? val : `0${val}`;
3 | };
4 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 通用的工具库
3 | */
4 | import { localStorage, sessionStorage, memoryStorage } from './storage';
5 | import { addZero } from './helpers';
6 |
7 | // 用户头像默认加载失败的图片
8 | const avatarError = (e, defaultUrl = '') => {
9 | const url = defaultUrl ? defaultUrl : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADgAAAA4CAIAAAAn5KxJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NjdGNjRBRjMyOTdCMTFFN0I1OTZGMkRFNTM0MDBBNUEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NjdGNjRBRjQyOTdCMTFFN0I1OTZGMkRFNTM0MDBBNUEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2N0Y2NEFGMTI5N0IxMUU3QjU5NkYyREU1MzQwMEE1QSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2N0Y2NEFGMjI5N0IxMUU3QjU5NkYyREU1MzQwMEE1QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pkm31lAAAALtSURBVHja7JnZbtpAFIY9m228YkNL0uT936kXlapWUVUE2diMt+kPpGlCCNhjyyaVj7gg0gz+fPZzQr5++6l9BKHaB5EOtAPtQDvQDrSa8Hpel1Lfc23L5JzHSbJYrJarVRynhJwTqC7ExWjIGNv9aeg6PmHgJ0m6WC7XcZImKehbBoUuR58Hz5QvRQje973d9yRNp9N7qJmoKrmqj8LiMPfJY4JzaN1xbCllO6Cuaxc//GkQMMrUWCuBmqbBaIlfgN3D0M9zFdRKoAiaslcc2wKu1GSjoAiXsldACTvIvFlQSqmSHYTcSnOgaqmmBdNrsrmLXVPSgf7XoFJ+EFD0RAq38jxvGnQVRQq31utEodmrBBpFUVpSqShIy2ilkaaDidw/zEpdeJwt0D2RTXkijUb9bL6I46Swd8rx5JaCkZKGNbqR8WRapMPAmZtfv7MsQytDtDZAodHbu4fjZ9I0+/7jBtMpGm1KVQanGkDx2Nl8fiI/rKL1OmYYRBhVm+/qqkwnns0423gmJa1Noc/+lx9t2kG4fRf1hURttT6Xx+oN50yrVm9rAz0+jvKtVOi0awLFOHrS+TzPqdLE1APa992TZwZBHw7Q2qYEihyGgWX1Coys5PrLhW7oaqyKSzLBua4Lw9Ad2zq4IXsvpK4vR8ip26Vkso5TKfP6QRENtmX2TBN8ahP9Tno9E5+njjZJ0Ssul5uCUBUUxrVta7OlNY3a+3YhuBCO5zroAdDfPM7mWZaXBgWi59q+5xY3rnp2Y6zve3gWcO/uH99OAe+CIj7Cvq+wXaoYmtAu/B5dDrT7MuXxQy9Hh4PQ+utGLQycFAAB/G08nmTossmh9AQTXF2OWqT8F3OmcX11gaS2S2f7oGHgN+CRxR3Xd538LSjqtV0gdTcpcIB8u6KmexmO1PivoToEZYUSug+ql191NyAoMDD+a9Bmk1HR1szUpfZaoxgYzhBUF2Lf9JydJyjfNz3Gr3MEReRI+UeAAQA6cgV7TSGW7QAAAABJRU5ErkJggg==';
10 | e.target.src = url;
11 | };
12 |
13 | // 格式化时间
14 | const formatDateToStr = (date, formatStr) => {
15 | formatStr = formatStr || 'yyyy-MM-dd';
16 | date = date || new Date();
17 | let str = formatStr;
18 |
19 | str = str.replace(/yyyy|YYYY/, date.getFullYear());
20 | str = str.replace(/yy|YY/, (date.getYear() % 100) > 9 ? (date.getYear() % 100).toString() : '0' + (date.getYear() % 100));
21 | str = str.replace(/MM/, addZero(date.getMonth() + 1));
22 | str = str.replace(/M/g, date.getMonth() + 1);
23 | str = str.replace(/dd|DD/, addZero(date.getDate()));
24 | str = str.replace(/d|D/g, date.getDate());
25 | str = str.replace(/hh|HH/, addZero(date.getHours()));
26 | str = str.replace(/h|H/g, date.getHours());
27 | str = str.replace(/mm/, addZero(date.getMinutes()));
28 | str = str.replace(/m/g, date.getMinutes());
29 | str = str.replace(/ss|SS/, addZero(date.getSeconds()));
30 | str = str.replace(/s|S/g, date.getSeconds());
31 | return str;
32 | };
33 |
34 | export default {
35 | formatDateToStr,
36 | avatarError,
37 | localStorage,
38 | sessionStorage,
39 | memoryStorage
40 | };
41 |
--------------------------------------------------------------------------------
/src/utils/storage.js:
--------------------------------------------------------------------------------
1 | const prefix = '_fm_';
2 | const s = sessionStorage;
3 | export const localStorage = {
4 | set (key, val) {
5 | try {
6 | window.localStorage.setItem(prefix + key, JSON.stringify(val));
7 | } catch (err) {
8 | alert('localStorage 写入出错');
9 | }
10 | },
11 |
12 | get (key) {
13 | try {
14 | const val = window.localStorage.getItem(prefix + key);
15 | return JSON.parse(val);
16 | } catch (e) {
17 | return window.localStorage.getItem(prefix + key);
18 | }
19 | },
20 |
21 | remove (key) {
22 | window.localStorage.removeItem(prefix + key);
23 | },
24 |
25 | clear () {
26 | window.localStorage.clear();
27 | },
28 |
29 | getAll () {
30 | const res = {};
31 | for (let i = window.localStorage.length - 1; i >= 0; i--) {
32 | let key = window.localStorage.key(i);
33 | if (key.startsWith(prefix)) {
34 | key = key.slice(prefix.length);
35 | res[key] = localStorage.get(key);
36 | }
37 | }
38 | return res;
39 | }
40 | };
41 |
42 | export const sessionStorage = {
43 | set (key, val) {
44 | try {
45 | window.sessionStorage.setItem(prefix + item, JSON.stringify(val));
46 | } catch (err) {
47 | alert('sessionStorage 写入出错');
48 | }
49 | },
50 |
51 | get (key) {
52 | try {
53 | const val = window.sessionStorage.getItem(prefix + key);
54 | return JSON.parse(val);
55 | } catch (e) {
56 | return window.sessionStorage.getItem(prefix + key);
57 | }
58 | },
59 |
60 | remove (key) {
61 | window.sessionStorage.removeItem(prefix + key);
62 | },
63 |
64 | clear () {
65 | window.sessionStorage.clear();
66 | },
67 |
68 | getAll () {
69 | const res = {};
70 | for (let i = window.sessionStorage.length - 1; i >= 0; i--) {
71 | let key = window.sessionStorage.key(i);
72 | if (key.startsWith(prefix)) {
73 | key = key.slice(prefix.length);
74 | res[key] = sessionStorage.get(key);
75 | }
76 | }
77 | return res;
78 | }
79 | };
80 |
81 | let _memoryStorage = {};
82 | export const memoryStorage = {
83 | set (key, val) {
84 | _memoryStorage[prefix + key] = val;
85 | },
86 |
87 | get (key) {
88 | return _memoryStorage[prefix + key];
89 | },
90 |
91 | remove (key) {
92 | delete _memoryStorage[prefix + key];
93 | },
94 |
95 | clear () {
96 | _memoryStorage = {};
97 | },
98 |
99 | getAll () {
100 | return _memoryStorage;
101 | }
102 | };
103 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | const testsContext = require.context('./specs', true, /\.spec\.js$/);
2 | testsContext.keys().forEach(testsContext);
3 |
--------------------------------------------------------------------------------
/test/karma.conf.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpackTestConfig = require('../build/webpack.test.config');
4 | /* eslint-disable */
5 | let isCI = process.env.CONTINUOUS_INTEGRATION ? true : false;
6 |
7 | module.exports = config => {
8 | config.set({
9 | frameworks: ['mocha', 'chai'],
10 | files: [
11 | './index.js'
12 | ],
13 | browsers: [isCI ? 'ChromeTravisCI' : 'Chrome'],
14 | customLaunchers: {
15 | ChromeTravisCI: {
16 | base: 'Chrome',
17 | flags: ['--no-sandbox']
18 | }
19 | },
20 | plugins: [
21 | 'karma-chrome-launcher',
22 | 'karma-mocha',
23 | 'karma-sourcemap-loader',
24 | 'karma-webpack',
25 | 'karma-mocha-reporter',
26 | 'karma-chai',
27 | 'karma-coverage'
28 | ],
29 | reporters: ['progress', 'mocha', 'coverage'],
30 | singleRun: true,
31 | autoRun: true,
32 | mochaReporter: {
33 | colors: {
34 | success: 'blue',
35 | info: 'bgGreen',
36 | warning: 'cyan',
37 | error: 'bgRed'
38 | },
39 | symbols: {
40 | success: '+',
41 | info: '#',
42 | warning: '!',
43 | error: 'x'
44 | }
45 | },
46 | coverageReporter: {
47 | dir: './coverage',
48 | reporters: [
49 | { type: 'lcov', subdir: '.' },
50 | { type: 'text-summary' }
51 | ]
52 | },
53 | preprocessors: {
54 | './index.js': ['webpack', 'sourcemap']
55 | },
56 | logLevel: config.LOG_INFO,
57 | colors: true,
58 | webpack: webpackTestConfig,
59 | webpackMiddleware: {
60 | noInfo: true
61 | }
62 | });
63 | };
--------------------------------------------------------------------------------
/test/specs/button.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | import { createCompTest, destroyVM, createVM } from '../utils';
4 | import Button from 'components/button';
5 |
6 | describe('Button', () => {
7 | let vm;
8 | afterEach(() => {
9 | destroyVM(vm);
10 | });
11 |
12 | it('create', () => {
13 | vm = createCompTest(Button, {
14 | type: 'primary'
15 | });
16 | const buttonElm = vm.$el;
17 | expect(buttonElm.classList.contains('fm-btn-primary')).to.be.true;
18 | });
19 |
20 | it('disabled', () => {
21 | vm = createCompTest(Button, {
22 | disabled: true
23 | });
24 | const buttonElm = vm.$el;
25 | expect(buttonElm.classList.contains('fm-btn-disabled')).to.be.true;
26 | });
27 |
28 | it('size', () => {
29 | vm = createCompTest(Button, {
30 | size: 'large'
31 | });
32 | const buttonElm = vm.$el;
33 | expect(buttonElm.classList.contains('fm-btn-large')).to.be.true;
34 | });
35 |
36 | it('click/text', done => {
37 | let res = '';
38 | vm = createVM({
39 | template: `测试按钮`,
40 | methods: {
41 | testClick (e) {
42 | res = '点击';
43 | }
44 | }
45 | });
46 | const buttonElm = vm.$el;
47 | buttonElm.click();
48 | expect(buttonElm.textContent).to.equal('测试按钮');
49 | setTimeout(() => {
50 | expect(res).to.equal('点击');
51 | done();
52 | }, 20);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/utils.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import FMUI from 'main/index';
3 |
4 | Vue.use(FMUI);
5 |
6 | // 创建 vm 实例
7 | export const createVM = (opts) => {
8 | return new Vue(opts).$mount();
9 | };
10 |
11 | // 销毁 vm 实例
12 | export const destroyVM = (vm) => {
13 | vm.$destroy && vm.$destroy();
14 | vm.$el &&
15 | vm.$el.parentNode &&
16 | vm.$el.parentNode.removeChild(vm.$el);
17 | };
18 |
19 | /**
20 | * 创建组件测试实例
21 | * https://cn.vuejs.org/v2/guide/unit-testing.html#编写可被测试的组件
22 | */
23 | export const createCompTest = (Component, propsData) => {
24 | const Ctor = Vue.extend(Component);
25 | const vm = new Ctor({ propsData: propsData }).$mount();
26 | return vm;
27 | };
28 |
--------------------------------------------------------------------------------
/theme-default/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const less = require('gulp-less');
3 | const autoprefixer = require('gulp-autoprefixer');
4 | const cssmin = require('gulp-cssmin');
5 |
6 | gulp.task('build', function () {
7 | return gulp.src('./src/**/*.less')
8 | .pipe(less())
9 | .pipe(autoprefixer({
10 | browsers: ['ie > 8', 'safari > 8', 'last 5 versions'],
11 | cascade: false
12 | }))
13 | .pipe(cssmin())
14 | .pipe(gulp.dest('../lib/theme-default'));
15 | });
16 |
--------------------------------------------------------------------------------
/theme-default/src/base.less:
--------------------------------------------------------------------------------
1 | @import "./common/transition.less";
2 |
--------------------------------------------------------------------------------
/theme-default/src/button.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-button {
4 | display: inline-block;
5 | background: transparent;
6 | border: 1px solid @border-regular-color-6;
7 | text-align: center;
8 | color: @color-regular-2;
9 | font-size: @min-font-size;
10 | cursor: pointer;
11 | border-radius: 4px;
12 | outline: none;
13 | line-height: 1.5;
14 | vertical-align: middle;
15 | box-sizing: border-box;
16 | }
17 |
18 | .fm-button-radius-large {
19 | border-radius: 100px;
20 | }
21 |
22 | .fm-button-loading-icon {
23 | width: 10px;
24 | height: 10px;
25 | border: none;
26 | background: transparent;
27 | display: inline-block;
28 | font-size: 0;
29 | line-height: 0;
30 | margin: 0 auto;
31 | margin-right: 2px;
32 | }
33 |
34 | .fm-button-ghost {
35 | border-color: @border-primary-color;
36 | color: @color-primary;
37 |
38 | &:hover {
39 | border-color: @border-primary-color;
40 | background: @color-primary;
41 | color: @color-white;
42 | }
43 | }
44 |
45 | .fm-button-primary {
46 | border-color: @border-primary-color;
47 | background: @color-primary;
48 | color: @color-white;
49 |
50 | &:hover {
51 | background: @color-primary-hover;
52 | }
53 | }
54 |
55 | .fm-button-default {
56 | &:hover {
57 | border-color: @border-primary-color;
58 | color: @color-primary;
59 | }
60 | }
61 |
62 | .fm-button-invert {
63 | &.fm-button-default {
64 | background: #515151;
65 | border: none;
66 | color: @color-white;
67 |
68 | &:hover {
69 | background: #797979;
70 | }
71 | }
72 |
73 | &.fm-button-ghost {
74 | background: @color-white;
75 | border: none;
76 | color: #111;
77 |
78 | &:hover {
79 | background: #ccc;
80 | }
81 | }
82 | }
83 |
84 | .fm-button-disabled {
85 | background: @background-color-disabled;
86 | color: @border-disabled-color;
87 | border: 1px solid @border-disabled-color;
88 | pointer-events: none;
89 | }
90 |
91 | .fm-button-small {
92 | min-height: 24px;
93 | min-width: 48px;
94 | font-size: @min-font-size;
95 | padding: 0 8px;
96 |
97 | .fm-button-loading-icon {
98 | transform: scale(2.4);
99 | }
100 | }
101 |
102 | .fm-button-medium {
103 | min-height: 30px;
104 | min-width: 60px;
105 | font-size: @normal-font-size;
106 | padding: 0 10px;
107 |
108 | .fm-button-loading-icon {
109 | transform: scale(3);
110 | }
111 | }
112 |
113 | .fm-button-large {
114 | min-width: 80px;
115 | min-height: 40px;
116 | padding: 0 16px;
117 | font-size: @mid-font-size;
118 |
119 | .fm-button-loading-icon {
120 | transform: scale(4);
121 | }
122 | }
123 |
124 | .fm-button-xlarge {
125 | min-height: 50px;
126 | min-width: 100px;
127 | font-size: @max-font-size;
128 | padding: 0 20px;
129 |
130 | .fm-button-loading-icon {
131 | transform: scale(5);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/theme-default/src/carousel.less:
--------------------------------------------------------------------------------
1 | .fm-swiper-wrapper {
2 | position: relative;
3 | overflow: hidden;
4 | }
5 |
6 | .fm-swiper-imgs {
7 | display: inline-block;
8 | width: 100%;
9 | z-index: 1;
10 |
11 | a {
12 | display: block;
13 | }
14 | }
15 |
16 | .fm-swiper-dot {
17 | position: absolute;
18 | bottom: 10px;
19 | left: 50%;
20 | transform: translateX(-50%);
21 |
22 | li {
23 | display: inline-block;
24 | margin: 0 4px;
25 | width: 10px;
26 | height: 10px;
27 | border-radius: 50%;
28 | cursor: pointer;
29 | background: rgba(255, 255, 255, .5);
30 | -webkit-transition: all .3s linear;
31 | transition: all .3s linear;
32 |
33 | &.actived {
34 | width: 20px;
35 | border-radius: 10px;
36 | background: rgba(255, 255, 255, 1);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/theme-default/src/checkbox.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .checkbox-inner-after(@top, @left, @width, @height, @border-width, @border-color) {
4 | top: @top;
5 | left: @left;
6 | height: @height;
7 | width: @width;
8 | border: @border-width solid @border-color;
9 | border-left: 0;
10 | border-top: 0;
11 | }
12 |
13 | .fm-checkbox {
14 | position: relative;
15 | display: inline-block;
16 | color: @color-regular-1;
17 | font-size: @normal-font-size;
18 | cursor: pointer;
19 | white-space: nowrap;
20 | user-select: none;
21 |
22 | &.is-disabled {
23 | cursor: not-allowed;
24 |
25 | .fm-checkbox-label {
26 | color: @color-regular;
27 | }
28 | }
29 | }
30 |
31 | .fm-checkbox-input {
32 | position: relative;
33 | top: -1px;
34 | display: inline-block;
35 | box-sizing: border-box;
36 | background: @background-color-white;
37 | line-height: 1;
38 | white-space: nowrap;
39 | outline: none;
40 | cursor: pointer;
41 | vertical-align: middle;
42 |
43 | .fm-checkbox-inner {
44 | position: relative;
45 | display: inline-block;
46 | box-sizing: border-box;
47 | width: 100%;
48 | height: 100%;
49 | z-index: @z-index-normal;
50 | border-radius: @border-radius-normal;
51 | border: @border-width-base solid @border-regular-color;
52 |
53 | &::after {
54 | position: absolute;
55 | content: '';
56 | top: 0;
57 | left: 4px;
58 | height: 7px;
59 | width: 3px;
60 | border: @border-width-base solid @border-primary-color;
61 | border-left: 0;
62 | border-top: 0;
63 | transition: transform .15s cubic-bezier(.71, -.46, .88, .6) .05s;
64 | transform-origin: center;
65 | transform: rotate(45deg) scaleY(0);
66 | }
67 | }
68 |
69 | &.fm-checkbox-small {
70 | width: 16px;
71 | height: 16px;
72 |
73 | .fm-checkbox-inner::after {
74 | .checkbox-inner-after(0, 4px, 5px, 10px, @border-width-normal, @border-primary-color);
75 | }
76 | }
77 |
78 | &.fm-checkbox-medium {
79 | width: 20px;
80 | height: 20px;
81 |
82 | .fm-checkbox-inner::after {
83 | .checkbox-inner-after(0, 6px, 5px, 12px, @border-width-normal, @border-primary-color);
84 | }
85 | }
86 |
87 | &.fm-checkbox-large {
88 | width: 24px;
89 | height: 24px;
90 |
91 | .fm-checkbox-inner::after {
92 | .checkbox-inner-after(1px, 8px, 6px, 14px, @border-width-normal, @border-primary-color);
93 | }
94 | }
95 |
96 | &.is-disabled {
97 | background: @background-color-disabled;
98 | cursor: not-allowed;
99 |
100 | .fm-checkbox-inner {
101 | border: @border-width-base solid #edeef2;
102 | }
103 | }
104 |
105 | &.is-checked {
106 | .fm-checkbox-inner {
107 | border: @border-width-base solid @border-primary-color;
108 |
109 | &::after {
110 | transform: rotate(45deg) scaleY(1);
111 | }
112 | }
113 |
114 | &.is-disabled {
115 | cursor: not-allowed;
116 |
117 | .fm-checkbox-inner {
118 | border: @border-width-base solid #edeef2;
119 |
120 | &::after {
121 | transform: rotate(45deg) scaleY(1);
122 | border: @border-width-base solid @border-regular-color-3;
123 | border-left: 0;
124 | border-top: 0;
125 | }
126 | }
127 |
128 | &.fm-checkbox-medium {
129 | .fm-checkbox-inner::after {
130 | .checkbox-inner-after(2px, 6px, 4px, 8px, @border-width-normal, @border-regular-color-3);
131 | }
132 | }
133 |
134 | &.fm-checkbox-large {
135 | .fm-checkbox-inner::after {
136 | .checkbox-inner-after(2px, 8px, 5px, 10px, @border-width-normal, @border-regular-color-3);
137 | }
138 | }
139 | }
140 | }
141 | }
142 |
143 | .fm-checkbox-original {
144 | opacity: 0;
145 | outline: none;
146 | position: absolute;
147 | margin: 0;
148 | width: 0;
149 | height: 0;
150 | left: -999px;
151 | }
152 |
153 | .fm-checkbox-label {
154 | display: inline-block;
155 | padding-left: 5px;
156 | line-height: 1;
157 | font-size: @normal-font-size;
158 | }
159 |
--------------------------------------------------------------------------------
/theme-default/src/collapse-item.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/theme-default/src/collapse-item.less
--------------------------------------------------------------------------------
/theme-default/src/collapse.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-collapse {
4 | width: 100%;
5 | height: auto;
6 | }
7 |
8 | .fm-collapse-item {
9 | position: relative;
10 | border: @border-width-base solid @border-regular-color;
11 | border-radius: @border-radius-normal;
12 | overflow: hidden;
13 | }
14 |
15 | .fm-collapse-item-header {
16 | line-height: 14px;
17 | overflow: hidden;
18 | padding: 15px;
19 | background-color: @background-color-gray;
20 | cursor: pointer;
21 | &:hover {
22 | background-color: @background-color-white;
23 | }
24 | &.fm-collapse-item-active {
25 | background-color: @background-color-white;
26 | border-bottom: @border-width-base solid @border-regular-color;
27 | }
28 | span {
29 | display: inline-block;
30 | //white-space: nowrap;
31 | //overflow: hidden;
32 | //text-overflow: ellipsis;
33 | font-size: @mid-font-size;
34 | &.fm-collapse-item-title {
35 | width: 145px;
36 | }
37 | &.fm-collapse-item-info {
38 | margin-left: 15px;
39 | color: @color-regular-4;
40 | width: auto;
41 | }
42 | &.fm-collapse-item-act {
43 | position: absolute;
44 | right: 15px;
45 | color: @color-regular-3;
46 | &:hover {
47 | color: @color-primary;
48 | }
49 | }
50 | }
51 | }
52 |
53 | .fm-collapse-item-content {
54 | background-color: @background-color-white;
55 | overflow: hidden;
56 | }
57 |
58 | .fm-collapse-enter-transition {
59 | transition: all 0.5s;
60 | // transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;
61 | }
62 |
63 | .fm-collapse-leave-transition {
64 | transition: all 0.3s;
65 | }
66 |
--------------------------------------------------------------------------------
/theme-default/src/common/transition.less:
--------------------------------------------------------------------------------
1 | @import "./var.less";
2 |
3 | // 通用的 trasition
4 | .fm-common-enter {
5 | opacity: 0;
6 | }
7 |
8 | .fm-common-enter-active {
9 | transition: all .5s ease;
10 | }
11 |
12 | .fm-common-leave {
13 | opacity: 1;
14 | }
15 |
16 | .fm-common-leave-active {
17 | transition: @all-transition;
18 | opacity: 0;
19 | }
20 |
--------------------------------------------------------------------------------
/theme-default/src/common/var.less:
--------------------------------------------------------------------------------
1 | // Transition
2 | @all-transition: all .3s cubic-bezier(.645, .045, .355, 1);
3 | @fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
4 | @fade-linear-transition: opacity 200ms linear;
5 | @md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms, opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) 100ms;
6 | @border-transition-base: border-color .2s cubic-bezier(.645, .045, .355, 1);
7 | @color-transition-base: color .2s cubic-bezier(.645, .045, .355, 1);
8 |
9 | // Fonts
10 | @min-font-size: 12px;
11 | @normal-font-size: 14px;
12 | @mid-font-size: 16px;
13 | @max-font-size: 18px;
14 | @large-font-size: 20px;
15 |
16 | @font-weight-normal: 400;
17 | @font-weight-mid: 500;
18 | @font-weight-max: 700;
19 |
20 | // Colors
21 | @color-white: #fff;
22 | @color-black: #000;
23 |
24 | @color-primary: #ff6200;
25 | @color-primary-hover: #f05102;
26 | @color-regular: #a1a4ad;
27 | @color-regular-1: #333;
28 | @color-regular-2: #555;
29 | @color-regular-3: #999;
30 | @color-regular-4: #777;
31 | @color-regular-5: #ffa871;
32 | @color-placeholder: #b4bccc;
33 | // Icon Colors
34 | @icon-color-success: #1fbb95;
35 | @icon-color-info: #999;
36 | @icon-color-warning: #f5ab3b;
37 | @icon-color-error: #eb4e5c;
38 |
39 | // Background
40 | @background-color-base: #fafafc;
41 | @background-color-transparent: transparent;
42 | @background-color-mask: rgba(0, 0, 0, 0.298);
43 | @background-color-white: @color-white;
44 | @background-color-primary: @color-primary;
45 | @background-color-disabled: #fafafa;
46 | @background-color-gray: #f5f5f7;
47 | @background-color-light-1: @color-primary;
48 | @background-color-light-2: #f5f6fa;
49 | @background-color-light-3: #9c9c9d;
50 | @background-color-light-4: #ccc;
51 | @background-color-light-5: @color-regular-5;
52 |
53 | // Border
54 | @border-primary-color: @color-primary;
55 | @border-white-color: @color-white;
56 | @border-regular-color-1: @color-regular;
57 | @border-regular-color: #e1e2e6;
58 | @border-regular-color-2: #ff8030;
59 | @border-regular-color-3: #c6c8cf;
60 | @border-regular-color-4: @color-regular-5;
61 | @border-regular-color-5: #e9e9e9;
62 | @border-regular-color-6: #ccc;
63 | @border-disabled-color: #d9d9d9;
64 |
65 | @border-width-base: 1px;
66 | @border-width-normal: 2px;
67 | @border-radius-base: 1px;
68 | @border-radius-normal: 2px;
69 | @border-radius-max: 4px;
70 |
71 | // z-index
72 | @z-index-normal: 1;
73 | @z-index-regular: 5;
74 | @z-index-nav: 150;
75 | @z-index-mask: 1500;
76 | @z-index-popper: 15000;
77 |
--------------------------------------------------------------------------------
/theme-default/src/dialog.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-model-dialog-wrap {
4 | position: fixed;
5 | z-index: @z-index-popper;
6 | top: 0;
7 | left: 0;
8 | right: 0;
9 | bottom: 0;
10 | overflow: hidden;
11 | text-align: center;
12 | white-space: nowrap;
13 | font-size: 0;
14 |
15 | &:after {
16 | content: "";
17 | display: inline-block;
18 | height: 100%;
19 | vertical-align: middle;
20 | }
21 | }
22 |
23 | .fm-model-dialog-mask {
24 | position: fixed;
25 | z-index: -1;
26 | top: 0;
27 | left: 0;
28 | right: 0;
29 | bottom: 0;
30 | overflow: hidden;
31 | }
32 |
33 | .fm-dialog-close-icon {
34 | position: absolute;
35 | top: 13px;
36 | right: 20px;
37 | cursor: pointer;
38 | font-size: 24px;
39 | line-height: 24px;
40 | width: 24px;
41 | height: 24px;
42 | border-radius: 50%;
43 | color: #767676;
44 | text-align: center;
45 | vertical-align: middle;
46 | //&::before, &:after {
47 | // position: absolute;
48 | // left: 2px;
49 | // content: '';
50 | // width: 2px;
51 | // height: 10px;
52 | // background: @background-color-light-3;
53 | //}
54 | //
55 | //&::before {
56 | // transform: rotate(-45deg);
57 | //}
58 | //
59 | //&::after {
60 | // transform: rotate(45deg);
61 | //}
62 |
63 | &:hover {
64 | background: #f5f5f5;
65 | }
66 | }
67 |
68 | .fm-model-dialog {
69 | position: relative;
70 | display: inline-block;
71 | vertical-align: middle;
72 | border-radius: 6px;
73 | background: @background-color-white;
74 | text-align: left;
75 | white-space: normal;
76 | box-shadow: 0 4px 20px 1px rgba(0, 0, 0, 0.2);
77 |
78 | .fm-dialog-top {
79 | min-width: 350px;
80 | max-width: 430px;
81 | height: 50px;
82 | line-height: 50px;
83 | border-bottom: @border-width-base solid @border-regular-color-5;
84 |
85 | h1 {
86 | font-size: @mid-font-size;
87 | font-weight: @font-weight-mid;
88 | color: @color-regular-1;
89 | margin: 0 0 0 20px;
90 | }
91 | }
92 |
93 | .fm-dialog-middle {
94 | font-weight: @font-weight-normal;
95 | max-width: 430px;
96 | padding: 28px 20px;
97 | //text-align: center;
98 | box-sizing: border-box;
99 |
100 | .fm-dialog-middle-icon {
101 | display: inline-block;
102 | position: relative;
103 | font-size: 36px;
104 | vertical-align: middle;
105 |
106 | &.success {
107 | color: @icon-color-success;
108 | }
109 |
110 | &.info {
111 | color: @icon-color-info;
112 | }
113 |
114 | &.warning {
115 | color: @icon-color-warning;
116 | }
117 |
118 | &.error {
119 | color: @icon-color-error;
120 | }
121 | }
122 |
123 | span {
124 | display: inline-block;
125 | font-size: @normal-font-size;
126 | line-height: 22px;
127 | vertical-align: middle;
128 | color: #666;
129 | }
130 | }
131 |
132 | .fm-dialog-bottom {
133 | text-align: right;
134 | cursor: pointer;
135 | padding: 0 20px;
136 | margin-bottom: 20px;
137 |
138 | &.fm-dialog-bottom-center {
139 | text-align: center;
140 | }
141 |
142 | span {
143 | display: inline-block;
144 | line-height: 30px;
145 | text-align: center;
146 | font-size: @mid-font-size;
147 |
148 | &.fm-confirm {
149 | min-width: 60px;
150 | height: 30px;
151 | border-radius: 15px;
152 | background: @background-color-light-1;
153 | color: @color-white;
154 | margin-left: 20px;
155 | font-size: 14px;
156 |
157 | &:hover {
158 | opacity: 0.8;
159 | }
160 |
161 | &.disabled {
162 | cursor: not-allowed;
163 | border: 1px solid #d9d9d9;
164 | background: #f5f5f5;
165 | color: #ccc;
166 | }
167 | }
168 |
169 | &.fm-cancel {
170 | font-size: 14px;
171 | color: @color-regular-1;
172 | }
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/theme-default/src/index.less:
--------------------------------------------------------------------------------
1 | @import "./base.less";
2 | @import "./button.less";
3 | @import "./dialog.less";
4 | @import "./toast.less";
5 | @import "./notification.less";
6 | @import "./loading.less";
7 | @import "./collapse.less";
8 | @import "./collapse-item.less";
9 | @import "./tabs.less";
10 | @import "./tab-panel.less";
11 | @import "./rate.less";
12 | @import "./carousel.less";
13 | @import "./input.less";
14 | @import "./checkbox.less";
15 | @import "./radio.less";
16 | @import "./switch.less";
17 | @import "./option.less";
18 | @import "./select.less";
19 | @import "./radio-button-group.less";
20 |
--------------------------------------------------------------------------------
/theme-default/src/input.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-input {
4 | position: relative;
5 | font-size: @normal-font-size;
6 | display: inline-block;
7 |
8 | &.fm-input-large {
9 | min-width: 210px;
10 | }
11 |
12 | &.fm-input-medium {
13 | min-width: 180px;
14 | }
15 |
16 | &.fm-input-small {
17 | min-width: 150px;
18 | }
19 |
20 | &.fm-input-mini {
21 | min-width: 120px;
22 | }
23 |
24 | &.fm-input-disabled {
25 | .fm-inner-input {
26 | background-color: @background-color-disabled;
27 | border-color: @border-regular-color;
28 | color: @color-placeholder;
29 | cursor: not-allowed;
30 | }
31 | }
32 | }
33 |
34 | .fm-inner-input {
35 | appearance: none;
36 | background-color: @background-color-white;
37 | background-image: none;
38 | border-radius: @border-radius-max;
39 | box-sizing: border-box;
40 | display: inline-block;
41 | font-size: inherit;
42 | color: @color-regular-1;
43 | line-height: 1;
44 | outline: none;
45 | width: 100%;
46 | padding: 0 15px;
47 | border: @border-width-base solid @border-regular-color;
48 |
49 | &:hover {
50 | border-color: @border-regular-color-1;
51 | }
52 |
53 | &.fm-inner-input-large {
54 | height: 40px;
55 | }
56 |
57 | &.fm-inner-input-medium {
58 | height: 36px;
59 | }
60 |
61 | &.fm-inner-input-small {
62 | font-size: 12px;
63 | height: 32px;
64 | }
65 |
66 | &.fm-inner-input-mini {
67 | font-size: 12px;
68 | height: 28px;
69 | }
70 | }
71 |
72 | .fm-inner-input-group-prepend {
73 | background-color: #f5f7fa;
74 | color: #909399;
75 | vertical-align: middle;
76 | display: table-cell;
77 | position: relative;
78 | border: 1px solid #dcdfe6;
79 | border-radius: 4px;
80 | padding: 0 20px;
81 | width: 1px;
82 | white-space: nowrap;
83 | border-right: 0;
84 | border-top-right-radius: 0;
85 | border-bottom-right-radius: 0;
86 | }
87 |
88 | .fm-inner-input-group-append {
89 | background-color: #f5f7fa;
90 | color: #909399;
91 | vertical-align: middle;
92 | display: table-cell;
93 | position: relative;
94 | border: 1px solid #dcdfe6;
95 | border-radius: 4px;
96 | padding: 0 20px;
97 | width: 1px;
98 | white-space: nowrap;
99 | border-left: 0;
100 | border-top-left-radius: 0;
101 | border-bottom-left-radius: 0;
102 | }
103 |
104 | .fm-input-group {
105 | display: inline-table;
106 | border-collapse: separate;
107 |
108 | & > .fm-inner-input {
109 | vertical-align: middle;
110 | display: table-cell;
111 | }
112 |
113 | &.fm-input-group-prepend {
114 | & > .fm-inner-input {
115 | border-top-left-radius: 0;
116 | border-bottom-left-radius: 0;
117 | }
118 | }
119 |
120 | &.fm-input-group-append {
121 | & > .fm-inner-input {
122 | border-top-right-radius: 0;
123 | border-bottom-right-radius: 0;
124 | }
125 | }
126 | }
127 |
128 | .fm-inner-suffix-icon {
129 | position: absolute;
130 | width: 20px;
131 | height: 100%;
132 | right: 5px;
133 | top: 0;
134 | text-align: center;
135 | font-size: @normal-font-size;
136 | line-height: 16px;
137 | cursor: pointer;
138 | }
139 |
140 | .fm-input-icon {
141 | display: inline-block;
142 | content: '';
143 | width: 14px;
144 | height: 14px;
145 | vertical-align: middle;
146 | }
147 |
148 | .fm-input-clear-icon {
149 | position: relative;
150 | top: 50%;
151 | left: 50%;
152 | transform: translate(-50%, -50%);
153 | display: inline-block;
154 | content: '';
155 | width: 14px;
156 | height: 14px;
157 | border-radius: 50%;
158 | background: #b8bcc5;
159 | margin-top: -7px;
160 | margin-right: 7px;
161 |
162 | &::before, &::after {
163 | top: 3px;
164 | position: absolute;
165 | content: '';
166 | width: 1px;
167 | height: 7px;
168 | background: @background-color-white;
169 | }
170 |
171 | &::before {
172 | transform: rotate(45deg);
173 | }
174 |
175 | &::after {
176 | transform: rotate(-45deg);
177 | }
178 |
179 | &:hover {
180 | background: #909399;
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/theme-default/src/loading.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-loading-wrap {
4 | position: absolute;
5 | z-index: @z-index-normal;
6 | top: 50%;
7 | left: 50%;
8 | transform: translate(-50%, -50%);
9 | background: @background-color-white;
10 | text-align: center;
11 |
12 | .fm-loading-text {
13 | font-weight: @font-weight-normal;
14 | color: @color-regular-2;
15 | font-size: @normal-font-size;
16 | }
17 |
18 | & > img {
19 | vertical-align: middle;
20 | padding: 5px;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/theme-default/src/notification.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-notification-fade-enter,
4 | .fm-notification-fade-leave-active {
5 | opacity: 0;
6 | margin-right: -100%;
7 | }
8 |
9 | .fm-notification {
10 | position: fixed;
11 | z-index: @z-index-popper;
12 | top: 80px;
13 | right: 20px;
14 | background: @background-color-white;
15 | display: block;
16 | vertical-align: middle;
17 | border-radius: 6px;
18 | text-align: left;
19 | white-space: normal;
20 | box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.15);
21 | border: 1px solid rgba(230, 230, 230, 1);
22 | padding: 10px 20px 18px;
23 | min-height: 80px;
24 | min-width: 380px;
25 | transition: all 0.3s;
26 |
27 | .fm-notification-header {
28 | .fm-notification-icon {
29 | font-size: 36px;
30 | line-height: 36px;
31 | vertical-align: middle;
32 | display: table-cell;
33 |
34 | &.success {
35 | color: @icon-color-success;
36 | }
37 |
38 | &.info {
39 | color: @icon-color-info;
40 | }
41 |
42 | &.warning {
43 | color: @icon-color-warning;
44 | }
45 |
46 | &.error {
47 | color: @icon-color-error;
48 | }
49 | }
50 |
51 | .fm-notification-title {
52 | font-size: @font-weight-mid;
53 | font-family: PingFangSC-Semibold, sans-serif;
54 | line-height: 22px;
55 | color: #333;
56 | vertical-align: middle;
57 | display: table-cell;
58 | padding-left: 5px;
59 | font-weight: bold;
60 | }
61 | }
62 |
63 | .fm-notification-message {
64 | font-size: 14px;
65 | font-family: PingFangSC-Regular, sans-serif;
66 | line-height: 20px;
67 | color: #666;
68 | display: block;
69 | max-width: 305px;
70 | vertical-align: middle;
71 | word-break: break-word;
72 | margin-left: 40px;
73 | }
74 |
75 | .fm-notification-close-icon {
76 | position: absolute;
77 | top: 10px;
78 | right: 10px;
79 | cursor: pointer;
80 | font-size: 24px;
81 | line-height: 24px;
82 | width: 24px;
83 | height: 24px;
84 | border-radius: 50%;
85 | color: #767676;
86 | text-align: center;
87 | vertical-align: middle;
88 |
89 | &:hover {
90 | background: #f5f5f5;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/theme-default/src/option.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/theme-default/src/option.less
--------------------------------------------------------------------------------
/theme-default/src/radio-button-group.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-radio-button-group {
4 | display: inline-block;
5 | vertical-align: middle;
6 | font-size: 0;
7 | }
8 |
9 | .fm-radio-button {
10 | display: inline-block;
11 | vertical-align: middle;
12 | font-size: @min-font-size;
13 | cursor: pointer;
14 | color: @color-regular-1;
15 | line-height: 25px;
16 | // height: 25px;
17 | text-align: center;
18 | padding-left: 5px;
19 | padding-right: 5px;
20 | border-top: @border-width-base solid @border-regular-color;
21 | border-bottom: @border-width-base solid @border-regular-color;
22 | border-left: @border-width-base solid @border-regular-color;
23 | background: @background-color-white;
24 |
25 | &:first-child {
26 | border-top-left-radius: @border-width-normal;
27 | border-bottom-left-radius: @border-width-normal;
28 | }
29 |
30 | &:last-child {
31 | border-right: @border-width-base solid @border-regular-color;
32 | border-top-right-radius: @border-width-normal;
33 | border-bottom-right-radius: @border-width-normal;
34 | }
35 |
36 | &.active {
37 | border: none;
38 | border-top: @border-width-base solid @background-color-light-5;
39 | border-bottom: @border-width-base solid @background-color-light-5;
40 | border-radius: @border-width-normal;
41 | background-color: @background-color-light-5;
42 | color: @color-white;
43 |
44 | + .fm-radio-button {
45 | border-left: none;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/theme-default/src/radio.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-radio {
4 | position: relative;
5 | display: inline-block;
6 | color: @color-regular-1;
7 | font-size: @normal-font-size;
8 | cursor: pointer;
9 | white-space: nowrap;
10 | user-select: none;
11 |
12 | &.is-disabled {
13 | cursor: not-allowed;
14 |
15 | .fm-radio-label {
16 | color: @color-regular;
17 | }
18 | }
19 | }
20 |
21 | .fm-radio-input {
22 | position: relative;
23 | top: -1px;
24 | display: inline-block;
25 | box-sizing: border-box;
26 | background: @background-color-white;
27 | line-height: 1;
28 | white-space: nowrap;
29 | outline: none;
30 | cursor: pointer;
31 | border-radius: 50%;
32 | vertical-align: middle;
33 |
34 | &.fm-radio-small {
35 | width: 16px;
36 | height: 16px;
37 | }
38 |
39 | .fm-radio-inner {
40 | position: relative;
41 | display: inline-block;
42 | box-sizing: border-box;
43 | width: 100%;
44 | height: 100%;
45 | z-index: @z-index-normal;
46 | border-radius: 50%;
47 | border: @border-width-base solid @border-regular-color;
48 |
49 | &::after {
50 | position: absolute;
51 | content: '';
52 | left: 50%;
53 | top: 50%;
54 | width: 6px;
55 | height: 6px;
56 | border-radius: 50%;
57 | background-color: #fff;
58 | transform: translate(-50%, -50%) scale(0);
59 | transition: transform .15s cubic-bezier(.71, -.46, .88, .6);
60 | }
61 | }
62 |
63 | &.fm-radio-medium {
64 | width: 20px;
65 | height: 20px;
66 |
67 | .fm-radio-inner::after {
68 | width: 8px;
69 | height: 8px;
70 | }
71 | }
72 |
73 | &.fm-radio-large {
74 | width: 24px;
75 | height: 24px;
76 |
77 | .fm-radio-inner::after {
78 | width: 10px;
79 | height: 10px;
80 | }
81 | }
82 |
83 | &.is-disabled {
84 | background: @background-color-disabled;
85 | cursor: not-allowed;
86 |
87 | .fm-radio-inner {
88 | border: @border-width-base solid #edeef2;
89 | }
90 | }
91 |
92 | &.is-checked {
93 | .fm-radio-inner {
94 | background: @background-color-light-1;
95 | border-color: @border-primary-color;
96 |
97 | &::after {
98 | transform: translate(-50%, -50%) scale(1);
99 | }
100 | }
101 |
102 | &.is-disabled {
103 | cursor: not-allowed;
104 |
105 | .fm-radio-inner {
106 | background: #eceff5;
107 | border: @border-width-base solid #edeef2;
108 |
109 | &::after {
110 | transform: translate(-50%, -50%) scale(1);
111 | }
112 | }
113 | }
114 | }
115 | }
116 |
117 | .fm-radio-original {
118 | opacity: 0;
119 | outline: none;
120 | position: absolute;
121 | margin: 0;
122 | width: 0;
123 | height: 0;
124 | left: -999px;
125 | }
126 |
127 | .fm-radio-label {
128 | display: inline-block;
129 | padding-left: 5px;
130 | line-height: 1;
131 | font-size: @normal-font-size;
132 | }
133 |
--------------------------------------------------------------------------------
/theme-default/src/rate.less:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "fmiconfont";
3 | src: url('//at.alicdn.com/t/font_498037_qkagtul9gjozjjor.eot?t=1512529692256');
4 | /* IE9 */
5 | src:
6 | url('//at.alicdn.com/t/font_498037_qkagtul9gjozjjor.eot?t=1512529692256#iefix') format('embedded-opentype'),
7 | /* IE6-IE8 */
8 | url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAXgAAsAAAAACGQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW8kf1Y21hcAAAAYAAAABkAAABnM7EabpnbHlmAAAB5AAAAfgAAAIodwKuDWhlYWQAAAPcAAAAMQAAADYPukQyaGhlYQAABBAAAAAgAAAAJAffA4JobXR4AAAEMAAAABAAAAAQD+n//WxvY2EAAARAAAAACgAAAAoBigDObWF4cAAABEwAAAAfAAAAIAETAF1uYW1lAAAEbAAAAUUAAAJtPlT+fXBvc3QAAAW0AAAAKwAAAEAlyByKeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/sU4gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDzjYG7438AQw9zI0AIUZgTJAQAkyQx7eJzFkNENwCAIRI9qDWk6Sj+bDtSvjuDErGEP9McJPPMELkQMAHYAiVwkA/JB4HrpSvgJR/gZD2vl2RirFdPWpswl0aGRbf6yFCyTrBs964z7HpXvuw74RSsd36tpB+kHFucPq3icHY+9b9NAGMbvvcudv2K78dk+x26cT9uFpokSgjMgEhVYihiQmBgZOjCAxFQhZUgHKEgdstEZIfFPVGJDiAqpAyMSLWSBHTEAF6y+eob3GR7p90MUodU5OSYB4mgDDdAtdBchYJvQsnANmtmohzfBa1JPuBbJ2llTabd65DqIFnP9YT5KBVOYDRbEcKU5zLMezuDqaIKvwdCvAVSj8J6TrDtkAXqQxc/kbfwavHp73Z5syZ3u1B02uLpXdpyq4xyqjFIV45JtwSPha1TTmXxD7dA7rl/CdShXs/DOfbMROQ9ejB7XEqEBzOfAo4b1dloJK0Vmoc+dqrJmqkFotjsu7C2NgJdr6XdUHC5cX5EV2UUR6iMkWlk65lmajxOeDwWnMdjAfZYllBHhKyTJBSXpFJI0M4ApnxXFwOMfumbBjYVSUxZw09L0n2MsTHkmv9LDUxUGAIYBMMDaqTwrs/k+K+/SYrC91CPSh45pyi99EulL+c6qsKMjgrdPVFN+O2DsAGJTPbmMDSjJv0bBimD1bzUrEbKPsqJcMMYgLjAtEAVpH9KWDaywyNPiL0QmAE9mutCfGm0fuKtYH9ZMvEW488vrur8dTgbYtN9bqluIdvBDKHEu/3gR7ZLA/ljpCnnu2fpLXX+u2R7Eout8sgOyQcP/7Qph8HicY2BkYGAA4mWRRg7x/DZfGbhZGEDgmm+DDIz+//d/DQszcyOQy8HABBIFABwcCqwAAAB4nGNgZGBgbvjfwBDDwvD/7/+/LMwMQBEUwAIAoJEGaAQAAAAD6QAABAAAAAQA//0AAAAAAHYAzgEUAAB4nGNgZGBgYGEIZGBlAAEmIOYCQgaG/2A+AwAREgFxAHicZY9NTsMwEIVf+gekEqqoYIfkBWIBKP0Rq25YVGr3XXTfpk6bKokjx63UA3AejsAJOALcgDvwSCebNpbH37x5Y08A3OAHHo7fLfeRPVwyO3INF7gXrlN/EG6QX4SbaONVuEX9TdjHM6bCbXRheYPXuGL2hHdhDx18CNdwjU/hOvUv4Qb5W7iJO/wKt9Dx6sI+5l5XuI1HL/bHVi+cXqnlQcWhySKTOb+CmV7vkoWt0uqca1vEJlODoF9JU51pW91T7NdD5yIVWZOqCas6SYzKrdnq0AUb5/JRrxeJHoQm5Vhj/rbGAo5xBYUlDowxQhhkiMro6DtVZvSvsUPCXntWPc3ndFsU1P9zhQEC9M9cU7qy0nk6T4E9XxtSdXQrbsuelDSRXs1JErJCXta2VELqATZlV44RelzRiT8oZ0j/AAlabsgAAAB4nGNgYoAALgbsgIWRiZGZkYWRlYGxgqO4JLFINz8tjR3CyGNgAABOMQaRAA==') format('woff'),
9 | url('//at.alicdn.com/t/font_498037_qkagtul9gjozjjor.ttf?t=1512529692256') format('truetype'),
10 | /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
11 | url('//at.alicdn.com/t/font_498037_qkagtul9gjozjjor.svg?t=1512529692256#iconfont') format('svg');
12 | /* iOS 4.1- */
13 | }
14 |
15 | .fm-iconfont {
16 | font-family: "fmiconfont" !important;
17 | font-size: 20px;
18 | margin-right: 6px;
19 | transition: .3s;
20 | color: #ffbe58;
21 | font-style: normal;
22 | -webkit-font-smoothing: antialiased;
23 | -moz-osx-font-smoothing: grayscale;
24 | }
25 |
26 | .fm-icon-star-off:before {
27 | content: "\e606";
28 | }
29 |
30 | .fm-icon-star-on:before {
31 | content: "\e608";
32 | }
33 | .fm-rate-icon-item {
34 | font-size: 0;
35 | display: inline-block;
36 | position: relative;
37 | }
38 | .fm-score {
39 | font-size: 16px;
40 | }
41 |
--------------------------------------------------------------------------------
/theme-default/src/select.less:
--------------------------------------------------------------------------------
1 | @import 'common/var.less';
2 |
3 | .fm-zoom-in-top-enter-active,
4 | .fm-zoom-in-top-leave-active {
5 | opacity: 1;
6 | transform: scaleY(1);
7 | transition: @all-transition;
8 | transform-origin: center top;
9 | }
10 |
11 | .fm-zoom-in-top-enter,
12 | .fm-zoom-in-top-leave-active {
13 | opacity: 0;
14 | transform: scaleY(0);
15 | }
16 |
17 | .fm-select {
18 | position: relative;
19 | display: inline-block;
20 | vertical-align: top;
21 | user-select: none;
22 | min-width: 180px;
23 | height: 40px;
24 | cursor: pointer;
25 | background: @background-color-white;
26 | border-radius: @border-radius-normal;
27 | border-bottom: @border-width-base solid @background-color-light-4;
28 |
29 | &.is-disabled {
30 | background: @background-color-disabled;
31 | cursor: not-allowed;
32 | padding-left: 6px;
33 |
34 | &:hover {
35 | border-color: @background-color-light-4;
36 | }
37 |
38 | .fm-selected-trigger {
39 | cursor: not-allowed;
40 | }
41 | }
42 |
43 | .fm-selected-trigger {
44 | position: relative;
45 | display: block;
46 | font-size: @mid-font-size;
47 | cursor: pointer;
48 | margin: 0;
49 | overflow: hidden;
50 | padding: 9px 20px 9px 0;
51 | line-height: 22px;
52 | }
53 |
54 | .fm-select-icon {
55 | position: absolute;
56 | top: 50%;
57 | margin-top: -2px;
58 | right: 6px;
59 | content: '';
60 | width: 0;
61 | height: 0;
62 | border: 6px solid transparent;
63 | border-top-color: #767676;
64 | transition: all .3s linear;
65 | transform-origin: center;
66 |
67 | &.active {
68 | margin-top: -8px;
69 | transform: rotate(180deg);
70 | }
71 | }
72 | }
73 |
74 | .fm-selectable-list-wrap {
75 | position: absolute;
76 | width: 100%;
77 | z-index: @z-index-popper;
78 | background-color: @background-color-white;
79 | box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.1);
80 | box-sizing: border-box;
81 | margin: 5px 0;
82 | overflow: hidden;
83 | border-radius: 6px;
84 |
85 | p {
86 | padding: 6px 0;
87 | margin: 0;
88 | text-align: center;
89 | color: @color-regular-3;
90 | font-size: @normal-font-size;
91 | }
92 | }
93 |
94 | .fm-selectable-list {
95 | position: relative;
96 | padding: 5px 0;
97 | margin: 0;
98 | box-sizing: border-box;
99 | max-height: 275px;
100 | width: 100%;
101 | overflow-x: hidden;
102 | overflow-y: auto;
103 | font-size: @normal-font-size;
104 |
105 | .fm-select-option {
106 | position: relative;
107 | white-space: nowrap;
108 | overflow: hidden;
109 | text-overflow: ellipsis;
110 | cursor: pointer;
111 | height: 40px;
112 | width: 100%;
113 | padding: 5px 10px;
114 | line-height: 22px;
115 | font-size: 16px;
116 | box-sizing: border-box;
117 | color: #333;
118 |
119 | &.hover {
120 | background-color: #f0f0f0;
121 | }
122 |
123 | &.selected {
124 | position: relative;
125 | color: @color-primary;
126 |
127 | i {
128 | position: absolute;
129 | right: 16px;
130 | top: 8px;
131 | }
132 | }
133 | }
134 | }
135 |
136 | .focus-border {
137 | position: absolute;
138 | bottom: 0;
139 | left: 0;
140 | width: 0;
141 | height: 1px;
142 | transition: 0.4s;
143 | }
144 |
145 | .focus-border-active {
146 | width: 100%;
147 | height: 2px;
148 | transition: 0.4s;
149 | background-color: @color-primary;
150 | }
151 |
--------------------------------------------------------------------------------
/theme-default/src/switch.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-switch {
4 | display: inline-block;
5 | position: relative;
6 | box-sizing: border-box;
7 | font-size: @normal-font-size;
8 | line-height: 22px;
9 | height: 22px;
10 | min-width: 46px;
11 | vertical-align: middle;
12 | cursor: pointer;
13 |
14 | .fm-switch-input {
15 | display: none;
16 | }
17 | }
18 |
19 | .fm-switch-label {
20 | position: absolute;
21 | z-index: @z-index-normal;
22 | top: 0;
23 | right: 0;
24 | bottom: 0;
25 | left: 0;
26 | border-radius: 12px;
27 | background: @background-color-disabled;
28 | transition: background .3s;
29 |
30 | &.is-checked {
31 | background: @background-color-primary;
32 | }
33 |
34 | &.is-disabled {
35 | cursor: not-allowed;
36 | opacity: 0.8;
37 | }
38 | }
39 |
40 | .fm-switch-btn {
41 | position: absolute;
42 | z-index: @z-index-regular;
43 | top: 50%;
44 | margin-top: -8px;
45 | width: 16px;
46 | height: 16px;
47 | border-radius: 50%;
48 | background: @background-color-white;
49 | transition: left .3s;
50 | }
51 |
--------------------------------------------------------------------------------
/theme-default/src/tab-panel.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fmfe/fm-vue-ui/abf32d041b5dce268b273309eae9152a802be076/theme-default/src/tab-panel.less
--------------------------------------------------------------------------------
/theme-default/src/tabs.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-tabs-wrap {
4 | position: relative;
5 | background-color: @background-color-white;
6 | }
7 |
8 | .fm-tabs-nav-list {
9 | position: relative;
10 | border-bottom: @border-width-base solid @border-regular-color-3;
11 | height: 45px;
12 | font-size: 0;
13 | }
14 |
15 | .fm-tabs-content {
16 | position: relative;
17 | overflow: hidden;
18 | z-index: @z-index-normal;
19 | padding-top: 15px;
20 | background-color: @background-color-white;
21 | }
22 |
23 | .fm-tabs-nav-item {
24 | position: relative;
25 | display: inline-block;
26 | margin-right: 1px;
27 | width: 110px;
28 | font-size: 0;
29 | color: @color-regular-2;
30 | font-weight: @font-weight-max;
31 | text-align: center;
32 | cursor: pointer;
33 | overflow: hidden;
34 | &:hover {
35 | color: @color-primary;
36 | }
37 | &.fm-tabs-nav-panel {
38 | height: 43px;
39 | line-height: 43px;
40 | font-size: @normal-font-size;
41 | &:after {
42 | content: '';
43 | position: absolute;
44 | top: 50%;
45 | right: -1px;
46 | height: 12px;
47 | border: none;
48 | width: 1px;
49 | margin-top: -6px;
50 | background-color: @background-color-light-4;
51 | }
52 | &:last-child:after {
53 | display: none;
54 | }
55 | &.active {
56 | height: 45px;
57 | color: @color-primary;
58 | border-bottom: @border-width-normal solid @color-primary;
59 | }
60 | }
61 | &.fm-tabs-nav-card {
62 | height: 45px;
63 | line-height: 45px;
64 | margin-right: 0;
65 | font-size: @normal-font-size;
66 | border: @border-width-base solid @border-regular-color-3;
67 | border-right: none;
68 | &:after {
69 | display: none;
70 | }
71 | &:first-child {
72 | border-top-left-radius: @border-radius-max;
73 | }
74 | &:last-child {
75 | border-right: @border-width-base solid @border-regular-color-3;
76 | border-top-right-radius: @border-radius-max;
77 | }
78 | &.active {
79 | color: @color-primary;
80 | border-bottom-color: @border-white-color;
81 | background-color: @background-color-white;
82 | }
83 | }
84 | .fm-tab-delete-icon {
85 | position: absolute;
86 | top: 2px;
87 | right: 2px;
88 | width: 12px;
89 | height: 12px;
90 | cursor: pointer;
91 | background-color: @background-color-light-1;
92 | &::before, &::after {
93 | position: absolute;
94 | top: 3px;
95 | content: '';
96 | height: 7px;
97 | width: 1px;
98 | background-color: @background-color-white;
99 | }
100 | &::before {
101 | transform: rotate(45deg);
102 | }
103 | &::after {
104 | transform: rotate(-45deg);
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/theme-default/src/toast.less:
--------------------------------------------------------------------------------
1 | @import "./common/var.less";
2 |
3 | .fm-toast-fade-enter,
4 | .fm-toast-fade-leave-active {
5 | opacity: 0;
6 | margin-top: -100%;
7 | }
8 |
9 | .fm-toast {
10 | position: fixed;
11 | z-index: @z-index-popper;
12 | top: 60px;
13 | left: 50%;
14 | background: @background-color-white;
15 | transform: translateX(-50%);
16 | display: block;
17 | vertical-align: middle;
18 | border-radius: 6px;
19 | text-align: left;
20 | white-space: normal;
21 | min-width: 140px;
22 | box-shadow: 0 0 16px 0 rgba(0, 0, 0, 0.15);
23 | border: 1px solid rgba(230, 230, 230, 1);
24 | padding: 12px 20px;
25 | transition: all 0.3s;
26 | box-sizing: border-box;
27 |
28 | .fm-toast-icon {
29 | font-size: 28px;
30 | vertical-align: middle;
31 | display: table-cell;
32 |
33 | &.success {
34 | color: @icon-color-success;
35 | }
36 |
37 | &.info {
38 | color: @icon-color-info;
39 | }
40 |
41 | &.warning {
42 | color: @icon-color-warning;
43 | }
44 |
45 | &.error {
46 | color: @icon-color-error;
47 | }
48 | }
49 |
50 | .fm-toast-message {
51 | font-size: 14px;
52 | color: #333;
53 | line-height: 20px;
54 | display: table-cell;
55 | max-width: 300px;
56 | vertical-align: middle;
57 | word-break: break-word;
58 | padding-left: 5px;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------