提交 fd7268df 编写于 作者: B break60 提交者: qiaozhanwei

Ans UI upgrade to version 0.1.0 (#1181)

+ 1
上级 de996377
......@@ -14,6 +14,8 @@
"build:combined": "npm run clean && cross-env NODE_ENV=production PUBLIC_PATH=/dolphinscheduler/ui webpack --config ./build/webpack.config.combined.js"
},
"dependencies": {
"ans-ui": "0.1.0",
"axios": "^0.16.2",
"jquery": "1.12.4",
"vue": "^2.5.17",
"vue-router": "2.7.0",
......@@ -27,7 +29,6 @@
"echarts": "^4.1.0",
"html2canvas": "^0.5.0-beta4",
"jsplumb": "^2.8.6",
"axios": "^0.16.2",
"lodash": "^4.17.11",
"vuex-router-sync": "^4.1.2"
},
......
......@@ -28,9 +28,9 @@ import Chart from '@/module/ana-charts'
import '@/module/filter/formatDate'
import themeData from '@/module/echarts/themeData.json'
import Permissions from '@/module/permissions'
import '~/@analysys/ans-ui/lib/ans-ui.min.css'
import ans from '~/@analysys/ans-ui/lib/ans-ui.min'
import en_US from '~/@analysys/ans-ui/lib/locale/en' // eslint-disable-line
import 'ans-ui/lib/ans-ui.min.css'
import ans from 'ans-ui/lib/ans-ui.min'
import en_US from 'ans-ui/lib/locale/en' // eslint-disable-line
import 'sass/conf/home/index.scss'
// Component internationalization
......
......@@ -21,8 +21,8 @@ import $ from 'jquery'
import Vue from 'vue'
import App from './App'
import i18n from '@/module/i18n'
import '~/@analysys/ans-ui/lib/ans-ui.min.css'
import ans from '~/@analysys/ans-ui/lib/ans-ui.min'
import 'ans-ui/lib/ans-ui.min.css'
import ans from 'ans-ui/lib/ans-ui.min'
import 'sass/conf/login/index.scss'
......
## Ans-UI
component x base on vue.js
### Install
安装node > 8的LTS版本,https://nodejs.org/en/
```sh
yarn add @analysys/ans-ui | npm i @analysys/ans-ui
```
### Usage
全部引入
```javascript
import Vue from 'vue';
import '@analysys/ans-ui/lib/ans-ui.min.css';
import ans from '@analysys/ans-ui/lib/ans-ui.min.js';
Vue.use(ans);
```
按需引入
```javascript
import Vue from 'vue';
import '@analysys/ans-ui/lib/ans-ui.min.css';
import { xButton } from '@analysys/ans-ui/lib/ans-ui.min.js';
Vue.use(xButton);
```
### Build
```sh
yarn global add parcel-bundler | npm i -g parcel-bundler
# development default listen to 4000
yarn dev | npm run dev
# production
yarn build | npm run build
```
### Build Single Component
```sh
yarn global add parcel-bundler | npm i -g parcel-bundler
# development button 可以替换为任意组件名
yarn dev:c button | npm run dev:c button
# production button 可以替换为任意组件名
yarn build:c button | npm run build:c button
#or
yarn dev:c | npm run dev:c
```
因为 它太大了无法显示 source diff 。你可以改为 查看blob
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("ans-ui/locale",[],t):"object"==typeof exports?exports["ans-ui/locale"]=t():e["ans-ui/locale"]=t()}("undefined"!=typeof self?self:this,function(){return function(e){var t={};function o(n){if(t[n])return t[n].exports;var a=t[n]={i:n,l:!1,exports:{}};return e[n].call(a.exports,a,a.exports,o),a.l=!0,a.exports}return o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="/lib/locale/",o(o.s=0)}([function(e,t,o){"use strict";t.__esModule=!0,t.default={ans:{modal:{confirm:"OK",cancel:"Cancel"},cascader:{placeholder:"Select",noMatch:"No matching data",noData:"No data"},datepicker:{placeholder:"Select date",cancel:"Cancel",confirm:"OK",year:"",month1:"January",month2:"February",month3:"March",month4:"April",month5:"May",month6:"June",month7:"July",month8:"August",month9:"September",month10:"October",month11:"November",month12:"December",weeks:{sun:"Sun",mon:"Mon",tue:"Tue",wed:"Wed",thu:"Thu",fri:"Fri",sat:"Sat"},selectTime:"Select time",startTime:"Start time",endTime:"End time"},input:{placeholder:"Please enter..."},page:{goto:"Go to",pagesize:"/page",total:"Total {total}",pageClassifier:""},poptip:{confirm:"OK",cancel:"Cancel"},select:{placeholder:"Select",noMatch:"No matching data",noData:"No data",search:"Keyword"},table:{emptyText:"No data"},timepicker:{clear:"Clear",confirm:"OK",placeholder:"Select"}}}}])});
//# sourceMappingURL=en.js.map
\ No newline at end of file
{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap a8176009a698a35da039","webpack:///./src/locale/lang/en.js"],"names":["root","factory","exports","module","define","amd","self","this","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","ans","modal","confirm","cancel","cascader","placeholder","noMatch","noData","datepicker","year","month1","month2","month3","month4","month5","month6","month7","month8","month9","month10","month11","month12","weeks","sun","mon","tue","wed","thu","fri","sat","selectTime","startTime","endTime","input","page","goto","pagesize","total","pageClassifier","poptip","select","search","table","emptyText","timepicker","clear"],"mappings":"CAAA,SAAAA,EAAAC,GACA,iBAAAC,SAAA,iBAAAC,OACAA,OAAAD,QAAAD,IACA,mBAAAG,eAAAC,IACAD,OAAA,mBAAAH,GACA,iBAAAC,QACAA,QAAA,iBAAAD,IAEAD,EAAA,iBAAAC,IARA,CASC,oBAAAK,UAAAC,KAAA,WACD,mBCTA,IAAAC,KAGA,SAAAC,EAAAC,GAGA,GAAAF,EAAAE,GACA,OAAAF,EAAAE,GAAAR,QAGA,IAAAC,EAAAK,EAAAE,IACAC,EAAAD,EACAE,GAAA,EACAV,YAUA,OANAW,EAAAH,GAAAI,KAAAX,EAAAD,QAAAC,IAAAD,QAAAO,GAGAN,EAAAS,GAAA,EAGAT,EAAAD,QAqCA,OAhCAO,EAAAM,EAAAF,EAGAJ,EAAAO,EAAAR,EAGAC,EAAAQ,EAAA,SAAAf,EAAAgB,EAAAC,GACAV,EAAAW,EAAAlB,EAAAgB,IACAG,OAAAC,eAAApB,EAAAgB,GACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,KAMAV,EAAAiB,EAAA,SAAAvB,GACA,IAAAgB,EAAAhB,KAAAwB,WACA,WAA2B,OAAAxB,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAM,EAAAQ,EAAAE,EAAA,IAAAA,GACAA,GAIAV,EAAAW,EAAA,SAAAQ,EAAAC,GAAsD,OAAAR,OAAAS,UAAAC,eAAAjB,KAAAc,EAAAC,IAGtDpB,EAAAuB,EAAA,eAGAvB,IAAAwB,EAAA,6DC5DEC,KACEC,OACEC,QAAS,KACTC,OAAQ,UAEVC,UACEC,YAAa,SACbC,QAAS,mBACTC,OAAQ,WAEVC,YACEH,YAAa,cACbF,OAAQ,SACRD,QAAS,KACTO,KAAM,GACNC,OAAQ,UACRC,OAAQ,WACRC,OAAQ,QACRC,OAAQ,QACRC,OAAQ,MACRC,OAAQ,OACRC,OAAQ,OACRC,OAAQ,SACRC,OAAQ,YACRC,QAAS,UACTC,QAAS,WACTC,QAAS,WACTC,OACEC,IAAK,MACLC,IAAK,MACLC,IAAK,MACLC,IAAK,MACLC,IAAK,MACLC,IAAK,MACLC,IAAK,OAEPC,WAAY,cACZC,UAAW,aACXC,QAAS,YAEXC,OACE5B,YAAa,mBAEf6B,MACEC,KAAM,QACNC,SAAU,QACVC,MAAO,gBACPC,eAAgB,IAElBC,QACErC,QAAS,KACTC,OAAQ,UAEVqC,QACEnC,YAAa,SACbC,QAAS,mBACTC,OAAQ,UACRkC,OAAQ,WAEVC,OACEC,UAAW,WAEbC,YACEC,MAAO,QACP3C,QAAS,KACTG,YAAa","file":"en.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"ans-ui/locale\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ans-ui/locale\"] = factory();\n\telse\n\t\troot[\"ans-ui/locale\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/lib/locale/\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap a8176009a698a35da039","export default {\n ans: {\n modal: {\n confirm: 'OK',\n cancel: 'Cancel'\n },\n cascader: {\n placeholder: 'Select',\n noMatch: 'No matching data',\n noData: 'No data'\n },\n datepicker: {\n placeholder: 'Select date',\n cancel: 'Cancel',\n confirm: 'OK',\n year: '',\n month1: 'January',\n month2: 'February',\n month3: 'March',\n month4: 'April',\n month5: 'May',\n month6: 'June',\n month7: 'July',\n month8: 'August',\n month9: 'September',\n month10: 'October',\n month11: 'November',\n month12: 'December',\n weeks: {\n sun: 'Sun',\n mon: 'Mon',\n tue: 'Tue',\n wed: 'Wed',\n thu: 'Thu',\n fri: 'Fri',\n sat: 'Sat'\n },\n selectTime: 'Select time',\n startTime: 'Start time',\n endTime: 'End time'\n },\n input: {\n placeholder: 'Please enter...'\n },\n page: {\n goto: 'Go to',\n pagesize: '/page',\n total: 'Total {total}',\n pageClassifier: ''\n },\n poptip: {\n confirm: 'OK',\n cancel: 'Cancel'\n },\n select: {\n placeholder: 'Select',\n noMatch: 'No matching data',\n noData: 'No data',\n search: 'Keyword'\n },\n table: {\n emptyText: 'No data'\n },\n timepicker: {\n clear: 'Clear',\n confirm: 'OK',\n placeholder: 'Select'\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/locale/lang/en.js"],"sourceRoot":""}
\ No newline at end of file
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("ans-ui/locale",[],t):"object"==typeof exports?exports["ans-ui/locale"]=t():e["ans-ui/locale"]=t()}("undefined"!=typeof self?self:this,function(){return function(e){var t={};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}return o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="/lib/locale/",o(o.s=1)}([,function(e,t,o){"use strict";t.__esModule=!0,t.default={ans:{modal:{confirm:"确定",cancel:"取消"},cascader:{placeholder:"请选择",noMatch:"搜索无结果",noData:"暂无数据"},datepicker:{placeholder:"请选择日期",cancel:"取消",confirm:"确定",year:"",month1:"1 月",month2:"2 月",month3:"3 月",month4:"4 月",month5:"5 月",month6:"6 月",month7:"7 月",month8:"8 月",month9:"9 月",month10:"10 月",month11:"11 月",month12:"12 月",weeks:{sun:"",mon:"",tue:"",wed:"",thu:"",fri:"",sat:""},selectTime:"选择时间",startTime:"开始时间",endTime:"结束时间"},input:{placeholder:"请输入..."},page:{goto:"跳转至",pagesize:"条/页",total:"共 {total} 条",pageClassifier:""},poptip:{confirm:"确定",cancel:"取消"},select:{placeholder:"请选择",noMatch:"搜索无结果",noData:"暂无数据",search:"搜索"},table:{emptyText:"暂无数据"},timepicker:{clear:"清空",confirm:"确定",placeholder:"请选择时间"}}}}])});
//# sourceMappingURL=zh-CN.js.map
\ No newline at end of file
{"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap a8176009a698a35da039","webpack:///./src/locale/lang/zh-CN.js"],"names":["root","factory","exports","module","define","amd","self","this","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","ans","modal","confirm","cancel","cascader","placeholder","noMatch","noData","datepicker","year","month1","month2","month3","month4","month5","month6","month7","month8","month9","month10","month11","month12","weeks","sun","mon","tue","wed","thu","fri","sat","selectTime","startTime","endTime","input","page","goto","pagesize","total","pageClassifier","poptip","select","search","table","emptyText","timepicker","clear"],"mappings":"CAAA,SAAAA,EAAAC,GACA,iBAAAC,SAAA,iBAAAC,OACAA,OAAAD,QAAAD,IACA,mBAAAG,eAAAC,IACAD,OAAA,mBAAAH,GACA,iBAAAC,QACAA,QAAA,iBAAAD,IAEAD,EAAA,iBAAAC,IARA,CASC,oBAAAK,UAAAC,KAAA,WACD,mBCTA,IAAAC,KAGA,SAAAC,EAAAC,GAGA,GAAAF,EAAAE,GACA,OAAAF,EAAAE,GAAAR,QAGA,IAAAC,EAAAK,EAAAE,IACAC,EAAAD,EACAE,GAAA,EACAV,YAUA,OANAW,EAAAH,GAAAI,KAAAX,EAAAD,QAAAC,IAAAD,QAAAO,GAGAN,EAAAS,GAAA,EAGAT,EAAAD,QAqCA,OAhCAO,EAAAM,EAAAF,EAGAJ,EAAAO,EAAAR,EAGAC,EAAAQ,EAAA,SAAAf,EAAAgB,EAAAC,GACAV,EAAAW,EAAAlB,EAAAgB,IACAG,OAAAC,eAAApB,EAAAgB,GACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,KAMAV,EAAAiB,EAAA,SAAAvB,GACA,IAAAgB,EAAAhB,KAAAwB,WACA,WAA2B,OAAAxB,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAM,EAAAQ,EAAAE,EAAA,IAAAA,GACAA,GAIAV,EAAAW,EAAA,SAAAQ,EAAAC,GAAsD,OAAAR,OAAAS,UAAAC,eAAAjB,KAAAc,EAAAC,IAGtDpB,EAAAuB,EAAA,eAGAvB,IAAAwB,EAAA,8DC5DEC,KACEC,OACEC,QAAS,KACTC,OAAQ,MAEVC,UACEC,YAAa,MACbC,QAAS,QACTC,OAAQ,QAEVC,YACEH,YAAa,QACbF,OAAQ,KACRD,QAAS,KACTO,KAAM,IACNC,OAAQ,MACRC,OAAQ,MACRC,OAAQ,MACRC,OAAQ,MACRC,OAAQ,MACRC,OAAQ,MACRC,OAAQ,MACRC,OAAQ,MACRC,OAAQ,MACRC,QAAS,OACTC,QAAS,OACTC,QAAS,OACTC,OACEC,IAAK,IACLC,IAAK,IACLC,IAAK,IACLC,IAAK,IACLC,IAAK,IACLC,IAAK,IACLC,IAAK,KAEPC,WAAY,OACZC,UAAW,OACXC,QAAS,QAEXC,OACE5B,YAAa,UAEf6B,MACEC,KAAM,MACNC,SAAU,MACVC,MAAO,cACPC,eAAgB,KAElBC,QACErC,QAAS,KACTC,OAAQ,MAEVqC,QACEnC,YAAa,MACbC,QAAS,QACTC,OAAQ,OACRkC,OAAQ,MAEVC,OACEC,UAAW,QAEbC,YACEC,MAAO,KACP3C,QAAS,KACTG,YAAa","file":"zh-CN.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"ans-ui/locale\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ans-ui/locale\"] = factory();\n\telse\n\t\troot[\"ans-ui/locale\"] = factory();\n})(typeof self !== 'undefined' ? self : this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/lib/locale/\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap a8176009a698a35da039","export default {\n ans: {\n modal: {\n confirm: '确定',\n cancel: '取消'\n },\n cascader: {\n placeholder: '请选择',\n noMatch: '搜索无结果',\n noData: '暂无数据'\n },\n datepicker: {\n placeholder: '请选择日期',\n cancel: '取消',\n confirm: '确定',\n year: '年',\n month1: '1 月',\n month2: '2 月',\n month3: '3 月',\n month4: '4 月',\n month5: '5 月',\n month6: '6 月',\n month7: '7 月',\n month8: '8 月',\n month9: '9 月',\n month10: '10 月',\n month11: '11 月',\n month12: '12 月',\n weeks: {\n sun: '日',\n mon: '一',\n tue: '二',\n wed: '三',\n thu: '四',\n fri: '五',\n sat: '六'\n },\n selectTime: '选择时间',\n startTime: '开始时间',\n endTime: '结束时间'\n },\n input: {\n placeholder: '请输入...'\n },\n page: {\n goto: '跳转至',\n pagesize: '条/页',\n total: '共 {total} 条',\n pageClassifier: '页'\n },\n poptip: {\n confirm: '确定',\n cancel: '取消'\n },\n select: {\n placeholder: '请选择',\n noMatch: '搜索无结果',\n noData: '暂无数据',\n search: '搜索'\n },\n table: {\n emptyText: '暂无数据'\n },\n timepicker: {\n clear: '清空',\n confirm: '确定',\n placeholder: '请选择时间'\n }\n }\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/locale/lang/zh-CN.js"],"sourceRoot":""}
\ No newline at end of file
{
"name": "@analysys/ans-ui",
"version": "0.0.22",
"description": "vue components for analysys",
"keywords": [
"analysys",
"UI"
],
"main": "lib/ans-ui.min.js",
"style": "lib/ans-ui.min.css",
"files": [
"lib",
"src",
"packages"
],
"license": "MIT",
"scripts": {
"build": "npm run clean && cross-env NODE_ENV=production webpack --config ./build/webpack.config.prod.js && webpack --config ./build/webpack.config.locale.js",
"dev": "npm run clean && parcel ./example/index.html -p 4000",
"build:c": "node build/component/buildComponent.js",
"dev:c": "npm run clean && rimraf .cache && node build/component/devComponent.js",
"clean": "rimraf dist",
"lint": "standard \"**/*.{js,vue}\"",
"lint:fix": "standard \"**/*.{js,vue}\" --fix",
"theme": "node-sass --output-style compressed ./src/style/index.scss > ./theme/ans.min.css && cp -rf ./src/style/font/. ./theme/font",
"prepublishOnly": "npm run build",
"start": "npm run dev",
"test": "npm run lint"
},
"peerDependencies": {
"vue": ">=2"
},
"dependencies": {
"async-validator": "^1.10.0",
"dayjs": "^1.7.7",
"deepmerge": "^3.2.0",
"normalize-wheel": "^1.0.1",
"popper.js": "^1.14.4"
},
"devDependencies": {
"@fedor/progress-webpack-plugin": "^1.0.0",
"@fedor/standard": "^1.0.3",
"autoprefixer": "^9.0.1",
"babel-core": "^6.25.0",
"babel-eslint": "^8.2.6",
"babel-helper-vue-jsx-merge-props": "^2.0.0",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-plugin-transform-vue-jsx": "^3.7.0",
"babel-preset-env": "^1.5.2",
"cross-env": "^5.2.0",
"css-loader": "0.28.8",
"cssnano": "^4.0.3",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.11",
"ghooks": "^2.0.4",
"inquirer": "^6.2.0",
"node-sass": "^4.9.3",
"optimize-css-assets-webpack-plugin": "3.2.0",
"postcss-loader": "^2.1.6",
"rimraf": "^2.6.2",
"sass-loader": "^7.0.3",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.7",
"url-loader": "^1.0.1",
"vue": "^2.5.17",
"vue-loader": "^15.4.2",
"vue-router": "^3.0.1",
"vue-style-loader": "^4.1.1",
"vue-template-compiler": "^2.5.17",
"webpack": "^3",
"webpack-merge": "^4.1.3"
},
"maintainers": [
{
"name": "liuxin",
"email": "liuxin@analysys.com.cn"
}
],
"standard": {
"parser": "babel-eslint",
"ignore": [
"lib/*",
"packages/*"
]
},
"config": {
"ghooks": {}
}
}
## Box
Box包含modal、message、notice三个组件。
### Modal options
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
title | 标题 | String / DOM | - | -
content | 内容 | String / DOM | - | -
width | 宽度 | Number / String | - | 520
className | 自定义样式名称 | String | - | -
closable | 是否显示关闭 | Boolean | - | true
escClose | 是否按 esc 键关闭 | Boolean | - | false
ok | 点击确定的回调 | Object | {show [Boolean] ,text [String], handle[Function]} | -
cancel | 点击取消的回调 | Object | {show [Boolean] ,text [String], handle[Function]} | -
render | 自定义内容 | Function | 使用时 content, title ,ok , cancel 失效 | -
showMask | 是否显示遮罩 | Boolean | - | false
maskClosable | 点击遮罩是否关闭 | Boolean | - | false
#### Modal 实例方法
instance.remove() 销毁当前实例
#### Modal 全局相关
this.$modal.destroy() 全局销毁所有实例
### Message options
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
content | 内容 | String | - | -
duration | 自动关闭的延时,单位秒,不关闭可以写 0 | Number | - | 1.5
onClose | 关闭时的回调 | Function | - | -
closable | 是否显示关闭图标 | Boolean | - | false
#### Message 全局相关
this.$message.destroy() 全局销毁所有实例
this.$message.config(options) 全局配置
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
top | 提示组件距离顶端的距离,单位像素 | Number | - | 60
duration | 默认自动关闭的延时,单位秒 | Number | - | 1.5
transitionName | 默认动画类名 | String | - | x-ani-move-in
fixed | 显示是否固定位置 | String | Boolean | true
### Notice options
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
title | 标题 | String | - | -
content | 内容 | String | - | -
duration | 自动关闭的延时,单位秒,不关闭可以写 0 | Number | - | 1.5
onClose | 关闭时的回调 | Function | - | -
closable | 是否显示关闭图标 | Boolean | - | false
#### Notice 全局相关
this.$notice.destroy() 全局销毁所有实例
this.$notice.config(options) 全局配置
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
top | 提示组件距离顶端的距离,单位像素 | Number | - | 60
right | 提示组件距离屏幕右侧的距离,单位像素 | Number | - | 20
duration | 默认自动关闭的延时,单位秒 | Number | - | 1.5
transitionName | 默认动画类名 | String | - | x-ani-move-right
list | 显示是否以列表形式展示 | Boolean | - | true
\ No newline at end of file
<template>
<div>
<section class="demo-section">
<h4>modal</h4>
<div>
<x-button type="primary" @click="handleModal">Modal Dialog</x-button>
<x-button type="primary" @click="handleAbstract">Abstract Modal Dialog</x-button>
</div>
</section>
<section class="demo-section">
<h4>message</h4>
<div>
<x-button type="primary" @click="info">消息</x-button>
<x-button type="success" @click="success">成功</x-button>
<x-button type="warning" @click="warning">警告</x-button>
<x-button type="error" @click="error">错误</x-button>
<x-button type="primary" @click="loading">Get...</x-button>
<x-button @click="loading">Loading</x-button>
</div>
</section>
<section class="demo-section">
<h4>notice</h4>
<div>
<x-button type="primary" @click="infoNotice">消息</x-button>
<x-button type="success" @click="successNotice">成功</x-button>
<x-button type="warning" @click="warningNotice">警告</x-button>
<x-button type="error" @click="errorNotice">错误</x-button>
<x-button type="primary" @click="loadingNotice">Get...</x-button>
</div>
</section>
</div>
</template>
<script>
import Vue from 'vue'
import { xButton } from '../../vue-button/src'
import { xModal, xMessage, xNotice } from '../src'
Vue.$message = Vue.prototype.$message = xMessage
Vue.$modal = Vue.prototype.$modal = xModal
Vue.$notice = Vue.prototype.$notice = xNotice
const ModalTest = {
data () {
return {
name: 1
}
},
methods: {
handleAbstract () {
this.$modal.dialog({
title: '你好',
width: 200,
escClose: true,
closable: false,
showMask: true,
className: 'x-modal-custom',
maskClosable: true,
render (h) {
return (
<div class='customize-dialog'>This is a abstract modal dialog.</div>
)
}
})
},
handleModal () {
let self = this
this.$modal.dialog({
className: 'x-modal-custom',
width: 350,
closable: true,
showMask: true,
maskClosable: true,
title: '你好',
content: 'hello word' + (this.name++),
ok: {
show: true,
className: 'x-btn-ok',
handle (e) {
self.$notice.success({
title: 'Success',
content: 'ok',
duration: 2,
onClose () {},
closable: false
})
console.log('ok event handled.', e)
}
},
cancel: {
handle (e) {
self.$notice.info({
title: 'Canceled',
content: 'cancel',
duration: 2,
onClose () {},
closable: false
})
console.log('cancel event handled.', e)
}
}
})
}
}
}
export default {
mixins: [ ModalTest ],
components: { xButton },
data () {
return {
name: 1
}
},
methods: {
info () {
this.$message.info({
content: 'hello word' + (this.name++),
duration: 2,
onClose: function () {
},
closable: false
})
},
infoNotice () {
this.$notice.info({
title: '标题',
content: 'hello word' + (this.name++),
duration: 0,
onClose: function () {
},
closable: true
})
},
success () {
this.$message.success({
content: 'hello word' + (this.name++),
duration: 2,
onClose: function () {
},
closable: false
})
},
successNotice () {
this.$notice.success({
title: '成功',
content: 'hello word' + (this.name++),
duration: 2,
onClose: function () {
},
closable: false
})
},
error () {
this.$message.error({
content: 'hello wordhello wordhello word' + (this.name++),
duration: 0,
onClose: function () {
},
closable: true
})
},
errorNotice () {
this.$notice.error({
title: '错误',
content: 'hello wordhello wordhello word' + (this.name++),
duration: 0,
onClose: function () {
},
closable: true
})
},
loading () {
this.$message.loading({
content: 'hello word' + (this.name++),
duration: 2,
onClose: function () {
},
closable: true
})
},
loadingNotice () {
this.$notice.loading({
title: '加载中',
content: 'hello word' + (this.name++),
duration: 2,
onClose: function () {
},
closable: true
})
},
warning () {
this.$message.warning({
content: 'hello word' + (this.name++),
duration: 2,
onClose: function () {
},
closable: false
})
},
warningNotice () {
this.$notice.warning({
title: '警告',
content: 'hello word' + (this.name++),
duration: 2,
onClose: function () {
},
closable: false
})
}
}
}
</script>
<style lang="scss">
.customize-dialog {
padding: 10px;
background: #ccc;
border-radius: 4px;
}
</style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1">
<script src="https://s1.analysys.cn/libs/??js-polyfills/0.1.42/polyfill.min.js"></script>
<link rel="stylesheet" href="//s2.analysys.cn/libs/??-/@analysys/fss-demo.css@">
<link rel="stylesheet" href="../../../src/style/index.scss">
<title>demo</title>
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
import Vue from 'vue'
import App from './app.vue'
new Vue({
el: '#app',
render: h => h(App),
mounted () {
console.log('success')
}
})
import xMessage from './source/layer/message/message'
import xModal from './source/layer/modal/modal'
import xNotice from './source/layer/notice/notice'
export {
xMessage,
xModal,
xNotice
}
<template>
<transition :name="transitionName">
<div :class="classes" :style="styles">
<template v-if="type === 'message'">
<div :class="[baseClass + '-content']" ref="content">
<div :class="[baseClass + '-content-text']" v-html="content"></div>
<a :class="[baseClass + '-close']" @click="close" v-if="closable">
<i class="ans-icon-close"></i>
</a>
</div>
</template>
<template v-if="type === 'modal'">
<div :class="{msk: className.split(' ').indexOf('mask')!==-1}" ></div>
<div :class="[baseClass + '-content-wrapper']">
<div :class="[baseClass + '-content']" ref="content" v-html="content"></div>
<a :class="[baseClass + '-close']" @click="close" v-if="closable">
<i class="ans-icon-close"></i>
</a>
</div>
</template>
<template v-if="type === 'notice'">
<div :class="[baseClass + '-content']" ref="content">
<div :class="[baseClass + '-content__inner']" v-html="content"></div>
<a :class="[baseClass + '-close']" @click="close" v-if="closable">
<i class="ans-icon-close"></i>
</a>
</div>
</template>
</div>
</transition>
</template>
<script>
import { findComponentUpward } from '../../../../../src/util'
export default {
props: {
name: {
type: String,
required: true
},
type: {
type: String
},
prefixCls: {
type: String,
default: ''
},
transitionName: {
type: String
},
duration: {
type: Number,
default: 1.5
},
content: {
type: String,
default: ''
},
styles: {
type: Object,
default: function () {
return {
right: '50%'
}
}
},
closable: {
type: Boolean,
default: false
},
onClose: {
type: Function,
default: function () {}
},
className: {
type: String,
default: ''
},
escClose: {
type: Boolean,
default: false
}
},
methods: {
clearCloseTimer () {
if (this.closeTimer) {
clearTimeout(this.closeTimer)
this.closeTimer = null
}
},
close () {
this.clearCloseTimer()
this.onClose()
let $parent = findComponentUpward(this, 'xBoxManager')
if ($parent) {
$parent.close(this.name)
}
},
escHandler (event) {
/* eslint-disable */
let e = event || window.event || arguments.callee.caller.arguments[0]
if (e && e.keyCode == 27) { // 按 Esc
this.close()
}
}
},
computed: {
baseClass () {
return `${this.prefixCls}-box`
},
classes () {
return [
`${this.baseClass}`,
`${this.className}`
]
}
},
mounted () {
this.clearCloseTimer()
if (this.duration !== 0) {
this.closeTimer = setTimeout(() => {
this.close()
}, this.duration * 1000)
}
if(this.$listeners.onrender){
this.$listeners.onrender(this.name)
}
// this.$emit('on-mounted', this.name)
if (this.escClose) {
document.onkeyup = this.escHandler
}
}
}
</script>
<template>
<div :class="classes" :style="styles">
<box
v-for="box in boxs"
:key="box.name"
:name="box.name"
:type="box.type"
:prefix-cls="prefixCls"
:transition-name="box.transitionName"
:duration="box.duration"
:content="box.content"
:styles="box.styles"
:closable="box.closable"
:on-close="box.onClose"
:class-name="box.className||''"
:esc-close="box.escClose"
@onrender="box.$onRender"
@on-mounted="_onMounted">
</box>
</div>
</template>
<script>
import { LIB_NAME } from '../../../../../src/util'
import Box from './Box.vue'
const prefixCls = `${LIB_NAME}-layer`
let seed = 0
const now = Date.now()
function getUuid () {
return `${LIB_NAME}-Box_${now}_${seed++}`
}
export default {
name: 'xBoxManager',
components: { Box },
props: {
prefixCls: {
type: String,
default: prefixCls
},
styles: {
type: Object,
default: function () {
return {
top: '65px',
left: '50%'
}
}
},
className: {
type: String
}
},
data () {
return {
boxs: []
}
},
computed: {
classes () {
return [
`${this.prefixCls}`,
`${this.prefixCls}-wrapper`,
{
[`${this.className}`]: !!this.className
}
]
}
},
methods: {
onrender(){},
add (notice) {
const name = notice.name || getUuid()
let _notice = Object.assign({
styles: {
right: '50%'
},
content: '',
duration: 1.5,
closable: false,
name: name
}, notice)
_notice.$onRender = _notice.$onRender?_notice.$onRender: () => {}
this.boxs.push(_notice)
},
close (name) {
const boxs = this.boxs
for (let i = 0; i < boxs.length; i++) {
if (boxs[i].name === name) {
this.boxs.splice(i, 1)
break
}
}
},
closeAll () {
this.boxs = []
},
_onMounted (boxName) {
this.$emit('on-mounted', boxName)
}
}
}
</script>
/**
* Created by tangwei on 17/8/25.
*/
import Vue from 'vue'
import BoxManager from './BoxManager.vue'
BoxManager.newInstance = properties => {
const _props = properties || {}
const Instance = new Vue({
data: _props,
render (h) {
return h(BoxManager, {
props: _props
})
}
})
const component = Instance.$mount()
document.body.appendChild(component.$el)
const notification = Instance.$children[0]
return {
notice (noticeProps) {
notification.add(noticeProps)
},
remove (name) {
notification.close(name)
},
component: notification,
destroy (classname) {
notification.closeAll()
setTimeout(function () {
document.body.removeChild(document.getElementsByClassName(classname)[0])
}, 500)
}
}
}
export default BoxManager
import { LIB_NAME, ANIMATION_PREFIX } from '../../../../../../src/util'
import BoxManager from '../../base/index'
const prefixCls = `${LIB_NAME}-message`
const prefixKey = `${LIB_NAME}_message_key_`
let messageInstance
let name = 1
let defaultConfig = {
// 设置全局的自动关闭时间,为0时不自动消失
duration: 1.5,
// 设置出现的位置在浏览器顶部的距离
top: 60,
transitionName: `${ANIMATION_PREFIX}move-in`,
fixed: true
}
let iconTypes = {
'info': 'ans-icon-notice-solid',
'success': 'ans-icon-success-solid',
'warning': 'ans-icon-warn-solid',
'error': 'ans-icon-fail-solid',
'loading': 'ans-icon-spinner'
}
function getMessageInstance () {
messageInstance = messageInstance || BoxManager.newInstance({
prefixCls: prefixCls,
styles: {
top: defaultConfig.top + 'px',
left: '50%'
},
className: defaultConfig.fixed ? `${prefixCls}-fixed` : ''
})
return messageInstance
}
function notice (content = '', duration = defaultConfig.duration, type, onClose = function () {}, closable = false) {
let instance = getMessageInstance()
instance.notice({
name: `${prefixKey}${name}`,
duration: duration,
transitionName: defaultConfig.transitionName,
styles: {},
content:
`
<i class="${iconTypes[type]} ${type}"></i><span>${content}</span>
`,
onClose: onClose,
closable: closable,
type: 'message'
})
name++
}
function formatOptions (options) {
const type = typeof options
if (type === 'string') {
options = {
content: options
}
}
return options
}
export default {
name: 'Message',
info (options) {
options = formatOptions(options)
return notice(options.content, options.duration, 'info', options.onClose, options.closable)
},
success (options) {
options = formatOptions(options)
return notice(options.content, options.duration, 'success', options.onClose, options.closable)
},
warning (options) {
options = formatOptions(options)
return notice(options.content, options.duration, 'warning', options.onClose, options.closable)
},
error (options) {
options = formatOptions(options)
return notice(options.content, options.duration, 'error', options.onClose, options.closable)
},
loading (options) {
options = formatOptions(options)
return notice(options.content, options.duration, 'loading', options.onClose, options.closable)
},
config (cfg = {}) {
defaultConfig = Object.assign(defaultConfig, cfg)
},
destroy () {
let instance = getMessageInstance()
messageInstance = null
instance.destroy(prefixCls)
}
}
import Vue from 'vue'
import BoxManager from '../../base/index'
import { on, hasClass } from '../../../../../../src/util'
import { xButton } from '../../../../../vue-button/src'
import { LIB_NAME, ANIMATION_PREFIX } from '../../../../../../src/util/constants'
import { t } from '../../../../../../src/locale'
const prefixCls = `${LIB_NAME}-modal`
const prefixKey = `${LIB_NAME}_modal_key_`
let messageInstance
let name = 1
/* eslint-disable no-unused-vars */
let customModal
let defaultConfig = {
// 设置全局的自动关闭时间,为0时不自动消失
duration: 0,
transitionName: `${ANIMATION_PREFIX}modal-down`
}
function getMessageInstance () {
messageInstance = messageInstance || BoxManager.newInstance({
prefixCls: prefixCls,
styles: {}
})
return messageInstance
}
/**
* @params options {object} 生成modal的配置参数
*
* transitionName {String} 弹框动画
* className {String} 弹窗的自定义样式名称
* content {String} 内容(支持dom字符串)
* onClose {Function} 点击关闭图标的回调
* closable {Boolean} 是否显示关闭图标
* width {Number} 设置弹框宽度
* title {String} 设置标题
* ok {Object} {show [Boolean], text [String], handle [Function] }
* cancel 同 ok
* render {vue[render]函数} 当需要自定义显示内用时 (content, title, ok, cancel 失效)
* showMask 是否显示遮罩
* maskClosable
*/
function notice (options) {
let instance = getMessageInstance()
let keyName = `${prefixKey}${name}`
let onRender = function (boxName) {
if (keyName !== boxName) {
return
}
let comp = instance.component.$children.find(o => o.name === keyName)
if (options.render) {
customModal = new Vue({
name: 'customModal',
render: options.render,
mounted () {
on(this.$el, 'click', function (e) {
// e.stopPropagation()
})
if (options.maskClosable) {
on(comp.$el.children[0], 'click', function (e) {
instance.remove(boxName)
if (hasClass(e.target, 'msk')) {
options.onClose && options.onClose()
}
})
}
}
}).$mount(comp.$refs.content)
} else {
customModal = new Vue({
name: 'defaultModal',
data: {
width: options.width ? options.width : '520',
content: options.content || '',
title: options.title || '',
ok: Object.assign({
show: true,
text: t('ans.modal.confirm'),
handle: function () {
}
}, options.ok),
cancel: Object.assign({
show: true,
text: t('ans.modal.cancel'),
handle: function () {
}
}, options.cancel)
},
render () {
const { width, content, title, ok, cancel } = this
return (
<div class={`${prefixCls}-box-content`} style={{ width: width + 'px' }}>
{
title ? (
<div class={`${prefixCls}-content-header`}>
<div class={`${prefixCls}-header-inner`} domPropsInnerHTML={title} />
</div>) : null
}
{
content ? (
<div class={`${prefixCls}-content-body`} domPropsInnerHTML={content} />
) : null
}
{
(ok.show || cancel.show) ? (
<div class={`${prefixCls}-content-footer`}>
{
cancel.show ? (
<x-button type='text' shape={cancel.shape || ''} class={cancel.className || 'x-btn-cancel'} onClick={this.cancelClick}>{cancel.text}</x-button>
) : null
}
{
ok.show ? (
<x-button type='primary' shape={ok.shape || ''} class={ok.className || 'x-btn-submit'} onClick={this.okClick}>{ok.text}</x-button>
) : null
}
</div>
) : null
}
</div>
)
},
components: { xButton },
props: {},
methods: {
cancelClick (e) {
this.cancel.handle(e)
instance.remove(boxName)
},
okClick (e) {
this.ok.handle(e)
instance.remove(boxName)
}
},
computed: {
},
mounted () {
on(this.$el, 'click', function (e) {
e.stopPropagation()
})
if (options.maskClosable) {
on(comp.$el, 'click', function (e) {
instance.remove(boxName)
if (hasClass(e.target, 'msk')) {
options.onClose && options.onClose()
}
})
}
}
}).$mount(comp.$refs.content)
}
}
instance.notice({
$onRender: onRender,
name: keyName,
duration: defaultConfig.duration,
// 弹框动画样式
transitionName: options.transitionName || defaultConfig.transitionName,
// 弹框的样式
styles: {},
// 弹框的内容
content: '',
// 弹框的关闭回调函数
onClose: options.onClose || (() => { }),
// 弹框是否显示关闭按钮(右上角的关闭)
escClose: (typeof options.escClose) === 'boolean' ? options.escClose : false,
closable: (typeof options.closable) === 'boolean' ? options.closable : true,
className: `${options.className} ${options.showMask ? 'mask' : ''} `,
// 弹框的类型
type: 'modal'
})
return (function () {
let target = name++
return {
remove: () => {
instance.remove(`${prefixKey}${target}`)
}
}
})()
}
export default {
dialog (options) {
return notice(options)
},
config (cfg = {}) {
defaultConfig = Object.assign(defaultConfig, cfg)
},
destroy () {
let instance = getMessageInstance()
messageInstance = null
instance.destroy(prefixCls)
}
}
import { LIB_NAME, ANIMATION_PREFIX } from '../../../../../../src/util'
import BoxManager from '../../base/index'
const prefixCls = `${LIB_NAME}-notice`
const prefixKey = `${LIB_NAME}_notice_key_`
let messageInstance
let name = 1
let defaultConfig = {
// 设置全局的自动关闭时间,为0时不自动消失
duration: 1.5,
// 设置出现的位置在浏览器顶部的距离
top: 60,
right: 20,
transitionName: `${ANIMATION_PREFIX}move-right`,
list: true
}
let iconTypes = {
'info': 'ans-icon-notice-solid',
'success': 'ans-icon-success-solid',
'warning': 'ans-icon-warn-solid',
'error': 'ans-icon-fail-solid',
'loading': 'ans-icon-spinner'
}
function getMessageInstance () {
messageInstance = messageInstance || BoxManager.newInstance({
prefixCls: prefixCls,
styles: {
top: defaultConfig.top + 'px',
right: defaultConfig.right + 'px'
},
className: defaultConfig.list ? `${prefixCls}-list` : ''
})
return messageInstance
}
function notice (title = '', content = '', duration = defaultConfig.duration, type, onClose = function () {}, closable = false) {
let instance = getMessageInstance()
instance.notice({
name: `${prefixKey}${name}`,
duration: duration,
transitionName: defaultConfig.transitionName,
styles: {},
content:
`
<div class="${prefixCls}-custom-content">
<i class="${iconTypes[type]} ${type} ${prefixCls}__icon"></i>
<span class="${prefixCls}__title">${title}</span>
<div class="${prefixCls}__content">${content}</div>
</div>
`,
onClose: onClose,
closable: closable,
type: 'notice'
})
name++
}
function formatOptions (options) {
const type = typeof options
if (type === 'string') {
options = {
content: options
}
}
return options
}
export default {
name: 'Notice',
info (options) {
options = formatOptions(options)
return notice(options.title, options.content, options.duration, 'info', options.onClose, options.closable)
},
success (options) {
options = formatOptions(options)
return notice(options.title, options.content, options.duration, 'success', options.onClose, options.closable)
},
warning (options) {
options = formatOptions(options)
return notice(options.title, options.content, options.duration, 'warning', options.onClose, options.closable)
},
error (options) {
options = formatOptions(options)
return notice(options.title, options.content, options.duration, 'error', options.onClose, options.closable)
},
loading (options) {
options = formatOptions(options)
return notice(options.title, options.content, options.duration, 'loading', options.onClose, options.closable)
},
config (cfg = {}) {
defaultConfig = Object.assign(defaultConfig, cfg)
},
destroy () {
let instance = getMessageInstance()
messageInstance = null
instance.destroy(prefixCls)
}
}
## Button
常用的操作按钮
### Button props
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
title | 标题 | String / DOM | - | -
type | 类型 | String | primary、ghost、dashed、text、info、success、warning、error | -
shape | 形状 | String | circle或者不设置 | -
size | 大小 | String | large、small、default、xsmall | -
loading | 是否为加载中状态 | Boolean | - | -
disabled | 是否禁用 | Boolean | - | -
visible | 在按钮组中,按钮是否显示 | Boolean | - | true
html-type | 设置button原生的type | String | button、submit、reset | button
icon | 按钮的图标类型 | String | - | -
long | 开启后,长度为 100% | Boolean | - | false
value | 按钮的值,可用于双向绑定 | any | - | -
ButtonGroup props
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
size | 大小 | String | large、default、small、xsmall | -
shape | 形状 | String | - | -
vertical | 是否纵向排列按钮组 | Boolean | - | -
value | 按钮的值,可用于双向绑定 | any | - | -
<template>
<div>
<section class="demo-section">
<h4>类型</h4>
<div>
<x-button type="info" @click="info">Info</x-button>
<x-button type="success" @click="success">Success</x-button>
<x-button type="warning" @click="warning">Warning</x-button>
<x-button type="error" @click="error">Error</x-button>
<br><br>
<x-button>Default</x-button>
<x-button type="ghost">Ghost</x-button>
<x-button type="dashed">Dashed</x-button>
<x-button type="text">Text</x-button>
<x-button type="ghost" disabled>Ghost(Disabled)</x-button>
<x-button type="dashed" disabled>Dashed(Disabled)</x-button>
<x-button type="text" disabled>Text(Disabled)</x-button>
</div>
</section>
<section class="demo-section">
<h4>形状和图标</h4>
<div>
<x-button type="primary" shape="circle" icon="ans-icon-warn-empty" size="small"></x-button>
<x-button type="primary" icon="ans-icon-search">Plugin</x-button>
<x-button type="primary" shape="circle" icon="ans-icon-search">Plugin</x-button>
<x-button type="primary" shape="circle">Circle</x-button>
<br><br>
<x-button type="ghost" shape="circle" icon="ans-icon-warn-empty"></x-button>
<x-button type="ghost" icon="ans-icon-search">Plugin</x-button>
<x-button type="ghost" shape="circle" icon="ans-icon-search">Plugin</x-button>
<x-button type="ghost" shape="circle">Circle</x-button>
</div>
</section>
<section class="demo-section">
<h4>大小</h4>
<div>
<x-button type="primary" size="large">Large</x-button>
<x-button type="primary">Default</x-button>
<x-button type="primary" size="small">Small</x-button>
<x-button type="primary" size="xsmall">xSmall</x-button>
<br><br>
<x-button type="primary" shape="circle" size="large">Large</x-button>
<x-button type="primary" shape="circle">Default</x-button>
<x-button type="primary" shape="circle" size="small">Small</x-button>
<x-button type="primary" shape="circle" size="xsmall">xSmall</x-button>
</div>
</section>
<section class="demo-section">
<h4>状态</h4>
<div>
<x-button type="primary" disabled @click="info">Primary (Disabled)</x-button>
<x-button type="dashed" disabled>Dashed (Disabled)</x-button>
<x-button type="ghost" shape="circle" disabled>Ghost With Circle (Disabled)</x-button>
<x-button type="primary" icon="ans-icon-search" :loading="spinnerLoading" ref="spinnerBtn" @click="handleLoadClick">Loading...</x-button>
</div>
</section>
<section class="demo-section">
<h4>基本按钮组</h4>
<div>
<x-button-group v-model="checkedValue">
<x-button value="1">Cancel</x-button>
<x-button value="2">Confirm</x-button>
</x-button-group>
<x-button-group size="small">
<x-button type="ghost">Yesterday</x-button>
<x-button disabled type="ghost">Today</x-button>
<x-button type="ghost">Tomorrow</x-button>
</x-button-group>
</div>
</section>
<section class="demo-section">
<h4>图标类型</h4>
<div>
<x-button-group :size="'large'" shape="circle">
<x-button type="primary" icon="ans-icon-search"></x-button>
<x-button type="primary" icon="ans-icon-more"></x-button>
<x-button type="primary" icon="ans-icon-close"></x-button>
<x-button type="primary" icon="ans-icon-more"></x-button>
</x-button-group>
<x-button-group shape="circle">
<x-button type="primary" icon="ans-icon-arrow-to-left"></x-button>
<x-button type="primary" icon="ans-icon-arrow-to-right"></x-button>
</x-button-group>
</div>
</section>
<section class="demo-section">
<h4>垂直排列</h4>
<div>
<x-button-group :vertical="true">
<x-button type="ghost" icon="ans-icon-arrow-up"></x-button>
<x-button type="ghost" icon="ans-icon-arrow-right"></x-button>
<x-button type="ghost" icon="ans-icon-arrow-down"></x-button>
<x-button type="ghost" icon="ans-icon-arrow-left"></x-button>
</x-button-group>
</div>
</section>
<section class="demo-section">
<h4>圆角</h4>
<div>
<x-button-group shape="circle" value="open">
<x-button type="primary" value="open">开启</x-button>
<x-button type="primary" value="close">关闭</x-button>
</x-button-group>
<x-button-group shape="circle" value="2">
<x-button :visible="false" type="ghost" icon="fa-github">苹果</x-button>
<x-button type="ghost" icon="fa-html5" value="2">李子</x-button>
<x-button type="ghost" icon="fa-firefox">橘子</x-button>
<x-button :visible="false" type="ghost" icon="fa-chrome">香蕉</x-button>
</x-button-group>
</div>
</section>
<section class="demo-section">
<h4>大小</h4>
<div>
<x-button-group size="large">
<x-button type="ghost">Large</x-button>
<x-button type="ghost">Large</x-button>
</x-button-group>
<x-button-group>
<x-button type="ghost">Default</x-button>
<x-button type="ghost">Default</x-button>
</x-button-group>
<x-button-group size="small">
<x-button type="ghost">Small</x-button>
<x-button type="ghost">Small</x-button>
</x-button-group>
<x-button-group size="xsmall">
<x-button type="ghost">xSmall</x-button>
<x-button type="ghost">xSmall</x-button>
</x-button-group>
</div>
<div>
<x-button-group size="large" shape="circle">
<x-button type="ghost">Large</x-button>
<x-button type="ghost">Large</x-button>
</x-button-group>
<x-button-group shape="circle">
<x-button type="ghost">Default</x-button>
<x-button type="ghost">Default</x-button>
</x-button-group>
<x-button-group size="small" shape="circle">
<x-button type="ghost">Small</x-button>
<x-button type="ghost">Small</x-button>
</x-button-group>
<x-button-group size="xsmall" shape="circle">
<x-button type="ghost">xSmall</x-button>
<x-button type="ghost">xSmall</x-button>
</x-button-group>
</div>
</section>
</div>
</template>
<script>
import { xButton, xButtonGroup } from '../src'
export default {
name: 'app',
data: function () {
return {
name: 1,
timeout: 0,
spinnerLoading: false,
checkedValue: '2' // for button-group two-way binding.
}
},
watch: {
// Watcher for pipe the button-group value to console
checkedValue (newValue) {
console.log(newValue)
}
},
methods: {
handleLoadClick () {
// toggle button `loading` manually
this.spinnerLoading = !this.spinnerLoading
var timeout = 10
// Simulate a sync progress
let loop = () => {
timeout -= 1
if (timeout <= 0) {
// release the async locker
this.spinnerLoading = false
return
}
setTimeout(loop, 1000)
}
setTimeout(loop, 500)
},
handleLockerClick (e, next) {
this.timeout = 10
// Simulate a sync progress
let loop = () => {
this.timeout -= 1
if (this.timeout <= 0) {
// release the async locker
next()
return
}
setTimeout(loop, 1000)
}
setTimeout(loop, 500)
},
handleBtnGroupSelected (value) {
console.log(value)
},
info () {
// vMessage.info({ content: 'hello word' + (this.name++), duration: 2, onClose: function () {}, closable: false })
},
success () {
// message.success({ content: 'hello word' + (this.name++), duration: 2, onClose: function () {}, closable: false })
},
error () {
// message.error({ content: 'hello wordhello wordhello word' + (this.name++), duration: 0, onClose: function () {}, closable: true })
},
loading () {
// message.loading({ content: 'hello word' + (this.name++), duration: 2, onClose: function () {}, closable: true })
},
warning () {
// message.warning({ content: 'hello word' + (this.name++), duration: 2, onClose: function () {}, closable: false })
}
},
components: { xButtonGroup, xButton }
}
</script>
<style lang="scss"></style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1">
<script src="https://s1.analysys.cn/libs/??js-polyfills/0.1.42/polyfill.min.js"></script>
<link rel="stylesheet" href="//s2.analysys.cn/libs/??-/@analysys/fss-demo.css@">
<link rel="stylesheet" href="../../../src/style/index.scss">
<title>demo</title>
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
import Vue from 'vue'
import App from './app.vue'
new Vue({
el: '#app',
render: h => h(App),
mounted () {
console.log('success')
}
})
import xButton from './source/Button.vue'
import xButtonGroup from './source/ButtonGroup.vue'
export {
xButton,
xButtonGroup
}
<template>
<button
v-show="visible"
:type="htmlType"
:class="wrapClasses"
:disabled="disabled"
@click="handleClick"
>
<i v-if="icon && !showSpin" :class="['i', iconPrefix, {[icon]:true}]"></i>
<i v-if="showSpin" :class="spinClasses"></i>
<span v-if="showSlot" ref="slot"><slot></slot></span>
</button>
</template>
<script>
import { LIB_NAME, emitter } from '../../../../src/util'
const prefixCls = `${LIB_NAME}-btn`
export default {
name: 'xButton',
data: function () {
return {
showSlot: true,
iconPrefix: `${LIB_NAME}-icon`,
isFirst: false,
isLast: false
}
},
mixins: [emitter],
props: {
type: {
type: String,
default: 'primary',
validator (v) {
return ['primary', 'ghost', 'dashed', 'text', 'info', 'success', 'warning', 'error'].includes(v)
}
},
shape: {
type: String,
validator (v) {
return ['circle', ''].includes(v)
}
},
size: {
type: String,
default: 'default',
validator (v) {
return ['xsmall', 'small', 'large', 'default'].includes(v)
}
},
// 设置按钮为加载中状态
loading: Boolean,
disabled: Boolean,
visible: {
type: Boolean,
default: true
},
// 设置 button 原生的 type,可选值为 button、submit、reset
htmlType: {
type: String,
default: 'button',
validator (v) {
return ['button', 'submit', 'reset'].includes(v)
}
},
// 设置按钮的图标类型
icon: String,
// 开启后,按钮的长度为 100%
long: {
type: Boolean,
default: false
},
// 用于双向绑定
value: {
default: ''
}
},
computed: {
showSpin () {
return this.loading
},
spinClasses () {
return [ this.iconPrefix, `x-fa-spin ${LIB_NAME}-icon-spinner` ]
},
wrapClasses () {
return [
`${prefixCls}`,
{
'first-child': this.isFirst,
'last-child': this.isLast,
[`${prefixCls}-${this.type}`]: !!this.type,
[`${prefixCls}-long`]: this.long,
[`${prefixCls}-${this.shape}`]: !!this.shape,
[`${prefixCls}-${this.size}`]: !!this.size,
[`${prefixCls}-loading`]: this.showSpin,
[`${prefixCls}-icon-only`]: !this.showSlot && (!!this.icon || this.loading)
}
]
}
},
watch: {
visible (newVal) {
this.dispatch('xButtonGroup', 'buttonVisibleChanged')
}
},
methods: {
handleClick (...args) {
if (this.disabled || this.loading) {
return
}
this.$emit('click', ...args)
},
handlePositionChange ({ first, last }) {
this.isFirst = first === this
this.isLast = last === this
}
},
mounted () {
this.showSlot = this.$slots.default !== undefined
},
created () {
this.$on('checkPosition', this.handlePositionChange)
}
}
</script>
<template>
<div :class="classes">
<slot></slot>
</div>
</template>
<script>
import { LIB_NAME, hasClass, addClass, removeClass, emitter } from '../../../../src/util'
import vButton from './Button.vue'
const prefixCls = `${LIB_NAME}-btn-group`
export default {
name: 'xButtonGroup',
data () {
return {
buttons: [],
checkedList: [],
activeClass: 'active'
}
},
mixins: [emitter],
props: {
size: {
type: String,
default: 'default',
validator (v) {
return ['xsmall', 'small', 'large', 'default'].includes(v)
}
},
shape: {
type: String,
validator (v) {
return ['circle', ''].includes(v)
}
},
vertical: Boolean,
// 用于双向绑定
value: {
default: ''
}
},
components: { vButton },
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-${this.size}`]: !!this.size,
[`${prefixCls}-${this.shape}`]: !!this.shape,
[`${prefixCls}-vertical`]: this.vertical
}
]
},
firstVisibleChild () {
return this.buttons.find(b => b.visible)
},
lastVisibleChild () {
for (let i = this.buttons.length - 1; i > 0; i--) {
const b = this.buttons[i]
if (b.visible) return b
}
return null
}
},
methods: {
initComponents () {
this.buttons = this.$children.filter(v => v.$options.name === 'xButton')
// sort by rendering index
this.buttons.sort((a, b) => {
const aIndex = Array.prototype.indexOf.call(a.$el.parentNode.children, a.$el)
const bIndex = Array.prototype.indexOf.call(b.$el.parentNode.children, b.$el)
return aIndex - bIndex
})
this.handleButtonVisible()
this.buttons.forEach(btn => {
if (!btn._$bind) {
btn._$bind = true
btn.$on('click', this.handleChange.bind(this, btn))
}
btn.checked = btn.value !== undefined && btn.value !== '' && btn.value === this.value
})
this.$nextTick(() => {
this.updateModel()
})
},
updateModel () {
let activeClass = this.activeClass
this.buttons.forEach(child => {
let has = hasClass(child.$el, activeClass)
if (child.checked) {
if (!has) addClass(child.$el, activeClass)
} else {
if (has) removeClass(child.$el, activeClass)
}
})
},
handleChange (child, ...args) {
if ((this.buttons.length === 1 && this.buttons[0].checked) || child.checked) {
return
}
let prev = this.buttons.find(v => v.checked && v !== child)
if (prev) {
prev.checked = false
}
child.checked = true
this.checkedList = this.buttons.filter(o => o.checked).map(o => o.value)
this.$emit('input', this.checkedList[0], () => this.updateModel())
this.updateModel()
},
handleButtonVisible () {
if (!this.buttons.length) return
this.broadcast('xButton', 'checkPosition', {
first: this.firstVisibleChild,
last: this.lastVisibleChild
})
}
},
created () {
this.checkedList = [this.value]
this.$on('buttonVisibleChanged', this.handleButtonVisible)
},
mounted () {
this.initComponents()
},
updated () {
this.initComponents()
}
}
</script>
## Cascader
### Cascader props
| 属性 | 说明 | required | 类型 | 默认值 |
| :----| :------| :--------| :---:| :------|
| options | 可选项数据源,键名可通过 props 属性配置, 配置选项: { value, label, html, children, disabled } | Required | Array | - |
| prop | N/A | Optional | Object | {...} |
| value | 选中项绑定值 `v-model` | Optional | Array | {...} |
| separator | N/A | Optional | String | / |
| placeholder | N/A | Optional | String | 请选择 ... |
| disabled | N/A | Optional | Any | - |
| clearable | 是否支持清空选项 | Optional | Any | - |
| change-on-select | 是否允许选择任意一级的选项 | Optional | Any | - |
| popper-class | 自定义浮层类名 | Optional | Any | - |
| expand-trigger | 次级菜单的展开方式 [ click / hover ] | Optional | String | click |
| filterable | 是否可搜索选项 | Optional | Any | - |
| no-data-text | 无数据提示 | Optional | String | 暂无数据 |
| no-match-text | 搜索无结果提示 | Optional | String | 搜索无结果 |
| multiple | 是否多选 | Optional | Boolean | false |
| placement | 弹出位置 | Optional | String | bottom-start |
| distance | 与参考元素距离,单位为 px | Optional | Number | 1 |
| append-to-body | 弹出层是否插入 body | Optional | Boolean | false |
| position-fixed | 弹出层是否 fixed 定位 | Boolean | — | false |
| viewport | 弹出层是否基于 viewport 定位 | Boolean | — | false |
| popper-options | Popper.js 的可选项 | Optional | Object | — |
### Cascader events
- `on-change` Fired when the selected value is changed.
---
<template>
<div>
<section class="demo-section">
<h4>基本用法</h4>
<div>
<x-cascader
:options="phones"
v-model="selectedOptions"
@on-change="handleChange">
</x-cascader>
</div>
<br>
<div>
<x-cascader
expand-trigger="hover"
:options="components"
v-model="selectedOptions2"
@on-change="handleChange">
</x-cascader>
</div>
<br>
<div>
<x-cascader
multiple
expand-trigger="click"
:options="components"
@on-change="handleChange">
</x-cascader>
</div>
</section>
<section class="demo-section">
<h4>允许选择任意一级的选项</h4>
<div>
<x-cascader
change-on-select
:options="phones"
:show-all-levels="false"
@on-change="handleChange">
</x-cascader>
</div>
</section>
<section class="demo-section">
<h4>搜索</h4>
<div>
<x-cascader
filterable
clearable
:options="phones"
@on-change="handleChange">
</x-cascader>
</div>
</section>
</div>
</template>
<script>
import { xCascader } from '../src/index'
import { phones, components } from './data'
export default {
data () {
return {
phones,
components,
selectedOptions: ['xiaomi', 'mi6', ['12878']],
selectedOptions2: []
}
},
components: { xCascader },
methods: {
handleChange (value) {
console.log(value)
}
}
}
</script>
<style lang="scss">
.ans-cascader-drop__menu .highlight {
color: red;
font-weight: normal;
}
</style>
let phones = [
{
value: '305444499',
label: '华为',
html: '<b class="highlight">华</b>为',
children: [
{
value: '12840',
label: '华为 麦芒5',
children: [{
value: '128401',
label: '华为 麦芒51',
},{
value: '128402',
label: '华为 麦芒52',
}]
},
{
value: '12820',
label: '华为 Mate 9'
},
{
value: '12841',
label: '华为 麦芒5 高配版'
},
{
value: '12848',
label: '华为 P9 Plus'
},
{
value: '12850',
label: '华为 nova'
},
{
value: '12879',
label: '华为 畅享6S'
},
{
value: '12886',
label: '华为 P10'
},
{
value: '12918',
label: '华为 P10 Plus'
},
{
value: '13033',
label: '华为 nova青春版'
},
{
value: '13036',
label: '华为 nova 2 Plus'
},
{
value: '13044',
label: '华为 畅享7 Plus'
},
{
value: '13069',
label: '华为 揽阅M3'
},
{
value: '1614',
label: '华为 Mate 8'
},
{
value: '1617',
label: '华为 畅享5S'
},
{
value: '2435',
label: '华为 P9'
},
{
value: '537',
label: '华为 P8 Lite'
},
{
value: '556',
label: '华为 麦芒3S'
},
{
value: '569',
label: '华为 Mate S'
},
{
value: '604',
label: '华为 P8'
},
{
value: '604',
label: '华为 Ascend P8'
},
{
value: '625',
label: '华为 Mate 7'
},
{
value: '626',
label: '华为 P7'
},
{
value: '627',
label: '华为 P8max'
},
{
value: '629',
label: '华为 麦芒4'
},
{
value: '635',
label: '华为 畅享5'
}
]
},
{
value: '560',
children: [
{
value: '12834',
label: '荣耀 8'
},
{
value: '12835',
label: '荣耀 畅玩6X'
},
{
value: '12841',
label: '荣耀 畅玩5C 全网通'
},
{
value: '12842',
label: '荣耀 畅玩5C'
},
{
value: '12843',
label: '荣耀 5A'
},
{
value: '12863',
label: '荣耀 V8 高配版'
},
{
value: '12883',
label: '荣耀 V8'
},
{
value: '12888',
label: '荣耀 Note8'
},
{
value: '12946',
label: '荣耀 8青春版'
},
{
value: '12963',
label: '荣耀 V9'
},
{
value: '13031',
label: '荣耀 9'
},
{
value: '13066',
label: '荣耀 Magic'
},
{
value: '538',
label: '荣耀 畅玩5'
},
{
value: '548',
label: '荣耀 6'
},
{
value: '553999999',
label: '荣耀 7i'
},
{
value: '567',
label: '荣耀 畅玩4X'
},
{
value: '568',
label: '荣耀 畅玩4C'
},
{
value: '628',
label: '荣耀 6 Plus'
},
{
value: '662',
label: '荣耀 7'
},
{
value: '666',
label: '荣耀 畅玩5X'
}
],
label: '荣耀'
},
{
value: 'xiaomi',
children: [
{
value: '121',
label: '小米 MI 5'
},
{
value: '1250',
label: '小米 MI 1S'
},
{
value: '1254',
label: '小米 MI 2S'
},
{
value: '1255',
label: '小米 MI 3'
},
{
value: '1256',
label: '小米 MI 4'
},
{
value: '1262',
label: '小米 note顶配版'
},
{
value: '1264',
label: '小米 4C'
},
{
value: '12821',
label: '小米 红米3S'
},
{
value: '12830',
label: '小米 Max'
},
{
value: '12859',
label: '小米 5S'
},
{
value: '12866',
label: '小米 5S plus'
},
{
value: '12927',
label: '小米 Note2'
},
{
value: '12928',
label: '小米 MIX'
},
{
value: 'mi6',
label: '小米 6',
children: [// {{{
{
value: '12858',
label: '三星 GALAXY C7'
},
{
value: '12878',
label: '三星 S8003'
},
{
value: '13039',
label: '三星 GALAXY S8'
},
{
value: '1408',
label: '三星 Galaxy A5'
},
{
value: '1410',
label: '三星 Galaxy A8'
},
{
value: '1433',
label: '三星 Galaxy J5'
},
{
value: '1446',
label: '三星 Galaxy Note 3'
},
{
value: '1448',
label: '三星 Galaxy Note 4'
},
{
value: '1449',
label: '三星 Galaxy Note 5'
},
{
value: '1451',
label: '三星 Galaxy Note Edge'
},
{
value: '1452',
label: '三星 Galaxy On7'
},
{
value: '1465',
label: '三星 Galaxy S5'
},
{
value: '1468',
label: '三星 Galaxy S6'
},
{
value: '1469',
label: '三星 Galaxy S6 Edge'
},
{
value: '1470',
label: '三星 Galaxy S6 Edge+'
},
{
value: '1976',
label: '三星 Galaxy S7'
},
{
value: '1977',
label: '三星 Galaxy S7 Edge'
}
]// }}}
},
{
value: '13047',
label: '小米 Max 2'
},
{
value: '947',
label: '小米 note'
}
],
label: '小米'
},
{
value: '63',
children: [
{
value: '1167',
label: 'vivo X5Pro'
},
{
value: '1191',
label: 'vivo Y27'
},
{
value: '12809',
label: 'vivo X9'
},
{
value: '12814',
label: 'vivo X7'
},
{
value: '12819',
label: 'vivo X7 Plus'
},
{
value: '12823',
label: 'vivo Y55'
},
{
value: '12826',
label: 'vivo V3Max'
},
{
value: '12829',
label: 'vivo Y67'
},
{
value: '12842',
label: 'vivo X9 Plus'
},
{
value: '12887',
label: 'vivo Xplay6'
},
{
value: '13068',
label: 'vivo Xplay 5S'
},
{
value: '13082',
label: 'vivo X6'
},
{
value: '1641',
label: 'vivo X6 D'
},
{
value: '1686',
label: 'vivo X6Plus A'
},
{
value: '1937',
label: 'vivo X6s'
},
{
value: '1938',
label: 'vivo X6s A'
},
{
value: '2436',
label: 'vivo Xplay5 旗舰版'
}
],
label: 'vivo',
disabled: true
}
]
let components = [
{
value: 'zhinan',
label: '指南',
disabled: true,
children: [{
value: 'shejiyuanze',
label: '设计原则',
children: [{
value: 'yizhi',
label: '一致'
}, {
value: 'fankui',
label: '反馈'
}, {
value: 'xiaolv',
label: '效率'
}, {
value: 'kekong',
label: '可控'
}]
}, {
value: 'daohang',
label: '导航',
children: [{
value: 'cexiangdaohang',
label: '侧向导航'
}, {
value: 'dingbudaohang',
label: '顶部导航'
}]
}]
},
{
value: 'zujian',
label: '组件',
children: [{
value: 'basic',
label: 'Basic',
children: [{
value: 'layout',
label: 'Layout 布局'
}, {
value: 'color',
label: 'Color 色彩'
}, {
value: 'typography',
label: 'Typography 字体'
}, {
value: 'icon',
label: 'Icon 图标'
}, {
value: 'button',
label: 'Button 按钮'
}]
}, {
value: 'form',
label: 'Form',
children: [{
value: 'radio',
label: 'Radio 单选框'
}, {
value: 'checkbox',
label: 'Checkbox 多选框'
}, {
value: 'input',
label: 'Input 输入框'
}, {
value: 'input-number',
label: 'InputNumber 计数器'
}, {
value: 'select',
label: 'Select 选择器'
}, {
value: 'cascader',
label: 'Cascader 级联选择器'
}, {
value: 'switch',
label: 'Switch 开关'
}, {
value: 'slider',
label: 'Slider 滑块'
}, {
value: 'time-picker',
label: 'TimePicker 时间选择器'
}, {
value: 'date-picker',
label: 'DatePicker 日期选择器'
}, {
value: 'datetime-picker',
label: 'DateTimePicker 日期时间选择器'
}, {
value: 'upload',
label: 'Upload 上传'
}, {
value: 'rate',
label: 'Rate 评分'
}, {
value: 'form',
label: 'Form 表单'
}]
}, {
value: 'data',
label: 'Data',
children: [{
value: 'table',
label: 'Table 表格'
}, {
value: 'tag',
label: 'Tag 标签'
}, {
value: 'progress',
label: 'Progress 进度条'
}, {
value: 'tree',
label: 'Tree 树形控件'
}, {
value: 'pagination',
label: 'Pagination 分页'
}, {
value: 'badge',
label: 'Badge 标记'
}]
}, {
value: 'notice',
label: 'Notice',
children: [{
value: 'alert',
label: 'Alert 警告'
}, {
value: 'loading',
label: 'Loading 加载'
}, {
value: 'message',
label: 'Message 消息提示'
}, {
value: 'message-box',
label: 'MessageBox 弹框'
}, {
value: 'notification',
label: 'Notification 通知'
}]
}, {
value: 'navigation',
label: 'Navigation',
children: [{
value: 'menu',
label: 'NavMenu 导航菜单'
}, {
value: 'tabs',
label: 'Tabs 标签页'
}, {
value: 'breadcrumb',
label: 'Breadcrumb 面包屑'
}, {
value: 'dropdown',
label: 'Dropdown 下拉菜单'
}, {
value: 'steps',
label: 'Steps 步骤条'
}]
}, {
value: 'others',
label: 'Others',
children: [{
value: 'dialog',
label: 'Dialog 对话框'
}, {
value: 'tooltip',
label: 'Tooltip 文字提示'
}, {
value: 'popover',
label: 'Popover 弹出框'
}, {
value: 'card',
label: 'Card 卡片'
}, {
value: 'carousel',
label: 'Carousel 走马灯'
}, {
value: 'collapse',
label: 'Collapse 折叠面板'
}]
}]
},
{
value: 'ziyuan',
label: '资源',
children: [{
value: 'axure',
label: 'Axure Components'
}, {
value: 'sketch',
label: 'Sketch Templates'
}, {
value: 'jiaohu',
label: '组件交互文档'
}]
},
{
value: 'test',
label: 'Test',
children: [{
value: 'foo',
label: 'Foo'
}, {
value: 'bar',
label: 'Bar',
html: '<b class="highlight">Baaa..r</b>'
}, {
value: 'kit',
label: 'Kitty'
}]
}
]
export { phones, components }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1">
<script src="https://s1.analysys.cn/libs/??js-polyfills/0.1.42/polyfill.min.js"></script>
<link rel="stylesheet" href="//s2.analysys.cn/libs/??-/@analysys/fss-demo.css@">
<link rel="stylesheet" href="../../../src/style/index.scss">
<title>demo</title>
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
import Vue from 'vue'
import App from './app.vue'
new Vue({
el: '#app',
render: h => h(App),
mounted () {
console.log('success')
}
})
import xCascader from './source/Cascader.vue'
/* istanbul ignore next */
xCascader.install = function (Vue) {
Vue.component(xCascader.name, xCascader)
}
export { xCascader }
<template>
<div :class="[prefixCls, 'ans-select', popperClass]" v-click-outside="handleClose">
<span>
<div
:class="['tag-container', scrollbar]"
ref="multiple"
v-if="multiple"
:style="{maxWidth: (inputWidth - 25) + 'px'}"
@click="toggle"
@keydown.esc="handleClose">
<span
class="tag-wrapper"
v-for="(o, index) in selectedOptions"
:key="typeof o.value === 'object' ? index : o.value">
<span class="tag-text">{{o.label}}</span>
<i class="remove-tag ans-icon-close" @click.stop="handleRemoveTag(selected, o)"></i>
</span>
</div>
<x-input
class="inner-input"
size="default"
ref="input"
:value="displayRender"
:readonly="!filterable"
:disabled="disabled"
@input="handleQuery"
:placeholder="multiple && selectedOptions.length ? '' : placeholder"
@on-click="toggle"
@on-click-icon="toggle"
>
<template slot="suffix">
<i v-if="clearable" v-show="showClear" class="ans-icon-fail-solid clear" @click.stop="handleClear"></i>
<i v-show="!showClear" class="ans-icon-arrow-down arrow-down" :style="{ transform : visible ? 'rotateZ(180deg)' : '' }"></i>
</template>
</x-input>
</span>
<transition :name="transitionName">
<div ref="popper" :class="getCls('drop')" v-show="visible">
<caspanel :data="list"
:multiple="multiple"
:prefix-cls="getCls('drop')"
:trigger="expandTrigger"
:change-on-select="changeOnSelect"
v-show="!showFilterList && list.length">
</caspanel>
<div v-show="!showFilterList && !list.length">
<ul :class="[getCls('drop__menu'), 'nodata']">
<li><i class="ans-icon-no-data"></i></li>
<li>{{noDataText}}</li>
</ul>
</div>
<div v-show="showFilterList">
<ul v-show="querySelections.length" :class="getCls('drop__menu')" >
<li
v-for="(item, i) in querySelections"
:key="i"
v-html="item.display"
:class="getCls('drop__list')"
@click="handleSelectItem(item)">
</li>
</ul>
<ul v-show="!querySelections.length" :class="[getCls('drop__menu'), 'nodata']">
<li><i class="ans-icon-search-no-data"></i></li>
<li>{{noMatchText}}</li>
</ul>
</div>
</div>
</transition>
</div>
</template>
<script>
import { LIB_NAME, Popper, emitter, clickOutside, ANIMATION_PREFIX } from '../../../../src/util'
import { xInput } from '../../../vue-input/src'
import Caspanel from './Caspanel.vue'
import { t } from '../../../../src/locale'
const popperMixin = Object.assign({}, Popper, {
props: {
placement: {
type: String,
default: 'bottom-start'
},
reference: HTMLElement,
// 与参考元素距离,单位为 px
distance: {
type: Number,
default: 1
},
appendToBody: {
type: Boolean,
default: false
},
positionFixed: {
type: Boolean,
default: false
},
viewport: {
type: Boolean,
default: false
},
popperOptions: Object
}
})
const CHILDREN_NAME = 'Caspanel'
export default {
name: 'xCascader',
components: { xInput, Caspanel },
mixins: [popperMixin, emitter],
directives: { clickOutside: clickOutside },
data () {
return {
prefixCls: `${LIB_NAME}-cascader`,
scrollbar: `${LIB_NAME}-scrollbar`,
queryStr: '',
tmpSelected: [],
selected: [],
currentValue: [],
updatingValue: false,
list: [],
childrenKey: 'children',
transitionName: `${ANIMATION_PREFIX}drop`,
selectedOptions: [],
inputWidth: 0
}
},
props: {
// 下拉级连框数据
options: {
type: Array,
default: () => []
},
// 字段code
prop: {
type: Object,
default () {
return {
children: 'children',
label: 'label',
value: 'value',
disabled: 'disabled'
}
}
},
// 子菜单触发方式
expandTrigger: {
validator (value) {
return ['click', 'hover'].indexOf(value) > -1
},
default: 'click'
},
// 是否可清除
clearable: {
type: Boolean,
default: true
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default () {
return t('ans.cascader.placeholder')
}
},
// 是否可搜索
filterable: Boolean,
// 无数据提示文字
noDataText: {
type: String,
default () {
return t('ans.cascader.noData')
}
},
// 无数据提示文字
noMatchText: {
type: String,
default () {
return t('ans.cascader.noMatch')
}
},
changeOnSelect: Boolean,
value: {
type: Array,
default: () => []
},
separator: {
type: String,
default: '/'
},
popperClass: String,
multiple: Boolean
},
watch: {
value: {
deep: true,
immediate: true,
handler (val, oldVal) {
if (JSON.stringify(val) !== JSON.stringify(oldVal)) {
this.currentValue = val
if (!val.length) this.selected = []
}
}
},
currentValue (val, oldVal) {
if (JSON.stringify(val) !== oldVal) {
this.$emit('input', this.currentValue)
if (this.updatingValue) {
this.updatingValue = false
return
}
this.updateSelected(true)
}
},
options: {
deep: true,
immediate: true,
handler (val) {
this.list = this.handlerData(val)
}
},
visible (val) {
this.broadcast(CHILDREN_NAME, 'on-visible-change', val)
}
},
computed: {
showFilterList () {
return this.filterable && this.queryStr !== ''
},
// 是否显示清除按钮
showClear () {
return this.currentValue && this.currentValue.length && this.clearable && !this.disabled
},
displayRender () {
let label = []
for (let i = 0; i < this.selected.length; i++) {
let item = this.isArray(this.selected[i]) && this.selected[i].length
? this.selected[i][0].label
: this.selected[i].label
label.push(item)
}
return this.multiple ? '' : label.join(this.separator)
},
// filter list
querySelections () {
let selections = []
let _this = this
function getSelections (arr, label, value) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
item.__label = label ? label + ' / ' + item.label : item.label
item.__value = value ? value + ',' + item.value : item.value
let obj = {
label: item.__label,
value: item.__value,
display: item.__label,
item: item,
disabled: !!item.disabled
}
if (item.children && item.children.length) {
getSelections(item.children, item.__label, item.__value)
_this.changeOnSelect && selections.unshift(obj)
delete item.__label
delete item.__value
} else {
selections.unshift(obj)
}
}
}
getSelections(this.list)
selections = selections.filter(item => {
return item.label ? item.label.indexOf(this.queryStr) > -1 : false
}).map(item => {
item.display = item.display.replace(new RegExp(this.queryStr, 'g'), `<span>${this.queryStr}</span>`)
return item
})
return selections
}
},
methods: {
toggle () {
if (this.disabled) return
this.visible = !this.visible
this.$refs.input.focus()
},
handleClear () {
const oldVal = JSON.stringify(this.currentValue)
this.currentValue = this.selected = this.selectedOptions = this.tmpSelected = []
this.handleClose()
this.emitValue(this.currentValue, oldVal)
this.broadcast(CHILDREN_NAME, 'on-clear', true)
this.setInputHeight()
},
updateResult (result) {
this.tmpSelected = result
},
emitValue (val, oldVal) {
if (JSON.stringify(val) !== oldVal) {
this.$emit('on-change', this.currentValue, JSON.parse(JSON.stringify(this.selected)))
}
},
updateSelected (init = false) {
if (!this.changeOnSelect || init) {
this.broadcast(CHILDREN_NAME, 'on-find-selected', { value: this.currentValue })
}
},
handleClose () {
this.visible = false
},
handleQuery () {
this.visible = true
this.queryStr = this.$refs.input.currentValue.trim()
this.updateElementHandler()
},
handleSelectItem (item) {
this.queryStr = ''
this.$refs.input.currentValue = ''
const oldVal = JSON.stringify(this.currentValue)
this.currentValue = item.value.split(',')
this.emitValue(this.currentValue, oldVal)
this.handleClose()
},
handlerData (arr) {
let list = JSON.parse(JSON.stringify(arr))
list.forEach(t => {
Object.keys(this.prop).forEach(v => {
if (!t[v]) {
let val = t[this.prop[v]]
t[v] = (v === this.childrenKey && val) ? this.handlerData(val) : val
}
})
})
return list
},
getCls (cls) {
return this.prefixCls + '-' + cls
},
// 多选时,删除单个
handleRemoveTag (arr, o) {
if (!arr) return
arr.forEach((v, i) => {
if (v.value === o.value) {
arr.splice(i, 1)
this.setInputHeight()
this.updateValue(arr)
return false
} else {
this.isArray(v) && this.handleRemoveTag(v, o)
}
})
},
isArray (obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
},
setInputHeight () {
const { input, multiple } = this.$refs
if (!input || !multiple) return
this.$nextTick(() => {
const calculateHeight = multiple.clientHeight + 4
input.$refs.input.style.height = Math.max(32, calculateHeight) + 'px'
this.updateElementHandler()
})
},
// 根据selected,得到只包含value的对应数组
getNewVal (selected) {
let newVal = []
selected.forEach((item) => {
if (this.isArray(item)) {
const tmp = []
this.selectedOptions = item
item.forEach(v => {
tmp.push(v.value)
})
newVal.push(tmp)
} else {
newVal.push(item.value)
}
})
return newVal
},
updateValue (newVal) {
this.updatingValue = true
let oldVal = this.currentValue
this.currentValue = newVal
this.emitValue(this.currentValue, oldVal)
},
onResultChange () {
this.$on('on-result-change', (params) => {
let { lastValue, changeOnSelect, formInit } = params
!this.multiple && lastValue && !formInit && (this.handleClose())
let newVal = []
if (lastValue || changeOnSelect) {
this.selected = this.tmpSelected
newVal = this.getNewVal(this.selected)
}
if (!formInit) {
this.setInputHeight()
this.updateValue(newVal)
}
})
}
},
created () {
this.onResultChange()
},
mounted () {
this.updateSelected(true)
this.$refs.reference = this.$el
if (this.$refs.input) {
this.inputWidth = this.$refs.input.$el.clientWidth
}
},
beforeDestroy () {
this.$off('on-result-change')
}
}
</script>
<template>
<span style="white-space: nowrap">
<ul v-if="data && data.length" :class="prefixCls+ '__menu'">
<li v-for="item in data"
@click.stop="handleClickItem(item)"
@mouseenter.stop="handleHoverItem(item)"
:class="itemClass(item)">
<a v-html="item.html || item.label" href="javascript:;"></a>
<i :class="iconClass(item)"></i>
</li>
</ul>
<Caspanel
:data="subList" :trigger="trigger"
:prefix-cls="prefixCls"
:multiple="multiple"
v-show="curItem.length"
:change-on-select=changeOnSelect
style="margin-left: -5px;"
v-if="subList && subList.length">
</Caspanel>
</span>
</template>
<script>
import { emitter, findComponentDownward, scrollIntoView } from '../../../../src/util'
const PARENT_NAME = 'xCascader'
const PANEL_NAME = 'Caspanel'
export default {
name: PANEL_NAME,
mixins: [emitter],
data() {
return {
subList: [],
// 选中的item
curItem: [],
}
},
watch: {
data () {
this.subList = []
this.curItem = []
}
},
props: {
data: {
type: Array,
default: () => []
},
trigger: {
validator (value) {
return ['click', 'hover'].indexOf(value) > -1
},
default: 'click'
},
changeOnSelect: Boolean,
prefixCls: String,
multiple: Boolean
},
methods: {
getCurItem(item, fromInit) {
const baseItem = this.getBaseItem(item)
if (fromInit) {
this.curItem.push(baseItem)
} else if (this.multiple) {
const index = this.inTmp(baseItem)
if (!~index) {
this.hasChildren(item) && (this.curItem = [])
this.curItem.forEach((v, i) => {
const id = this.data.findIndex(t => t.value === v.value && this.hasChildren(t));
~id && this.curItem.splice(i, 1)
})
this.curItem.push(baseItem)
} else {
this.curItem.splice(index, 1)
}
} else {
this.curItem = [baseItem]
}
},
// 处理item触发
handleTriggerItem (item, fromInit) {
if (item.disabled) return
this.subList = []
this.hasChildren(item) && (this.subList = item.children)
this.getCurItem(item, fromInit)
this.emitUpdate(this.curItem);
this.dispatch(PARENT_NAME, 'on-result-change', {
lastValue: !this.hasChildren(item),
changeOnSelect: this.changeOnSelect,
fromInit: fromInit
});
},
handleClickItem(item) {
if (this.trigger !== 'click' && this.hasChildren(item)) return
this.handleTriggerItem(item)
},
handleHoverItem(item) {
if (this.trigger !== 'hover' || !this.hasChildren(item)) return
this.handleTriggerItem(item)
},
getBaseItem (item) {
let backItem = Object.assign({}, item);
backItem.children && delete backItem.children;
return backItem;
},
updateResult (item) {
this.emitUpdate(this.result = this.curItem.concat(item))
},
// 调用父级方法 更新result
emitUpdate (result) {
this.$parent.updateResult(result[0] && result[0].__label != undefined ? [result] : result);
},
inTmp(item) {
return this.curItem.findIndex(t => t.value === item.value && t.label === item.label)
},
itemClass(item) {
return [
`${this.prefixCls}__list`,
{
'active': ~this.inTmp(item),
'disabled': item.disabled
}
]
},
iconClass(item) {
return [
{
'selected-mark': !this.hasChildren(item) && this.multiple,
'ans-icon-arrow-right': this.hasChildren(item)
}
]
},
hasChildren(item) {
return item && item.children && item.children.length
},
onFindSelected() {
this.$on('on-find-selected', (params) => {
const val = params.value;
let value = [...val];
for (let i = 0; i < value.length; i++) {
for (let j = 0; j < this.data.length; j++) {
if (typeof value[i] === 'object') {
value[i].forEach(t => {
if (t === this.data[j].value) {
value[i].splice(0, 1)
this.handleTriggerItem(this.data[j], true);
this.$nextTick(() => {
this.broadcast(PANEL_NAME, 'on-find-selected', {value: value});
});
return false
}
})
} else if (value[i] === this.data[j].value) {
value.splice(0, 1);
this.handleTriggerItem(this.data[j], true);
this.$nextTick(() => {
this.broadcast(PANEL_NAME, 'on-find-selected', {value: value});
});
return false;
}
}
}
});
},
onClear() {
this.$on('on-clear', (deep = false) => {
this.sublist = [];
this.curItem = [];
if (deep) {
const panel = findComponentDownward(this, PANEL_NAME);
panel && panel.$emit('on-clear', true);
}
});
},
onVisibleChange() {
let _this = this
_this.$on('on-visible-change', (val) => {
val && _this.$nextTick(() => {
let actives = document.getElementsByClassName('ans-cascader-drop__list active')
for (let item = 0; item < actives.length; item ++) {
scrollIntoView(actives[item].parentNode, actives[item])
}
this.broadcast(PANEL_NAME, 'drop-visible-change', val);
actives = null
})
})
}
},
mounted () {
// 初始化已选的值
this.onFindSelected()
// 清空
this.onClear()
this.onVisibleChange()
},
beforeDestroy() {
this.$off('on-find-selected')
this.$off('on-clear')
this.$off('on-visible-change')
}
}
</script>
\ No newline at end of file
## Checkbox
用于一组可选项多项选择,或者单独用于标记切换某种状态。
### Checkbox props
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
value | 单独使用时有效,可用于v-model双向绑定 | String / Number / Boolean | - | -
label | 组合使用时有效,指定当前选项value值 | String / Number / Boolean | - | -
disabled | 是否禁用 | Boolean | - | false
true-value | 自定义选中时的值 | String / Number / Boolean | - | true
false-value | 自定义未选中时的值 | String / Number / Boolean | - | false
### Checkbox events
事件名称 | 说明 | 回调参数
--- | --- | ---
on-change | 在选项状态发生改变时触发,返回当前状态。通过修改外部的数据改变时不会触发 | 选中的 Checkbox value 值
### CheckboxGroup props
属性 | 说明 | 类型 | 可选值 | 默认值
--- | --- | --- | --- | ---
value | 当前选中的值,可用于v-model双向绑定 | Array | - | []
### CheckboxGroup events
事件名称 | 说明 | 回调参数
--- | --- | ---
on-change | 在选项状态发生改变时触发,返回当前状态。通过修改外部的数据改变时不会触发 | 选中的 Checkbox label 值
\ No newline at end of file
<template>
<div>
<section class="demo-section">
<h4>基本用法</h4>
<div>
<p><x-checkbox @on-change="clickMe">普通</x-checkbox></p>
<p><x-checkbox v-model="ck" true-value="真" false-value="假" @on-change="clickMe">{{ck}}</x-checkbox></p>
</div>
</section>
<section class="demo-section">
<h4>组合</h4>
<div>
<x-checkbox-group v-model="md" @on-change="onChange">
<x-checkbox :label="'香蕉'" disabled>香蕉</x-checkbox>
<x-checkbox :label="'苹果'">苹果</x-checkbox>
<x-checkbox :label="'橘子'">橘子</x-checkbox>
</x-checkbox-group>
</div>
</section>
</div>
</template>
<script>
import { xCheckboxGroup, xCheckbox } from '../src'
export default {
name: 'app',
data () {
return {
md: ['香蕉', '苹果', '橘子'],
ck: ''
}
},
components: { xCheckboxGroup, xCheckbox },
methods: {
onChange (data) {
console.log(data)
},
clickMe (d) {
console.log(d)
}
}
}
</script>
<style lang="scss">
</style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1">
<script src="https://s1.analysys.cn/libs/??js-polyfills/0.1.42/polyfill.min.js"></script>
<link rel="stylesheet" href="//s2.analysys.cn/libs/??-/@analysys/fss-demo.css@">
<link rel="stylesheet" href="../../../src/style/index.scss">
<title>demo</title>
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
import Vue from 'vue'
import App from './app.vue'
new Vue({
el: '#app',
render: h => h(App),
mounted () {
console.log('success')
}
})
import xCheckbox from './source/Checkbox.vue'
import xCheckboxGroup from './source/CheckboxGroup.vue'
export {
xCheckbox,
xCheckboxGroup
}
<template>
<label :class="className">
<span :class="checkedClass">
<span :class="prefixCls+'-inner'"></span>
<input type="checkbox" :class="prefixCls+'-input'"
v-if="group"
:value="label"
v-model="model"
:disabled="disabled"
@change="change"/>
<input type="checkbox" :class="prefixCls+'-input'"
v-if="!group"
:checked="currentValue"
:disabled="disabled"
@change="change"/>
</span>
<span class="checkbox-label" v-if="$slots.default || label">
<slot></slot><template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
import { findComponentUpward, emitter } from '../../../../src/util'
import { LIB_NAME } from '../../../../src/util/constants'
const prefixCls = `${LIB_NAME}-checkbox`
export default {
name: 'xCheckbox',
mixins: [emitter],
data () {
return {
prefixCls: prefixCls,
currentValue: this.value === this.trueValue,
// 是否在组中
group: false,
// 组中用到,保存组中所有的状态
model: [],
// 是否显示子组件
showSlot: true
}
},
props: {
// 只在组中有效,判断当前是否选中
label: {
type: [String, Number, Boolean],
default: ''
},
// 只在单独时有效,控制是否选中
value: [String, Number, Boolean],
// 是否无效
disabled: {
type: Boolean,
default: false
},
trueValue: {
type: [Boolean, String, Number],
default: true
},
falseValue: {
type: [Boolean, String, Number],
default: false
}
},
computed: {
className () {
return [{
[`${prefixCls}-wrapper`]: true,
[`${prefixCls}-wrapper-checked`]: !!this.currentValue,
[`${prefixCls}-wrapper-disabled`]: !!this.disabled
}]
},
checkedClass () {
return [{
[`${prefixCls}`]: true,
[`${prefixCls}-checked`]: !!this.currentValue
}]
}
},
mounted () {
this.parent = findComponentUpward(this, 'xCheckboxGroup')
if (this.parent) this.group = true
if (!this.group) {
this.updateModel()
} else {
this.parent.updateModel(true)
}
this.showSlot = this.$slots.default !== undefined
},
methods: {
change (event) {
if (this.disabled) {
return
}
const checked = event.target.checked
this.currentValue = checked
let current = checked ? this.trueValue : this.falseValue
this.$emit('input', current)
if (this.group) {
this.$parent.change(this.model)
} else {
this.$emit('on-change', current)
this.dispatch('xFormItem', 'on-form-change', current)
}
},
updateModel () {
this.currentValue = this.value === this.trueValue
}
},
watch: {
value () {
this.updateModel()
}
}
}
</script>
<template>
<div :class="classes">
<slot></slot>
</div>
</template>
<script>
import { findComponentsDownward, emitter } from '../../../../src/util'
import { LIB_NAME } from '../../../../src/util/constants'
const prefixCls = `${LIB_NAME}-checkbox-group`
export default {
name: 'xCheckboxGroup',
mixins: [emitter],
props: {
value: {
type: Array,
default () {
return []
}
}
},
data () {
return {
currentValue: this.value,
childrens: []
}
},
computed: {
classes () {
return `${prefixCls}`
}
},
mounted () {
this.updateModel(true)
},
methods: {
updateModel (update) {
const value = this.value
this.childrens = findComponentsDownward(this, 'xCheckbox')
if (this.childrens) {
this.childrens.forEach(child => {
child.model = value
if (update) {
child.currentValue = value.indexOf(child.label) >= 0
child.group = true
}
})
}
},
change (data) {
this.currentValue = data
this.$emit('input', data)
this.$emit('on-change', data)
this.dispatch('xFormItem', 'on-form-change', data)
}
},
watch: {
value () {
this.updateModel(true)
}
}
}
</script>
An component project
### Setup
- 安装node > 8的LTS版本,https://nodejs.org/en/
- 增加npm本地仓库host,106.75.23.50 npm.analysys.cn
- 没安装yarn的,可以忽略以下yarn命令
```sh
# set registry
npm config set registry http://registry.npm.analysys.cn
# install parcel
yarn global add parcel-bundler | npm i -g parcel-bundler
# install dependencies
yarn | npm i
# startup development server (defaults to 3000)
# -> http://localhost:3000
yarn start | npm start
```
### Lint
```sh
yarn test | npm run test
yarn lint:fix | npm run lint:fix
```
<template>
<div>
<section class="demo-section">
<h4>基础用法</h4>
<div>
<x-datepicker clearable @on-change="change"></x-datepicker>
</div>
</section>
<section class="demo-section">
<h4>选择日期带时分秒</h4>
<div>
<x-datepicker placeholder="时分秒" format="YYYY-MM-DD HH:mm:ss"></x-datepicker>
<x-datepicker placeholder="时分" format="YYYY-MM-DD HH:mm"></x-datepicker>
<x-datepicker placeholder="时" format="YYYY-MM-DD HH"></x-datepicker>
</div>
</section>
<section class="demo-section">
<h4>选择年月</h4>
<div>
<x-datepicker placeholder="month" type="month" format="YYYY-MM"></x-datepicker>
<x-datepicker placeholder="year" type="year" format="YYYY"></x-datepicker>
</div>
</section>
<section class="demo-section">
<h4>日期区间</h4>
<div>
<x-datepicker type="daterange"></x-datepicker>
</div>
</section>
<section class="demo-section">
<h4>日期区间带时分秒</h4>
<div>
<x-datepicker type="daterange" placeholder="时分秒" format="YYYY-MM-DD HH:mm:ss"></x-datepicker>
<x-datepicker type="daterange" placeholder="时分" format="YYYY-MM-DD HH:mm"></x-datepicker>
<x-datepicker type="daterange" placeholder="时" format="YYYY-MM-DD HH"></x-datepicker>
</div>
</section>
<section class="demo-section">
<h4>快捷选择</h4>
<div>
<x-datepicker type="daterange" :options-width="90" :options="options" placeholder="快捷选择" style="width:400px"></x-datepicker>
</div>
</section>
<section class="demo-section">
<h4>日期区间多选</h4>
<div>
<x-datepicker type="daterange" :multiple="0" placeholder="区间多选" style="width:600px"></x-datepicker>
</div>
</section>
<section class="demo-section">
<h4>多个日期面板</h4>
<div>
<x-datepicker type="daterange" :panel-num="5" placeholder="5"></x-datepicker>
</div>
</section>
</div>
</template>
<script>
import { xDatepicker, dateUtil } from '../src/index'
export default {
name: 'app',
components: { xDatepicker },
data () {
return {
options: [
{
text: '今日',
type: 'text',
value () {
return [dateUtil().format('YYYY/MM/DD'), dateUtil().format('YYYY/MM/DD')]
},
click: () => {}
}, {
text: '近29日',
value () {
return [dateUtil().subtract(29, 'days').format('YYYY/MM/DD'), dateUtil().format('YYYY/MM/DD')]
},
click: () => {}
}
]
}
},
methods: {
change (d) {
console.log(d)
}
}
}
</script>
<style lang="scss"></style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name=viewport content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1">
<script src="https://s1.analysys.cn/libs/??js-polyfills/0.1.42/polyfill.min.js"></script>
<link rel="stylesheet" href="//s2.analysys.cn/libs/??-/@analysys/fss-demo.css@">
<link rel="stylesheet" href="../../../src/style/index.scss">
<title>demo</title>
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
import Vue from 'vue'
import App from './app.vue'
new Vue({
el: '#app',
render: h => h(App),
mounted () {
console.log('success')
}
})
import xDatepicker from './source/datepicker.vue'
import dateUtil from 'dayjs'
export { xDatepicker, dateUtil }
<template>
<div class="x-date-packer-confirm">
<div class="confirm-slot">
<span class="ck-act date-time-text disabled" v-if="ishms()" v-show="!isTime">{{text}}</span>
<x-poptip popper-class="date-poptip" placement="top-start" v-if="ishms()" :popper-options = "{boundariesElement:'viewport'}" @on-show="tipShow" ref="poptipTime">
<span slot="reference" class="ck-act date-time-text" v-show="isTime">{{text}}</span>
<times ref="times" @change="timeChange" :format="format" :type="type"></times>
</x-poptip>
<slot name="confirm"></slot>
</div>
<div class="confirm-btn">
<span class="ck-act" @click="cancel">{{t('ans.datepicker.cancel')}}</span>
<button @click="$emit('confirm-btn')">{{t('ans.datepicker.confirm')}}</button>
</div>
</div>
</template>
<script>
import ishms from '../util/ishms.js'
import { xPoptip } from '../../../../vue-poptip/src/index'
import times from '../base/time.vue'
import isValid from '../util/isValid.js'
import { Locale } from '../../../../../src/util'
import { t } from '../../../../../src/locale'
export default {
components: { xPoptip, confirm, times },
mixins: [Locale],
data () {
return {
text: t('ans.datepicker.selectTime'),
isTime: false
}
},
props: {
format: String,
type: String
},
methods: {
ishms () {
return ishms(this.format)
},
timsInit (date, date1) {
if (date && isValid(date)) {
this.isTime = true
this.$refs.times.init(date, date1)
} else {
this.$refs.poptipTime.doClose()
this.isTime = false
}
},
timeChange (date) {
this.$emit('time-change', date)
},
cancel () {
this.isTime = false
this.$emit('cancel')
},
tipShow () {
this.$refs.times.setScrollTop()
}
}
}
</script>
<template>
<div class="x-date-packer-panel" ref="dateDay">
<ym v-show="ymShow" ref="dateYm" @change="ymChange" @hide="ymShow = false"></ym>
<div class="x-date-packer-hd">
<span class="hd-icon hd-icon-left"><i class="ans-icon-arrow-to-left" @click="setToDate('subtract', 'y')"></i> <i class="ans-icon-arrow-left ans-icon-arrow-left" @click="setToDate('subtract', 'M')"></i></span>
<span class="hd-txt" @click="showYm('y')">{{year + t('ans.datepicker.year')}}</span>
<span class="hd-txt" @click="showYm('m')">{{month && t('ans.datepicker.month' + month)}}</span>
<span class="hd-icon hd-icon-right"><i class="ans-icon-arrow-right" @click="setToDate('add', 'M')"></i> <i class="ans-icon-arrow-to-right" @click="setToDate('add', 'y')"></i></span>
</div>
<div class="x-date-packer-day">
<div class="x-date-packer-day-week">
<span v-for="item in week" class="lattice">{{item}}</span>
</div>
<div class="x-date-packer-day-day">
<label class="lattice" v-for="i in weeks"></label><span class="lattice em" v-for="d in days" v-attr="hoverDate(d)" @click="selected(d, $event)"
:key="uuId()" @mouseover="hover(d, $event)" :class="{'picker-disabled': getDisabledDate(d)}">
<label :data-mouseover="year + '-' + month + '-' + d" class="dataMouseoverAct"><em :data-today="today(d)" :data-select="selectDay(d)">{{d}}</em></label>
</span>
</div>
</div>
</div>
</template>
<script>
import moment from 'dayjs'
import toDate from '../util/toDate'
import isValid from '../util/isValid.js'
import ym from './years.vue'
import { uuid, Locale } from '../../../../../src/util'
import { t } from '../../../../../src/locale'
let toDateCache = toDate()
const colorAlls = ['#0098e1', '#ffcf3d', '#7281c2', '#f2ac6f', '#f07d7d', '#e84d80', '#a463b0', '#7a56b8', '#625ad1', '#8ba8d6']
for (let i = 10; i < 100; i++) {
colorAlls[i] = '#' + Math.floor(Math.random() * 0xffffff).toString(16)
}
const WEEKS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
const daysInWeek = WEEKS.map(w => t(`ans.datepicker.weeks.${w}`))
export default {
components: { ym },
data () {
return {
week: daysInWeek,
// 本月一号星期几
weeks: 0,
// 本月多少天
days: 0,
year: 0,
month: 0,
// 当前面板日期
toDate: new Date(),
ymShow: false
}
},
mixins: [Locale],
props: {
type: {
type: String,
default: 'date'
},
// 展示的日期格式
format: {
type: String,
default: 'YYYY/MM/DD'
},
// 选中日期
selectedDate: {
type: [Array],
default () {
// return [new Date()]
return null
}
},
// 是否
isHover: {
type: [Number, String, Boolean],
default: false
},
// hover start time
hoverStartDate: {
type: [Array],
default: null
// default () {
// return ['2018-06-03', '2018-06-10', '2018-06-15']
// }
},
// hover end time
hoverEndDate: {
type: [Array],
default: null
// default () {
// return ['2018-06-07', '2018-06-14', '2018-07-23']
// }
},
// 禁用日期
disabledDate: {
type: Function
}
},
methods: {
init (date) {
let { year, month, week, day } = this.thisDate()
this.year = year
this.month = month
this.weeks = week
this.days = day
},
// 获取当前面板信息
thisDate () {
let dates = moment(new Date(this.toDate)).toDate(), year = dates.getFullYear(), month = dates.getMonth() + 1
return {
year: year, // 年份
month: month, // 月份
day: new Date(year, month, 0).getDate(), // 每月多少天
week: new Date(year, month - 1, 1).getDay(), // 每月第一天星期几
hours: dates.getHours(),
minutes: dates.getMinutes(),
seconds: dates.getSeconds()
}
},
// 日期格式化
dateFmt (date, fmt) {
return moment(date).format(fmt)
},
// 给今天添加样式
today (d) {
return new Date(this.year, this.month, d).getTime() === new Date(toDateCache.year, toDateCache.month, toDateCache.today).getTime()
},
//
hoverDate (d) {
let toDate = moment(new Date(this.year, this.month - 1, d)).format('YYYYMMDD')
let hStart = this.hoverStartDate, hEnd = this.hoverEndDate
return { toDate, hStart, hEnd }
},
// 当前选中日期
selectDay (d) {
let selDate = this.selectedDate
if (!selDate || this.type !== 'date') return false
if (selDate.length > 0) {
let toDate = moment(new Date(this.year, this.month - 1, d)).format('YYYYMMDD')
for (let i = 0, len = selDate.length; i < len; i++) {
if (isValid(selDate[i])) {
let thisDate = moment(selDate[i]).format('YYYYMMDD')
if (toDate === thisDate) return true
}
}
return false
}
},
setToDateInit (type, key) {
this.toDate = moment(new Date(this.toDate))[type](1, key).format('YYYY-MM-DD')
this.init()
},
// 加减日期
setToDate (type, key) {
this.setToDateInit(type, key)
this.$emit('switch-date-panel', type, key)
},
// 选中
selected (d, e) {
if (/picker-disabled/.test(e.currentTarget.className)) return
let date = new Date(this.year, this.month - 1, d)
this.$emit('change', date)
},
// hover
hover (d, e) {
if (/picker-disabled/.test(e.currentTarget.className)) return
if (this.isHover) {
this.$emit('hover', new Date(this.year, this.month - 1, d))
}
},
// 显示选择年月面板
showYm (type) {
this.ymShow = true
this.$refs.dateYm.init(this.toDate, type)
},
ymChange (date) {
this.toDate = date
this.ymShow = false
this.init([date])
this.$emit('ym-change', date, this.$refs.dateDay)
},
// 设置不可选日期
getDisabledDate (d) {
return this.disabledDate ? this.disabledDate(moment(new Date(this.year, this.month - 1, d)).toDate()) : false
},
uuId () {
return uuid() + uuid() + uuid()
}
},
directives: {
attr: {
bind: (el, binding) => {
let colors = colorAlls
let { toDate, hStart, hEnd } = binding.value
if (hStart && hEnd) {
for (let i = 0, len = hStart.length; i < len; i++) {
if (hStart[i] && hEnd[i]) {
let startDate = moment(new Date(hStart[i])).format('YYYYMMDD')
let endDate = moment(new Date(hEnd[i])).format('YYYYMMDD')
let style = el.getAttribute('style')
let b = document.createElement('b')
if (toDate === startDate) {
!style ? el.setAttribute('data-hover-start', 'true') : b.setAttribute('data-hover-start', 'true')
}
if (toDate === endDate) {
!style ? el.setAttribute('data-hover-end', 'true') : b.setAttribute('data-hover-end', 'true')
}
if (toDate >= startDate && toDate <= endDate) {
if (!style) {
el.style.background = colors[i] || '#0098e1'
} else {
b.style.background = colors[i] || '#0098e1'
el.appendChild(b)
}
el.setAttribute('data-hover', 'true')
el.setAttribute('data-hover-' + i, 'true')
}
}
}
}
}
}
}
}
</script>
<template>
<div class="x-date-packer-time" :style="{width: type==='daterange' ? '305px' : 'auto'}">
<div class="packer-time-body" :class="[getType()]">
<div class="hd-text" v-if="type==='daterange'">{{t('ans.datepicker.startTime')}}</div>
<div class="picker-time-div time-hours" ref="hours">
<ul>
<li v-for="(item,index) in 24" :class="{'time-act': index == hours}" @click="change(index, 'hours')">{{numLen(index)}}</li>
</ul>
</div>
<div class="picker-time-div time-minute" ref="minute">
<ul>
<li v-for="(item,index) in 60" :class="{'time-act': index == minute}" @click="change(index, 'minute')">{{numLen(index)}}</li>
</ul>
</div>
<div class="picker-time-div time-second" ref="second">
<ul>
<li v-for="(item,index) in 60" :class="{'time-act': index == second}" @click="change(index, 'second')">{{numLen(index)}}</li>
</ul>
</div>
</div>
<div class="packer-time-body" :class="[getType()]" v-if="type==='daterange'" style="float:right">
<div class="hd-text" v-if="type==='daterange'">{{t('ans.datepicker.endTime')}}</div>
<div class="picker-time-div time-hours bd-left" ref="hours1">
<ul>
<li v-for="(item,index) in 24" :class="{'time-act': index == hours1}" @click="changeLast(index, 'hours1')">{{numLen(index)}}</li>
</ul>
</div>
<div class="picker-time-div time-minute" ref="minute1">
<ul>
<li v-for="(item,index) in 60" :class="{'time-act': index == minute1}" @click="changeLast(index, 'minute1')">{{numLen(index)}}</li>
</ul>
</div>
<div class="picker-time-div time-second" ref="second1">
<ul>
<li v-for="(item,index) in 60" :class="{'time-act': index == second1}" @click="changeLast(index, 'second1')">{{numLen(index)}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import moment from 'dayjs'
import { Locale } from '../../../../../src/util'
export default {
data () {
return {
hours: null,
minute: null,
second: null,
hours1: null,
minute1: null,
second1: null
}
},
props: {
format: String,
type: String
},
mixins: [Locale],
methods: {
init (date, date1) {
this.setDate(date, date1)
if (date) {
this.hours = moment(new Date(date)).format('H')
this.minute = moment(new Date(date)).format('m')
this.second = moment(new Date(date)).format('s')
} else {
this.hours = null
this.minute = null
this.second = null
}
if (date1) {
this.hours1 = moment(new Date(date1)).format('H')
this.minute1 = moment(new Date(date1)).format('m')
this.second1 = moment(new Date(date1)).format('s')
} else {
this.hours1 = null
this.minute1 = null
this.second1 = null
}
},
setScrollTop () {
setTimeout(() => {
this.$refs.hours.scrollTop = this.hours * 25
this.$refs.minute.scrollTop = this.minute * 25
this.$refs.second.scrollTop = this.second * 25
if (this.$refs.hours1) this.$refs.hours1.scrollTop = this.hours1 * 25
if (this.$refs.minute1) this.$refs.minute1.scrollTop = this.minute1 * 25
if (this.$refs.second1) this.$refs.second1.scrollTop = this.second1 * 25
}, 10)
},
setDate (date, date1) {
let Dates = new Date(date || new Date())
this.year = Dates.getFullYear()
this.month = Dates.getMonth()
this.day = Dates.getDate()
let Dates1 = new Date(date1 || new Date())
this.year1 = Dates1.getFullYear()
this.month1 = Dates1.getMonth()
this.day1 = Dates1.getDate()
},
setHms (value, type) {
this[type] = value
this.$refs[type].scrollTop = value * 25
let key = ['hours', 'minute', 'second', 'hours1', 'minute1', 'second1']
key.forEach(item => {
if (this[item] === null) this[item] = 0
})
},
change (value, type) {
this.setHms(value, type)
if (this.year === this.year1 && this.month === this.month1 && this.day === this.day1) {
if (type === 'hours') {
if (this.hours1 <= value && this.minute > this.minute1) {
this.minute1 = this.minute
this.$refs.minute1.scrollTop = this.minute * 25
}
if (this.hours1 < value) {
this.hours1 = value
if (this.$refs.hours1) this.$refs.hours1.scrollTop = value * 25
}
}
if (type === 'minute' && this.hours1 === this.hours && this.minute1 < value) {
this.minute1 = value
if (this.$refs.minute1) this.$refs.minute1.scrollTop = value * 25
}
if (type === 'second' && this.hours1 === this.hours && this.minute1 === this.minute && this.second1 < value) {
this.second1 = value
if (this.$refs.second1) this.$refs.second1.scrollTop = value * 25
}
}
this.onChange()
},
changeLast (value, type) {
if (this.year === this.year1 && this.month === this.month1 && this.day === this.day1) {
if (type === 'hours1' && this.hours > value) return
if (type === 'minute1' && this.hours1 === this.hours && this.minute > value) return
if (type === 'second1' && this.hours1 === this.hours && this.minute1 === this.minute && this.second > value) return
}
this.setHms(value, type)
this.onChange()
},
onChange () {
if (this.type === 'date') {
this.$emit('change', new Date(this.year, this.month, this.day, this.hours, this.minute, this.second))
} else {
this.$emit('change', [new Date(this.year, this.month, this.day, this.hours, this.minute, this.second), new Date(this.year1, this.month1, this.day1, this.hours1, this.minute1, this.second1)])
}
},
numLen (index) {
return index.toString().length === 1 ? 0 + '' + index : index
},
getType () {
let fmt = this.format
if (/H|h/.test(fmt) && /m/.test(fmt) && /s/.test(fmt)) return 'hms'
if (/H|h/.test(fmt) && /m/.test(fmt)) return 'hm'
if (/H|h/.test(fmt)) return 'h'
}
}
}
</script>
<template>
<div class="x-date-packer-ym">
<div class="x-date-packer-hd">
<span class="hd-icon hd-icon-left" style="display:block"><i class="ans-icon-arrow-left" @click="switchYear('left')"></i></span>
<span class="hd-txt" @click="selectText">{{year + t('ans.datepicker.year')}}</span>
<span class="hd-icon hd-icon-right" style="display:block"><i class="ans-icon-arrow-right" @click="switchYear('right')"></i></span>
</div>
<div class="x-date-packer-item">
<div class="x-date-packer-year" v-if="type === 'y'">
<div class="year-item" v-for="item in yearList"><span :class="{act: isYear(item)}" @click="selectYear(item)">{{item}}</span></div>
</div>
<div class="x-date-packer-month" v-else>
<div class="year-item" v-for="item in 12"><span :class="{act: isMonth(item)}" @click="selectMonth(item)">{{t('ans.datepicker.month' + item)}}</span></div>
</div>
</div>
</div>
</template>
<script>
import moment from 'dayjs'
import { Locale } from '../../../../../src/util'
export default {
data () {
return {
date: null,
year: 0,
month: 0,
type: 'y',
yearList: []
}
},
mixins: [Locale],
props: {
types: String
},
methods: {
init (date, type) {
this.date = date
this.type = type
this.year = moment(date).format('YYYY') - 0
this.setYearList()
},
setYearList () {
let list = []
for (let i = this.year; i <= (this.year - 0) + 10; i++) {
list.push(i)
}
this.yearList = list
},
isYear (y) {
return y === moment(this.date).format('YYYY') - 0
},
isMonth (m) {
return this.year === (moment(this.date).format('YYYY') - 0) && m === (moment(this.date).format('M') - 0)
},
// 选择年份
selectYear (y) {
this.year = y
if (this.types === 'year') {
let date = new Date(this.year, 0, 1)
this.date = date
this.$emit('change', date)
return
}
this.type = 'm'
},
// 选择月份
selectMonth (m) {
this.month = m
let date = new Date(this.year, this.month - 1, 1)
this.$emit('change', date)
this.date = date
},
// 点击年份文本
selectText () {
if (this.type === 'y') {
this.$emit('hide')
} else {
this.type = 'y'
this.year = moment(this.date).format('YYYY')
}
},
// 左右切换年份
switchYear (type) {
if (type === 'left') {
if (this.type === 'y') {
this.year = this.year - 10
this.setYearList()
} else {
this.year = this.year - 1
}
} else {
if (this.type === 'y') {
this.year = (this.year - 0) + 10
this.setYearList()
} else {
this.year = this.year + 1
}
}
}
}
}
</script>
<template>
<x-poptip
popper-class="date-poptip"
class="x-datepicker"
:placement="placement"
transition="datepicker-animation"
:append-to-body="appendToBody"
:position-fixed="positionFixed"
:viewport="viewport"
:popper-options="popperOptions"
ref="datepickerPoptip"
@on-hide="pickerPoptipHide"
@on-show="pickerPoptipShow">
<div slot="reference">
<slot name="input" :placeholder="placeholder" :value="text" :suffix-icon="suffixIcon" :prefix-icon="prefixIcon" :disabled="disabled" :size="size" :readonly="readonly">
<x-input :value="text" :clearable="clearable" :placeholder="placeholder" :suffix-icon="suffixIcon" :prefix-icon="prefixIcon" :size="size" :readonly="readonly" @on-clear="empty"></x-input>
</slot>
</div>
<date
v-if="type==='date'"
ref="date"
:value="dateValue"
:format="format"
:disabled-date="disabledDate"
:confirm="confirm"
@change="dateChange"
@cancel="cancel"
@confirm="$refs.datepickerPoptip.doClose()"><template slot="confirm"><slot name="confirm"></slot></template></date>
<daterange v-if="type==='daterange'"
ref="refDaterange"
:value="dateValue"
:format="format"
:confirm="confirm"
:disabled-date="disabledDate"
:options="options"
:options-width="optionsWidth"
@confirm="$refs.datepickerPoptip.doClose()"
:multiple="multiple"
:select-index="selectIndex"
@change="dateChange"
@cancel="cancel"
:panel-num="panelNum"><template slot="confirm"><slot name="confirm"></slot></template></daterange>
<month v-if="type==='year' || type==='month'"
:type="type" :value="dateValue" @change="dateChange"></month>
</x-poptip>
</template>
<script>
import { emitter } from '../../../../src/util'
import { xInput } from '../../../vue-input/src/index'
import { xPoptip } from '../../../vue-poptip/src/index'
import date from './panel/date.vue'
import Daterange from './panel/daterange.vue'
import isType from './util/isType.js'
import isValid from './util/isValid.js'
import ishms from './util/ishms.js'
import moment from 'dayjs'
import month from './panel/month.vue'
import { t } from '../../../../src/locale'
export default {
name: 'xDatepicker',
mixins: [emitter],
components: { date, xPoptip, xInput, Daterange, month },
props: {
// 日期面板显示或者隐藏
show: {
type: [String, Number, Boolean],
default: false
},
// 初始值
value: {
type: [Array, String, Number, Date],
default () {
return null
}
},
/*
* 日期类型
* 选项值: date || daterange || year || month
*/
type: {
type: String,
default: 'date'
},
// 展示的日期格式
format: {
type: String,
default: 'YYYY-MM-DD'
},
// 不可选日期
disabledDate: {
type: Function
},
// 是否显示底部控制栏
confirm: {
type: [Number, Boolean],
default: 0
},
// 选项
options: {
type: [Array, Object],
default: null
},
// 左边快捷选项宽度
optionsWidth: {
type: Number,
default: 150
},
/**
* 日期面板显示方向
* 选项值: bottom-left || bottom-right || top-left || top-right
*/
placement: {
type: String,
default: 'bottom-start'
},
// 是否添加到body
appendToBody: {
type: Boolean,
default: false
},
positionFixed: Boolean,
viewport: Boolean,
// Popper.js 的可选项
popperOptions: {
type: Object,
default () {
return {}
}
},
// 文本框描述
placeholder: {
type: String,
default () {
return t('ans.datepicker.placeholder')
}
},
clearable: Boolean,
// disabled
disabled: {
type: Boolean,
default: false
},
// // 尺寸,可选值为large、small、default或者不设置
size: {
type: String,
default: 'default'
},
suffixIcon: {
type: String,
default: 'ans-icon-calendar'
},
prefixIcon: {
type: String,
default: ''
},
// 是否只读
readonly: {
type: Boolean,
default: true
},
// 日期区间连接符,在daterange模式下有效
rangeSeparator: {
default: ' - '
},
// 可选择多个日期,默认一个, 0为无限个
multiple: {
type: Number,
default: 1
},
// 多选日期时,点选,指定要选的index, 从0开始
selectIndex: {
type: Number,
default: -1
},
// 支持面板个数,在daterange模式下有效
panelNum: {
type: Number,
default: 3
}
},
watch: {
value (v, v1) {
if (v.toString() !== (v1 && v1.toString()) && v.toString() !== this.dateValueBar.toString()) {
this.init(true)
}
}
},
data () {
return {
text: '',
dateValue: null,
dateValueBar: null,
display: false,
isChange: true
}
},
created () {
this.init()
},
methods: {
init (isChild) {
let { dataValue } = this.getDateValue()
this.setText(dataValue)
this.dateValue = dataValue
this.dateValueChange = [...dataValue]
this.dateValueBar = [...dataValue]
if (isChild) {
if (this.type === 'daterange') this.$refs.refDaterange.init(this.dateValue, this.display)
if (this.type === 'date') this.$refs.date.init(this.dateValue)
}
},
// 获取赋值Value
getDateValue () {
let value = this.value, dataValue = []
if (value) {
if (isType(value) === 'array') {
// daterange类型的老数据
if (this.type === 'daterange' && value.length && isType(value[0]) !== 'array') {
dataValue.push(value)
} else {
value.forEach(item => { dataValue.push(item) })
}
} else {
// let isData = isValid(value)
// 兼容日期区间之前已保存的数据格式 "近xx日" => [['近xx日']]
this.type === 'daterange' ? dataValue.push([value]) : dataValue.push(value)
}
}
return { dataValue }
},
doClose () {
this.$refs.datepickerPoptip.doClose()
},
isConfirm () {
if (this.confirm || ishms(this.format)) return true
return false
},
dateChange (data, optItem) {
this.dateValueChange = data
this.optItem = optItem ? JSON.parse(JSON.stringify(optItem)) : null
let fmtDate = this.fmtDate(data)
this.setText(data, optItem)
if (optItem && this.multiple === 1) {
this.doClose()
}
!this.isConfirm() ? this.doClose() : this.$emit('on-text-change', fmtDate, optItem)
},
dateFormat (date) {
return moment(new Date(date)).format(this.format)
},
// 获取文本展示文本
setText (data) {
if (data && data.length) {
let type = this.type
let fmtDate = this.fmtDate(data)
if (type === 'date') this.text = fmtDate[0]
if (type === 'daterange') {
if (data.length === 1) {
let dataFirst = fmtDate[0]
this.text = dataFirst.join(this.rangeSeparator)
} else {
let text = []
fmtDate.forEach(o => { text.push(o.join(this.rangeSeparator)) })
this.text = text
}
}
if (type === 'year' || type === 'month') this.text = fmtDate[0]
} else {
this.text = ''
}
},
// 日期选择面板显示
pickerPoptipShow () {
this.$emit('on-open-change')
this.display = true
},
// 日期选择面板关闭
pickerPoptipHide () {
if (this.isChange) this.change()
this.$emit('on-close-change')
this.display = false
this.isChange = true
},
// 清空
empty () {
this.dateValue = []
this.dateValueChange = []
this.text = ''
if (this.type === 'daterange') this.$refs.refDaterange.init(this.dateValue)
if (this.type === 'date') this.$refs.date.init(this.dateValue)
this.$emit('on-empty')
},
// 取消
cancel () {
this.isChange = false
this.$emit('on-clear')
this.doClose()
},
change () {
if (this.dateValueChange.length) {
let date = [...this.dateValueChange]
let fmtDate = this.fmtDate(date)
if (this.dateValueBar.toString() !== date.toString()) {
let changeDate = fmtDate, type = this.type
if (type === 'date') {
changeDate = fmtDate[0]
}
if (type === 'daterange' && this.multiple === 1) {
let dataFirst = fmtDate[0]
changeDate = dataFirst.length === 1 ? dataFirst[0] : dataFirst
}
if (type === 'year') {
changeDate = fmtDate[0]
}
if (type === 'month') {
changeDate = fmtDate[0]
}
this.$emit('on-change', changeDate, date, this.optItem)
this.$emit('input', changeDate)
this.dispatch('xFormItem', 'on-form-change', changeDate)
this.dateValueBar = date
}
}
},
// 格式化数据数组
fmtDate (date, optItem) {
if (date && date.length) {
let fmtDate = []
date.forEach(o => {
if (isType(o) === 'array') {
let arr = []
for (let i = 0; i < o.length; i++) {
isValid(o[i]) ? arr.push(moment(o[i]).format(this.format)) : arr.push(o[i])
}
fmtDate.push(arr)
} else {
isValid(o) ? fmtDate.push(moment(o).format(this.format)) : fmtDate.push(o)
}
})
return fmtDate
}
}
}
}
</script>
<template>
<div class="x-date-panel">
<div class="x-date-panel-day">
<!-- <template v-if="isTimes()">
<times v-show="isTimesShow" ref="times" @change="timeChange"></times>
</template> -->
<day :selected-date="selectedDate" @change="dayChange" :format="format" :disabled-date="disabledDate" ref="vDate"></day>
</div>
<confirm v-if="isConfirm()"
:format="format"
@confirm-btn="$emit('confirm')"
@cancel="$emit('cancel')"
type="date"
ref="confirm"
@time-change="timeChange"
></confirm>
</div>
</template>
<script>
import day from '../base/day.vue'
import confirm from '../base/confirm.vue'
import ishms from '../util/ishms.js'
import isValid from '../util/isValid.js'
export default {
name: 'panelDate',
components: { day, confirm },
data () {
return {
selectedDate: null,
// 是否展示times
isTimesShow: false
}
},
props: {
// 初始值
value: {
type: [Array],
default () {
return null
}
},
// 展示的日期格式
format: String,
// 不可选日期
disabledDate: {
type: Function,
default: null
},
// confirm
confirm: {
type: [Number, Boolean],
default: 0
},
// 选项
options: {
type: [Array, Object],
default: null
}
},
mounted () {
this.init()
},
methods: {
init (date) {
let dateValue = date || this.value
if (dateValue && dateValue.length) {
this.selectedDate = dateValue
if (isValid(dateValue[0])) this.$refs.vDate.toDate = dateValue[0]
} else {
this.selectedDate = []
}
this.$refs.vDate.init(this.selectedDate)
this.timeInit()
},
timeInit () {
if (this.isTimes()) {
this.$refs.confirm.timsInit(this.selectedDate[this.selectedDate.length - 1])
}
},
dayChange (date) {
this.selectedDate = [date]
this.timeInit()
this.$emit('change', this.selectedDate)
},
timeChange (date) {
this.selectedDate = [date]
this.$emit('change', this.selectedDate)
},
isTimes () {
return ishms(this.format)
},
isConfirm () {
if (this.confirm || this.isTimes()) return true
return false
}
}
}
</script>
\ No newline at end of file
<template>
<div class="x-date-packer-panel">
<years :types="type" ref="years" style="position:inherit;" @change="change"></years>
</div>
</template>
<script>
import years from '../base/years.vue'
export default {
components: { years },
data () {
return {
}
},
props: {
type: {
type: String,
default: 'year'
},
value: Array
},
mounted () {
let t = this.type === 'year' ? 'y' : 'm'
let d = this.value && this.value.length ? this.value[0] : new Date()
this.$refs.years.init(d, t)
},
methods: {
change (date) {
this.$emit('change', [date])
}
}
}
</script>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册