break60
5 years ago
committed by
qiaozhanwei
256 changed files with 7 additions and 24177 deletions
@ -1,55 +0,0 @@
|
||||
## 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 |
||||
``` |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@
|
||||
!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
|
File diff suppressed because one or more lines are too long
@ -1,2 +0,0 @@
|
||||
!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
|
File diff suppressed because one or more lines are too long
@ -1,95 +0,0 @@
|
||||
{ |
||||
"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": {} |
||||
} |
||||
} |
@ -1,73 +0,0 @@
|
||||
## 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 |
@ -1,221 +0,0 @@
|
||||
<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> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,9 +0,0 @@
|
||||
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 |
||||
} |
@ -1,135 +0,0 @@
|
||||
<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> |
@ -1,106 +0,0 @@
|
||||
<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> |
@ -1,40 +0,0 @@
|
||||
/** |
||||
* 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 |
@ -1,99 +0,0 @@
|
||||
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) |
||||
} |
||||
} |
@ -1,203 +0,0 @@
|
||||
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) |
||||
} |
||||
} |
@ -1,104 +0,0 @@
|
||||
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) |
||||
} |
||||
} |
@ -1,28 +0,0 @@
|
||||
## 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 | - | - |
@ -1,227 +0,0 @@
|
||||
<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> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,7 +0,0 @@
|
||||
import xButton from './source/Button.vue' |
||||
import xButtonGroup from './source/ButtonGroup.vue' |
||||
|
||||
export { |
||||
xButton, |
||||
xButtonGroup |
||||
} |
@ -1,145 +0,0 @@
|
||||
<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> |
@ -1,152 +0,0 @@
|
||||
<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> |
@ -1,32 +0,0 @@
|
||||
## 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. |
||||
|
||||
--- |
@ -1,83 +0,0 @@
|
||||
<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> |
@ -1,635 +0,0 @@
|
||||
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 } |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,8 +0,0 @@
|
||||
import xCascader from './source/Cascader.vue' |
||||
|
||||
/* istanbul ignore next */ |
||||
xCascader.install = function (Vue) { |
||||
Vue.component(xCascader.name, xCascader) |
||||
} |
||||
|
||||
export { xCascader } |
@ -1,440 +0,0 @@
|
||||
<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> |
@ -1,213 +0,0 @@
|
||||
<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> |
@ -1,31 +0,0 @@
|
||||
## 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 值 |
@ -1,48 +0,0 @@
|
||||
<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> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,7 +0,0 @@
|
||||
import xCheckbox from './source/Checkbox.vue' |
||||
import xCheckboxGroup from './source/CheckboxGroup.vue' |
||||
|
||||
export { |
||||
xCheckbox, |
||||
xCheckboxGroup |
||||
} |
@ -1,117 +0,0 @@
|
||||
<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> |
@ -1,64 +0,0 @@
|
||||
<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> |
@ -1,30 +0,0 @@
|
||||
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 |
||||
``` |
@ -1,103 +0,0 @@
|
||||
<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> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,4 +0,0 @@
|
||||
import xDatepicker from './source/datepicker.vue' |
||||
import dateUtil from 'dayjs' |
||||
|
||||
export { xDatepicker, dateUtil } |
@ -1,69 +0,0 @@
|
||||
<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> |
@ -1,260 +0,0 @@
|
||||
<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> |
@ -1,180 +0,0 @@
|
||||
<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> |
@ -1,120 +0,0 @@
|
||||
<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> |
@ -1,406 +0,0 @@
|
||||
<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> |
@ -1,114 +0,0 @@
|
||||
<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> |
@ -1,436 +0,0 @@
|
||||
<template> |
||||
<div class="date-packer-daterange-body"> |
||||
<div class="date-packer-daterange"> |
||||
<div class="date-packer-opt" v-if="options" :style="{width: optionsWidth + 'px'}"> |
||||
<div class="picker-panel-shortcut" :style="item.style" |
||||
v-for="item in options" |
||||
@click="optionsClick(item, $event)" |
||||
@mouseenter="optionsHover(item, $event)" |
||||
@mouseout="optionsHoverOut()" |
||||
:class="{act: getOptItemAct(item)}">{{item.text}}</div> |
||||
</div> |
||||
<div class="date-interval" ref="datePackerDay" :style="{width: 202 * panelNum + 'px', marginLeft: options ? optionsWidth + 'px' : ''}"> |
||||
<template v-for="(item, i) in panelNum"> |
||||
<day |
||||
type="daterange" |
||||
:format="format" |
||||
:is-hover="isHover" |
||||
:selected-date="selectedDate" |
||||
:hover-start-date="hoverStartDate" |
||||
:hover-end-date="hoverEndDate" |
||||
:disabled-date="disabledDate" |
||||
@change="dayChange" |
||||
@hover="hoverChange" |
||||
@ym-change="ymChange" |
||||
:ref="'dateRange' + i" |
||||
:class="{'date-start': i === 0, 'date-end': i === panelNum - 1}" |
||||
@switch-date-panel="switchDatePanel" :index="i"></day> |
||||
</template> |
||||
</div> |
||||
|
||||
</div> |
||||
<confirm v-if="isConfirm()" :format="format" @confirm-btn="$emit('confirm')" @cancel="cancel" type="daterange" ref="confirm" @time-change="timeChange"> |
||||
<template slot="confirm"> |
||||
<slot name="confirm"></slot> |
||||
</template> |
||||
</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' |
||||
import isType from '../util/isType.js' |
||||
import moment from 'dayjs' |
||||
|
||||
const deepFlatten = arr => [].concat(...arr.map(v => Array.isArray(v) ? deepFlatten(v) : v)) |
||||
export default { |
||||
name: 'Daterange', |
||||
components: { day, confirm }, |
||||
|
||||
data () { |
||||
return { |
||||
selectedDate: [], |
||||
|
||||
hoverStartDate: [], |
||||
|
||||
hoverEndDate: [], |
||||
|
||||
isHover: false, |
||||
|
||||
arrDate: [] |
||||
} |
||||
}, |
||||
props: { |
||||
|
||||
// 初始值 |
||||
value: { |
||||
type: [Array], |
||||
default () { |
||||
return null |
||||
} |
||||
}, |
||||
|
||||
// 展示的日期格式 |
||||
format: String, |
||||
|
||||
// 不可选日期 |
||||
disabledDate: { |
||||
type: Function |
||||
}, |
||||
|
||||
// confirm |
||||
confirm: { |
||||
type: [Number, Boolean], |
||||
default: 0 |
||||
}, |
||||
|
||||
// 选项 |
||||
options: { |
||||
type: [Array, Object], |
||||
default: null |
||||
}, |
||||
|
||||
optionsWidth: Number, |
||||
|
||||
// 支持面板个数 |
||||
panelNum: { |
||||
type: Number, |
||||
default: 2 |
||||
}, |
||||
|
||||
// 可选择多个日期,默认一个, 0为无限个 |
||||
multiple: 0, |
||||
|
||||
// 多选日期时,指定选择的位数 |
||||
selectIndex: { |
||||
type: Number, |
||||
default: -1 |
||||
} |
||||
}, |
||||
|
||||
mounted () { |
||||
this.init() |
||||
}, |
||||
|
||||
methods: { |
||||
init (date, display) { |
||||
let value = moment(new Date()).subtract(this.panelNum - 1, 'M'), selectedDate = [] |
||||
this.hoverStartDate = [] |
||||
this.hoverEndDate = [] |
||||
this.arrDate = [] |
||||
this.isHover = false |
||||
let dateValue = date || this.value |
||||
if (dateValue) { |
||||
selectedDate = deepFlatten(this.value) |
||||
this.selectedDate = dateValue |
||||
|
||||
// 面板包含当前选中日期 |
||||
if (isType(dateValue[0]) === 'array' && isValid(dateValue[0][0])) { |
||||
value = moment(dateValue[0][0]).subtract(this.panelNum - 2, 'M') |
||||
} |
||||
|
||||
dateValue.forEach((item, i) => { |
||||
if (item.length === 2) { |
||||
this.hoverStartDate.push(item[0]) |
||||
this.hoverEndDate.push(item[1]) |
||||
} else { |
||||
let options = this.options |
||||
if (options && options.length) { |
||||
for (let i = 0; i < options.length; i++) { |
||||
if (options[i].text === item[0] && options[i].value) { |
||||
let value = options[i].value() |
||||
this.hoverStartDate.push(value[0]) |
||||
this.hoverEndDate.push(value[1]) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
|
||||
if (!display) { |
||||
this.switchPanel(value, selectedDate) |
||||
} |
||||
|
||||
this.timeInit() |
||||
}, |
||||
|
||||
timeInit (isShow = true) { |
||||
if (ishms(this.format)) { |
||||
let selectedDate = this.selectedDate |
||||
let len = selectedDate.length |
||||
if (len) { |
||||
let date = selectedDate[len - 1] |
||||
if (date.length === 2 && isShow) { |
||||
this.$refs.confirm.timsInit(date[0], date[1]) |
||||
} else { |
||||
this.$refs.confirm.timsInit() |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
|
||||
// 切换面板到当前选中日期 |
||||
switchPanel (value, selectedDate) { |
||||
for (let i = 0; i < this.panelNum; i++) { |
||||
this.$refs['dateRange' + i][0].toDate = value |
||||
value = moment(new Date(value)).add(1, 'M') |
||||
this.$refs['dateRange' + i][0].init(selectedDate) |
||||
} |
||||
}, |
||||
|
||||
dayChange (date) { |
||||
let multiple = this.multiple |
||||
multiple === 1 ? this.radio(date) : this.multiSel(date) |
||||
}, |
||||
|
||||
hoverChange (date) { |
||||
let arrDate = this.arrDate |
||||
date.getTime() > arrDate[0].getTime() ? this.hoverClass([arrDate[0], date]) : this.hoverClass([date, arrDate[0]]) |
||||
}, |
||||
|
||||
// 单选 |
||||
radio (date) { |
||||
if (this.arrDate.length === 0) { |
||||
this.selectedDate = [[date]] |
||||
this.hoverStartDate = [date] |
||||
this.hoverEndDate = [] |
||||
this.arrDate.push(date) |
||||
this.isHover = true |
||||
} else { |
||||
if (date.getTime() > this.selectedDate[0][0].getTime()) { |
||||
this.selectedDate[0].push(date) |
||||
this.hoverEndDate = [date] |
||||
} else { |
||||
let endDate = this.selectedDate[0][0] |
||||
this.selectedDate = [] |
||||
this.hoverStartDate = [] |
||||
this.selectedDate = [[date, endDate]] |
||||
this.hoverStartDate = [date] |
||||
this.hoverEndDate = [endDate] |
||||
} |
||||
this.isHover = false |
||||
this.arrDate = [] |
||||
this.change() |
||||
} |
||||
this.timeInit() |
||||
}, |
||||
|
||||
// 多选 |
||||
multiSel (date) { |
||||
let set = () => { |
||||
let arrDate = this.arrDate |
||||
let selectIndex = this.selectIndex |
||||
if (arrDate.length === 0) { |
||||
selectIndex === -1 ? this.hoverStartDate.push(date) : this.hoverStartDate[selectIndex] = date |
||||
arrDate.push(date) |
||||
this.isHover = true |
||||
this.timeInit(false) |
||||
} else { |
||||
if (date.getTime() > arrDate[0].getTime()) { |
||||
if (selectIndex === -1) { |
||||
this.selectedDate.push([arrDate[0], date]) |
||||
this.hoverEndDate.push(date) |
||||
} else { |
||||
this.$set(this.selectedDate, selectIndex, [arrDate[0], date]) |
||||
this.hoverEndDate[selectIndex] = date |
||||
} |
||||
} else { |
||||
if (selectIndex === -1) { |
||||
this.hoverStartDate.pop() |
||||
this.selectedDate.push([date, arrDate[0]]) |
||||
this.hoverStartDate.push(date) |
||||
this.hoverEndDate.push(arrDate[0]) |
||||
} else { |
||||
this.$set(this.selectedDate, selectIndex, [date, arrDate[0]]) |
||||
this.hoverStartDate[selectIndex] = date |
||||
this.hoverEndDate[selectIndex] = arrDate[0] |
||||
} |
||||
} |
||||
this.isHover = false |
||||
this.arrDate = [] |
||||
this.change() |
||||
this.timeInit() |
||||
} |
||||
} |
||||
|
||||
this.getMultipleFn(set) |
||||
}, |
||||
|
||||
isConfirm () { |
||||
if (this.confirm || ishms(this.format)) return true |
||||
return false |
||||
}, |
||||
|
||||
// 左右切换日期 |
||||
switchDatePanel (type, key) { |
||||
if (type === 'add') { |
||||
for (let i = 0; i < this.panelNum - 1; i++) { |
||||
this.$refs['dateRange' + i][0].setToDateInit(type, key) |
||||
} |
||||
} else { |
||||
for (let i = 1; i < this.panelNum; i++) { |
||||
this.$refs['dateRange' + i][0].setToDateInit(type, key) |
||||
} |
||||
} |
||||
}, |
||||
|
||||
// options 选择 |
||||
optionsClick (item, e) { |
||||
if (/act/.test(e.currentTarget.className)) return |
||||
item.click && item.click(item) |
||||
let value = item.value() |
||||
let type = item.type |
||||
let dateValue = type === 'text' ? [item.text] : value |
||||
|
||||
if (value.length === 2 && isValid(value[0]) && isValid(value[1])) { |
||||
let startDate = moment(new Date(value[0])).subtract(this.panelNum - 2, 'M') |
||||
this.switchPanel(startDate, value) |
||||
} |
||||
|
||||
this.getMultipleFn(() => { |
||||
let selectIndex = this.selectIndex |
||||
if (selectIndex === -1) { |
||||
this.hoverStartDate.push(value[0]) |
||||
this.hoverEndDate.push(value[1]) |
||||
this.selectedDate.push(dateValue) |
||||
} else { |
||||
this.hoverStartDate[selectIndex] = value[0] |
||||
this.hoverEndDate[selectIndex] = value[1] |
||||
this.$set(this.selectedDate, selectIndex, dateValue) |
||||
} |
||||
}, () => { |
||||
this.selectedDate = [dateValue] |
||||
this.hoverStartDate = [value[0]] |
||||
this.hoverEndDate = [value[1]] |
||||
}) |
||||
this.change(item) |
||||
this.reset() |
||||
}, |
||||
|
||||
// |
||||
optionsHover (item, e) { |
||||
if (/act/.test(e.currentTarget.className) || !item.value) return |
||||
this.timeOut = setTimeout(() => { |
||||
this.hoverClass(item.value()) |
||||
}, 200) |
||||
}, |
||||
|
||||
optionsHoverOut () { |
||||
if (this.timeOut) { |
||||
clearTimeout(this.timeOut) |
||||
this.timeOut = null |
||||
} |
||||
this.hoverClass([]) |
||||
}, |
||||
|
||||
|
||||
// 根据multiplen单选多选回调 |
||||
getMultipleFn (fn, fn1) { |
||||
let num = this.multiple |
||||
if (num === 1) { // 单选 |
||||
fn1 && fn1() |
||||
} else { // 多选 |
||||
if (num === 0) { |
||||
fn && fn() |
||||
} else { |
||||
let len = this.hoverEndDate.length |
||||
if (len === num && this.selectIndex === -1) { |
||||
this.selectedDate.pop() |
||||
this.hoverStartDate.pop() |
||||
this.hoverEndDate.pop() |
||||
} |
||||
fn && fn() |
||||
} |
||||
} |
||||
}, |
||||
|
||||
// 获取当前opt Item是否选中 |
||||
getOptItemAct (item) { |
||||
let value = item.value().toString() |
||||
let selectedDate = this.selectedDate |
||||
return selectedDate.find((n) => n[0] === item.text || n.toString() === value) |
||||
}, |
||||
|
||||
hoverClass (date) { |
||||
let $day = this.$refs.datePackerDay.querySelectorAll('.dataMouseoverAct') |
||||
if (date.length === 2) { |
||||
for (let i = 0, len = $day.length; i < len; i++) { |
||||
let o = $day[i] |
||||
let toDate = moment(o.getAttribute('data-mouseover')).format('YYYYMMDD') |
||||
let startDate = moment(new Date(date[0])).format('YYYYMMDD') |
||||
let endDate = moment(new Date(date[1])).format('YYYYMMDD') |
||||
|
||||
if (toDate === startDate && toDate === endDate) { |
||||
o.className = 'dataMouseoverAct bg-hover bg-hover-centre' |
||||
} else if (toDate === startDate) { |
||||
o.className = 'dataMouseoverAct bg-hover bg-hover-start' |
||||
} else if (toDate === endDate) { |
||||
o.className = 'dataMouseoverAct bg-hover bg-hover-end' |
||||
} else if (toDate > startDate && toDate < endDate) { |
||||
o.className = 'dataMouseoverAct bg-hover' |
||||
} else { |
||||
o.className = 'dataMouseoverAct' |
||||
} |
||||
} |
||||
} else { |
||||
for (let i = 0, len = $day.length; i < len; i++) { |
||||
let o = $day[i] |
||||
o.className = 'dataMouseoverAct' |
||||
} |
||||
} |
||||
}, |
||||
|
||||
// 监听切换年月 |
||||
ymChange (date, ref) { |
||||
let index = ref.getAttribute('index') - 0, value = date, decrease = date |
||||
for (let i = index + 1; i < this.panelNum; i++) { |
||||
let vDateRange = this.$refs['dateRange' + i][0] |
||||
value = moment(value).add(1, 'M') |
||||
vDateRange.toDate = value |
||||
vDateRange.init() |
||||
} |
||||
|
||||
for (let i = index - 1; i >= 0; i--) { |
||||
let vDateRange = this.$refs['dateRange' + i][0] |
||||
decrease = moment(decrease).subtract(1, 'M') |
||||
vDateRange.toDate = decrease |
||||
vDateRange.init() |
||||
} |
||||
}, |
||||
|
||||
// Reset |
||||
reset () { |
||||
this.isHover = false |
||||
this.arrDate = [] |
||||
this.hoverClass([]) |
||||
}, |
||||
|
||||
cancel () { |
||||
this.$emit('cancel') |
||||
this.reset() |
||||
}, |
||||
|
||||
timeChange (date) { |
||||
this.selectedDate[this.selectedDate.length - 1] = date |
||||
this.change() |
||||
}, |
||||
|
||||
change (optItem) { |
||||
let date = [...this.selectedDate] |
||||
date.forEach(o => { |
||||
if (o.length === 2) { |
||||
o[0] = new Date(o[0]) |
||||
o[1] = new Date(o[1]) |
||||
} |
||||
}) |
||||
this.$emit('change', date, optItem) |
||||
} |
||||
} |
||||
} |
||||
</script> |
@ -1,41 +0,0 @@
|
||||
<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> |
@ -1,324 +0,0 @@
|
||||
/** |
||||
* https://github.com/taylorhakes/fecha
|
||||
* Parse or format dates |
||||
* @class fecha |
||||
*/ |
||||
|
||||
|
||||
var fecha = {} |
||||
var token = /d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g |
||||
var twoDigits = /\d\d?/ |
||||
var threeDigits = /\d{3}/ |
||||
var fourDigits = /\d{4}/ |
||||
var word = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i |
||||
var literal = /\[([^]*?)\]/gm |
||||
var noop = function () { |
||||
} |
||||
|
||||
function shorten (arr, sLen) { |
||||
var newArr = [] |
||||
for (var i = 0, len = arr.length; i < len; i++) { |
||||
newArr.push(arr[i].substr(0, sLen)) |
||||
} |
||||
return newArr |
||||
} |
||||
|
||||
function monthUpdate (arrName) { |
||||
return function (d, v, i18n) { |
||||
var index = i18n[arrName].indexOf(v.charAt(0).toUpperCase() + v.substr(1).toLowerCase()) |
||||
if (~index) { |
||||
d.month = index |
||||
} |
||||
} |
||||
} |
||||
|
||||
function pad (val, len) { |
||||
val = String(val) |
||||
len = len || 2 |
||||
while (val.length < len) { |
||||
val = '0' + val |
||||
} |
||||
return val |
||||
} |
||||
|
||||
var dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] |
||||
var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] |
||||
var monthNamesShort = shorten(monthNames, 3) |
||||
var dayNamesShort = shorten(dayNames, 3) |
||||
fecha.i18n = { |
||||
dayNamesShort: dayNamesShort, |
||||
dayNames: dayNames, |
||||
monthNamesShort: monthNamesShort, |
||||
monthNames: monthNames, |
||||
amPm: ['am', 'pm'], |
||||
DoFn: function DoFn (D) { |
||||
return D + ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : (D - D % 10 !== 10) * D % 10] |
||||
} |
||||
} |
||||
|
||||
var formatFlags = { |
||||
D: function (dateObj) { |
||||
return dateObj.getDate() |
||||
}, |
||||
DD: function (dateObj) { |
||||
return pad(dateObj.getDate()) |
||||
}, |
||||
Do: function (dateObj, i18n) { |
||||
return i18n.DoFn(dateObj.getDate()) |
||||
}, |
||||
d: function (dateObj) { |
||||
return dateObj.getDay() |
||||
}, |
||||
dd: function (dateObj) { |
||||
return pad(dateObj.getDay()) |
||||
}, |
||||
ddd: function (dateObj, i18n) { |
||||
return i18n.dayNamesShort[dateObj.getDay()] |
||||
}, |
||||
dddd: function (dateObj, i18n) { |
||||
return i18n.dayNames[dateObj.getDay()] |
||||
}, |
||||
M: function (dateObj) { |
||||
return dateObj.getMonth() + 1 |
||||
}, |
||||
MM: function (dateObj) { |
||||
return pad(dateObj.getMonth() + 1) |
||||
}, |
||||
MMM: function (dateObj, i18n) { |
||||
return i18n.monthNamesShort[dateObj.getMonth()] |
||||
}, |
||||
MMMM: function (dateObj, i18n) { |
||||
return i18n.monthNames[dateObj.getMonth()] |
||||
}, |
||||
YY: function (dateObj) { |
||||
return String(dateObj.getFullYear()).substr(2) |
||||
}, |
||||
YYYY: function (dateObj) { |
||||
return pad(dateObj.getFullYear(), 4) |
||||
}, |
||||
h: function (dateObj) { |
||||
return dateObj.getHours() % 12 || 12 |
||||
}, |
||||
hh: function (dateObj) { |
||||
return pad(dateObj.getHours() % 12 || 12) |
||||
}, |
||||
H: function (dateObj) { |
||||
return dateObj.getHours() |
||||
}, |
||||
HH: function (dateObj) { |
||||
return pad(dateObj.getHours()) |
||||
}, |
||||
m: function (dateObj) { |
||||
return dateObj.getMinutes() |
||||
}, |
||||
mm: function (dateObj) { |
||||
return pad(dateObj.getMinutes()) |
||||
}, |
||||
s: function (dateObj) { |
||||
return dateObj.getSeconds() |
||||
}, |
||||
ss: function (dateObj) { |
||||
return pad(dateObj.getSeconds()) |
||||
}, |
||||
S: function (dateObj) { |
||||
return Math.round(dateObj.getMilliseconds() / 100) |
||||
}, |
||||
SS: function (dateObj) { |
||||
return pad(Math.round(dateObj.getMilliseconds() / 10), 2) |
||||
}, |
||||
SSS: function (dateObj) { |
||||
return pad(dateObj.getMilliseconds(), 3) |
||||
}, |
||||
a: function (dateObj, i18n) { |
||||
return dateObj.getHours() < 12 ? i18n.amPm[0] : i18n.amPm[1] |
||||
}, |
||||
A: function (dateObj, i18n) { |
||||
return dateObj.getHours() < 12 ? i18n.amPm[0].toUpperCase() : i18n.amPm[1].toUpperCase() |
||||
}, |
||||
ZZ: function (dateObj) { |
||||
var o = dateObj.getTimezoneOffset() |
||||
return (o > 0 ? '-' : '+') + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4) |
||||
} |
||||
} |
||||
|
||||
var parseFlags = { |
||||
D: [twoDigits, function (d, v) { |
||||
d.day = v |
||||
}], |
||||
Do: [new RegExp(twoDigits.source + word.source), function (d, v) { |
||||
d.day = parseInt(v, 10) |
||||
}], |
||||
M: [twoDigits, function (d, v) { |
||||
d.month = v - 1 |
||||
}], |
||||
YY: [twoDigits, function (d, v) { |
||||
var da = new Date(), cent = +('' + da.getFullYear()).substr(0, 2) |
||||
d.year = '' + (v > 68 ? cent - 1 : cent) + v |
||||
}], |
||||
h: [twoDigits, function (d, v) { |
||||
d.hour = v |
||||
}], |
||||
m: [twoDigits, function (d, v) { |
||||
d.minute = v |
||||
}], |
||||
s: [twoDigits, function (d, v) { |
||||
d.second = v |
||||
}], |
||||
YYYY: [fourDigits, function (d, v) { |
||||
d.year = v |
||||
}], |
||||
S: [/\d/, function (d, v) { |
||||
d.millisecond = v * 100 |
||||
}], |
||||
SS: [/\d{2}/, function (d, v) { |
||||
d.millisecond = v * 10 |
||||
}], |
||||
SSS: [threeDigits, function (d, v) { |
||||
d.millisecond = v |
||||
}], |
||||
d: [twoDigits, noop], |
||||
ddd: [word, noop], |
||||
MMM: [word, monthUpdate('monthNamesShort')], |
||||
MMMM: [word, monthUpdate('monthNames')], |
||||
a: [word, function (d, v, i18n) { |
||||
var val = v.toLowerCase() |
||||
if (val === i18n.amPm[0]) { |
||||
d.isPm = false |
||||
} else if (val === i18n.amPm[1]) { |
||||
d.isPm = true |
||||
} |
||||
}], |
||||
ZZ: [/([\+\-]\d\d:?\d\d|Z)/, function (d, v) { |
||||
if (v === 'Z') v = '+00:00' |
||||
var parts = (v + '').match(/([\+\-]|\d\d)/gi), minutes |
||||
|
||||
if (parts) { |
||||
minutes = +(parts[1] * 60) + parseInt(parts[2], 10) |
||||
d.timezoneOffset = parts[0] === '+' ? minutes : -minutes |
||||
} |
||||
}] |
||||
} |
||||
parseFlags.dd = parseFlags.d |
||||
parseFlags.dddd = parseFlags.ddd |
||||
parseFlags.DD = parseFlags.D |
||||
parseFlags.mm = parseFlags.m |
||||
parseFlags.hh = parseFlags.H = parseFlags.HH = parseFlags.h |
||||
parseFlags.MM = parseFlags.M |
||||
parseFlags.ss = parseFlags.s |
||||
parseFlags.A = parseFlags.a |
||||
|
||||
|
||||
// Some common format strings
|
||||
fecha.masks = { |
||||
default: 'ddd MMM DD YYYY HH:mm:ss', |
||||
shortDate: 'M/D/YY', |
||||
mediumDate: 'MMM D, YYYY', |
||||
longDate: 'MMMM D, YYYY', |
||||
fullDate: 'dddd, MMMM D, YYYY', |
||||
shortTime: 'HH:mm', |
||||
mediumTime: 'HH:mm:ss', |
||||
longTime: 'HH:mm:ss.SSS' |
||||
} |
||||
|
||||
/*** |
||||
* Format a date |
||||
* @method format |
||||
* @param {Date|number} dateObj |
||||
* @param {string} mask Format of the date, i.e. 'mm-dd-yy' or 'shortDate' |
||||
*/ |
||||
fecha.format = function (dateObj, mask, i18nSettings) { |
||||
var i18n = i18nSettings || fecha.i18n |
||||
|
||||
if (typeof dateObj === 'number') { |
||||
dateObj = new Date(dateObj) |
||||
} |
||||
|
||||
if (Object.prototype.toString.call(dateObj) !== '[object Date]' || isNaN(dateObj.getTime())) { |
||||
throw new Error('Invalid Date in fecha.format') |
||||
} |
||||
|
||||
mask = fecha.masks[mask] || mask || fecha.masks['default'] |
||||
|
||||
var literals = [] |
||||
|
||||
// Make literals inactive by replacing them with ??
|
||||
mask = mask.replace(literal, function ($0, $1) { |
||||
literals.push($1) |
||||
return '??' |
||||
}) |
||||
// Apply formatting rules
|
||||
mask = mask.replace(token, function ($0) { |
||||
return $0 in formatFlags ? formatFlags[$0](dateObj, i18n) : $0.slice(1, $0.length - 1) |
||||
}) |
||||
// Inline literal values back into the formatted value
|
||||
return mask.replace(/\?\?/g, function () { |
||||
return literals.shift() |
||||
}) |
||||
} |
||||
|
||||
/** |
||||
* Parse a date string into an object, changes - into / |
||||
* @method parse |
||||
* @param {string} dateStr Date string |
||||
* @param {string} format Date parse format |
||||
* @returns {Date|boolean} |
||||
*/ |
||||
fecha.parse = function (dateStr, format, i18nSettings) { |
||||
var i18n = i18nSettings || fecha.i18n |
||||
|
||||
if (typeof format !== 'string') { |
||||
throw new Error('Invalid format in fecha.parse') |
||||
} |
||||
|
||||
format = fecha.masks[format] || format |
||||
|
||||
// Avoid regular expression denial of service, fail early for really long strings
|
||||
// https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS
|
||||
if (dateStr.length > 1000) { |
||||
return false |
||||
} |
||||
|
||||
var isValid = true |
||||
var dateInfo = {} |
||||
format.replace(token, function ($0) { |
||||
if (parseFlags[$0]) { |
||||
var info = parseFlags[$0] |
||||
var index = dateStr.search(info[0]) |
||||
if (!~index) { |
||||
isValid = false |
||||
} else { |
||||
dateStr.replace(info[0], function (result) { |
||||
info[1](dateInfo, result, i18n) |
||||
dateStr = dateStr.substr(index + result.length) |
||||
return result |
||||
}) |
||||
} |
||||
} |
||||
|
||||
return parseFlags[$0] ? '' : $0.slice(1, $0.length - 1) |
||||
}) |
||||
|
||||
if (!isValid) { |
||||
return false |
||||
} |
||||
|
||||
var today = new Date() |
||||
if (dateInfo.isPm === true && dateInfo.hour != null && +dateInfo.hour !== 12) { |
||||
dateInfo.hour = +dateInfo.hour + 12 |
||||
} else if (dateInfo.isPm === false && +dateInfo.hour === 12) { |
||||
dateInfo.hour = 0 |
||||
} |
||||
|
||||
var date |
||||
if (dateInfo.timezoneOffset != null) { |
||||
dateInfo.minute = +(dateInfo.minute || 0) - +dateInfo.timezoneOffset |
||||
date = new Date(Date.UTC(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1, |
||||
dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0)) |
||||
} else { |
||||
date = new Date(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1, |
||||
dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0) |
||||
} |
||||
return date |
||||
} |
||||
|
||||
export default fecha |
@ -1,12 +0,0 @@
|
||||
/* |
||||
* |
||||
* 判断数据类型 |
||||
* |
||||
*/ |
||||
|
||||
function isType (value) { |
||||
let type = Object.prototype.toString.call(value) |
||||
return type.match(/\[object (.*?)\]/)[1].toLowerCase() |
||||
} |
||||
|
||||
export default isType |
@ -1,12 +0,0 @@
|
||||
/* |
||||
* |
||||
* 验证日期是否合法 |
||||
* |
||||
*/ |
||||
|
||||
import moment from 'dayjs' |
||||
|
||||
export default (date) => { |
||||
// return Date.parse(date) > 0
|
||||
return moment(date).isValid() |
||||
} |
@ -1,15 +0,0 @@
|
||||
export default (fmt) => { |
||||
if (/[HhmsS]/.test(fmt)) { |
||||
return 'second' |
||||
} |
||||
|
||||
if (/[Hhm]/.test(fmt)) { |
||||
return 'minute' |
||||
} |
||||
|
||||
if (/[Hh]/.test(fmt)) { |
||||
return 'hour' |
||||
} |
||||
|
||||
return false |
||||
} |
@ -1,9 +0,0 @@
|
||||
|
||||
export default () => { |
||||
let date = new Date() |
||||
return { |
||||
year: date.getFullYear(), |
||||
month: date.getMonth() + 1, |
||||
today: date.getDate() |
||||
} |
||||
} |
@ -1,15 +0,0 @@
|
||||
## Drawer |
||||
|
||||
抽屉从父窗体边缘滑入,覆盖住部分父窗体内容。 |
||||
|
||||
### Drawer options |
||||
|
||||
属性 | 说明 | 类型 | 可选值 | 默认值 |
||||
--- | --- | --- | --- | --- |
||||
direction | 出现位置 | String | left / right / top / bottom | right |
||||
className | 自定义样式名称 | String | - | - |
||||
closable | 是否显示关闭 | Boolean | - | false |
||||
escClose | 是否按 esc 键关闭 | Boolean | - | true |
||||
transitionName | 动画类名 | String | - | - |
||||
showMask | 是否显示遮罩 | Boolean | - | true |
||||
maskClosable | 是否关闭遮罩 | Boolean | - | true |
@ -1,68 +0,0 @@
|
||||
<template> |
||||
<div> |
||||
<section class="demo-section"> |
||||
<h4>基本用法</h4> |
||||
<div> |
||||
<x-button @click="$left">left</x-button> |
||||
<x-button @click="$right">right</x-button> |
||||
<x-button @click="$top">top</x-button> |
||||
<x-button @click="$bottom">bottom</x-button> |
||||
</div> |
||||
</section> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { xButton } from '../../vue-button/src' |
||||
import { xDrawer } from '../src' |
||||
import Test from './test.vue' |
||||
|
||||
|
||||
export default { |
||||
name: 'app', |
||||
methods: { |
||||
$left () { |
||||
xDrawer({ |
||||
direction: 'left', |
||||
className: 'hhh', |
||||
render (h) { |
||||
return h(Test) |
||||
} |
||||
}) |
||||
}, |
||||
$right () { |
||||
xDrawer({ |
||||
className: 'hhh', |
||||
render (h) { |
||||
return h(Test) |
||||
} |
||||
}) |
||||
}, |
||||
$top () { |
||||
xDrawer({ |
||||
className: 'hhh', |
||||
direction: 'top', |
||||
render (h) { |
||||
return h(Test) |
||||
} |
||||
}) |
||||
}, |
||||
$bottom () { |
||||
xDrawer({ |
||||
className: 'hhh', |
||||
direction: 'bottom', |
||||
render (h) { |
||||
return h(Test) |
||||
} |
||||
}) |
||||
} |
||||
}, |
||||
components: { xButton }, |
||||
created () { |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
|
||||
</style> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,7 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App) |
||||
}) |
@ -1,18 +0,0 @@
|
||||
<template> |
||||
<div class="test-div"> |
||||
测试文字 |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: 'test' |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"> |
||||
.test-div{ |
||||
width: 100px; |
||||
background: red; |
||||
} |
||||
</style> |
@ -1,5 +0,0 @@
|
||||
import xDrawer from './source/drawer' |
||||
|
||||
export { |
||||
xDrawer |
||||
} |
@ -1,33 +0,0 @@
|
||||
import { xModal } from '../../../vue-box/src' |
||||
import { LIB_NAME } from '../../../../src/util/constants' |
||||
|
||||
const prefixCls = `${LIB_NAME}-drawer` |
||||
const animationName = `${prefixCls}-animation` |
||||
|
||||
/** |
||||
* @desc 推荐用 render 函数去创建 dom 在打开和关闭的 生命钩子函数由使用者自己通过事件的方式去实现 |
||||
* @desc 关闭的方法同 box |
||||
* @param direction {String} ' left right top bottom | right' |
||||
* @param className |
||||
* @param showMask |
||||
* @param maskClosable |
||||
* @param escClose |
||||
* @param render |
||||
* */ |
||||
let defaultOpt = { |
||||
transitionName: animationName, |
||||
className: prefixCls, |
||||
closable: false, |
||||
direction: 'right', |
||||
showMask: true, |
||||
maskClosable: true, |
||||
escClose: true |
||||
} |
||||
|
||||
export default function (opt) { |
||||
let options = Object.assign({}, defaultOpt, opt) |
||||
let direction = options.direction |
||||
options.className = opt.className ? `${defaultOpt.className} ${prefixCls}-${direction} ${opt.className}` : `${defaultOpt.className} ${prefixCls}-${direction}` |
||||
options.transitionName = `${options.transitionName}-${options.direction}` |
||||
return xModal.dialog(options) |
||||
} |
@ -1,41 +0,0 @@
|
||||
## Form |
||||
|
||||
表单验证组件,基于`async-validator`开发。 |
||||
|
||||
### Form props |
||||
|
||||
属性 | 说明 | 类型 | 可选值 | 默认值 |
||||
--- | --- | --- | --- | --- |
||||
model | 表单数据对象 | Object | - | - |
||||
rules | 表单验证规则,详见`async-validator` | Object | - | - |
||||
label-width | 表单域标签的的宽度 | Number / String | - | - |
||||
label-height | 表单域标签的的高度 | Number / String | - | - |
||||
|
||||
### Form methods |
||||
|
||||
方法名 | 说明 | 参数 |
||||
--- | --- | --- |
||||
validate | 对整个表单进行校验,参数为检验完的回调,会返回一个 Boolean 表示成功与失败,支持 Promise | callback |
||||
resetFields | 对整个表单进行重置,将所有字段值重置为空并移除校验结果 | — |
||||
validateField | 对部分表单字段进行校验的方法,参数1为需校验的 prop,参数2为检验完回调,返回错误信息 | prop, callback |
||||
|
||||
### FormItem props |
||||
|
||||
属性 | 说明 | 类型 | 可选值 | 默认值 |
||||
--- | --- | --- | --- | --- |
||||
prop | 对应表单域 model 里的字段 | String | - | - |
||||
rules | 表单验证规则,会合并父级的规则 | Object / Array | - | - |
||||
label | 标签文本 | String | - | - |
||||
required | 是否必填,如不设置,则会根据校验规则自动生成 | Boolean | - | false |
||||
label-width | 表单域标签的的宽度 | Number / String | - | - |
||||
label-height | 表单域标签的的高度 | Number / String | - | - |
||||
label-for | 指定原生的 label 标签的 for 属性 | String | - | - |
||||
|
||||
### FormItem slots |
||||
|
||||
名称 | 说明 | slot-scope 属性 |
||||
--- | --- | --- |
||||
default | 内容 | - |
||||
label | label内容 | - |
||||
input | 使用原生input时用这个插入内容 | - |
||||
select | 使用原生select时用这个插入内容 | - |
@ -1,141 +0,0 @@
|
||||
<template> |
||||
<div> |
||||
<section class="demo-section"> |
||||
<h4>基本用法</h4> |
||||
<div> |
||||
<x-form ref="formVal" :model="formVal" :rules="formRules" label-width="80" label-height="32"> |
||||
<x-form-item prop="username" label="帐号"> |
||||
<x-input v-model="formVal.username" placeholder="请输入帐号" style="width: 200px;"></x-input> |
||||
</x-form-item> |
||||
<x-form-item prop="passcode" label="密码"> |
||||
<x-input v-model="formVal.passcode" type="password" placeholder="请输入密码" style="width: 200px;"></x-input> |
||||
</x-form-item> |
||||
<x-form-item prop="gender" label="性别"> |
||||
<x-radio-group v-model="formVal.gender"> |
||||
<x-radio label="1">男</x-radio> |
||||
<x-radio label="2">女</x-radio> |
||||
</x-radio-group> |
||||
</x-form-item> |
||||
<x-form-item prop="fruit" label="水果"> |
||||
<x-checkbox-group v-model="formVal.fruit"> |
||||
<x-checkbox label="香蕉">香蕉</x-checkbox> |
||||
<x-checkbox label="苹果">苹果</x-checkbox> |
||||
<x-checkbox label="橘子">橘子</x-checkbox> |
||||
</x-checkbox-group> |
||||
</x-form-item> |
||||
<x-form-item prop="city" label="城市"> |
||||
<x-select v-model="formVal.city" clearable style="width: 200px;"> |
||||
<x-option v-for="item in cities" :key="item.value" :value="item.value" :label="item.label"></x-option> |
||||
</x-select> |
||||
</x-form-item> |
||||
<x-form-item prop="startTime" label="开始时间"> |
||||
<x-datepicker v-model="formVal.startTime" placeholder="选择单日" style="width: 200px;"></x-datepicker> |
||||
</x-form-item> |
||||
<x-form-item prop="desc" label="备注"> |
||||
<x-input type="textarea" v-model="formVal.desc" placeholder="请输入备注" style="width: 200px;"></x-input> |
||||
</x-form-item> |
||||
<x-form-item label=" "> |
||||
<x-button @click="submit()">登 录</x-button> |
||||
</x-form-item> |
||||
</x-form> |
||||
</div> |
||||
</section> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { xForm, xFormItem } from '../src' |
||||
import { xInput } from '../../vue-input/src' |
||||
import { xButton } from '../../vue-button/src' |
||||
import { xRadio, xRadioGroup } from '../../vue-radio/src' |
||||
import { xCheckbox, xCheckboxGroup } from '../../vue-checkbox/src' |
||||
import { xSelect, xOption, xOptionGroup } from '../../vue-select/src' |
||||
import { xDatepicker } from '../../vue-datepicker/src' |
||||
|
||||
export default { |
||||
name: 'app', |
||||
components: { |
||||
xForm, |
||||
xFormItem, |
||||
xInput, |
||||
xButton, |
||||
xRadio, |
||||
xRadioGroup, |
||||
xCheckbox, |
||||
xCheckboxGroup, |
||||
xSelect, |
||||
xOption, |
||||
xOptionGroup, |
||||
xDatepicker |
||||
}, |
||||
data () { |
||||
return { |
||||
formVal: { |
||||
username: '', |
||||
passcode: '', |
||||
gender: '', |
||||
fruit: [], |
||||
city: '', |
||||
startTime: '', |
||||
desc: '' |
||||
}, |
||||
formRules: { |
||||
username: [ |
||||
{ required: true, message: '请输入帐号', trigger: 'blur' } |
||||
], |
||||
passcode: [ |
||||
{ required: true, message: '请输入密码', trigger: 'blur' }, |
||||
{ required: true, min: 6, max: 12, message: '密码长度在6-12之间', trigger: 'blur' } |
||||
], |
||||
gender: [ |
||||
{ required: true, message: '请选择性别', trigger: 'change' } |
||||
], |
||||
fruit: [ |
||||
{ required: true, type: 'array', message: '请选择水果', trigger: 'change' } |
||||
], |
||||
city: [ |
||||
{ required: true, message: '请选择城市', trigger: 'change' } |
||||
], |
||||
startTime: [ |
||||
{ required: true, message: '请选择日期', trigger: 'change' } |
||||
], |
||||
desc: [ |
||||
{ required: true, message: '请输入备注', trigger: 'blur' } |
||||
] |
||||
}, |
||||
cities: [{ |
||||
value: 'Beijing', |
||||
label: '北京' |
||||
}, { |
||||
value: 'Shanghai', |
||||
label: '上海' |
||||
}, { |
||||
value: 'Nanjing', |
||||
label: '南京' |
||||
}, { |
||||
value: 'Chengdu', |
||||
label: '成都' |
||||
}, { |
||||
value: 'Shenzhen', |
||||
label: '深圳' |
||||
}, { |
||||
value: 'Guangzhou', |
||||
label: '广州' |
||||
}] |
||||
} |
||||
}, |
||||
methods: { |
||||
submit () { |
||||
this.$refs['formVal'].validate(valid => { |
||||
if (valid) { |
||||
console.log('success') |
||||
} else { |
||||
console.log('error') |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"></style> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,7 +0,0 @@
|
||||
import xForm from './source/Form' |
||||
import xFormItem from './source/FormItem' |
||||
|
||||
export { |
||||
xForm, |
||||
xFormItem |
||||
} |
@ -1,93 +0,0 @@
|
||||
<template> |
||||
<form :class="wrapClasses"> |
||||
<slot></slot> |
||||
</form> |
||||
</template> |
||||
|
||||
<script> |
||||
import { LIB_NAME } from '../../../../src/util' |
||||
|
||||
const prefixCls = `${LIB_NAME}-form` |
||||
|
||||
export default { |
||||
name: 'xForm', |
||||
props: { |
||||
model: { |
||||
type: Object |
||||
}, |
||||
rules: { |
||||
type: Object |
||||
}, |
||||
labelWidth: { |
||||
type: [Number, String] |
||||
}, |
||||
labelHeight: { |
||||
type: [Number, String] |
||||
} |
||||
}, |
||||
provide () { |
||||
return { form: this } |
||||
}, |
||||
data () { |
||||
return { |
||||
fields: [] |
||||
} |
||||
}, |
||||
computed: { |
||||
wrapClasses () { |
||||
return [ |
||||
`${prefixCls}` |
||||
] |
||||
} |
||||
}, |
||||
methods: { |
||||
resetFields () { |
||||
this.fields.forEach(field => { |
||||
field.resetField() |
||||
}) |
||||
}, |
||||
validate (callback) { |
||||
return new Promise(resolve => { |
||||
let valid = true |
||||
let count = 0 |
||||
|
||||
this.fields.forEach(field => { |
||||
field.validate('', error => { |
||||
if (error) { |
||||
valid = false |
||||
} |
||||
|
||||
if (++count === this.fields.length) { |
||||
resolve(valid) |
||||
if (typeof callback === 'function') { |
||||
callback(valid) |
||||
} |
||||
} |
||||
}) |
||||
}) |
||||
}) |
||||
}, |
||||
validateField (prop, cb) { |
||||
const field = this.fields.filter(field => field.prop === prop)[0] |
||||
if (!field) { |
||||
throw new Error('请校验有效的prop') |
||||
} |
||||
|
||||
field.validate('', cb) |
||||
} |
||||
}, |
||||
created () { |
||||
this.$on('on-form-item-add', field => { |
||||
if (field) { |
||||
this.fields.push(field) |
||||
} |
||||
}) |
||||
|
||||
this.$on('on-form-item-remove', field => { |
||||
if (field.prop) { |
||||
this.fields.splice(this.fields.indexOf(field), 1) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
</script> |
@ -1,253 +0,0 @@
|
||||
<template> |
||||
<div :class="wrapClasses"> |
||||
<label :class="labelClasses" :for="labelFor" :style="labelStyles" v-if="label || $slots.label"> |
||||
<slot name="label">{{label}}</slot> |
||||
</label> |
||||
<div :class="contentClasses"> |
||||
<slot name="input"></slot> |
||||
<slot name="select"></slot> |
||||
<slot></slot> |
||||
<transition name="fade"> |
||||
<div :class="errorClasses" v-if="validateState === 'error'">{{validateMessage}}</div> |
||||
</transition> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { LIB_NAME, emitter } from '../../../../src/util' |
||||
import AsyncValidator from 'async-validator' |
||||
|
||||
const prefixCls = `${LIB_NAME}-form-item` |
||||
|
||||
// 根据path获取数据 |
||||
function getPropByPath (obj, path) { |
||||
let tempObj = obj |
||||
|
||||
path = path.replace(/\[(\w+)\]/g, '.$1') |
||||
path = path.replace(/^\./, '') |
||||
|
||||
let keyArr = path.split('.') |
||||
let i = 0 |
||||
|
||||
for (let len = keyArr.length; i < len - 1; ++i) { |
||||
let key = keyArr[i] |
||||
if (key in tempObj) { |
||||
tempObj = tempObj[key] |
||||
} else { |
||||
throw new Error('请转换一个有效的prop') |
||||
} |
||||
} |
||||
|
||||
return { |
||||
o: tempObj, |
||||
k: keyArr[i], |
||||
v: tempObj[keyArr[i]] |
||||
} |
||||
} |
||||
|
||||
export default { |
||||
name: 'xFormItem', |
||||
mixins: [emitter], |
||||
props: { |
||||
prop: { |
||||
type: String |
||||
}, |
||||
rules: { |
||||
type: [Object, Array] |
||||
}, |
||||
label: { |
||||
type: String, |
||||
default: '' |
||||
}, |
||||
required: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
labelWidth: { |
||||
type: [Number, String] |
||||
}, |
||||
labelHeight: { |
||||
type: [Number, String] |
||||
}, |
||||
labelFor: { |
||||
type: String |
||||
} |
||||
}, |
||||
inject: ['form'], |
||||
data () { |
||||
return { |
||||
validateState: '', |
||||
validateMessage: '', |
||||
validateDisabled: false |
||||
} |
||||
}, |
||||
computed: { |
||||
// 包裹样式类 |
||||
wrapClasses () { |
||||
return [ |
||||
`${prefixCls}` |
||||
] |
||||
}, |
||||
// label样式 |
||||
labelClasses () { |
||||
return [ |
||||
`${prefixCls}-label` |
||||
] |
||||
}, |
||||
// content样式 |
||||
contentClasses () { |
||||
return [ |
||||
`${prefixCls}-content` |
||||
] |
||||
}, |
||||
// error样式 |
||||
errorClasses () { |
||||
return [ |
||||
`${prefixCls}-error-tip` |
||||
] |
||||
}, |
||||
// 获取label的样式 |
||||
labelStyles () { |
||||
let style = {} |
||||
const labelWidth = this.labelWidth === 0 || this.labelWidth ? this.labelWidth : this.form.labelWidth |
||||
const labelHeight = this.labelHeight === 0 || this.labelHeight ? this.labelHeight : this.form.labelHeight |
||||
|
||||
if (labelWidth || labelWidth === 0) { |
||||
style.width = `${labelWidth}px` |
||||
} |
||||
if (labelHeight || labelHeight === 0) { |
||||
style.height = `${labelHeight}px` |
||||
style.lineHeight = `${labelHeight}px` |
||||
} |
||||
return style |
||||
}, |
||||
// 实时获取表单元素的值 |
||||
fieldValue () { |
||||
const model = this.form.model |
||||
|
||||
if (!model || !this.prop) { |
||||
return |
||||
} |
||||
|
||||
let path = this.prop |
||||
|
||||
if (~path.indexOf(':')) { |
||||
path = path.replace(/:/, '.') |
||||
} |
||||
|
||||
return getPropByPath(model, path)['v'] |
||||
} |
||||
}, |
||||
methods: { |
||||
// 获取校验规则 |
||||
getRules () { |
||||
let formRules = this.form.rules |
||||
let selfRules = this.rules |
||||
|
||||
formRules = formRules ? formRules[this.prop] : [] |
||||
|
||||
return [].concat(selfRules || formRules || []) |
||||
}, |
||||
// 获取过滤后的校验规则 |
||||
getFilteredRule (trigger) { |
||||
const rules = this.getRules() |
||||
|
||||
return rules.filter(rule => !rule.trigger || ~rule.trigger.indexOf(trigger)) |
||||
}, |
||||
validate (trigger, callback = function () {}) { |
||||
let rules = this.getFilteredRule(trigger) |
||||
|
||||
if (!rules || !rules.length) { |
||||
if (!this.required) { |
||||
callback() |
||||
return true |
||||
} else { |
||||
rules = [{ required: true }] |
||||
} |
||||
} |
||||
|
||||
this.validateState = 'validating' |
||||
|
||||
let descriptor = {} |
||||
descriptor[this.prop] = rules |
||||
|
||||
const validator = new AsyncValidator(descriptor) |
||||
let model = {} |
||||
|
||||
model[this.prop] = this.fieldValue |
||||
|
||||
validator.validate(model, { firstFields: true }, error => { |
||||
this.validateState = !error ? 'success' : 'error' |
||||
this.validateMessage = error ? error[0].message : '' |
||||
|
||||
callback(this.validateMessage) |
||||
}) |
||||
|
||||
this.validateDisabled = false |
||||
}, |
||||
onFieldBlur () { |
||||
this.validate('blur') |
||||
}, |
||||
onFieldChange () { |
||||
if (this.validateDisabled) { |
||||
this.validateDisabled = false |
||||
return |
||||
} |
||||
this.validate('change') |
||||
}, |
||||
resetField () { |
||||
this.validateState = '' |
||||
this.validateMessage = '' |
||||
|
||||
let model = this.form.model |
||||
let value = this.fieldValue |
||||
let path = this.prop |
||||
|
||||
if (~path.indexOf(':')) { |
||||
path = path.replace(/:/, '.') |
||||
} |
||||
|
||||
let prop = getPropByPath(model, path) |
||||
this.validateDisabled = true |
||||
|
||||
if (Array.isArray(value)) { |
||||
prop.o[prop.k] = [].concat(this.initialValue) |
||||
} else { |
||||
prop.o[prop.k] = this.initialValue |
||||
} |
||||
} |
||||
}, |
||||
mounted () { |
||||
// 保存初始值 |
||||
if (this.prop) { |
||||
this.dispatch('xForm', 'on-form-item-add', this) |
||||
|
||||
let initialValue = this.fieldValue |
||||
if (Array.isArray(initialValue)) { |
||||
initialValue = [].concat(initialValue) |
||||
} |
||||
|
||||
Object.defineProperty(this, 'initialValue', { |
||||
value: initialValue |
||||
}) |
||||
|
||||
let rules = this.getRules() |
||||
|
||||
if (rules.length || this.required !== undefined) { |
||||
// 监听vue组件的blur & change事件 |
||||
this.$on('on-form-blur', this.onFieldBlur) |
||||
this.$on('on-form-change', this.onFieldChange) |
||||
|
||||
// 对于非vue组件的事件监听 |
||||
this.$slots.input && this.$slots.input[0].elm.addEventListener('blur', this.onFieldBlur) |
||||
this.$slots.select && this.$slots.select[0].elm.addEventListener('blur', this.onFieldBlur) |
||||
this.$slots.select && this.$slots.select[0].elm.addEventListener('change', this.onFieldChange) |
||||
} |
||||
} |
||||
}, |
||||
beforeDestroy () { |
||||
this.dispatch('xForm', 'on-form-item-remove', this) |
||||
} |
||||
} |
||||
</script> |
@ -1,57 +0,0 @@
|
||||
## Input |
||||
|
||||
基本表单组件,支持 input 和 textarea,并在原生控件基础上进行了功能扩展,可以组合使用。 |
||||
|
||||
### Input props |
||||
|
||||
属性 | 说明 | 类型 | 可选值 | 默认值 |
||||
--- | --- | --- | --- | --- |
||||
type | 类型 | String | text、textarea 和其他原生 input 的 type 值 | text |
||||
size | 尺寸,type="textarea" 时无效 | String | large / default / small | default |
||||
value | 绑定值 | String / Number | — | — |
||||
clearable | 是否可清空 | Boolean | — | false |
||||
suffix-icon | 前置图标 | String | — | — |
||||
prefix-icon | 后置图标 | String | — | — |
||||
label | aria-label 属性 | String | — | — |
||||
no-border | 是否无边框 | Boolean | — | false |
||||
name | 原生属性 | String | — | — |
||||
placeholder | 原生属性 | String | — | 请输入... |
||||
disabled | 原生属性 | Boolean | — | false |
||||
readonly | 原生属性 | Boolean | — | false |
||||
autofocus | 原生属性 | Boolean | — | false |
||||
autocomplete | 原生属性 | String | — | off |
||||
maxlength | 原生属性 | Number | — | — |
||||
minlength | 原生属性 | Number | — | — |
||||
tabindex | 原生属性 | Number | — | — |
||||
resize | 文本域是否可以拉伸 | String | — | — |
||||
autosize | textarea 自适应,如 { minRows: 2, maxRows: 6 } | Boolean / Object | — | false |
||||
rows | textarea 原生属性 | Number | — | 2 |
||||
|
||||
### Input slots |
||||
|
||||
名称 | 说明 |
||||
--- | --- |
||||
prefix | 前置图标插槽,插槽显示在输入框内 |
||||
suffix | 后置图标插槽,插槽显示在输入框内 |
||||
prepend | 前置内容插槽 |
||||
append | 后置内容插槽 |
||||
|
||||
### Input events |
||||
|
||||
事件名称 | 说明 | 回调参数 |
||||
--- | --- | --- |
||||
on-enterkey | 输入回车时触发 | event: Event |
||||
on-click | 点击时触发 | event: Event |
||||
on-blur | 失去焦点时触发 | event: Event |
||||
on-focus | 获得焦点时触发 | event: Event |
||||
on-change | 失去焦点并且输入值改变时触发 | value: String |
||||
on-clear | 点击清空图标时触发 | — |
||||
on-click-icon | 点击前置/后置图标时触发 | event: Event |
||||
|
||||
### Input methods |
||||
|
||||
方法名 | 说明 | 参数 |
||||
--- | --- | --- |
||||
focus | 使 Input 组件获得焦点 | — |
||||
blur | 使 Input 组件失去焦点 | — |
||||
clear | 清空文本并且获得焦点 | — |
@ -1,122 +0,0 @@
|
||||
<template> |
||||
<div> |
||||
<section class="demo-section"> |
||||
<h4>基本用法</h4> |
||||
<div> |
||||
<x-input :value="value" @on-click="changeValue" style="width: 300px;"></x-input> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>双向绑定</h4> |
||||
<div> |
||||
<x-input v-model="model" style="width: 200px;"></x-input>{{model}} |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>可清空</h4> |
||||
<div> |
||||
<x-input clearable value="可清空测试" style="width: 200px;"></x-input> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>原生属性</h4> |
||||
<div> |
||||
<p><x-input readonly name="readonly" value="readonly" style="width: 200px;"></x-input></p> |
||||
<p><x-input disabled name="disabled" value="disabled" style="width: 200px;"></x-input></p> |
||||
<p><x-input autofocus value="autofocus" style="width: 200px;"></x-input></p> |
||||
<p><x-input autocomplete="on" value="autocomplete" style="width: 200px;"></x-input></p> |
||||
<p><x-input :maxlength="10" value="maxlength" style="width: 200px;"></x-input></p> |
||||
<p><x-input :tabindex="10" value="tabindex" style="width: 200px;"></x-input></p> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>不同尺寸</h4> |
||||
<div> |
||||
<x-input size="large" value="大尺寸" style="width: 200px;"></x-input> |
||||
<x-input size="default" value="默认尺寸" style="width: 200px;"></x-input> |
||||
<x-input size="small" value="小尺寸" style="width: 200px;"></x-input> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>无边框</h4> |
||||
<div> |
||||
<x-input no-border style="width: 200px;"></x-input> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>密码框</h4> |
||||
<div> |
||||
<x-input type="password" placeholder="请输入密码" style="width: 200px;"></x-input> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>文本域</h4> |
||||
<div> |
||||
<x-input type="textarea" style="width: 200px;"></x-input> |
||||
</div> |
||||
<div> |
||||
<x-input type="textarea" resize="none" value="不可拉伸" style="width: 200px;"></x-input> |
||||
</div> |
||||
<div> |
||||
<x-input type="textarea" resize="none" :autosize="{minRows:2,maxRows:4}" value="自适应" style="width: 200px;"></x-input> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>图标</h4> |
||||
<div> |
||||
<div> |
||||
<x-input :prefix-icon="searchClass" style="width: 200px;"></x-input> |
||||
</div> |
||||
<div> |
||||
<x-input :suffix-icon="searchClass" style="width: 200px;"></x-input> |
||||
</div> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>插槽</h4> |
||||
<div> |
||||
<x-input size="large" value="前置图标插槽" style="width: 200px;"> |
||||
<i slot="prefix" :class="searchClass"></i> |
||||
</x-input> |
||||
</div> |
||||
<div> |
||||
<x-input size="large" value="后置图标插槽" style="width: 200px;"> |
||||
<i slot="suffix" :class="searchClass"></i> |
||||
</x-input> |
||||
</div> |
||||
<div> |
||||
<x-input size="large" value="前置内容插槽" style="width: 200px;"> |
||||
<span slot="prepend">Http://</span> |
||||
</x-input> |
||||
</div> |
||||
<div> |
||||
<x-input size="large" value="后置内容插槽" style="width: 200px;"> |
||||
<span slot="append">忘记密码?</span> |
||||
</x-input> |
||||
</div> |
||||
</section> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { xInput } from '../src' |
||||
|
||||
export default { |
||||
name: 'app', |
||||
components: { xInput }, |
||||
data () { |
||||
return { |
||||
model: '双向绑定数据', |
||||
value: '点击测试响应绑定值', |
||||
searchClass: `ans-icon-search` |
||||
} |
||||
}, |
||||
methods: { |
||||
changeValue () { |
||||
this.value = '输入框绑定的值改变了' |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"></style> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,5 +0,0 @@
|
||||
import xInput from './source/Input.vue' |
||||
|
||||
export { |
||||
xInput |
||||
} |
@ -1,354 +0,0 @@
|
||||
<template> |
||||
<div |
||||
:class="[ |
||||
`${wrapperClass}`, |
||||
{ |
||||
[`${wrapperClass}--${size}`]: isNotTextarea |
||||
}, |
||||
{ |
||||
[`${wrapperClass}--prepend`]: !!$slots.prepend, |
||||
[`${wrapperClass}--append`]: !!$slots.append |
||||
} |
||||
]" |
||||
@mouseenter="hovering = true" |
||||
@mouseleave="hovering = false" |
||||
> |
||||
<template v-if="isNotTextarea"> |
||||
<!-- 前置元素 --> |
||||
<div ref="prepend" class="prepend-area" v-if="$slots.prepend"> |
||||
<slot name="prepend"></slot> |
||||
</div> |
||||
<!-- 输入框 --> |
||||
<input |
||||
ref="input" |
||||
class="input-element" |
||||
:class="{ |
||||
'no-border': noBorder, |
||||
prefix: hasPrefix, |
||||
suffix: hasSuffix, |
||||
active: focused, |
||||
disabled |
||||
}" |
||||
v-model="currentValue" |
||||
:name="name" |
||||
:type="type" |
||||
:placeholder="placeholder" |
||||
:disabled="disabled" |
||||
:readonly="readonly" |
||||
:autofocus="autofocus" |
||||
:autocomplete="autocomplete" |
||||
:maxlength="maxlength" |
||||
:tabindex="tabindex" |
||||
@focus="handleFocus" |
||||
@blur="handleBlur" |
||||
@keyup.enter="handleEnter" |
||||
@change="handleChange" |
||||
@input="hanldleInput" |
||||
@click="handleClick" |
||||
@compositionstart="handleComposition" |
||||
@compositionupdate="handleComposition" |
||||
@compositionend="handleComposition" |
||||
:aria-label="label" |
||||
/> |
||||
<!-- 前置图标 --> |
||||
<span class="prefix-area" :style="prefixStyles" v-if="hasPrefix" @click="handleIconClick"> |
||||
<slot v-if="$slots.prefix" name="prefix"></slot> |
||||
<i v-else class="icon" :class="prefixIcon"></i> |
||||
</span> |
||||
<!-- 后置图标 --> |
||||
<span class="suffix-area" :style="suffixStyles" v-if="hasSuffix" @click="handleIconClick"> |
||||
<slot v-if="$slots.suffix" name="suffix"></slot> |
||||
<template v-else> |
||||
<i v-if="clearable" v-show="showClear" class="icon ans-icon-fail-solid close"></i> |
||||
<i v-else class="icon" :class="suffixIcon"></i> |
||||
</template> |
||||
</span> |
||||
<!-- 后置元素 --> |
||||
<div ref="append" class="append-area" v-if="$slots.append"> |
||||
<slot name="append"></slot> |
||||
</div> |
||||
<div class="no-border-shadow" v-if="noBorder" v-show="focused"></div> |
||||
</template> |
||||
<textarea |
||||
v-else |
||||
ref="textarea" |
||||
class="input-element input-textarea" |
||||
:class="{ |
||||
active: focused, |
||||
disabled |
||||
}" |
||||
:style="textareaStyle" |
||||
v-model="currentValue" |
||||
:name="name" |
||||
:placeholder="placeholder" |
||||
:disabled="disabled" |
||||
:readonly="readonly" |
||||
:autofocus="autofocus" |
||||
:autocomplete="autocomplete" |
||||
:maxlength="maxlength" |
||||
:minlength="minlength" |
||||
:tabindex="tabindex" |
||||
:rows="rows" |
||||
@keyup.enter="handleEnter" |
||||
@focus="handleFocus" |
||||
@blur="handleBlur" |
||||
@change="handleChange" |
||||
@input="hanldleInput" |
||||
@click="handleClick" |
||||
@compositionstart="handleComposition" |
||||
@compositionupdate="handleComposition" |
||||
@compositionend="handleComposition" |
||||
:aria-label="label" |
||||
> |
||||
</textarea> |
||||
</div> |
||||
</template> |
||||
<script> |
||||
import { LIB_NAME, findComponentUpward, emitter } from '../../../../src/util' |
||||
import calcTextareaHeight from './util/calcTextareaHeight.js' |
||||
import { t } from '../../../../src/locale' |
||||
|
||||
const wrapperClass = `${LIB_NAME}-input` |
||||
|
||||
export default { |
||||
name: 'xInput', |
||||
mixins: [ emitter ], |
||||
data () { |
||||
return { |
||||
// 组件容器样式名称 |
||||
wrapperClass, |
||||
// 当前输入 |
||||
currentValue: this.value, |
||||
// 文本域样式 |
||||
textareaCalcStyle: {}, |
||||
// 前置图标定位 |
||||
prefixStyles: {}, |
||||
// 后置图标定位 |
||||
suffixStyles: {}, |
||||
// 是否处于鼠标悬停状态 |
||||
hovering: false, |
||||
// 是否处于聚焦状态 |
||||
focused: false, |
||||
// 是否处于中文输入中 |
||||
isOnComposition: false |
||||
} |
||||
}, |
||||
props: { |
||||
// 输入框类型,可选值为 text、textarea 和其他原生 input 的 type 值 |
||||
type: { |
||||
default: 'text' |
||||
}, |
||||
// 输入框尺寸,可选值为 large、default、small |
||||
size: { |
||||
validator (value) { |
||||
return ['small', 'default', 'large'].includes(value) |
||||
}, |
||||
default: 'default' |
||||
}, |
||||
// 绑定的值,可使用 v-model 双向绑定 |
||||
value: [String, Number], |
||||
// 是否可清空 |
||||
clearable: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
// 输入框前置图标 |
||||
suffixIcon: String, |
||||
// 输入框后置图标 |
||||
prefixIcon: String, |
||||
// aria-label 对应值 |
||||
label: String, |
||||
// 是否无边框,该值为 true 时只有底部边框 |
||||
noBorder: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
|
||||
// 原生属性 |
||||
name: String, |
||||
placeholder: { |
||||
type: String, |
||||
default () { |
||||
return t('ans.input.placeholder') |
||||
} |
||||
}, |
||||
disabled: Boolean, |
||||
readonly: Boolean, |
||||
autofocus: Boolean, |
||||
autocomplete: { |
||||
type: String, |
||||
default: 'off' |
||||
}, |
||||
maxlength: Number, |
||||
tabindex: Number, |
||||
|
||||
// 文本域是否可以拉伸 |
||||
resize: String, |
||||
// 文本域默认行数,仅在 textarea 类型下有效 |
||||
rows: { |
||||
type: Number, |
||||
default: 2 |
||||
}, |
||||
// 自适应内容高度,只当 type='textarea' 时有效,可传入对象,如: {minRows: 2, maxRows: 6} |
||||
autosize: { |
||||
type: [Boolean, Object], |
||||
default: false |
||||
}, |
||||
// textarea 原生属性,与 submit 相关 |
||||
minlength: Number |
||||
}, |
||||
computed: { |
||||
// 当前文本域样式 |
||||
textareaStyle () { |
||||
return Object.assign({}, this.textareaCalcStyle, { resize: this.resize }) |
||||
}, |
||||
// 是否显示清除图标 |
||||
showClear () { |
||||
return !this.disabled && this.clearable && |
||||
this.currentValue && (this.focused || this.hovering) |
||||
}, |
||||
// 是否存在前置图标 |
||||
hasPrefix () { |
||||
return this.prefixIcon || this.$slots.prefix |
||||
}, |
||||
// 是否存在后置图标 |
||||
hasSuffix () { |
||||
return this.suffixIcon || this.clearable || this.$slots.suffix |
||||
}, |
||||
isNotTextarea () { |
||||
return this.type !== 'textarea' |
||||
} |
||||
}, |
||||
methods: { |
||||
/** |
||||
* 处理中文输入状态 |
||||
*/ |
||||
handleComposition (event) { |
||||
if (event.type === 'compositionend') { |
||||
this.isOnComposition = false |
||||
} else { |
||||
this.isOnComposition = true |
||||
} |
||||
}, |
||||
/** |
||||
* 按下回车键时触发 |
||||
*/ |
||||
handleEnter (e) { |
||||
this.$emit('on-enterkey', e) |
||||
}, |
||||
/** |
||||
* 点击触发 |
||||
*/ |
||||
handleClick (e) { |
||||
this.$emit('on-click', e) |
||||
}, |
||||
/** |
||||
* 输入框失去焦点时触发 |
||||
*/ |
||||
handleBlur (e) { |
||||
this.focused = false |
||||
this.$emit('on-blur', e) |
||||
if (!findComponentUpward(this, ['xDatepicker', 'xTimePicker', 'xCascader', 'xSelect'])) { |
||||
this.dispatch('xFormItem', 'on-form-blur', this.currentValue) |
||||
} |
||||
}, |
||||
/** |
||||
* 输入框聚焦时触发 |
||||
*/ |
||||
handleFocus (e) { |
||||
this.focused = true |
||||
this.$emit('on-focus', e) |
||||
}, |
||||
/** |
||||
* 用户输入时触发 |
||||
*/ |
||||
hanldleInput (e) { |
||||
if (this.isOnComposition) return |
||||
this.$emit('input', e.target.value) |
||||
}, |
||||
/** |
||||
* 数据改变时触发 |
||||
*/ |
||||
handleChange (e) { |
||||
this.$emit('on-change', e.target.value) |
||||
}, |
||||
/** |
||||
* 点击前置/后置图标时触发 |
||||
*/ |
||||
handleIconClick (e) { |
||||
if (this.showClear) { |
||||
this.clear() |
||||
this.$emit('on-clear') |
||||
} |
||||
this.$emit('on-click-icon', e) |
||||
}, |
||||
/** |
||||
* 获得焦点 |
||||
*/ |
||||
focus () { |
||||
(this.$refs.input || this.$refs.textarea).focus() |
||||
}, |
||||
/** |
||||
* 失去焦点 |
||||
*/ |
||||
blur () { |
||||
(this.$refs.input || this.$refs.textarea).blur() |
||||
}, |
||||
/** |
||||
* 清空输入 |
||||
*/ |
||||
clear () { |
||||
this.setCurrentValue('') |
||||
this.focus() |
||||
}, |
||||
/** |
||||
* 文本域自适应 |
||||
*/ |
||||
resizeTextarea () { |
||||
var { autosize, isNotTextarea } = this |
||||
if (isNotTextarea) return |
||||
if (!autosize) { |
||||
this.textareaCalcStyle = { |
||||
height: 'auto', |
||||
minHeight: calcTextareaHeight(this.$refs.textarea).minHeight |
||||
} |
||||
return |
||||
} |
||||
const { minRows, maxRows } = autosize |
||||
this.textareaCalcStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows) |
||||
}, |
||||
/** |
||||
* 设置当前输入 |
||||
*/ |
||||
setCurrentValue (value) { |
||||
if (value === this.currentValue) return |
||||
this.$nextTick(_ => { |
||||
this.resizeTextarea() |
||||
}) |
||||
this.currentValue = value |
||||
} |
||||
}, |
||||
watch: { |
||||
value (val, oldValue) { |
||||
this.setCurrentValue(val) |
||||
}, |
||||
currentValue () { |
||||
this.resizeTextarea() |
||||
} |
||||
}, |
||||
mounted () { |
||||
if (this.autofocus) { |
||||
this.focus() |
||||
} |
||||
this.resizeTextarea() |
||||
if (this.hasPrefix) { |
||||
const left = this.$slots.prepend ? `${this.$refs.prepend.clientWidth + 10}px` : '10px' |
||||
this.prefixStyles = { left } |
||||
} |
||||
if (this.hasSuffix) { |
||||
const right = this.$slots.append ? `${this.$refs.append.clientWidth + 10}px` : '10px' |
||||
this.suffixStyles = { right } |
||||
} |
||||
} |
||||
} |
||||
</script> |
@ -1,105 +0,0 @@
|
||||
let hiddenTextarea |
||||
|
||||
const HIDDEN_STYLE = ` |
||||
height:0 !important; |
||||
visibility:hidden !important; |
||||
overflow:hidden !important; |
||||
position:absolute !important; |
||||
z-index:-1000 !important; |
||||
top:0 !important; |
||||
right:0 !important |
||||
` |
||||
|
||||
const CONTEXT_STYLE = [ |
||||
'letter-spacing', |
||||
'line-height', |
||||
'padding-top', |
||||
'padding-bottom', |
||||
'font-family', |
||||
'font-weight', |
||||
'font-size', |
||||
'text-rendering', |
||||
'text-transform', |
||||
'width', |
||||
'text-indent', |
||||
'padding-left', |
||||
'padding-right', |
||||
'border-width', |
||||
'box-sizing' |
||||
] |
||||
|
||||
function calculateNodeStyling (targetElement) { |
||||
const style = window.getComputedStyle(targetElement) |
||||
|
||||
const boxSizing = style.getPropertyValue('box-sizing') |
||||
|
||||
const paddingSize = ( |
||||
parseFloat(style.getPropertyValue('padding-bottom')) + |
||||
parseFloat(style.getPropertyValue('padding-top')) |
||||
) |
||||
|
||||
const borderSize = ( |
||||
parseFloat(style.getPropertyValue('border-bottom-width')) + |
||||
parseFloat(style.getPropertyValue('border-top-width')) |
||||
) |
||||
|
||||
const contextStyle = CONTEXT_STYLE |
||||
.map(name => `${name}:${style.getPropertyValue(name)}`) |
||||
.join(';') |
||||
|
||||
return { contextStyle, paddingSize, borderSize, boxSizing } |
||||
} |
||||
|
||||
export default function calcTextareaHeight ( |
||||
targetElement, |
||||
minRows = 1, |
||||
maxRows = null |
||||
) { |
||||
let { |
||||
paddingSize, |
||||
borderSize, |
||||
boxSizing, |
||||
contextStyle |
||||
} = calculateNodeStyling(targetElement) |
||||
|
||||
if (!hiddenTextarea) { |
||||
hiddenTextarea = document.createElement('textarea') |
||||
hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`) |
||||
hiddenTextarea.value = targetElement.value || targetElement.placeholder || '' |
||||
document.body.appendChild(hiddenTextarea) |
||||
} |
||||
|
||||
let height = hiddenTextarea.scrollHeight |
||||
const result = {} |
||||
|
||||
if (boxSizing === 'border-box') { |
||||
height = height + borderSize |
||||
} else if (boxSizing === 'content-box') { |
||||
height = height - paddingSize |
||||
} |
||||
|
||||
hiddenTextarea.value = '' |
||||
let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize |
||||
|
||||
if (minRows !== null) { |
||||
let minHeight = singleRowHeight * minRows |
||||
if (boxSizing === 'border-box') { |
||||
minHeight = minHeight + paddingSize + borderSize |
||||
} |
||||
height = Math.max(minHeight, height) |
||||
result.minHeight = `${minHeight}px` |
||||
} |
||||
if (maxRows !== null) { |
||||
let maxHeight = singleRowHeight * maxRows |
||||
if (boxSizing === 'border-box') { |
||||
maxHeight = maxHeight + paddingSize + borderSize |
||||
} |
||||
height = Math.min(maxHeight, height) |
||||
} |
||||
result.height = `${height}px` |
||||
|
||||
hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea) |
||||
hiddenTextarea = null |
||||
|
||||
return result |
||||
} |
@ -1,24 +0,0 @@
|
||||
## Pagination |
||||
|
||||
当数据量过多时,使用分页分解数据。 |
||||
|
||||
### Pagination props |
||||
|
||||
属性 | 说明 | 类型 | 可选值 | 默认值 |
||||
--- | --- | --- | --- | --- |
||||
current | 当前页 | Number | - | 1 |
||||
total | 总数据条数 | Number | - | 0 |
||||
page-size | 每页数据条数 | Number | - | 10 |
||||
page-size-options | 每页条数切换的配置 | Array | - | [10, 20, 30, 40, 50] |
||||
pager-count | 页码按钮的数量,当总页数超过该值时会折叠(奇数) | Number | - | 7 |
||||
show-total | 是否显示总条数 | Boolean | - | false |
||||
show-elevator | 是否显示跳转页 | Boolean | - | false |
||||
show-sizer | 是否显示每页条数切换栏 | Boolean | - | false |
||||
simple | 是否使用简洁版 | Boolean | - | false |
||||
small | 是否显示迷你版 | Boolean | - | false |
||||
|
||||
#### Pagination events |
||||
|
||||
事件名称 | 说明 | 返回值 |
||||
--- | --- | --- |
||||
on-change | 页码切换时触发 | 返回当前页码 |
@ -1,64 +0,0 @@
|
||||
<template> |
||||
<div> |
||||
<section class="demo-section"> |
||||
<h4>基本用法</h4> |
||||
<div> |
||||
<x-page :current="current" :total="total" @on-change="pageChanged"></x-page> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>显示总条数</h4> |
||||
<div> |
||||
<x-page :current="current" :total="total" show-total @on-change="pageChanged"></x-page> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>跳转</h4> |
||||
<div> |
||||
<x-page :current="current" :total="total" show-total show-elevator @on-change="pageChanged"></x-page> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>每页条数</h4> |
||||
<div> |
||||
<x-page :current="current" :total="total" show-total show-elevator show-sizer @on-change="pageChanged"></x-page> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>无边框</h4> |
||||
<div> |
||||
<x-page :current="current" :total="total" small @on-change="pageChanged"></x-page> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>迷你版</h4> |
||||
<div> |
||||
<x-page :current="current" :total="total" simple @on-change="pageChanged"></x-page> |
||||
</div> |
||||
</section> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { xPage } from '../src' |
||||
|
||||
export default { |
||||
data: function () { |
||||
return { |
||||
current: 3, |
||||
total: 100 |
||||
} |
||||
}, |
||||
methods: { |
||||
pageChanged (v) { |
||||
console.log(v) |
||||
} |
||||
}, |
||||
components: { xPage }, |
||||
mounted () { |
||||
|
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"></style> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,10 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,3 +0,0 @@
|
||||
import xPage from './source/Page.vue' |
||||
|
||||
export { xPage } |
@ -1,432 +0,0 @@
|
||||
<template> |
||||
<div :class="wrapClasses"> |
||||
<ul :class="simpleClasses" v-if="simple"> |
||||
<li :class="prevClasses" @click="prevPage"> |
||||
<i class="ans-icon-arrow-left"></i> |
||||
</li> |
||||
<div :class="simpleContentClasses"> |
||||
<input |
||||
type="text" |
||||
class="page-input" |
||||
:value="currentPage" |
||||
autocomplete="off" |
||||
@keydown="keyDown" |
||||
@keyup="keyUp" |
||||
@change="keyUp"> |
||||
<span>/</span> |
||||
<span>{{lastPage}}</span> |
||||
</div> |
||||
<li :class="nextClasses" @click="nextPage"> |
||||
<i class="ans-icon-arrow-right"></i> |
||||
</li> |
||||
</ul> |
||||
|
||||
<ul :class="normalClasses" v-else> |
||||
<!-- `共100条` --> |
||||
<li class="data-show" v-if="showTotal"> |
||||
<span>{{t('ans.page.total', { total })}}</span> |
||||
</li> |
||||
<!-- `<` --> |
||||
<li :class="prevClasses" @click="prevPage"> |
||||
<i class="ans-icon-arrow-left"></i> |
||||
</li> |
||||
<!-- `1` --> |
||||
<li |
||||
v-if="lastPage > 0" |
||||
class="number" |
||||
:class="{active: currentPage === 1}" |
||||
@click="changePage(1)">1</li> |
||||
<!-- `...` --> |
||||
<li |
||||
v-if="showPrevMore" |
||||
class="number" |
||||
@click="fastPrev" |
||||
@mouseenter="onMouseenter('left')" |
||||
@mouseleave="quickPrevIconClass = 'ans-icon-more'"> |
||||
<i :class="quickPrevIconClass"></i> |
||||
</li> |
||||
<!-- `2 3 4...` --> |
||||
<li |
||||
class="number" |
||||
:class="{ active: currentPage === item }" |
||||
v-for="item in pages" |
||||
:key="getKey(item)" |
||||
@click="changePage(item)">{{item}}</li> |
||||
<!-- `...` --> |
||||
<li |
||||
v-if="showNextMore" |
||||
class="number" |
||||
@click="fastNext" |
||||
@mouseenter="onMouseenter('right')" |
||||
@mouseleave="quickNextIconClass = 'ans-icon-more'"> |
||||
<i :class="quickNextIconClass"></i> |
||||
</li> |
||||
<!-- 最后一页 --> |
||||
<li |
||||
v-if="lastPage > 1" |
||||
class="number" |
||||
:class="{ active: currentPage === lastPage }" |
||||
@click="changePage(lastPage)">{{lastPage}}</li> |
||||
<!-- `>` --> |
||||
<li :class="nextClasses" @click="nextPage"> |
||||
<i class="ans-icon-arrow-right"></i> |
||||
</li> |
||||
<!-- 每页条数 --> |
||||
<li class="sizer" v-if="showSizer"> |
||||
<x-select v-model="currentPageSize" @on-visible-change="handleSizerDropdown"> |
||||
<span |
||||
class="trigger" |
||||
slot="trigger" |
||||
slot-scope="{ selectedModel }"> |
||||
<span>{{selectedModel && selectedModel.label}}</span> |
||||
<i class="ans-icon-arrow-down arrow-down" :class="{reverse: sizerDropdownVisible}"></i> |
||||
</span> |
||||
<x-option |
||||
v-for="size in pageSizeOptions" |
||||
:key="size" |
||||
:value="size" |
||||
:label="`${size}${t('ans.page.pagesize')}`"> |
||||
</x-option> |
||||
</x-select> |
||||
</li> |
||||
<!-- `跳转至 页` --> |
||||
<li class="jump" v-if="showElevator"> |
||||
<span>{{t('ans.page.goto')}}</span> |
||||
<input type="text" class="page-input" autocomplete="off" @keyup.enter="jumpPage"> |
||||
<span>{{t('ans.page.pageClassifier')}}</span> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { LIB_NAME, Locale } from '../../../../src/util' |
||||
import { xSelect, xOption } from '../../../vue-select/src' |
||||
|
||||
const prefixCls = `${LIB_NAME}-page` |
||||
|
||||
// 清除 ul 元素内的空文本 |
||||
const deleteSpace = node => { |
||||
let childs = node.childNodes |
||||
for (let i = 0; i < childs.length; i++) { |
||||
if (childs[i].nodeType === 3 && /^\s+$/.test(childs[i].nodeValue)) { |
||||
node.removeChild(childs[i]) |
||||
} |
||||
} |
||||
} |
||||
|
||||
export default { |
||||
name: 'xPage', |
||||
|
||||
components: { xSelect, xOption }, |
||||
|
||||
mixins: [Locale], |
||||
|
||||
props: { |
||||
current: { |
||||
type: Number, |
||||
default: 1 |
||||
}, |
||||
|
||||
total: { |
||||
type: Number, |
||||
default: 0 |
||||
}, |
||||
|
||||
pageSize: { |
||||
type: Number, |
||||
default: 10 |
||||
}, |
||||
|
||||
pageSizeOptions: { |
||||
type: Array, |
||||
default () { |
||||
return [10, 20, 30, 40, 50] |
||||
} |
||||
}, |
||||
|
||||
pagerCount: { |
||||
type: Number, |
||||
default: 7, |
||||
validator (v) { |
||||
return v % 2 === 1 |
||||
} |
||||
}, |
||||
|
||||
showTotal: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
|
||||
showElevator: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
|
||||
showSizer: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
|
||||
simple: { |
||||
type: Boolean, |
||||
default: false |
||||
}, |
||||
|
||||
small: { |
||||
type: Boolean, |
||||
default: false |
||||
} |
||||
}, |
||||
|
||||
data () { |
||||
return { |
||||
currentPage: this.current, |
||||
currentPageSize: this.pageSize, |
||||
quickPrevIconClass: 'ans-icon-more', |
||||
quickNextIconClass: 'ans-icon-more', |
||||
sizerDropdownVisible: false |
||||
} |
||||
}, |
||||
|
||||
computed: { |
||||
lastPage () { |
||||
const lastPage = Math.ceil(this.total / this.currentPageSize) |
||||
return lastPage === 0 ? 1 : lastPage |
||||
}, |
||||
|
||||
showPrevMore () { |
||||
const { pagerCount, currentPage, lastPage } = this |
||||
const halfPagerCount = (pagerCount - 1) / 2 |
||||
return lastPage > pagerCount && currentPage > pagerCount - halfPagerCount |
||||
}, |
||||
|
||||
showNextMore () { |
||||
const { pagerCount, currentPage, lastPage } = this |
||||
const halfPagerCount = (pagerCount - 1) / 2 |
||||
return lastPage > pagerCount && currentPage < lastPage - halfPagerCount |
||||
}, |
||||
|
||||
// 当前的分页页码数组 |
||||
pages () { |
||||
const { pagerCount, currentPage, lastPage, showPrevMore, showNextMore } = this |
||||
|
||||
const array = [] |
||||
|
||||
if (showPrevMore && !showNextMore) { |
||||
const startPage = lastPage - (pagerCount - 2) |
||||
for (let i = startPage; i < lastPage; i++) { |
||||
array.push(i) |
||||
} |
||||
} else if (!showPrevMore && showNextMore) { |
||||
for (let i = 2; i < pagerCount; i++) { |
||||
array.push(i) |
||||
} |
||||
} else if (showPrevMore && showNextMore) { |
||||
const offset = Math.floor(pagerCount / 2) - 1 |
||||
for (let i = currentPage - offset; i <= currentPage + offset; i++) { |
||||
array.push(i) |
||||
} |
||||
} else { |
||||
for (let i = 2; i < lastPage; i++) { |
||||
array.push(i) |
||||
} |
||||
} |
||||
|
||||
return array |
||||
}, |
||||
|
||||
wrapClasses () { |
||||
return [ |
||||
`${prefixCls}` |
||||
] |
||||
}, |
||||
|
||||
simpleClasses () { |
||||
return `${prefixCls}-simple` |
||||
}, |
||||
|
||||
simpleContentClasses () { |
||||
return `${prefixCls}-simple-content` |
||||
}, |
||||
|
||||
normalClasses () { |
||||
return [ |
||||
`${prefixCls}-normal`, |
||||
{ small: this.small } |
||||
] |
||||
}, |
||||
|
||||
prevClasses () { |
||||
return [ |
||||
`${prefixCls}-prev`, |
||||
{ |
||||
[`${prefixCls}-disabled`]: this.currentPage === 1 |
||||
} |
||||
] |
||||
}, |
||||
|
||||
nextClasses () { |
||||
return [ |
||||
`${prefixCls}-next`, |
||||
{ |
||||
[`${prefixCls}-disabled`]: this.currentPage === this.lastPage |
||||
} |
||||
] |
||||
} |
||||
}, |
||||
|
||||
watch: { |
||||
current (v) { |
||||
this.currentPage = v |
||||
}, |
||||
|
||||
currentPageSize () { |
||||
if (this.currentPage > this.lastPage) { |
||||
this.changePage(this.lastPage) |
||||
} |
||||
}, |
||||
|
||||
showPrevMore (v) { |
||||
if (!v) { |
||||
this.quickPrevIconClass = 'ans-icon-more' |
||||
} |
||||
}, |
||||
|
||||
showNextMore (v) { |
||||
if (!v) { |
||||
this.quickNextIconClass = 'ans-icon-more' |
||||
} |
||||
} |
||||
}, |
||||
|
||||
methods: { |
||||
handleSizerDropdown (visible) { |
||||
this.sizerDropdownVisible = visible |
||||
}, |
||||
|
||||
// 防止缓存 bug |
||||
getKey () { |
||||
return Math.random() |
||||
}, |
||||
|
||||
changePage (page) { |
||||
if (this.currentPage !== page) { |
||||
this.currentPage = page |
||||
this.$emit('on-change', page) |
||||
} |
||||
}, |
||||
|
||||
prevPage () { |
||||
const _current = this.currentPage |
||||
|
||||
if (_current <= 1) { |
||||
return |
||||
} |
||||
|
||||
this.changePage(_current - 1) |
||||
}, |
||||
|
||||
nextPage () { |
||||
const _current = this.currentPage |
||||
|
||||
if (_current >= this.lastPage) { |
||||
return |
||||
} |
||||
|
||||
this.changePage(_current + 1) |
||||
}, |
||||
|
||||
fastPrev () { |
||||
const _page = this.currentPage - 5 |
||||
if (_page > 0) { |
||||
this.changePage(_page) |
||||
} else { |
||||
this.changePage(1) |
||||
} |
||||
}, |
||||
|
||||
fastNext () { |
||||
const _page = this.currentPage + 5 |
||||
if (_page > this.lastPage) { |
||||
this.changePage(this.lastPage) |
||||
} else { |
||||
this.changePage(_page) |
||||
} |
||||
}, |
||||
|
||||
keyDown (e) { |
||||
const _key = e.keyCode |
||||
const _condition = (_key >= 48 && _key <= 57) || (_key >= 96 && _key <= 105) || _key === 8 |
||||
|
||||
if (!_condition) { |
||||
e.preventDefault() |
||||
} |
||||
}, |
||||
|
||||
keyUp (e) { |
||||
const _key = e.keyCode |
||||
const _value = parseInt(e.target.value) |
||||
|
||||
if (_key === 37 || _key === 38) { |
||||
this.prevPage() |
||||
} else if (_key === 39 || _key === 40) { |
||||
this.nextPage() |
||||
} else if (_key === 13) { |
||||
let _page = 1 |
||||
|
||||
if (_value > this.lastPage) { |
||||
_page = this.lastPage |
||||
} else if (_value <= 0 || !_value) { |
||||
_page = 1 |
||||
} else { |
||||
_page = _value |
||||
} |
||||
|
||||
e.target.value = _page |
||||
this.changePage(_page) |
||||
} |
||||
}, |
||||
|
||||
jumpPage (e) { |
||||
let _value = parseInt(e.target.value.trim()) |
||||
let _page = 0 |
||||
|
||||
if (!_value || _value === this.currentPage) { |
||||
return |
||||
} |
||||
|
||||
if (_value > this.lastPage) { |
||||
_page = this.lastPage |
||||
} else { |
||||
_page = _value |
||||
} |
||||
|
||||
e.target.value = '' |
||||
this.changePage(_page) |
||||
}, |
||||
|
||||
onMouseenter (direction) { |
||||
if (direction === 'left') { |
||||
this.quickPrevIconClass = 'ans-icon-arrow-to-left' |
||||
} else { |
||||
this.quickNextIconClass = 'ans-icon-arrow-to-right' |
||||
} |
||||
}, |
||||
|
||||
cleanSpace () { |
||||
document.querySelectorAll(`.${prefixCls} ul`).forEach(item => { |
||||
deleteSpace(item) |
||||
}) |
||||
} |
||||
}, |
||||
|
||||
updated () { |
||||
this.cleanSpace() |
||||
}, |
||||
|
||||
mounted () { |
||||
this.cleanSpace() |
||||
} |
||||
} |
||||
</script> |
@ -1,38 +0,0 @@
|
||||
## Poptip |
||||
|
||||
以卡片的形式承载了更多的内容,比如链接、表格、按钮等。 |
||||
|
||||
### Poptip props |
||||
|
||||
属性 | 说明 | 类型 | 可选值 | 默认值 |
||||
--- | --- | --- | --- | --- |
||||
trigger | 触发方式 | String | hover, click, focus, 在 confirm 模式下,只有 click 有效 | click |
||||
placement | 出现位置 | String | 详见popper.js文档 | top |
||||
title | 标题 | String | — | — |
||||
content | 显示的正文内容,只在非 confirm 模式下有效 | String | — | — |
||||
disabled | 是否禁用 | Boolean | — | — |
||||
width | 宽度 | Number | — | — |
||||
visible-arrow | 是否显示箭头 | Boolean | — | true |
||||
confirm | 是否开启对话框模式 | Boolean | — | false |
||||
ok-text | 确定按钮的文字,只在 confirm 模式下有效 | String | — | 确定 |
||||
cancel-text | 取消按钮的文字,只在 confirm 模式下有效 | String | — | 取消 |
||||
distance | 弹出层与触发元素的距离 | Number | — | 5 |
||||
popper-class | 弹出层自定义样式 | String | — | — |
||||
append-to-body | 弹出层是否插入 body | Boolean | — | false |
||||
position-fixed | 弹出层是否 fixed 定位 | Boolean | — | false |
||||
viewport | 弹出层是否基于 viewport 定位 | Boolean | — | false |
||||
popper-options | Popper.js 的可选项 | Object | — | — |
||||
|
||||
### Poptip slots |
||||
|
||||
名称 | 说明 |
||||
--- | --- |
||||
default | 内嵌 HTML 文本 |
||||
reference | 触发元素 |
||||
|
||||
### Poptip events |
||||
|
||||
事件名称 | 说明 | 回调参数 |
||||
--- | --- | --- |
||||
on-ok | 点击确定的回调 | — |
||||
on-cancel | 点击取消的回调 | — |
@ -1,91 +0,0 @@
|
||||
<template> |
||||
<div> |
||||
<section class="demo-section"> |
||||
<h4>基础用法</h4> |
||||
<div> |
||||
<x-poptip |
||||
ref="poptip1" |
||||
placement="top-start" |
||||
title="标题" |
||||
:width="200" |
||||
trigger="hover" |
||||
content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。"> |
||||
</x-poptip> |
||||
|
||||
<x-poptip |
||||
ref="poptip2" |
||||
placement="bottom-start" |
||||
:width="200" |
||||
trigger="click" |
||||
:visible-arrow="false" |
||||
content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。"> |
||||
</x-poptip> |
||||
|
||||
<x-button v-poptip:poptip1>hover 激活</x-button> |
||||
<x-button v-poptip:poptip2>click 激活</x-button> |
||||
|
||||
<x-poptip |
||||
placement="right" |
||||
title="标题" |
||||
:width="200" |
||||
confirm |
||||
trigger="focus" |
||||
content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。"> |
||||
<x-button slot="reference">focus 激活</x-button> |
||||
<span slot="title">我是title</span> |
||||
</x-poptip> |
||||
|
||||
<x-poptip |
||||
placement="right" |
||||
title="标题" |
||||
:width="200" |
||||
confirm |
||||
trigger="manual" |
||||
:ok-text="'yes'" |
||||
:cancel-text="'no'" |
||||
content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。" |
||||
v-model="visible4"> |
||||
<x-button slot="reference" @click="visible4 = !visible4">manual 激活</x-button> |
||||
</x-poptip> |
||||
</div> |
||||
</section> |
||||
<section class="demo-section"> |
||||
<h4>嵌套操作</h4> |
||||
<div> |
||||
<x-poptip |
||||
ref="poptip5" |
||||
placement="bottom-start" |
||||
:width="180" |
||||
v-model="visible2"> |
||||
<p>这是一段内容这是一段内容确定删除吗?</p> |
||||
<div style="text-align: right; margin: 0"> |
||||
<x-button type="text" @click="visible2 = false">取消</x-button> |
||||
<x-button type="primary" @click="visible2 = false">确定</x-button> |
||||
</div> |
||||
</x-poptip> |
||||
<x-button v-poptip:poptip5>删除</x-button> |
||||
</div> |
||||
</section> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { xButton } from '../../vue-button/src' |
||||
import { xPoptip } from '../src' |
||||
|
||||
export default { |
||||
components: { xButton, xPoptip }, |
||||
data () { |
||||
return { |
||||
visible2: false, |
||||
visible3: false, |
||||
visible4: false |
||||
} |
||||
}, |
||||
methods: { |
||||
|
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss"></style> |
@ -1,16 +0,0 @@
|
||||
<!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> |
@ -1,13 +0,0 @@
|
||||
import Vue from 'vue' |
||||
import App from './app.vue' |
||||
import { xPoptip } from '../src' |
||||
|
||||
Vue.use(xPoptip) |
||||
|
||||
new Vue({ |
||||
el: '#app', |
||||
render: h => h(App), |
||||
mounted () { |
||||
console.log('success') |
||||
} |
||||
}) |
@ -1,13 +0,0 @@
|
||||
import xPoptip from './source/Poptip.vue' |
||||
import directive from './source/directive' |
||||
|
||||
xPoptip.directive = directive |
||||
|
||||
/* istanbul ignore next */ |
||||
xPoptip.install = Vue => { |
||||
Vue.directive('poptip', directive) |
||||
} |
||||
|
||||
export { |
||||
xPoptip |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue